ambrop7 14 лет назад
Родитель
Сommit
7d67a723c2

+ 1 - 0
blog_channels.txt

@@ -20,6 +20,7 @@ ncd_blocker 4
 ncd_ip_in_network 4
 ncd_run 4
 ncd_runonce 4
+ncd_daemon 4
 ncd_spawn 4
 ncd_call 4
 ncd_imperative 4

+ 4 - 0
generated/blog_channel_ncd_daemon.h

@@ -0,0 +1,4 @@
+#ifdef BLOG_CURRENT_CHANNEL
+#undef BLOG_CURRENT_CHANNEL
+#endif
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_daemon

+ 79 - 78
generated/blog_channels_defines.h

@@ -20,81 +20,82 @@
 #define BLOG_CHANNEL_ncd_ip_in_network 19
 #define BLOG_CHANNEL_ncd_run 20
 #define BLOG_CHANNEL_ncd_runonce 21
-#define BLOG_CHANNEL_ncd_spawn 22
-#define BLOG_CHANNEL_ncd_call 23
-#define BLOG_CHANNEL_ncd_imperative 24
-#define BLOG_CHANNEL_ncd_ref 25
-#define BLOG_CHANNEL_ncd_index 26
-#define BLOG_CHANNEL_ncd_alias 27
-#define BLOG_CHANNEL_ncd_process_manager 28
-#define BLOG_CHANNEL_ncd_ondemand 29
-#define BLOG_CHANNEL_ncd_foreach 30
-#define BLOG_CHANNEL_ncd_choose 31
-#define BLOG_CHANNEL_ncd_net_backend_waitdevice 32
-#define BLOG_CHANNEL_ncd_net_backend_waitlink 33
-#define BLOG_CHANNEL_ncd_net_backend_badvpn 34
-#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 35
-#define BLOG_CHANNEL_ncd_net_backend_rfkill 36
-#define BLOG_CHANNEL_ncd_net_up 37
-#define BLOG_CHANNEL_ncd_net_dns 38
-#define BLOG_CHANNEL_ncd_net_iptables 39
-#define BLOG_CHANNEL_ncd_net_ipv4_addr 40
-#define BLOG_CHANNEL_ncd_net_ipv4_route 41
-#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 42
-#define BLOG_CHANNEL_ncd_net_ipv4_arp_probe 43
-#define BLOG_CHANNEL_ncd_net_watch_interfaces 44
-#define BLOG_CHANNEL_ncd_sys_watch_input 45
-#define BLOG_CHANNEL_ncd_sys_watch_usb 46
-#define BLOG_CHANNEL_ncd_sys_evdev 47
-#define BLOG_CHANNEL_ncd_sys_watch_directory 48
-#define BLOG_CHANNEL_StreamPeerIO 49
-#define BLOG_CHANNEL_DatagramPeerIO 50
-#define BLOG_CHANNEL_BReactor 51
-#define BLOG_CHANNEL_BSignal 52
-#define BLOG_CHANNEL_FragmentProtoAssembler 53
-#define BLOG_CHANNEL_BPredicate 54
-#define BLOG_CHANNEL_ServerConnection 55
-#define BLOG_CHANNEL_Listener 56
-#define BLOG_CHANNEL_DataProto 57
-#define BLOG_CHANNEL_FrameDecider 58
-#define BLOG_CHANNEL_BSocksClient 59
-#define BLOG_CHANNEL_BDHCPClientCore 60
-#define BLOG_CHANNEL_BDHCPClient 61
-#define BLOG_CHANNEL_NCDIfConfig 62
-#define BLOG_CHANNEL_BUnixSignal 63
-#define BLOG_CHANNEL_BProcess 64
-#define BLOG_CHANNEL_PRStreamSink 65
-#define BLOG_CHANNEL_PRStreamSource 66
-#define BLOG_CHANNEL_PacketProtoDecoder 67
-#define BLOG_CHANNEL_DPRelay 68
-#define BLOG_CHANNEL_BThreadWork 69
-#define BLOG_CHANNEL_DPReceive 70
-#define BLOG_CHANNEL_BInputProcess 71
-#define BLOG_CHANNEL_NCDUdevMonitorParser 72
-#define BLOG_CHANNEL_NCDUdevMonitor 73
-#define BLOG_CHANNEL_NCDUdevCache 74
-#define BLOG_CHANNEL_NCDUdevManager 75
-#define BLOG_CHANNEL_BTime 76
-#define BLOG_CHANNEL_BEncryption 77
-#define BLOG_CHANNEL_SPProtoDecoder 78
-#define BLOG_CHANNEL_LineBuffer 79
-#define BLOG_CHANNEL_BTap 80
-#define BLOG_CHANNEL_lwip 81
-#define BLOG_CHANNEL_NCDConfigTokenizer 82
-#define BLOG_CHANNEL_NCDConfigParser 83
-#define BLOG_CHANNEL_nsskey 84
-#define BLOG_CHANNEL_addr 85
-#define BLOG_CHANNEL_PasswordListener 86
-#define BLOG_CHANNEL_NCDInterfaceMonitor 87
-#define BLOG_CHANNEL_NCDRfkillMonitor 88
-#define BLOG_CHANNEL_udpgw 89
-#define BLOG_CHANNEL_UdpGwClient 90
-#define BLOG_CHANNEL_SocksUdpGwClient 91
-#define BLOG_CHANNEL_BNetwork 92
-#define BLOG_CHANNEL_BConnection 93
-#define BLOG_CHANNEL_BSSLConnection 94
-#define BLOG_CHANNEL_BDatagram 95
-#define BLOG_CHANNEL_PeerChat 96
-#define BLOG_CHANNEL_BArpProbe 97
-#define BLOG_CHANNEL_NCDModuleIndex 98
-#define BLOG_NUM_CHANNELS 99
+#define BLOG_CHANNEL_ncd_daemon 22
+#define BLOG_CHANNEL_ncd_spawn 23
+#define BLOG_CHANNEL_ncd_call 24
+#define BLOG_CHANNEL_ncd_imperative 25
+#define BLOG_CHANNEL_ncd_ref 26
+#define BLOG_CHANNEL_ncd_index 27
+#define BLOG_CHANNEL_ncd_alias 28
+#define BLOG_CHANNEL_ncd_process_manager 29
+#define BLOG_CHANNEL_ncd_ondemand 30
+#define BLOG_CHANNEL_ncd_foreach 31
+#define BLOG_CHANNEL_ncd_choose 32
+#define BLOG_CHANNEL_ncd_net_backend_waitdevice 33
+#define BLOG_CHANNEL_ncd_net_backend_waitlink 34
+#define BLOG_CHANNEL_ncd_net_backend_badvpn 35
+#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 36
+#define BLOG_CHANNEL_ncd_net_backend_rfkill 37
+#define BLOG_CHANNEL_ncd_net_up 38
+#define BLOG_CHANNEL_ncd_net_dns 39
+#define BLOG_CHANNEL_ncd_net_iptables 40
+#define BLOG_CHANNEL_ncd_net_ipv4_addr 41
+#define BLOG_CHANNEL_ncd_net_ipv4_route 42
+#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 43
+#define BLOG_CHANNEL_ncd_net_ipv4_arp_probe 44
+#define BLOG_CHANNEL_ncd_net_watch_interfaces 45
+#define BLOG_CHANNEL_ncd_sys_watch_input 46
+#define BLOG_CHANNEL_ncd_sys_watch_usb 47
+#define BLOG_CHANNEL_ncd_sys_evdev 48
+#define BLOG_CHANNEL_ncd_sys_watch_directory 49
+#define BLOG_CHANNEL_StreamPeerIO 50
+#define BLOG_CHANNEL_DatagramPeerIO 51
+#define BLOG_CHANNEL_BReactor 52
+#define BLOG_CHANNEL_BSignal 53
+#define BLOG_CHANNEL_FragmentProtoAssembler 54
+#define BLOG_CHANNEL_BPredicate 55
+#define BLOG_CHANNEL_ServerConnection 56
+#define BLOG_CHANNEL_Listener 57
+#define BLOG_CHANNEL_DataProto 58
+#define BLOG_CHANNEL_FrameDecider 59
+#define BLOG_CHANNEL_BSocksClient 60
+#define BLOG_CHANNEL_BDHCPClientCore 61
+#define BLOG_CHANNEL_BDHCPClient 62
+#define BLOG_CHANNEL_NCDIfConfig 63
+#define BLOG_CHANNEL_BUnixSignal 64
+#define BLOG_CHANNEL_BProcess 65
+#define BLOG_CHANNEL_PRStreamSink 66
+#define BLOG_CHANNEL_PRStreamSource 67
+#define BLOG_CHANNEL_PacketProtoDecoder 68
+#define BLOG_CHANNEL_DPRelay 69
+#define BLOG_CHANNEL_BThreadWork 70
+#define BLOG_CHANNEL_DPReceive 71
+#define BLOG_CHANNEL_BInputProcess 72
+#define BLOG_CHANNEL_NCDUdevMonitorParser 73
+#define BLOG_CHANNEL_NCDUdevMonitor 74
+#define BLOG_CHANNEL_NCDUdevCache 75
+#define BLOG_CHANNEL_NCDUdevManager 76
+#define BLOG_CHANNEL_BTime 77
+#define BLOG_CHANNEL_BEncryption 78
+#define BLOG_CHANNEL_SPProtoDecoder 79
+#define BLOG_CHANNEL_LineBuffer 80
+#define BLOG_CHANNEL_BTap 81
+#define BLOG_CHANNEL_lwip 82
+#define BLOG_CHANNEL_NCDConfigTokenizer 83
+#define BLOG_CHANNEL_NCDConfigParser 84
+#define BLOG_CHANNEL_nsskey 85
+#define BLOG_CHANNEL_addr 86
+#define BLOG_CHANNEL_PasswordListener 87
+#define BLOG_CHANNEL_NCDInterfaceMonitor 88
+#define BLOG_CHANNEL_NCDRfkillMonitor 89
+#define BLOG_CHANNEL_udpgw 90
+#define BLOG_CHANNEL_UdpGwClient 91
+#define BLOG_CHANNEL_SocksUdpGwClient 92
+#define BLOG_CHANNEL_BNetwork 93
+#define BLOG_CHANNEL_BConnection 94
+#define BLOG_CHANNEL_BSSLConnection 95
+#define BLOG_CHANNEL_BDatagram 96
+#define BLOG_CHANNEL_PeerChat 97
+#define BLOG_CHANNEL_BArpProbe 98
+#define BLOG_CHANNEL_NCDModuleIndex 99
+#define BLOG_NUM_CHANNELS 100

