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

+ 1 - 0
blog_channels.txt

@@ -23,6 +23,7 @@ ncd_runonce 4
 ncd_spawn 4
 ncd_call 4
 ncd_rimp_call 4
+ncd_imperative 4
 ncd_ref 4
 ncd_index 4
 ncd_alias 4

+ 4 - 0
generated/blog_channel_ncd_imperative.h

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

+ 76 - 75
generated/blog_channels_defines.h

@@ -23,78 +23,79 @@
 #define BLOG_CHANNEL_ncd_spawn 22
 #define BLOG_CHANNEL_ncd_call 23
 #define BLOG_CHANNEL_ncd_rimp_call 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_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

@@ -23,6 +23,7 @@
 {.name = "ncd_spawn", .loglevel = 4},
 {.name = "ncd_call", .loglevel = 4},
 {.name = "ncd_rimp_call", .loglevel = 4},
+{.name = "ncd_imperative", .loglevel = 4},
 {.name = "ncd_ref", .loglevel = 4},
 {.name = "ncd_index", .loglevel = 4},
 {.name = "ncd_alias", .loglevel = 4},

+ 1 - 0
ncd/CMakeLists.txt

@@ -58,6 +58,7 @@ add_executable(badvpn-ncd
     modules/spawn.c
     modules/call.c
     modules/rimp_call.c
+    modules/imperative.c
     modules/ref.c
     modules/index.c
     modules/alias.c

+ 340 - 0
ncd/modules/imperative.c

@@ -0,0 +1,340 @@
+/**
+ * @file imperative.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
+ * 
+ * Imperative statement.
+ * 
+ * Synopsis:
+ *   imperative(string init_template, list init_args, string deinit_template, list deinit_args, string deinit_timeout)
+ * 
+ * Description:
+ *   Does the following, in order:
+ *     1. Starts a template process from (init_template, init_args) and waits for it to
+ *        initialize completely.
+ *     2. Initiates termination of the process and wait for it to terminate.
+ *     3. Puts the statement UP, then waits for a statement termination request (which may
+ *        already have been received).
+ *     4. Starts a template process from (deinit_template, deinit_args) and waits for it
+ *        to initialize completely, or for the timeout to elapse.
+ *     5. Initiates termination of the process and wait for it to terminate.
+ *     6. Terminates the statement.
+ * 
+ *   If init_template="<none>", steps (1-2) are skipped.
+ *   If deinit_template="<none>", steps (4-5) are skipped.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/parse_number.h>
+#include <misc/string_begins_with.h>
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_imperative.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+#define STATE_INIT_WORKING 1
+#define STATE_INIT_CLEANING 2
+#define STATE_UP 3
+#define STATE_DEINIT_WORKING 4
+#define STATE_DEINIT_CLEANING 5
+
+struct instance {
+    NCDModuleInst *i;
+    char *deinit_template;
+    NCDValue *deinit_args;
+    BTimer deinit_timer;
+    NCDModuleProcess process;
+    int state;
+    int dying;
+};
+
+static int start_process (struct instance *o, const char *templ, NCDValue *args, NCDModuleProcess_handler_event handler);
+static void go_deinit (struct instance *o);
+static void init_process_handler_event (struct instance *o, int event);
+static void deinit_process_handler_event (struct instance *o, int event);
+static int process_func_getspecialvar (struct instance *o, const char *name, NCDValue *out);
+static NCDModuleInst * process_func_getspecialobj (struct instance *o, const char *name);
+static void deinit_timer_handler (struct instance *o);
+static void instance_free (struct instance *o);
+
+static int start_process (struct instance *o, const char *templ, NCDValue *args, NCDModuleProcess_handler_event handler)
+{
+    // copy arguments
+    NCDValue args_copy;
+    if (!NCDValue_InitCopy(&args_copy, args)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
+        goto fail;
+    }
+    
+    // create process
+    if (!NCDModuleProcess_Init(&o->process, o->i, templ, args_copy, o, handler)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
+        NCDValue_Free(&args_copy);
+        goto fail;
+    }
+    
+    // set special functions
+    NCDModuleProcess_SetSpecialFuncs(&o->process,
+                                    (NCDModuleProcess_func_getspecialvar)process_func_getspecialvar,
+                                    (NCDModuleProcess_func_getspecialobj)process_func_getspecialobj);
+    return 1;
+    
+fail:
+    return 0;
+}
+
+static void go_deinit (struct instance *o)
+{
+    ASSERT(o->dying)
+    
+    // deinit is no-op?
+    if (!strcmp(o->deinit_template, "<none>")) {
+        instance_free(o);
+        return;
+    }
+    
+    // start deinit process
+    if (!start_process(o, o->deinit_template, o->deinit_args, (NCDModuleProcess_handler_event)deinit_process_handler_event)) {
+        instance_free(o);
+        return;
+    }
+    
+    // start timer
+    BReactor_SetTimer(o->i->reactor, &o->deinit_timer);
+    
+    // set state deinit working
+    o->state = STATE_DEINIT_WORKING;
+}
+
+static void init_process_handler_event (struct instance *o, int event)
+{
+    switch (event) {
+        case NCDMODULEPROCESS_EVENT_UP: {
+            ASSERT(o->state == STATE_INIT_WORKING)
+            
+            // start terminating
+            NCDModuleProcess_Terminate(&o->process);
+            
+            // set state init cleaning
+            o->state = STATE_INIT_CLEANING;
+        } break;
+        
+        case NCDMODULEPROCESS_EVENT_TERMINATED: {
+            ASSERT(o->state == STATE_INIT_CLEANING)
+            
+            // free process
+            NCDModuleProcess_Free(&o->process);
+            
+            // were we requested to die aleady?
+            if (o->dying) {
+                go_deinit(o);
+                return;
+            }
+            
+            // signal up
+            NCDModuleInst_Backend_Up(o->i);
+            
+            // set state up
+            o->state = STATE_UP;
+        } break;
+        
+        default: ASSERT(0);
+    }
+}
+
+static void deinit_process_handler_event (struct instance *o, int event)
+{
+    ASSERT(o->dying)
+    
+    switch (event) {
+        case NCDMODULEPROCESS_EVENT_UP: {
+            ASSERT(o->state == STATE_DEINIT_WORKING)
+            
+            // stop timer
+            BReactor_RemoveTimer(o->i->reactor, &o->deinit_timer);
+            
+            // start terminating
+            NCDModuleProcess_Terminate(&o->process);
+            
+            // set state deinit cleaning
+            o->state = STATE_DEINIT_CLEANING;
+        } break;
+        
+        case NCDMODULEPROCESS_EVENT_TERMINATED: {
+            ASSERT(o->state == STATE_DEINIT_CLEANING)
+            
+            // free process
+            NCDModuleProcess_Free(&o->process);
+            
+            // die
+            instance_free(o);
+            return;
+        } break;
+        
+        default: ASSERT(0);
+    }
+}
+
+static int process_func_getspecialvar (struct instance *o, const char *name, NCDValue *out)
+{
+    ASSERT(o->state != STATE_UP)
+    
+    size_t p;
+    if (p = string_begins_with(name, "_caller.")) {
+        return NCDModuleInst_Backend_GetVar(o->i, name + p, out);
+    }
+    
+    return 0;
+}
+
+static NCDModuleInst * process_func_getspecialobj (struct instance *o, const char *name)
+{
+    ASSERT(o->state != STATE_UP)
+    
+    size_t p;
+    if (p = string_begins_with(name, "_caller.")) {
+        return NCDModuleInst_Backend_GetObj(o->i, name + p);
+    }
+    
+    return NULL;
+}
+
+static void deinit_timer_handler (struct instance *o)
+{
+    ASSERT(o->state == STATE_DEINIT_WORKING)
+    
+    ModuleLog(o->i, BLOG_ERROR, "imperative deinit timeout elapsed");
+    
+    // start terminating
+    NCDModuleProcess_Terminate(&o->process);
+    
+    // set state deinit cleaning
+    o->state = STATE_DEINIT_CLEANING;
+}
+
+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;
+    
+    // check arguments
+    NCDValue *init_template_arg;
+    NCDValue *init_args;
+    NCDValue *deinit_template_arg;
+    NCDValue *deinit_timeout_arg;
+    if (!NCDValue_ListRead(i->args, 5, &init_template_arg, &init_args, &deinit_template_arg, &o->deinit_args, &deinit_timeout_arg)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(init_template_arg) != NCDVALUE_STRING || NCDValue_Type(init_args) != NCDVALUE_LIST ||
+        NCDValue_Type(deinit_template_arg) != NCDVALUE_STRING || NCDValue_Type(o->deinit_args) != NCDVALUE_LIST ||
+        NCDValue_Type(deinit_timeout_arg) != NCDVALUE_STRING) {
+        ModuleLog(i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    char *init_template = NCDValue_StringValue(init_template_arg);
+    o->deinit_template = NCDValue_StringValue(deinit_template_arg);
+    
+    // read timeout
+    uintmax_t timeout;
+    if (!parse_unsigned_integer(NCDValue_StringValue(deinit_timeout_arg), &timeout) || timeout > UINT64_MAX) {
+        ModuleLog(i, BLOG_ERROR, "wrong timeout");
+        goto fail1;
+    }
+    
+    // init timer
+    BTimer_Init(&o->deinit_timer, timeout, (BTimer_handler)deinit_timer_handler, o);
+    
+    if (!strcmp(init_template, "<none>")) {
+        // signal up
+        NCDModuleInst_Backend_Up(i);
+        
+        // set state up
+        o->state = STATE_UP;
+    } else {
+        // start init process
+        if (!start_process(o, init_template, init_args, (NCDModuleProcess_handler_event)init_process_handler_event)) {
+            goto fail1;
+        }
+        
+        // set state init working
+        o->state = STATE_INIT_WORKING;
+    }
+    
+    // set not dying
+    o->dying = 0;
+    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 instance
+    free(o);
+    
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    ASSERT(!o->dying)
+    
+    // set dying
+    o->dying = 1;
+    
+    if (o->state == STATE_UP) {
+        go_deinit(o);
+        return;
+    }
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "imperative",
+        .func_new = func_new,
+        .func_die = func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_imperative = {
+    .modules = modules
+};

+ 2 - 0
ncd/modules/modules.h

@@ -47,6 +47,7 @@ extern const struct NCDModuleGroup ncdmodule_runonce;
 extern const struct NCDModuleGroup ncdmodule_spawn;
 extern const struct NCDModuleGroup ncdmodule_call;
 extern const struct NCDModuleGroup ncdmodule_rimp_call;
+extern const struct NCDModuleGroup ncdmodule_imperative;
 extern const struct NCDModuleGroup ncdmodule_ref;
 extern const struct NCDModuleGroup ncdmodule_index;
 extern const struct NCDModuleGroup ncdmodule_alias;
@@ -99,6 +100,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_spawn,
     &ncdmodule_call,
     &ncdmodule_rimp_call,
+    &ncdmodule_imperative,
     &ncdmodule_ref,
     &ncdmodule_index,
     &ncdmodule_alias,