+ 1 - 0
generated/blog_channels_list.h

@@ -20,6 +20,7 @@
 {.name = "ncd_ip_in_network", .loglevel = 4},
 {.name = "ncd_run", .loglevel = 4},
 {.name = "ncd_runonce", .loglevel = 4},
+{.name = "ncd_daemon", .loglevel = 4},
 {.name = "ncd_spawn", .loglevel = 4},
 {.name = "ncd_call", .loglevel = 4},
 {.name = "ncd_imperative", .loglevel = 4},

+ 1 - 0
ncd/CMakeLists.txt

@@ -55,6 +55,7 @@ add_executable(badvpn-ncd
     modules/ip_in_network.c
     modules/run.c
     modules/runonce.c
+    modules/daemon.c
     modules/spawn.c
     modules/call.c
     modules/imperative.c

+ 274 - 0
ncd/modules/daemon.c

@@ -0,0 +1,274 @@
+/**
+ * @file daemon.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * @section DESCRIPTION
+ * 
+ * Runs a program in the background, restarting it if it crashes.
+ * On deinitialization, sends SIGTERM to the daemon and waits for it to terminate
+ * (unless it's crashed at the time).
+ * 
+ * Synopsis:
+ *   daemon(list(string) cmd)
+ * 
+ * Arguments:
+ *   cmd - Command for the daemon. The first element is the full path
+ *     to the executable, other elements are command line arguments (excluding
+ *     the zeroth argument).
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <misc/cmdline.h>
+#include <system/BProcess.h>
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_daemon.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+#define RETRY_TIME 10000
+
+#define STATE_RETRYING 1
+#define STATE_RUNNING 2
+#define STATE_RUNNING_DIE 3
+
+struct instance {
+    NCDModuleInst *i;
+    NCDValue *cmd_arg;
+    BTimer timer;
+    BProcess process;
+    int state;
+};
+
+static int build_cmdline (NCDModuleInst *i, NCDValue *cmd_arg, char **exec, CmdLine *cl);
+static void start_process (struct instance *o);
+static void timer_handler (struct instance *o);
+static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status);
+static void instance_free (struct instance *o);
+
+static int build_cmdline (NCDModuleInst *i, NCDValue *cmd_arg, char **exec, CmdLine *cl)
+{
+    if (NCDValue_Type(cmd_arg) != NCDVALUE_LIST) {
+        ModuleLog(i, BLOG_ERROR, "wrong type");
+        goto fail0;
+    }
+    
+    // read exec
+    NCDValue *exec_arg = NCDValue_ListFirst(cmd_arg);
+    if (!exec_arg) {
+        ModuleLog(i, BLOG_ERROR, "missing executable name");
+        goto fail0;
+    }
+    if (NCDValue_Type(exec_arg) != NCDVALUE_STRING) {
+        ModuleLog(i, BLOG_ERROR, "wrong type");
+        goto fail0;
+    }
+    if (!(*exec = strdup(NCDValue_StringValue(exec_arg)))) {
+        ModuleLog(i, BLOG_ERROR, "strdup failed");
+        goto fail0;
+    }
+    
+    // start cmdline
+    if (!CmdLine_Init(cl)) {
+        ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed");
+        goto fail1;
+    }
+    
+    // add header
+    if (!CmdLine_Append(cl, *exec)) {
+        ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed");
+        goto fail2;
+    }
+    
+    // add additional arguments
+    NCDValue *arg = exec_arg;
+    while (arg = NCDValue_ListNext(cmd_arg, arg)) {
+        if (NCDValue_Type(arg) != NCDVALUE_STRING) {
+            ModuleLog(i, BLOG_ERROR, "wrong type");
+            goto fail2;
+        }
+        
+        if (!CmdLine_Append(cl, NCDValue_StringValue(arg))) {
+            ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed");
+            goto fail2;
+        }
+    }
+    
+    // finish
+    if (!CmdLine_Finish(cl)) {
+        ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed");
+        goto fail2;
+    }
+    
+    return 1;
+    
+fail2:
+    CmdLine_Free(cl);
+fail1:
+    free(*exec);
+fail0:
+    return 0;
+}
+
+static void start_process (struct instance *o)
+{
+    // build cmdline
+    char *exec;
+    CmdLine cl;
+    if (!build_cmdline(o->i, o->cmd_arg, &exec, &cl)) {
+        goto fail;
+    }
+    
+    // start process
+    int res = BProcess_Init(&o->process, o->i->manager, (BProcess_handler)process_handler, o, exec, CmdLine_Get(&cl), NULL);
+    CmdLine_Free(&cl);
+    free(exec);
+    
+    if (!res) {
+        ModuleLog(o->i, BLOG_ERROR, "BProcess_Init failed");
+        goto fail;
+    }
+    
+    // set state running
+    o->state = STATE_RUNNING;
+    return;
+    
+fail:
+    // start timer
+    BReactor_SetTimer(o->i->reactor, &o->timer);
+    
+    // set state retrying
+    o->state = STATE_RETRYING;
+}
+
+static void timer_handler (struct instance *o)
+{
+    ASSERT(o->state == STATE_RETRYING)
+    
+    ModuleLog(o->i, BLOG_INFO, "restarting after crash");
+    
+    start_process(o);
+}
+
+static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status)
+{
+    ASSERT(o->state == STATE_RUNNING || o->state == STATE_RUNNING_DIE)
+    
+    // free process
+    BProcess_Free(&o->process);
+    
+    // if we were requested to die, die now
+    if (o->state == STATE_RUNNING_DIE) {
+        instance_free(o);
+        return;
+    }
+    
+    BLog(BLOG_ERROR, "daemon crashed");
+    
+    // start timer
+    BReactor_SetTimer(o->i->reactor, &o->timer);
+    
+    // set state retrying
+    o->state = STATE_RETRYING;
+}
+
+static void func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct instance *o = malloc(sizeof(*o));
+    if (!o) {
+        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
+        goto fail0;
+    }
+    NCDModuleInst_Backend_SetUser(i, o);
+    
+    // init arguments
+    o->i = i;
+    
+    // read arguments
+    if (!NCDValue_ListRead(i->args, 1, &o->cmd_arg)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    
+    // init timer
+    BTimer_Init(&o->timer, RETRY_TIME, (BTimer_handler)timer_handler, o);
+    
+    // signal up
+    NCDModuleInst_Backend_Up(i);
+    
+    // try starting process
+    start_process(o);
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void instance_free (struct instance *o)
+{
+    NCDModuleInst *i = o->i;
+    
+    // free timer
+    BReactor_RemoveTimer(o->i->reactor, &o->timer);
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    ASSERT(o->state != STATE_RUNNING_DIE)
+    
+    // if not running, die immediately
+    if (o->state == STATE_RETRYING) {
+        instance_free(o);
+        return;
+    }
+    
+    // request termination
+    BProcess_Terminate(&o->process);
+    
+    // set state running die
+    o->state = STATE_RUNNING_DIE;
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "daemon",
+        .func_new = func_new,
+        .func_die = func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_daemon = {
+    .modules = modules
+};

+ 2 - 0
ncd/modules/modules.h

@@ -44,6 +44,7 @@ extern const struct NCDModuleGroup ncdmodule_blocker;
 extern const struct NCDModuleGroup ncdmodule_ip_in_network;
 extern const struct NCDModuleGroup ncdmodule_run;
 extern const struct NCDModuleGroup ncdmodule_runonce;
+extern const struct NCDModuleGroup ncdmodule_daemon;
 extern const struct NCDModuleGroup ncdmodule_spawn;
 extern const struct NCDModuleGroup ncdmodule_call;
 extern const struct NCDModuleGroup ncdmodule_imperative;
@@ -96,6 +97,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_ip_in_network,
     &ncdmodule_run,
     &ncdmodule_runonce,
+    &ncdmodule_daemon,
     &ncdmodule_spawn,
     &ncdmodule_call,
     &ncdmodule_imperative,