Преглед изворни кода

ncd: Fix NCDModule to signal module->NCD events directly instead of via jobs. Fixes possible
crash/assertion failure with certain dependencies.
Remove module net.backend.physical.
Add modules net.backend.waitlink, net.up, sleep.
Other minor changes.

ambrop7 пре 15 година
родитељ
комит
1e408e6826

+ 3 - 1
blog_channels.txt

@@ -12,13 +12,15 @@ ncd_concatlist 4
 ncd_if 4
 ncd_strcmp 4
 ncd_logical 4
+ncd_sleep 4
 ncd_ip_in_network 4
 ncd_run 4
-ncd_net_backend_physical 4
 ncd_net_backend_waitdevice 4
+ncd_net_backend_waitlink 4
 ncd_net_backend_badvpn 4
 ncd_net_backend_wpa_supplicant 4
 ncd_net_backend_rfkill 4
+ncd_net_up 4
 ncd_net_dns 4
 ncd_net_iptables 4
 ncd_net_ipv4_addr 4

+ 4 - 0
generated/blog_channel_ncd_net_backend_waitlink.h

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

+ 4 - 0
generated/blog_channel_ncd_net_up.h

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

+ 4 - 0
generated/blog_channel_ncd_sleep.h

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

+ 41 - 39
generated/blog_channels_defines.h

@@ -12,43 +12,45 @@
 #define BLOG_CHANNEL_ncd_if 11
 #define BLOG_CHANNEL_ncd_strcmp 12
 #define BLOG_CHANNEL_ncd_logical 13
-#define BLOG_CHANNEL_ncd_ip_in_network 14
-#define BLOG_CHANNEL_ncd_run 15
-#define BLOG_CHANNEL_ncd_net_backend_physical 16
+#define BLOG_CHANNEL_ncd_sleep 14
+#define BLOG_CHANNEL_ncd_ip_in_network 15
+#define BLOG_CHANNEL_ncd_run 16
 #define BLOG_CHANNEL_ncd_net_backend_waitdevice 17
-#define BLOG_CHANNEL_ncd_net_backend_badvpn 18
-#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 19
-#define BLOG_CHANNEL_ncd_net_backend_rfkill 20
-#define BLOG_CHANNEL_ncd_net_dns 21
-#define BLOG_CHANNEL_ncd_net_iptables 22
-#define BLOG_CHANNEL_ncd_net_ipv4_addr 23
-#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 24
-#define BLOG_CHANNEL_ncd_net_ipv4_route 25
-#define BLOG_CHANNEL_StreamPeerIO 26
-#define BLOG_CHANNEL_DatagramPeerIO 27
-#define BLOG_CHANNEL_BReactor 28
-#define BLOG_CHANNEL_BSignal 29
-#define BLOG_CHANNEL_FragmentProtoAssembler 30
-#define BLOG_CHANNEL_BPredicate 31
-#define BLOG_CHANNEL_ServerConnection 32
-#define BLOG_CHANNEL_Listener 33
-#define BLOG_CHANNEL_DataProto 34
-#define BLOG_CHANNEL_FrameDecider 35
-#define BLOG_CHANNEL_BSocksClient 36
-#define BLOG_CHANNEL_BDHCPClientCore 37
-#define BLOG_CHANNEL_BDHCPClient 38
-#define BLOG_CHANNEL_NCDIfConfig 39
-#define BLOG_CHANNEL_BUnixSignal 40
-#define BLOG_CHANNEL_BProcess 41
-#define BLOG_CHANNEL_StreamSocketSink 42
-#define BLOG_CHANNEL_StreamSocketSource 43
-#define BLOG_CHANNEL_DatagramSocketSink 44
-#define BLOG_CHANNEL_DatagramSocketSource 45
-#define BLOG_CHANNEL_PRStreamSink 46
-#define BLOG_CHANNEL_PRStreamSource 47
-#define BLOG_CHANNEL_BSocketPRFileDesc 48
-#define BLOG_CHANNEL_PacketProtoDecoder 49
-#define BLOG_CHANNEL_DPRelay 50
-#define BLOG_CHANNEL_BThreadWork 51
-#define BLOG_CHANNEL_DPReceive 52
-#define BLOG_NUM_CHANNELS 53
+#define BLOG_CHANNEL_ncd_net_backend_waitlink 18
+#define BLOG_CHANNEL_ncd_net_backend_badvpn 19
+#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 20
+#define BLOG_CHANNEL_ncd_net_backend_rfkill 21
+#define BLOG_CHANNEL_ncd_net_up 22
+#define BLOG_CHANNEL_ncd_net_dns 23
+#define BLOG_CHANNEL_ncd_net_iptables 24
+#define BLOG_CHANNEL_ncd_net_ipv4_addr 25
+#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 26
+#define BLOG_CHANNEL_ncd_net_ipv4_route 27
+#define BLOG_CHANNEL_StreamPeerIO 28
+#define BLOG_CHANNEL_DatagramPeerIO 29
+#define BLOG_CHANNEL_BReactor 30
+#define BLOG_CHANNEL_BSignal 31
+#define BLOG_CHANNEL_FragmentProtoAssembler 32
+#define BLOG_CHANNEL_BPredicate 33
+#define BLOG_CHANNEL_ServerConnection 34
+#define BLOG_CHANNEL_Listener 35
+#define BLOG_CHANNEL_DataProto 36
+#define BLOG_CHANNEL_FrameDecider 37
+#define BLOG_CHANNEL_BSocksClient 38
+#define BLOG_CHANNEL_BDHCPClientCore 39
+#define BLOG_CHANNEL_BDHCPClient 40
+#define BLOG_CHANNEL_NCDIfConfig 41
+#define BLOG_CHANNEL_BUnixSignal 42
+#define BLOG_CHANNEL_BProcess 43
+#define BLOG_CHANNEL_StreamSocketSink 44
+#define BLOG_CHANNEL_StreamSocketSource 45
+#define BLOG_CHANNEL_DatagramSocketSink 46
+#define BLOG_CHANNEL_DatagramSocketSource 47
+#define BLOG_CHANNEL_PRStreamSink 48
+#define BLOG_CHANNEL_PRStreamSource 49
+#define BLOG_CHANNEL_BSocketPRFileDesc 50
+#define BLOG_CHANNEL_PacketProtoDecoder 51
+#define BLOG_CHANNEL_DPRelay 52
+#define BLOG_CHANNEL_BThreadWork 53
+#define BLOG_CHANNEL_DPReceive 54
+#define BLOG_NUM_CHANNELS 55

+ 3 - 1
generated/blog_channels_list.h

@@ -12,13 +12,15 @@
 {.name = "ncd_if", .loglevel = 4},
 {.name = "ncd_strcmp", .loglevel = 4},
 {.name = "ncd_logical", .loglevel = 4},
+{.name = "ncd_sleep", .loglevel = 4},
 {.name = "ncd_ip_in_network", .loglevel = 4},
 {.name = "ncd_run", .loglevel = 4},
-{.name = "ncd_net_backend_physical", .loglevel = 4},
 {.name = "ncd_net_backend_waitdevice", .loglevel = 4},
+{.name = "ncd_net_backend_waitlink", .loglevel = 4},
 {.name = "ncd_net_backend_badvpn", .loglevel = 4},
 {.name = "ncd_net_backend_wpa_supplicant", .loglevel = 4},
 {.name = "ncd_net_backend_rfkill", .loglevel = 4},
+{.name = "ncd_net_up", .loglevel = 4},
 {.name = "ncd_net_dns", .loglevel = 4},
 {.name = "ncd_net_iptables", .loglevel = 4},
 {.name = "ncd_net_ipv4_addr", .loglevel = 4},

+ 3 - 1
ncd/CMakeLists.txt

@@ -15,13 +15,15 @@ add_executable(badvpn-ncd
     modules/if.c
     modules/strcmp.c
     modules/logical.c
+    modules/sleep.c
     modules/ip_in_network.c
     modules/run.c
-    modules/net_backend_physical.c
     modules/net_backend_waitdevice.c
+    modules/net_backend_waitlink.c
     modules/net_backend_badvpn.c
     modules/net_backend_wpa_supplicant.c
     modules/net_backend_rfkill.c
+    modules/net_up.c
     modules/net_dns.c
     modules/net_iptables.c
     modules/net_ipv4_addr.c

+ 210 - 98
ncd/NCDModule.c

@@ -20,130 +20,172 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include <stdarg.h>
+
 #include <ncd/NCDModule.h>
 
-#define STATE_DOWN 1
-#define STATE_UP 2
-#define STATE_DYING 3
+#define STATE_INIT 1
+#define STATE_UNINIT 2
+#define STATE_DEAD 3
+#define STATE_DOWN_CLEAN 4
+#define STATE_UP 5
+#define STATE_DOWN_UNCLEAN 6
+#define STATE_DOWN_PCLEAN 7
+#define STATE_DOWN_DIE 8
+#define STATE_UP_DIE 9
+#define STATE_DYING 10
+#define STATE_UNDEAD 11
+
+static void frontend_event (NCDModuleInst *n, int event)
+{
+    n->handler_event(n->user, event);
+    return;
+}
 
-static void event_job_handler (NCDModuleInst *n)
+static void init_job_handler (NCDModuleInst *n)
 {
     DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_INIT)
     
-    n->handler_event(n->user, n->event_job_event);
+    n->state = STATE_DOWN_CLEAN;
+    
+    n->m->func_new(n);
     return;
 }
 
-static void toevent_job_handler (NCDModuleInst *n)
+static void uninit_job_handler (NCDModuleInst *n)
 {
     DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_UNINIT)
     
-    switch (n->toevent_job_event) {
-        case NCDMODULE_TOEVENT_DIE: {
-            ASSERT(n->state == STATE_DYING)
-            
-            if (!n->m->func_die) {
-                NCDModuleInst_Backend_Died(n, 0);
-                return;
-            }
-            
-            n->m->func_die(n->inst_user);
-            return;
-        } break;
-        
-        case NCDMODULE_TOEVENT_CLEAN: {
-            ASSERT(n->state == STATE_UP || n->state == STATE_DOWN)
-            
-            if (n->m->func_clean) {
-                n->m->func_clean(n->inst_user);
-                return;
-            }
-        } break;
-        
-        default:
-            ASSERT(0);
+    n->state = STATE_UNDEAD;
+    
+    frontend_event(n, NCDMODULE_EVENT_DEAD);
+    return;
+}
+
+static void die_job_handler (NCDModuleInst *n)
+{
+    DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_DOWN_DIE || n->state == STATE_UP_DIE)
+    
+    n->state = STATE_DYING;
+    
+    n->m->func_die(n->inst_user);
+    return;
+}
+
+static void clean_job_handler (NCDModuleInst *n)
+{
+    DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_DOWN_PCLEAN)
+    
+    n->state = STATE_DOWN_CLEAN;
+    
+    if (n->m->func_clean) {
+        n->m->func_clean(n->inst_user);
+        return;
     }
 }
 
-int NCDModuleInst_Init (NCDModuleInst *n, const char *name, const struct NCDModule *m, NCDValue *args, const char *logprefix, BReactor *reactor, BProcessManager *manager,
-                        NCDModule_handler_event handler_event, NCDModule_handler_died handler_died, NCDModule_handler_getvar handler_getvar, void *user)
+void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDValue *args, const char *logprefix, BReactor *reactor, BProcessManager *manager,
+                         NCDModule_handler_event handler_event, NCDModule_handler_getvar handler_getvar, void *user)
 {
     // init arguments
-    n->name = name;
     n->m = m;
     n->args = args;
     n->logprefix = logprefix;
     n->reactor = reactor;
     n->manager = manager;
     n->handler_event = handler_event;
-    n->handler_died = handler_died;
     n->handler_getvar = handler_getvar;
     n->user = user;
     
-    // init event job
-    BPending_Init(&n->event_job, BReactor_PendingGroup(n->reactor), (BPending_handler)event_job_handler, n);
+    // init jobs
+    BPending_Init(&n->init_job, BReactor_PendingGroup(n->reactor), (BPending_handler)init_job_handler, n);
+    BPending_Init(&n->uninit_job, BReactor_PendingGroup(n->reactor), (BPending_handler)uninit_job_handler, n);
+    BPending_Init(&n->die_job, BReactor_PendingGroup(n->reactor), (BPending_handler)die_job_handler, n);
+    BPending_Init(&n->clean_job, BReactor_PendingGroup(n->reactor), (BPending_handler)clean_job_handler, n);
     
-    // init toevent job
-    BPending_Init(&n->toevent_job, BReactor_PendingGroup(n->reactor), (BPending_handler)toevent_job_handler, n);
+    // set initial state
+    n->state = STATE_INIT;
+    BPending_Set(&n->init_job);
     
-    // set state
-    n->state = STATE_DOWN;
+    // set initial instance argument
+    n->inst_user = NULL;
     
-    // init backend
-    if (!(n->inst_user = n->m->func_new(n))) {
-        goto fail1;
-    }
+    // clear error flag
+    n->is_error = 0;
     
     DebugObject_Init(&n->d_obj);
-    DebugError_Init(&n->d_err, BReactor_PendingGroup(n->reactor));
-    
-    return 1;
-    
-fail1:
-    BPending_Free(&n->toevent_job);
-    BPending_Free(&n->event_job);
-    return 0;
 }
 
 void NCDModuleInst_Free (NCDModuleInst *n)
 {
-    DebugError_Free(&n->d_err);
     DebugObject_Free(&n->d_obj);
+    ASSERT(n->state == STATE_DEAD || n->state == STATE_UNDEAD)
     
-    // free backend
-    n->m->func_free(n->inst_user);
-    
-    // free toevent job
-    BPending_Free(&n->toevent_job);
-    
-    // free event job
-    BPending_Free(&n->event_job);
+    // free jobs
+    BPending_Free(&n->clean_job);
+    BPending_Free(&n->die_job);
+    BPending_Free(&n->uninit_job);
+    BPending_Free(&n->init_job);
 }
 
 void NCDModuleInst_Event (NCDModuleInst *n, int event)
 {
-    ASSERT(event == NCDMODULE_TOEVENT_DIE || event == NCDMODULE_TOEVENT_CLEAN)
-    ASSERT(n->state == STATE_UP || n->state == STATE_DOWN)
-    ASSERT(!BPending_IsSet(&n->event_job))
-    ASSERT(!BPending_IsSet(&n->toevent_job))
     DebugObject_Access(&n->d_obj);
+    ASSERT(event == NCDMODULE_TOEVENT_DIE || event == NCDMODULE_TOEVENT_CLEAN)
     
     if (event == NCDMODULE_TOEVENT_DIE) {
-        // set state
-        n->state = STATE_DYING;
+        switch (n->state) {
+            case STATE_INIT: {
+                n->state = STATE_UNINIT;
+                BPending_Unset(&n->init_job);
+                BPending_Set(&n->uninit_job);
+            } break;
+            
+            case STATE_DOWN_CLEAN:
+            case STATE_DOWN_UNCLEAN: {
+                n->state = STATE_DOWN_DIE;
+                BPending_Set(&n->die_job);
+            } break;
+            
+            case STATE_DOWN_PCLEAN: {
+                n->state = STATE_DOWN_DIE;
+                BPending_Unset(&n->clean_job);
+                BPending_Set(&n->die_job);
+            } break;
+            
+            case STATE_UP: {
+                n->state = STATE_UP_DIE;
+                BPending_Set(&n->die_job);
+            } break;
+            
+            default: ASSERT(0);
+        }
+    }
+    else if (event == NCDMODULE_TOEVENT_CLEAN) {
+        switch (n->state) {
+            case STATE_INIT:
+            case STATE_DOWN_CLEAN:
+            case STATE_DOWN_PCLEAN: {
+            } break;
+            
+            case STATE_DOWN_UNCLEAN: {
+                n->state = STATE_DOWN_PCLEAN;
+                BPending_Set(&n->clean_job);
+            } break;
+            
+            default: ASSERT(0);
+        }
     }
-    
-    // remember event
-    n->toevent_job_event = event;
-    
-    // set job
-    BPending_Set(&n->toevent_job);
 }
 
 int NCDModuleInst_GetVar (NCDModuleInst *n, const char *name, NCDValue *out)
 {
-    ASSERT(n->state == STATE_UP)
     DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_UP)
     
     if (!n->m->func_getvar) {
         return 0;
@@ -152,41 +194,98 @@ int NCDModuleInst_GetVar (NCDModuleInst *n, const char *name, NCDValue *out)
     return n->m->func_getvar(n->inst_user, name, out);
 }
 
-void NCDModuleInst_Backend_Event (NCDModuleInst *n, int event)
+int NCDModuleInst_HaveError (NCDModuleInst *n)
 {
-    ASSERT(event == NCDMODULE_EVENT_UP || event == NCDMODULE_EVENT_DOWN)
-    ASSERT(!(event == NCDMODULE_EVENT_UP) || n->state == STATE_DOWN)
-    ASSERT(!(event == NCDMODULE_EVENT_DOWN) || n->state == STATE_UP)
-    ASSERT(!BPending_IsSet(&n->event_job))
-    ASSERT(!BPending_IsSet(&n->toevent_job))
-    
-    switch (event) {
-        case NCDMODULE_EVENT_UP:
-            n->state = STATE_UP;
-            break;
-        case NCDMODULE_EVENT_DOWN:
-            n->state = STATE_DOWN;
-            break;
-        default:
-            ASSERT(0);
-    }
+    DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_DEAD || n->state == STATE_UNDEAD)
     
-    // remember event
-    n->event_job_event = event;
+    return n->is_error;
+}
+
+void NCDModuleInst_Backend_SetUser (NCDModuleInst *n, void *user)
+{
+    DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_DOWN_PCLEAN || n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN ||
+           n->state == STATE_UP || n->state == STATE_DOWN_DIE || n->state == STATE_UP_DIE ||
+           n->state == STATE_DYING)
     
-    // set job
-    BPending_Set(&n->event_job);
+    n->inst_user = user;
 }
 
-void NCDModuleInst_Backend_Died (NCDModuleInst *n, int is_error)
+void NCDModuleInst_Backend_Event (NCDModuleInst *n, int event)
 {
-    ASSERT(is_error == 0 || is_error == 1)
+    DebugObject_Access(&n->d_obj);
+    ASSERT(event == NCDMODULE_EVENT_UP || event == NCDMODULE_EVENT_DOWN || event == NCDMODULE_EVENT_DEAD)
     
-    DEBUGERROR(&n->d_err, n->handler_died(n->user, is_error))
+    if (event == NCDMODULE_EVENT_UP) {
+        switch (n->state) {
+            case STATE_DOWN_CLEAN:
+            case STATE_DOWN_UNCLEAN: {
+                n->state = STATE_UP;
+                frontend_event(n, NCDMODULE_EVENT_UP);
+            } break;
+            
+            case STATE_DOWN_PCLEAN: {
+                n->state = STATE_UP;
+                BPending_Unset(&n->clean_job);
+                frontend_event(n, NCDMODULE_EVENT_UP);
+            } break;
+            
+            case STATE_DOWN_DIE: {
+                n->state = STATE_UP_DIE;
+            } break;
+            
+            default: ASSERT(0);
+        }
+    }
+    else if (event == NCDMODULE_EVENT_DOWN) {
+        switch (n->state) {
+            case STATE_UP: {
+                n->state = STATE_DOWN_UNCLEAN;
+                frontend_event(n, NCDMODULE_EVENT_DOWN);
+            } break;
+            
+            case STATE_UP_DIE: {
+                n->state = STATE_DOWN_DIE;
+            } break;
+            
+            default: ASSERT(0);
+        }
+    }
+    else if (event == NCDMODULE_EVENT_DEAD) {
+        switch (n->state) {
+            case STATE_DOWN_DIE:
+            case STATE_UP_DIE: {
+                n->state = STATE_DEAD;
+                BPending_Unset(&n->die_job);
+            } break;
+            
+            case STATE_DOWN_CLEAN:
+            case STATE_DOWN_UNCLEAN:
+            case STATE_UP:
+            case STATE_DYING: {
+                n->state = STATE_DEAD;
+            } break;
+            
+            case STATE_DOWN_PCLEAN: {
+                n->state = STATE_DEAD;
+                BPending_Unset(&n->clean_job);
+            } break;
+            
+            default: ASSERT(0);
+        }
+        
+        frontend_event(n, NCDMODULE_EVENT_DEAD);
+        return;
+    }
 }
 
 int NCDModuleInst_Backend_GetVar (NCDModuleInst *n, const char *modname, const char *varname, NCDValue *out)
 {
+    DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_DOWN_PCLEAN || n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN ||
+           n->state == STATE_UP || n->state == STATE_DOWN_DIE || n->state == STATE_UP_DIE ||
+           n->state == STATE_DYING)
     ASSERT(modname)
     ASSERT(varname)
     
@@ -198,9 +297,22 @@ int NCDModuleInst_Backend_GetVar (NCDModuleInst *n, const char *modname, const c
 
 void NCDModuleInst_Backend_Log (NCDModuleInst *n, int channel, int level, const char *fmt, ...)
 {
+    DebugObject_Access(&n->d_obj);
+    
     va_list vl;
     va_start(vl, fmt);
     BLog_Append("%s", n->logprefix);
     BLog_LogToChannelVarArg(channel, level, fmt, vl);
     va_end(vl);
 }
+
+void NCDModuleInst_Backend_SetError (NCDModuleInst *n)
+{
+    DebugObject_Access(&n->d_obj);
+    ASSERT(n->state == STATE_DOWN_PCLEAN || n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN ||
+           n->state == STATE_UP || n->state == STATE_DOWN_DIE || n->state == STATE_UP_DIE ||
+           n->state == STATE_DYING)
+    ASSERT(!n->is_error)
+    
+    n->is_error = 1;
+}

+ 15 - 20
ncd/NCDModule.h

@@ -23,24 +23,21 @@
 #ifndef BADVPN_NCD_NCDMODULE_H
 #define BADVPN_NCD_NCDMODULE_H
 
-#include <stdarg.h>
-
-#include <misc/debugerror.h>
+#include <misc/debug.h>
 #include <system/BReactor.h>
 #include <system/BPending.h>
 #include <system/BLog.h>
 #include <process/BProcess.h>
-#include <ncdconfig/NCDConfig.h>
 #include <ncd/NCDValue.h>
 
 #define NCDMODULE_EVENT_UP 1
 #define NCDMODULE_EVENT_DOWN 2
+#define NCDMODULE_EVENT_DEAD 3
 
 #define NCDMODULE_TOEVENT_DIE 101
 #define NCDMODULE_TOEVENT_CLEAN 102
 
 typedef void (*NCDModule_handler_event) (void *user, int event);
-typedef void (*NCDModule_handler_died) (void *user, int is_error);
 typedef int (*NCDModule_handler_getvar) (void *user, const char *modname, const char *varname, NCDValue *out);
 
 struct NCDModule;
@@ -51,40 +48,39 @@ struct NCDModuleInitParams {
 };
 
 typedef struct {
-    const char *name;
     const struct NCDModule *m;
     NCDValue *args;
     const char *logprefix;
     BReactor *reactor;
     BProcessManager *manager;
     NCDModule_handler_event handler_event;
-    NCDModule_handler_died handler_died;
     NCDModule_handler_getvar handler_getvar;
     void *user;
-    BPending event_job;
-    int event_job_event;
-    BPending toevent_job;
-    int toevent_job_event;
+    BPending init_job;
+    BPending uninit_job;
+    BPending die_job;
+    BPending clean_job;
     int state;
     void *inst_user;
+    int is_error;
     DebugObject d_obj;
-    DebugError d_err;
 } NCDModuleInst;
 
-int NCDModuleInst_Init (NCDModuleInst *n, const char *name, const struct NCDModule *m, NCDValue *args, const char *logprefix, BReactor *reactor, BProcessManager *manager,
-                        NCDModule_handler_event handler_event, NCDModule_handler_died handler_died, NCDModule_handler_getvar handler_getvar, void *user);
+void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDValue *args, const char *logprefix, BReactor *reactor, BProcessManager *manager,
+                         NCDModule_handler_event handler_event, NCDModule_handler_getvar handler_getvar, void *user);
 void NCDModuleInst_Free (NCDModuleInst *n);
 void NCDModuleInst_Event (NCDModuleInst *n, int event);
-int NCDModuleInst_GetVar (NCDModuleInst *n, const char *name, NCDValue *out);
+int NCDModuleInst_GetVar (NCDModuleInst *n, const char *name, NCDValue *out) WARN_UNUSED;
+int NCDModuleInst_HaveError (NCDModuleInst *n);
+void NCDModuleInst_Backend_SetUser (NCDModuleInst *n, void *user);
 void NCDModuleInst_Backend_Event (NCDModuleInst *n, int event);
-void NCDModuleInst_Backend_Died (NCDModuleInst *n, int is_error);
-int NCDModuleInst_Backend_GetVar (NCDModuleInst *n, const char *modname, const char *varname, NCDValue *out);
+int NCDModuleInst_Backend_GetVar (NCDModuleInst *n, const char *modname, const char *varname, NCDValue *out) WARN_UNUSED;
 void NCDModuleInst_Backend_Log (NCDModuleInst *n, int channel, int level, const char *fmt, ...);
+void NCDModuleInst_Backend_SetError (NCDModuleInst *n);
 
 typedef int (*NCDModule_func_globalinit) (const struct NCDModuleInitParams params);
 typedef void (*NCDModule_func_globalfree) (void);
-typedef void * (*NCDModule_func_new) (NCDModuleInst *params);
-typedef void (*NCDModule_func_free) (void *o);
+typedef void (*NCDModule_func_new) (NCDModuleInst *params);
 typedef void (*NCDModule_func_die) (void *o);
 typedef int (*NCDModule_func_getvar) (void *o, const char *name, NCDValue *out);
 typedef void (*NCDModule_func_clean) (void *o);
@@ -92,7 +88,6 @@ typedef void (*NCDModule_func_clean) (void *o);
 struct NCDModule {
     const char *type;
     NCDModule_func_new func_new;
-    NCDModule_func_free func_free;
     NCDModule_func_die func_die;
     NCDModule_func_getvar func_getvar;
     NCDModule_func_clean func_clean;

+ 16 - 18
ncd/modules/command_template.c

@@ -20,8 +20,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#include <stdlib.h>
-#include <string.h>
+#include <misc/debug.h>
 
 #include <ncd/modules/command_template.h>
 
@@ -34,6 +33,7 @@
 
 static int start_process (command_template_instance *o, int remove);
 static void process_handler (command_template_instance *o, int normally, uint8_t normally_exit_status);
+static void free_template (command_template_instance *o, int is_error);
 
 int start_process (command_template_instance *o, int remove)
 {
@@ -71,7 +71,7 @@ static void lock_handler (command_template_instance *o)
     
     // start process
     if (!start_process(o, remove)) {
-        NCDModuleInst_Backend_Died(o->i, 1);
+        free_template(o, 1);
         return;
     }
     
@@ -99,7 +99,7 @@ void process_handler (command_template_instance *o, int normally, uint8_t normal
     if (!normally || normally_exit_status != 0) {
         NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "command failed");
         
-        NCDModuleInst_Backend_Died(o->i, 1);
+        free_template(o, 1);
         return;
     }
     
@@ -121,17 +121,19 @@ void process_handler (command_template_instance *o, int normally, uint8_t normal
         
         case STATE_DELETING: {
             // finish
-            NCDModuleInst_Backend_Died(o->i, 0);
+            free_template(o, 0);
             return;
         } break;
     }
 }
 
-int command_template_new (command_template_instance *o, NCDModuleInst *i, command_template_build_cmdline build_cmdline, int blog_channel, BEventLock *elock)
+void command_template_new (command_template_instance *o, NCDModuleInst *i, command_template_build_cmdline build_cmdline, command_template_free_func free_func, void *user, int blog_channel, BEventLock *elock)
 {
     // init arguments
     o->i = i;
     o->build_cmdline = build_cmdline;
+    o->free_func = free_func;
+    o->user = user;
     o->blog_channel = blog_channel;
     
     // init lock job
@@ -145,23 +147,17 @@ int command_template_new (command_template_instance *o, NCDModuleInst *i, comman
     
     // set state
     o->state = STATE_ADDING_LOCK;
-    
-    return 1;
 }
 
-void command_template_free (command_template_instance *o)
+void free_template (command_template_instance *o, int is_error)
 {
-    // free process
-    if (o->have_process) {
-        // kill process
-        BProcess_Kill(&o->process);
-        
-        // free process
-        BProcess_Free(&o->process);
-    }
+    ASSERT(!o->have_process)
     
     // free lock job
     BEventLockJob_Free(&o->elock_job);
+    
+    // call free function
+    o->free_func(o->user, is_error);
 }
 
 void command_template_die (command_template_instance *o)
@@ -170,7 +166,9 @@ void command_template_die (command_template_instance *o)
     
     switch (o->state) {
         case STATE_ADDING_LOCK: {
-            NCDModuleInst_Backend_Died(o->i, 0);
+            ASSERT(!o->have_process)
+            
+            free_template(o, 0);
             return;
         } break;
         

+ 4 - 2
ncd/modules/command_template.h

@@ -35,10 +35,13 @@
 #include <generated/blog_channel_ncd_net_iptables.h>
 
 typedef int (*command_template_build_cmdline) (NCDModuleInst *i, int remove, char **exec, CmdLine *cl);
+typedef void (*command_template_free_func) (void *user, int is_error);
 
 typedef struct {
     NCDModuleInst *i;
     command_template_build_cmdline build_cmdline;
+    command_template_free_func free_func;
+    void *user;
     int blog_channel;
     BEventLockJob elock_job;
     int state;
@@ -46,8 +49,7 @@ typedef struct {
     BProcess process;
 } command_template_instance;
 
-int command_template_new (command_template_instance *o, NCDModuleInst *i, command_template_build_cmdline build_cmdline, int blog_channel, BEventLock *elock);
-void command_template_free (command_template_instance *o);
+void command_template_new (command_template_instance *o, NCDModuleInst *i, command_template_build_cmdline build_cmdline, command_template_free_func free_func, void *user, int blog_channel, BEventLock *elock);
 void command_template_die (command_template_instance *o);
 
 #endif

+ 11 - 5
ncd/modules/concat.c

@@ -42,7 +42,7 @@ struct instance {
     char *string;
 };
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -50,6 +50,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -80,27 +81,32 @@ static void * func_new (NCDModuleInst *i)
     // set string
     o->string = ExpString_Get(&s);
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail2:
     ExpString_Free(&s);
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free string
     free(o->string);
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static int func_getvar (void *vo, const char *name, NCDValue *out)
@@ -123,7 +129,7 @@ static const struct NCDModule modules[] = {
     {
         .type = "concat",
         .func_new = func_new,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = NULL

+ 12 - 6
ncd/modules/concatlist.c

@@ -29,6 +29,7 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <ncd/NCDModule.h>
 
@@ -40,7 +41,7 @@ struct instance {
     NCDModuleInst *i;
 };
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -48,6 +49,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -59,26 +61,30 @@ static void * func_new (NCDModuleInst *i)
             ModuleLog(i, BLOG_ERROR, "wrong type");
             goto fail1;
         }
-        
         arg = NCDValue_ListNext(o->i->args, arg);
     }
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static int func_getvar (void *vo, const char *name, NCDValue *out)
@@ -125,7 +131,7 @@ static const struct NCDModule modules[] = {
     {
         .type = "concatlist",
         .func_new = func_new,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = NULL

+ 53 - 32
ncd/modules/depend.c

@@ -31,8 +31,10 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <misc/offset.h>
+#include <misc/debug.h>
 #include <structure/LinkedList2.h>
 #include <ncd/NCDModule.h>
 
@@ -85,7 +87,7 @@ static int func_globalinit (struct NCDModuleInitParams params)
     return 1;
 }
 
-static void * provide_func_new (NCDModuleInst *i)
+static void provide_func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct provide *o = malloc(sizeof(*o));
@@ -93,6 +95,7 @@ static void * provide_func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -124,6 +127,11 @@ static void * provide_func_new (NCDModuleInst *i)
     // set not dying
     o->dying = 0;
     
+    // signal up.
+    // This comes above the loop which follows, so that effects on related depend statements are
+    // computed before this process advances, avoiding problems like failed variable resolutions.
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    
     // attach free depends with this name
     LinkedList2Iterator it;
     LinkedList2Iterator_InitForward(&it, &free_depends);
@@ -139,36 +147,37 @@ static void * provide_func_new (NCDModuleInst *i)
         // remove from free depends list
         LinkedList2_Remove(&free_depends, &d->node);
         
-        // set provide
-        d->p = o;
-        
         // insert to provide's list
         LinkedList2_Append(&o->depends, &d->node);
         
+        // set provide
+        d->p = o;
+        
         // signal up
         NCDModuleInst_Backend_Event(d->i, NCDMODULE_EVENT_UP);
     }
     
-    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
-    
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void provide_func_free (void *vo)
+static void provide_free (struct provide *o)
 {
-    struct provide *o = vo;
     ASSERT(LinkedList2_IsEmpty(&o->depends))
+    NCDModuleInst *i = o->i;
     
     // remove from provides list
     LinkedList2_Remove(&provides, &o->provides_node);
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static void provide_func_die (void *vo)
@@ -178,7 +187,7 @@ static void provide_func_die (void *vo)
     
     // if we have no depends, die immediately
     if (LinkedList2_IsEmpty(&o->depends)) {
-        NCDModuleInst_Backend_Died(o->i, 0);
+        provide_free(o);
         return;
     }
     
@@ -198,7 +207,7 @@ static void provide_func_die (void *vo)
     }
 }
 
-static void * depend_func_new (NCDModuleInst *i)
+static void depend_func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct depend *o = malloc(sizeof(*o));
@@ -206,6 +215,7 @@ static void * depend_func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -223,35 +233,37 @@ static void * depend_func_new (NCDModuleInst *i)
     o->name = NCDValue_StringValue(name_arg);
     
     // find a provide with our name
-    o->p = find_provide(o->name);
+    struct provide *p = find_provide(o->name);
     
-    // do not attach to a dying provide
-    if (o->p && o->p->dying) {
-        o->p = NULL;
-    }
-    
-    if (o->p) {
+    if (p && !p->dying) {
         // insert to provide's list
-        LinkedList2_Append(&o->p->depends, &o->node);
+        LinkedList2_Append(&p->depends, &o->node);
+        
+        // set provide
+        o->p = p;
         
         // signal up
         NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     } else {
         // insert to free depends list
         LinkedList2_Append(&free_depends, &o->node);
+        
+        // set no provide
+        o->p = NULL;
     }
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void depend_func_free (void *vo)
+static void depend_free (struct depend *o)
 {
-    struct depend *o = vo;
+    NCDModuleInst *i = o->i;
     
     if (o->p) {
         // remove from provide's list
@@ -259,7 +271,7 @@ static void depend_func_free (void *vo)
         
         // if provide is dying and is empty, let it die
         if (o->p->dying && LinkedList2_IsEmpty(&o->p->depends)) {
-            NCDModuleInst_Backend_Died(o->p->i, 0);
+            provide_free(o->p);
         }
     } else {
         // remove free depends list
@@ -268,6 +280,15 @@ static void depend_func_free (void *vo)
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void depend_func_die (void *vo)
+{
+    struct depend *o = vo;
+    
+    depend_free(o);
 }
 
 static void depend_func_clean (void *vo)
@@ -281,17 +302,17 @@ static void depend_func_clean (void *vo)
     struct provide *p = o->p;
     
     // remove from provide's list
-    LinkedList2_Remove(&o->p->depends, &o->node);
-    
-    // set no provide
-    o->p = NULL;
+    LinkedList2_Remove(&p->depends, &o->node);
     
     // insert to free depends list
     LinkedList2_Append(&free_depends, &o->node);
     
+    // set no provide
+    o->p = NULL;
+    
     // if provide is empty, let it die
     if (LinkedList2_IsEmpty(&p->depends)) {
-        NCDModuleInst_Backend_Died(p->i, 0);
+        provide_free(p);
     }
 }
 
@@ -299,6 +320,7 @@ static int depend_func_getvar (void *vo, const char *name_orig, NCDValue *out)
 {
     struct depend *o = vo;
     ASSERT(o->p)
+    ASSERT(!o->p->dying)
     
     int ret = 0;
     
@@ -332,12 +354,11 @@ static const struct NCDModule modules[] = {
     {
         .type = "provide",
         .func_new = provide_func_new,
-        .func_free = provide_func_free,
         .func_die = provide_func_die
     }, {
         .type = "depend",
         .func_new = depend_func_new,
-        .func_free = depend_func_free,
+        .func_die = depend_func_die,
         .func_clean = depend_func_clean,
         .func_getvar = depend_func_getvar
     }, {

+ 18 - 10
ncd/modules/if.c

@@ -45,7 +45,7 @@ struct instance {
     NCDModuleInst *i;
 };
 
-static void * new_templ (NCDModuleInst *i, int not)
+static void new_templ (NCDModuleInst *i, int not)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -53,6 +53,7 @@ static void * new_templ (NCDModuleInst *i, int not)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -68,46 +69,53 @@ static void * new_templ (NCDModuleInst *i, int not)
         goto fail1;
     }
     
+    // compute logical value of argument
     int c = !strcmp(NCDValue_StringValue(arg), "true");
+    
+    // signal up if needed
     if ((not && !c) || (!not && c)) {
         NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     }
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
-    return new_templ(i, 0);
+    new_templ(i, 0);
 }
 
-static void * func_new_not (NCDModuleInst *i)
+static void func_new_not (NCDModuleInst *i)
 {
-    return new_templ(i, 1);
+    new_templ(i, 1);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static const struct NCDModule modules[] = {
     {
         .type = "if",
         .func_new = func_new,
-        .func_free = func_free
+        .func_die = func_die
     }, {
         .type = "ifnot",
         .func_new = func_new_not,
-        .func_free = func_free
+        .func_die = func_die
     }, {
         .type = NULL
     }

+ 13 - 7
ncd/modules/ip_in_network.c

@@ -44,7 +44,7 @@ struct instance {
     int value;
 };
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -52,6 +52,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -74,11 +75,11 @@ static void * func_new (NCDModuleInst *i)
     uint32_t addr2;
     int netprefix;
     if (!ipaddr_parse_ipv4_addr(NCDValue_StringValue(arg_addr1), &addr1)) {
-        ModuleLog(o->i, BLOG_ERROR, "wrong addr");
+        ModuleLog(o->i, BLOG_ERROR, "wrong addr1");
         goto fail1;
     }
     if (!ipaddr_parse_ipv4_addr(NCDValue_StringValue(arg_addr2), &addr2)) {
-        ModuleLog(o->i, BLOG_ERROR, "wrong netaddr");
+        ModuleLog(o->i, BLOG_ERROR, "wrong addr2");
         goto fail1;
     }
     if (!ipaddr_parse_ipv4_prefix(NCDValue_StringValue(arg_netprefix), &netprefix)) {
@@ -89,22 +90,27 @@ static void * func_new (NCDModuleInst *i)
     // test
     o->value = ipaddr_ipv4_addrs_in_network(addr1, addr2, netprefix);
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static int func_getvar (void *vo, const char *name, NCDValue *out)
@@ -129,7 +135,7 @@ static const struct NCDModule modules[] = {
     {
         .type = "ip_in_network",
         .func_new = func_new,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = NULL

+ 12 - 5
ncd/modules/list.c

@@ -29,6 +29,7 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <ncd/NCDModule.h>
 
@@ -40,7 +41,7 @@ struct instance {
     NCDModuleInst *i;
 };
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -48,24 +49,30 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static int func_getvar (void *vo, const char *name, NCDValue *out)
@@ -88,7 +95,7 @@ static const struct NCDModule modules[] = {
     {
         .type = "list",
         .func_new = func_new,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = NULL

+ 20 - 13
ncd/modules/logical.c

@@ -50,7 +50,7 @@ struct instance {
     int value;
 };
 
-static void * func_new (NCDModuleInst *i, int not, int or)
+static void func_new (NCDModuleInst *i, int not, int or)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -58,10 +58,12 @@ static void * func_new (NCDModuleInst *i, int not, int or)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
     
+    // compute value from arguments
     if (not) {
         NCDValue *arg;
         if (!NCDValue_ListRead(o->i->args, 1, &arg)) {
@@ -93,37 +95,42 @@ static void * func_new (NCDModuleInst *i, int not, int or)
         }
     }
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void * func_new_not (NCDModuleInst *i)
+static void func_new_not (NCDModuleInst *i)
 {
-    return func_new(i, 1, 0);
+    func_new(i, 1, 0);
 }
 
-static void * func_new_or (NCDModuleInst *i)
+static void func_new_or (NCDModuleInst *i)
 {
-    return func_new(i, 0, 1);
+    func_new(i, 0, 1);
 }
 
-static void * func_new_and (NCDModuleInst *i)
+static void func_new_and (NCDModuleInst *i)
 {
-    return func_new(i, 0, 0);
+    func_new(i, 0, 0);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static int func_getvar (void *vo, const char *name, NCDValue *out)
@@ -148,17 +155,17 @@ static const struct NCDModule modules[] = {
     {
         .type = "not",
         .func_new = func_new_not,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = "or",
         .func_new = func_new_or,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = "and",
         .func_new = func_new_and,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = NULL

+ 6 - 2
ncd/modules/modules.h

@@ -36,13 +36,15 @@ extern const struct NCDModuleGroup ncdmodule_concatlist;
 extern const struct NCDModuleGroup ncdmodule_if;
 extern const struct NCDModuleGroup ncdmodule_strcmp;
 extern const struct NCDModuleGroup ncdmodule_logical;
+extern const struct NCDModuleGroup ncdmodule_sleep;
 extern const struct NCDModuleGroup ncdmodule_ip_in_network;
 extern const struct NCDModuleGroup ncdmodule_run;
-extern const struct NCDModuleGroup ncdmodule_net_backend_physical;
 extern const struct NCDModuleGroup ncdmodule_net_backend_waitdevice;
+extern const struct NCDModuleGroup ncdmodule_net_backend_waitlink;
 extern const struct NCDModuleGroup ncdmodule_net_backend_badvpn;
 extern const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant;
 extern const struct NCDModuleGroup ncdmodule_net_backend_rfkill;
+extern const struct NCDModuleGroup ncdmodule_net_up;
 extern const struct NCDModuleGroup ncdmodule_net_dns;
 extern const struct NCDModuleGroup ncdmodule_net_iptables;
 extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr;
@@ -59,13 +61,15 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_if,
     &ncdmodule_strcmp,
     &ncdmodule_logical,
+    &ncdmodule_sleep,
     &ncdmodule_ip_in_network,
     &ncdmodule_run,
-    &ncdmodule_net_backend_physical,
     &ncdmodule_net_backend_waitdevice,
+    &ncdmodule_net_backend_waitlink,
     &ncdmodule_net_backend_badvpn,
     &ncdmodule_net_backend_wpa_supplicant,
     &ncdmodule_net_backend_rfkill,
+    &ncdmodule_net_up,
     &ncdmodule_net_dns,
     &ncdmodule_net_iptables,
     &ncdmodule_net_ipv4_addr,

+ 56 - 33
ncd/modules/multidepend.c

@@ -38,8 +38,10 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <misc/offset.h>
+#include <misc/debug.h>
 #include <structure/LinkedList2.h>
 #include <ncd/NCDModule.h>
 
@@ -106,30 +108,32 @@ static void depend_update (struct depend *o)
     
     // find best provide
     struct provide *bp = depend_find_best_provide(o);
+    ASSERT(!bp || !bp->dying)
     
     // has anything changed?
     if (bp == o->provide) {
         return;
     }
     
-    // if we have an existing provide, start collpsing
     if (o->provide) {
-        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
+        // set collapsing
         o->provide_collapsing = 1;
-        return;
+        
+        // signal down
+        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
+    } else {
+        // insert to provide's list
+        LinkedList2_Append(&bp->depends, &o->provide_node);
+        
+        // set not collapsing
+        o->provide_collapsing = 0;
+        
+        // set provide
+        o->provide = bp;
+        
+        // signal up
+        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     }
-    
-    // insert to provide's list
-    LinkedList2_Append(&bp->depends, &o->provide_node);
-    
-    // set not collapsing
-    o->provide_collapsing = 0;
-    
-    // set provide
-    o->provide = bp;
-    
-    // signal up
-    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
 }
 
 static int func_globalinit (struct NCDModuleInitParams params)
@@ -143,7 +147,7 @@ static int func_globalinit (struct NCDModuleInitParams params)
     return 1;
 }
 
-static void * provide_func_new (NCDModuleInst *i)
+static void provide_func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct provide *o = malloc(sizeof(*o));
@@ -151,6 +155,7 @@ static void * provide_func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -182,6 +187,11 @@ static void * provide_func_new (NCDModuleInst *i)
     // set not dying
     o->dying = 0;
     
+    // signal up.
+    // This comes above the loop which follows, so that effects on related depend statements are
+    // computed before this process advances, avoiding problems like failed variable resolutions.
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    
     // update depends
     LinkedList2Iterator it;
     LinkedList2Iterator_InitForward(&it, &depends);
@@ -191,27 +201,27 @@ static void * provide_func_new (NCDModuleInst *i)
         depend_update(d);
     }
     
-    // signal up
-    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
-    
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void provide_func_free (void *vo)
+static void provide_free (struct provide *o)
 {
-    struct provide *o = vo;
     ASSERT(LinkedList2_IsEmpty(&o->depends))
+    NCDModuleInst *i = o->i;
     
     // remove from provides list
     LinkedList2_Remove(&provides, &o->provides_node);
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static void provide_func_die (void *vo)
@@ -221,7 +231,7 @@ static void provide_func_die (void *vo)
     
     // if we have no depends, die immediately
     if (LinkedList2_IsEmpty(&o->depends)) {
-        NCDModuleInst_Backend_Died(o->i, 0);
+        provide_free(o);
         return;
     }
     
@@ -235,11 +245,13 @@ static void provide_func_die (void *vo)
     while (n = LinkedList2Iterator_Next(&it)) {
         struct depend *d = UPPER_OBJECT(n, struct depend, provide_node);
         ASSERT(d->provide == o)
+        
+        // update depend to make sure it is collapsing
         depend_update(d);
     }
 }
 
-static void * depend_func_new (NCDModuleInst *i)
+static void depend_func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct depend *o = malloc(sizeof(*o));
@@ -247,6 +259,7 @@ static void * depend_func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -282,17 +295,18 @@ static void * depend_func_new (NCDModuleInst *i)
     // update
     depend_update(o);
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void depend_func_free (void *vo)
+static void depend_free (struct depend *o)
 {
-    struct depend *o = vo;
+    NCDModuleInst *i = o->i;
     
     if (o->provide) {
         // remove from provide's list
@@ -300,7 +314,7 @@ static void depend_func_free (void *vo)
         
         // if provide is dying and is empty, let it die
         if (o->provide->dying && LinkedList2_IsEmpty(&o->provide->depends)) {
-            NCDModuleInst_Backend_Died(o->provide->i, 0);
+            provide_free(o->provide);
         }
     }
     
@@ -309,6 +323,15 @@ static void depend_func_free (void *vo)
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void depend_func_die (void *vo)
+{
+    struct depend *o = vo;
+    
+    depend_free(o);
 }
 
 static void depend_func_clean (void *vo)
@@ -324,7 +347,7 @@ static void depend_func_clean (void *vo)
     
     // if provide is dying and is empty, let it die
     if (o->provide->dying && LinkedList2_IsEmpty(&o->provide->depends)) {
-        NCDModuleInst_Backend_Died(o->provide->i, 0);
+        provide_free(o->provide);
     }
     
     // set no provide
@@ -339,6 +362,7 @@ static int depend_func_getvar (void *vo, const char *name_orig, NCDValue *out)
     struct depend *o = vo;
     ASSERT(o->provide)
     ASSERT(!o->provide_collapsing)
+    ASSERT(!o->provide->dying)
     
     int ret = 0;
     
@@ -372,12 +396,11 @@ static const struct NCDModule modules[] = {
     {
         .type = "multiprovide",
         .func_new = provide_func_new,
-        .func_free = provide_func_free,
         .func_die = provide_func_die
     }, {
         .type = "multidepend",
         .func_new = depend_func_new,
-        .func_free = depend_func_free,
+        .func_die = depend_func_die,
         .func_clean = depend_func_clean,
         .func_getvar = depend_func_getvar
     }, {

+ 40 - 33
ncd/modules/net_backend_badvpn.c

@@ -54,6 +54,7 @@ struct instance {
 static void try_process (struct instance *o);
 static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status);
 static void timer_handler (struct instance *o);
+static void instance_free (struct instance *o);
 
 void try_process (struct instance *o)
 {
@@ -72,19 +73,13 @@ void try_process (struct instance *o)
         goto fail1;
     }
     
-    // iterate arguments
+    // append arguments
     NCDValue *arg = NCDValue_ListFirst(o->args);
     while (arg) {
-        if (NCDValue_Type(arg) != NCDVALUE_STRING) {
-            ModuleLog(o->i, BLOG_ERROR, "wrong type");
-            goto fail1;
-        }
-        
         // append argument
         if (!CmdLine_Append(&c, NCDValue_StringValue(arg))) {
             goto fail1;
         }
-        
         arg = NCDValue_ListNext(o->args, arg);
     }
     
@@ -127,7 +122,7 @@ void process_handler (struct instance *o, int normally, uint8_t normally_exit_st
     o->started = 0;
     
     if (o->dying) {
-        NCDModuleInst_Backend_Died(o->i, 0);
+        instance_free(o);
         return;
     }
     
@@ -141,10 +136,11 @@ void timer_handler (struct instance *o)
     
     ModuleLog(o->i, BLOG_INFO, "retrying");
     
+    // try starting process again
     try_process(o);
 }
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -152,6 +148,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -175,6 +172,16 @@ static void * func_new (NCDModuleInst *i)
     o->exec = NCDValue_StringValue(exec_arg);
     o->args = args_arg;
     
+    // check arguments
+    NCDValue *arg = NCDValue_ListFirst(o->args);
+    while (arg) {
+        if (NCDValue_Type(arg) != NCDVALUE_STRING) {
+            ModuleLog(o->i, BLOG_ERROR, "wrong type");
+            goto fail1;
+        }
+        arg = NCDValue_ListNext(o->args, arg);
+    }
+    
     // create TAP device
     if (!NCDIfConfig_make_tuntap(o->ifname, o->user, 0)) {
         ModuleLog(o->i, BLOG_ERROR, "failed to create TAP device");
@@ -193,11 +200,13 @@ static void * func_new (NCDModuleInst *i)
     // init timer
     BTimer_Init(&o->timer, RETRY_TIME, (BTimer_handler)timer_handler, o);
     
-    try_process(o);
-    
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    // try starting process
+    try_process(o);
+    
+    return;
     
 fail2:
     if (!NCDIfConfig_remove_tuntap(o->ifname, 0)) {
@@ -206,24 +215,23 @@ fail2:
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+void instance_free (struct instance *o)
 {
-    struct instance *o = vo;
-    
-    if (o->started) {
-        // kill process
-        BProcess_Kill(&o->process);
-        
-        // free process
-        BProcess_Free(&o->process);
-    }
+    ASSERT(!o->started)
+    NCDModuleInst *i = o->i;
     
     // free timer
     BReactor_RemoveTimer(o->i->reactor, &o->timer);
     
+    // set device down
+    if (!NCDIfConfig_set_down(o->ifname)) {
+        ModuleLog(o->i, BLOG_ERROR, "failed to set device down");
+    }
+    
     // free TAP device
     if (!NCDIfConfig_remove_tuntap(o->ifname, 0)) {
         ModuleLog(o->i, BLOG_ERROR, "failed to remove TAP device");
@@ -231,6 +239,8 @@ static void func_free (void *vo)
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static void func_die (void *vo)
@@ -238,25 +248,22 @@ static void func_die (void *vo)
     struct instance *o = vo;
     ASSERT(!o->dying)
     
-    if (o->started) {
-        // request termination
-        BProcess_Terminate(&o->process);
-        
-        // remember dying
-        o->dying = 1;
-        
+    if (!o->started) {
+        instance_free(o);
         return;
     }
     
-    NCDModuleInst_Backend_Died(o->i, 0);
-    return;
+    // request termination
+    BProcess_Terminate(&o->process);
+    
+    // remember dying
+    o->dying = 1;
 }
 
 static const struct NCDModule modules[] = {
     {
         .type = "net.backend.badvpn",
         .func_new = func_new,
-        .func_free = func_free,
         .func_die = func_die
     }, {
         .type = NULL

+ 0 - 210
ncd/modules/net_backend_physical.c

@@ -1,210 +0,0 @@
-/**
- * @file net_backend_physical.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
- * 
- * Physical network interface module.
- * 
- * Synopsis: net.backend.physical(string ifname)
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <ncd/NCDModule.h>
-#include <ncd/NCDIfConfig.h>
-#include <ncd/NCDInterfaceMonitor.h>
-
-#include <generated/blog_channel_ncd_net_backend_physical.h>
-
-#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
-
-#define STATE_WAITDEVICE 1
-#define STATE_WAITLINK 2
-#define STATE_FINISHED 3
-
-struct instance {
-    NCDModuleInst *i;
-    const char *ifname;
-    NCDInterfaceMonitor monitor;
-    int state;
-};
-
-static int try_start (struct instance *o)
-{
-    // query interface state
-    int flags = NCDIfConfig_query(o->ifname);
-    
-    if (!(flags&NCDIFCONFIG_FLAG_EXISTS)) {
-        ModuleLog(o->i, BLOG_INFO, "device doesn't exist");
-        
-        // waiting for device
-        o->state = STATE_WAITDEVICE;
-    } else {
-        if ((flags&NCDIFCONFIG_FLAG_UP)) {
-            ModuleLog(o->i, BLOG_ERROR, "device already up - NOT configuring");
-            return 0;
-        }
-        
-        // set interface up
-        if (!NCDIfConfig_set_up(o->ifname)) {
-            ModuleLog(o->i, BLOG_ERROR, "failed to set device up");
-            return 0;
-        }
-        
-        ModuleLog(o->i, BLOG_INFO, "waiting for link");
-        
-        // waiting for link
-        o->state = STATE_WAITLINK;
-    }
-    
-    return 1;
-}
-
-static void monitor_handler (struct instance *o, const char *ifname, int if_flags)
-{
-    if (strcmp(ifname, o->ifname)) {
-        return;
-    }
-    
-    if (!(if_flags&NCDIFCONFIG_FLAG_EXISTS)) {
-        if (o->state > STATE_WAITDEVICE) {
-            int prev_state = o->state;
-            
-            ModuleLog(o->i, BLOG_INFO, "device down");
-            
-            // set state
-            o->state = STATE_WAITDEVICE;
-            
-            // report
-            if (prev_state == STATE_FINISHED) {
-                NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
-            }
-        }
-    } else {
-        if (o->state == STATE_WAITDEVICE) {
-            ModuleLog(o->i, BLOG_INFO, "device up");
-            
-            if (!try_start(o)) {
-                NCDModuleInst_Backend_Died(o->i, 1);
-                return;
-            }
-            
-            return;
-        }
-        
-        if ((if_flags&NCDIFCONFIG_FLAG_RUNNING)) {
-            if (o->state == STATE_WAITLINK) {
-                ModuleLog(o->i, BLOG_INFO, "link up");
-                
-                // set state
-                o->state = STATE_FINISHED;
-                
-                // report
-                NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
-            }
-        } else {
-            if (o->state == STATE_FINISHED) {
-                ModuleLog(o->i, BLOG_INFO, "link down");
-                
-                // set state
-                o->state = STATE_WAITLINK;
-                
-                // report
-                NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
-            }
-        }
-    }
-}
-
-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;
-    }
-    
-    // init arguments
-    o->i = i;
-    
-    // check arguments
-    NCDValue *arg;
-    if (!NCDValue_ListRead(i->args, 1, &arg)) {
-        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
-        goto fail1;
-    }
-    if (NCDValue_Type(arg) != NCDVALUE_STRING) {
-        ModuleLog(o->i, BLOG_ERROR, "wrong type");
-        goto fail1;
-    }
-    o->ifname = NCDValue_StringValue(arg);
-    
-    // init monitor
-    if (!NCDInterfaceMonitor_Init(&o->monitor, o->i->reactor, (NCDInterfaceMonitor_handler)monitor_handler, o)) {
-        ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed");
-        goto fail1;
-    }
-    
-    if (!try_start(o)) {
-        goto fail2;
-    }
-    
-    return o;
-    
-fail2:
-    NCDInterfaceMonitor_Free(&o->monitor);
-fail1:
-    free(o);
-fail0:
-    return NULL;
-}
-
-static void func_free (void *vo)
-{
-    struct instance *o = vo;
-    
-    // set interface down
-    if (o->state > STATE_WAITDEVICE) {
-        NCDIfConfig_set_down(o->ifname);
-    }
-    
-    // free monitor
-    NCDInterfaceMonitor_Free(&o->monitor);
-    
-    // free instance
-    free(o);
-}
-
-static const struct NCDModule modules[] = {
-    {
-        .type = "net.backend.physical",
-        .func_new = func_new,
-        .func_free = func_free
-    }, {
-        .type = NULL
-    }
-};
-
-const struct NCDModuleGroup ncdmodule_net_backend_physical = {
-    .modules = modules
-};

+ 10 - 5
ncd/modules/net_backend_rfkill.c

@@ -123,7 +123,7 @@ static void monitor_handler (struct instance *o, struct rfkill_event event)
     }
 }
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -131,6 +131,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -175,30 +176,34 @@ static void * func_new (NCDModuleInst *i)
     // set not up
     o->up = 0;
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free monitor
     NCDRfkillMonitor_Free(&o->monitor);
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static const struct NCDModule modules[] = {
     {
         .type = "net.backend.rfkill",
         .func_new = func_new,
-        .func_free = func_free
+        .func_die = func_die
     }, {
         .type = NULL
     }

+ 11 - 5
ncd/modules/net_backend_waitdevice.c

@@ -61,7 +61,7 @@ static void monitor_handler (struct instance *o, const char *ifname, int if_flag
     }
 }
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -69,6 +69,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -94,34 +95,39 @@ static void * func_new (NCDModuleInst *i)
     // query initial state
     o->up = !!(NCDIfConfig_query(o->ifname) & NCDIFCONFIG_FLAG_EXISTS);
     
+    // signal up if needed
     if (o->up) {
         NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     }
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free monitor
     NCDInterfaceMonitor_Free(&o->monitor);
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static const struct NCDModule modules[] = {
     {
         .type = "net.backend.waitdevice",
         .func_new = func_new,
-        .func_free = func_free
+        .func_die = func_die
     }, {
         .type = NULL
     }

+ 138 - 0
ncd/modules/net_backend_waitlink.c

@@ -0,0 +1,138 @@
+/**
+ * @file net_backend_waitlink.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
+ * 
+ * Module which waits for the link on a network interface.
+ * 
+ * Synopsis: net.backend.waitlink(string ifname)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <ncd/NCDModule.h>
+#include <ncd/NCDIfConfig.h>
+#include <ncd/NCDInterfaceMonitor.h>
+
+#include <generated/blog_channel_ncd_net_backend_waitlink.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct instance {
+    NCDModuleInst *i;
+    const char *ifname;
+    NCDInterfaceMonitor monitor;
+    int up;
+};
+
+static void monitor_handler (struct instance *o, const char *ifname, int if_flags)
+{
+    if (strcmp(ifname, o->ifname)) {
+        return;
+    }
+    
+    int was_up = o->up;
+    o->up = !!(if_flags & NCDIFCONFIG_FLAG_RUNNING);
+    
+    if (o->up && !was_up) {
+        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    }
+    else if (!o->up && was_up) {
+        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
+    }
+}
+
+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 *arg;
+    if (!NCDValue_ListRead(i->args, 1, &arg)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(arg) != NCDVALUE_STRING) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    o->ifname = NCDValue_StringValue(arg);
+    
+    // init monitor
+    if (!NCDInterfaceMonitor_Init(&o->monitor, o->i->reactor, (NCDInterfaceMonitor_handler)monitor_handler, o)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed");
+        goto fail1;
+    }
+    
+    // query initial state
+    o->up = !!(NCDIfConfig_query(o->ifname) & NCDIFCONFIG_FLAG_RUNNING);
+    
+    // signal up if needed
+    if (o->up) {
+        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    }
+    
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    NCDModuleInst *i = o->i;
+    
+    // free monitor
+    NCDInterfaceMonitor_Free(&o->monitor);
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "net.backend.waitlink",
+        .func_new = func_new,
+        .func_die = func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_net_backend_waitlink = {
+    .modules = modules
+};

+ 14 - 16
ncd/modules/net_backend_wpa_supplicant.c

@@ -73,6 +73,7 @@ static void free_pipe (struct instance *o);
 static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status);
 static void process_pipe_handler_error (struct instance *o, int component, int code);
 static void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len);
+static void instance_free (struct instance *o);
 
 int build_cmdline (struct instance *o, CmdLine *c)
 {
@@ -176,13 +177,12 @@ void process_handler (struct instance *o, int normally, uint8_t normally_exit_st
 {
     ModuleLog(o->i, (o->dying ? BLOG_INFO : BLOG_ERROR), "process terminated");
     
-    int was_dying = o->dying;
-    
-    // set dying so free doesn't attempt kill
-    o->dying = 1;
+    if (!o->dying) {
+        NCDModuleInst_Backend_SetError(o->i);
+    }
     
     // die
-    NCDModuleInst_Backend_Died(o->i, !was_dying);
+    instance_free(o);
     return;
 }
 
@@ -236,7 +236,7 @@ void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len)
     }
 }
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -244,6 +244,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -306,7 +307,7 @@ static void * func_new (NCDModuleInst *i)
     CmdLine_Free(&c);
     ASSERT_FORCE(close(pipefds[1]) == 0)
     
-    return o;
+    return;
     
 fail4:
     CmdLine_Free(&c);
@@ -318,17 +319,13 @@ fail2:
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+void instance_free (struct instance *o)
 {
-    struct instance *o = vo;
-    
-    if (!o->dying) {
-        // kill process
-        BProcess_Kill(&o->process);
-    }
+    NCDModuleInst *i = o->i;
     
     // free process
     BProcess_Free(&o->process);
@@ -343,6 +340,8 @@ static void func_free (void *vo)
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static void func_die (void *vo)
@@ -361,7 +360,6 @@ static const struct NCDModule modules[] = {
     {
         .type = "net.backend.wpa_supplicant",
         .func_new = func_new,
-        .func_free = func_free,
         .func_die = func_die
     }, {
         .type = NULL

+ 11 - 6
ncd/modules/net_dns.c

@@ -26,7 +26,6 @@
  * Synopsis: net.dns(list(string) servers, string priority)
  */
 
-#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -180,7 +179,7 @@ static int func_globalinit (struct NCDModuleInitParams params)
     return 1;
 }
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -188,6 +187,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -240,9 +240,10 @@ static void * func_new (NCDModuleInst *i)
         goto fail2;
     }
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail2:
     LinkedList2_Remove(&instances, &o->instances_node);
@@ -250,12 +251,14 @@ fail1:
     remove_ipv4_dns_entries(o);
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // remove from instances
     LinkedList2_Remove(&instances, &o->instances_node);
@@ -268,13 +271,15 @@ static void func_free (void *vo)
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static const struct NCDModule modules[] = {
     {
         .type = "net.dns",
         .func_new = func_new,
-        .func_free = func_free
+        .func_die = func_die
     }, {
         .type = NULL
     }

+ 27 - 20
ncd/modules/net_iptables.c

@@ -40,9 +40,12 @@
 
 #define IPTABLES_PATH "/sbin/iptables"
 
+static void template_free_func (void *vo, int is_error);
+
 BEventLock iptables_lock;
 
 struct instance {
+    NCDModuleInst *i;
     command_template_instance cti;
 };
 
@@ -183,43 +186,49 @@ static void func_globalfree (void)
     BEventLock_Free(&iptables_lock);
 }
 
-static void * func_new (NCDModuleInst *i, command_template_build_cmdline build_cmdline)
+static void func_new (NCDModuleInst *i, command_template_build_cmdline build_cmdline)
 {
+    // allocate instance
     struct instance *o = malloc(sizeof(*o));
     if (!o) {
         BLog(BLOG_ERROR, "malloc failed");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
-    if (!command_template_new(&o->cti, i, build_cmdline, BLOG_CURRENT_CHANNEL, &iptables_lock)) {
-        goto fail1;
-    }
+    // init arguments
+    o->i = i;
     
-    return o;
+    command_template_new(&o->cti, i, build_cmdline, template_free_func, o, BLOG_CURRENT_CHANNEL, &iptables_lock);
+    return;
     
-fail1:
-    free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void * append_func_new (NCDModuleInst *i)
+void template_free_func (void *vo, int is_error)
 {
-    return func_new(i, build_append_cmdline);
+    struct instance *o = vo;
+    NCDModuleInst *i = o->i;
+    
+    // free instance
+    free(o);
+    
+    if (is_error) {
+        NCDModuleInst_Backend_SetError(i);
+    }
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void * policy_func_new (NCDModuleInst *i)
+static void append_func_new (NCDModuleInst *i)
 {
-    return func_new(i, build_policy_cmdline);
+    func_new(i, build_append_cmdline);
 }
 
-static void func_free (void *vo)
+static void policy_func_new (NCDModuleInst *i)
 {
-    struct instance *o = vo;
-    
-    command_template_free(&o->cti);
-    
-    free(o);
+    func_new(i, build_policy_cmdline);
 }
 
 static void func_die (void *vo)
@@ -233,12 +242,10 @@ static const struct NCDModule modules[] = {
     {
         .type = "net.iptables.append",
         .func_new = append_func_new,
-        .func_free = func_free,
         .func_die = func_die
     }, {
         .type = "net.iptables.policy",
         .func_new = policy_func_new,
-        .func_free = func_free,
         .func_die = func_die
     }, {
         .type = NULL

+ 11 - 5
ncd/modules/net_ipv4_addr.c

@@ -42,7 +42,7 @@ struct instance {
     struct ipv4_ifaddr ifaddr;
 };
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -50,6 +50,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -82,19 +83,22 @@ static void * func_new (NCDModuleInst *i)
         goto fail1;
     }
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // remove address
     if (!NCDIfConfig_remove_ipv4_addr(o->ifname, o->ifaddr)) {
@@ -103,13 +107,15 @@ static void func_free (void *vo)
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static const struct NCDModule modules[] = {
     {
         .type = "net.ipv4.addr",
         .func_new = func_new,
-        .func_free = func_free
+        .func_die = func_die
     }, {
         .type = NULL
     }

+ 26 - 11
ncd/modules/net_ipv4_dhcp.c

@@ -47,23 +47,29 @@
 struct instance {
     NCDModuleInst *i;
     BDHCPClient dhcp;
+    int up;
 };
 
 static void dhcp_handler (struct instance *o, int event)
 {
     switch (event) {
-        case BDHCPCLIENT_EVENT_UP:
+        case BDHCPCLIENT_EVENT_UP: {
+            ASSERT(!o->up)
+            o->up = 1;
             NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
-            break;
-        case BDHCPCLIENT_EVENT_DOWN:
+        } break;
+        
+        case BDHCPCLIENT_EVENT_DOWN: {
+            ASSERT(o->up)
+            o->up = 0;
             NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
-            break;
-        default:
-            ASSERT(0);
+        } break;
+        
+        default: ASSERT(0);
     }
 }
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -71,6 +77,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -93,28 +100,36 @@ static void * func_new (NCDModuleInst *i)
         goto fail1;
     }
     
-    return o;
+    // set not up
+    o->up = 0;
+    
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free DHCP
     BDHCPClient_Free(&o->dhcp);
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static int func_getvar (void *vo, const char *name, NCDValue *out)
 {
     struct instance *o = vo;
+    ASSERT(o->up)
     
     if (!strcmp(name, "addr")) {
         uint32_t addr;
@@ -207,7 +222,7 @@ static const struct NCDModule modules[] = {
     {
         .type = "net.ipv4.dhcp",
         .func_new = func_new,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = NULL

+ 11 - 5
ncd/modules/net_ipv4_route.c

@@ -45,7 +45,7 @@ struct instance {
     const char *ifname;
 };
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -53,6 +53,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -107,19 +108,22 @@ static void * func_new (NCDModuleInst *i)
         goto fail1;
     }
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // remove route
     if (!NCDIfConfig_remove_ipv4_route(o->dest, (o->have_gateway ? &o->gateway : NULL), o->metric, o->ifname)) {
@@ -128,13 +132,15 @@ static void func_free (void *vo)
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static const struct NCDModule modules[] = {
     {
         .type = "net.ipv4.route",
         .func_new = func_new,
-        .func_free = func_free
+        .func_die = func_die
     }, {
         .type = NULL
     }

+ 116 - 0
ncd/modules/net_up.c

@@ -0,0 +1,116 @@
+/**
+ * @file net_up.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
+ * 
+ * Network interface up and down module.
+ * 
+ * Synopsis: net.up(string ifname)
+ * Description: Sets a network interface up on initialization and down on
+ *   deinitialization.
+ */
+
+#include <stdlib.h>
+
+#include <ncd/NCDModule.h>
+#include <ncd/NCDIfConfig.h>
+
+#include <generated/blog_channel_ncd_net_up.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct instance {
+    NCDModuleInst *i;
+    const char *ifname;
+};
+
+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
+    NCDValue *ifname_arg;
+    if (!NCDValue_ListRead(o->i->args, 1, &ifname_arg)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(ifname_arg) != NCDVALUE_STRING) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    o->ifname = NCDValue_StringValue(ifname_arg);
+    
+    // set interface up
+    if (!NCDIfConfig_set_up(o->ifname)) {
+        ModuleLog(o->i, BLOG_ERROR, "failed to set interface up");
+        goto fail1;
+    }
+    
+    // signal up
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    NCDModuleInst *i = o->i;
+    
+    // set interface down
+    if (!NCDIfConfig_set_down(o->ifname)) {
+        ModuleLog(o->i, BLOG_ERROR, "failed to set interface down");
+    }
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "net.up",
+        .func_new = func_new,
+        .func_die = func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_net_up = {
+    .modules = modules
+};

+ 23 - 14
ncd/modules/run.c

@@ -42,7 +42,10 @@
 
 #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
 
+static void template_free_func (void *vo, int is_error);
+
 struct instance {
+    NCDModuleInst *i;
     BEventLock lock;
     command_template_instance cti;
 };
@@ -120,38 +123,45 @@ fail0:
     return 0;
 }
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
+    // allocate instance
     struct instance *o = malloc(sizeof(*o));
     if (!o) {
         BLog(BLOG_ERROR, "malloc failed");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
-    BEventLock_Init(&o->lock, BReactor_PendingGroup(i->reactor));
+    // init arguments
+    o->i = i;
     
-    if (!command_template_new(&o->cti, i, build_cmdline, BLOG_CURRENT_CHANNEL, &o->lock)) {
-        goto fail1;
-    }
+    // init dummy event lock
+    BEventLock_Init(&o->lock, BReactor_PendingGroup(i->reactor));
     
-    return o;
+    command_template_new(&o->cti, i, build_cmdline, template_free_func, o, BLOG_CURRENT_CHANNEL, &o->lock);
+    return;
     
-fail1:
-    BEventLock_Free(&o->lock);
-    free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+void template_free_func (void *vo, int is_error)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
-    command_template_free(&o->cti);
-    
+    // free dummy event lock
     BEventLock_Free(&o->lock);
     
+    // free instance
     free(o);
+    
+    if (is_error) {
+        NCDModuleInst_Backend_SetError(i);
+    }
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static void func_die (void *vo)
@@ -165,7 +175,6 @@ static const struct NCDModule modules[] = {
     {
         .type = "run",
         .func_new = func_new,
-        .func_free = func_free,
         .func_die = func_die
     }, {
         .type = NULL

+ 151 - 0
ncd/modules/sleep.c

@@ -0,0 +1,151 @@
+/**
+ * @file sleep.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
+ * 
+ * Module which sleeps a given number of milliseconds on inititalization and
+ * deinitialization.
+ * 
+ * Synopsis: sleep(string ms_start, string ms_stop)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_sleep.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct instance {
+    NCDModuleInst *i;
+    btime_t ms_start;
+    btime_t ms_stop;
+    BTimer timer;
+    int dying;
+};
+
+static void instance_free (struct instance *o);
+
+static void timer_handler (void *vo)
+{
+    struct instance *o = vo;
+    
+    if (!o->dying) {
+        // signal up
+        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    } else {
+        // die
+        instance_free(o);
+    }
+}
+
+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 *ms_start_arg;
+    NCDValue *ms_stop_arg;
+    if (!NCDValue_ListRead(i->args, 2, &ms_start_arg, &ms_stop_arg)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(ms_start_arg) != NCDVALUE_STRING || NCDValue_Type(ms_stop_arg) != NCDVALUE_STRING) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    if (sscanf(NCDValue_StringValue(ms_start_arg), "%"SCNi64, &o->ms_start) != 1) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong time");
+        goto fail1;
+    }
+    if (sscanf(NCDValue_StringValue(ms_stop_arg), "%"SCNi64, &o->ms_stop) != 1) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong time");
+        goto fail1;
+    }
+    
+    // init timer
+    BTimer_Init(&o->timer, 0, timer_handler, o);
+    
+    // set not dying
+    o->dying = 0;
+    
+    // set timer
+    BReactor_SetTimerAfter(o->i->reactor, &o->timer, o->ms_start);
+    
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+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_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    
+    // set dying
+    o->dying = 1;
+    
+    // set timer
+    BReactor_SetTimerAfter(o->i->reactor, &o->timer, o->ms_stop);
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "sleep",
+        .func_new = func_new,
+        .func_die = func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_sleep = {
+    .modules = modules
+};

+ 12 - 6
ncd/modules/strcmp.c

@@ -33,7 +33,7 @@
 
 #include <ncd/NCDModule.h>
 
-#include <generated/blog_channel_ncd_var.h>
+#include <generated/blog_channel_ncd_strcmp.h>
 
 #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
 
@@ -43,7 +43,7 @@ struct instance {
     NCDValue *arg2;
 };
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -51,6 +51,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -65,22 +66,27 @@ static void * func_new (NCDModuleInst *i)
         goto fail1;
     }
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static int func_getvar (void *vo, const char *name, NCDValue *out)
@@ -105,7 +111,7 @@ static const struct NCDModule modules[] = {
     {
         .type = "strcmp",
         .func_new = func_new,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = NULL

+ 12 - 5
ncd/modules/var.c

@@ -29,6 +29,7 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <ncd/NCDModule.h>
 
@@ -41,7 +42,7 @@ struct instance {
     NCDValue *arg;
 };
 
-static void * func_new (NCDModuleInst *i)
+static void func_new (NCDModuleInst *i)
 {
     // allocate instance
     struct instance *o = malloc(sizeof(*o));
@@ -49,6 +50,7 @@ static void * func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // init arguments
     o->i = i;
@@ -59,22 +61,27 @@ static void * func_new (NCDModuleInst *i)
         goto fail1;
     }
     
+    // signal up
     NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
     
-    return o;
+    return;
     
 fail1:
     free(o);
 fail0:
-    return NULL;
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_free (void *vo)
+static void func_die (void *vo)
 {
     struct instance *o = vo;
+    NCDModuleInst *i = o->i;
     
     // free instance
     free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
 static int func_getvar (void *vo, const char *name, NCDValue *out)
@@ -97,7 +104,7 @@ static const struct NCDModule modules[] = {
     {
         .type = "var",
         .func_new = func_new,
-        .func_free = func_free,
+        .func_die = func_die,
         .func_getvar = func_getvar
     }, {
         .type = NULL

+ 223 - 254
ncd/ncd.c

@@ -22,7 +22,6 @@
 
 #include <stdint.h>
 #include <stdio.h>
-#include <stddef.h>
 #include <string.h>
 #include <stdlib.h>
 
@@ -84,6 +83,7 @@ struct process {
     size_t fp;
     BTimer wait_timer;
     BPending advance_job;
+    BPending work_job;
     LinkedList2Node list_node; // node in processes
 };
 
@@ -111,6 +111,7 @@ struct {
     int loglevel;
     int loglevels[BLOG_NUM_CHANNELS];
     char *config_file;
+    int retry_time;
 } options;
 
 // reactor
@@ -122,25 +123,12 @@ int terminating;
 // process manager
 BProcessManager manager;
 
-// configuration
-struct NCDConfig_interfaces *configuration;
+// config AST
+struct NCDConfig_interfaces *config_ast;
 
 // processes
 LinkedList2 processes;
 
-// job for initializing processes
-BPending init_job;
-
-// next process for init job
-struct NCDConfig_interfaces *init_next;
-
-// job for initiating shutdown of processes
-BPending free_job;
-
-// process iterator for free job
-LinkedList2Iterator free_it;
-
-static void terminate (void);
 static void print_help (const char *name);
 static void print_version (void);
 static int parse_arguments (int argc, char *argv[]);
@@ -155,18 +143,15 @@ static void process_free_statements (struct process *p);
 static size_t process_rap (struct process *p);
 static void process_assert_pointers (struct process *p);
 static void process_log (struct process *p, int level, const char *fmt, ...);
-static void process_work (struct process *p);
+static void process_schedule_work (struct process *p);
+static void process_work_job_handler (struct process *p);
 static void process_advance_job_handler (struct process *p);
-static void process_advance (struct process *p);
-static void process_wait (struct process *p);
 static void process_wait_timer_handler (struct process *p);
 static int process_resolve_variable (struct process *p, size_t pos, const char *modname, const char *varname, NCDValue *out);
 static void process_statement_log (struct process_statement *ps, int level, const char *fmt, ...);
 static void process_statement_set_error (struct process_statement *ps);
 static void process_statement_instance_handler_event (struct process_statement *ps, int event);
-static void process_statement_instance_handler_died (struct process_statement *ps, int is_error);
 static int process_statement_instance_handler_getvar (struct process_statement *ps, const char *modname, const char *varname, NCDValue *out);
-static void init_job_handler (void *unused);
 static void free_job_handler (void *unused);
 
 int main (int argc, char **argv)
@@ -261,7 +246,7 @@ int main (int argc, char **argv)
     }
     
     // parse config file
-    if (!NCDConfigParser_Parse((char *)file, file_len, &configuration)) {
+    if (!NCDConfigParser_Parse((char *)file, file_len, &config_ast)) {
         BLog(BLOG_ERROR, "NCDConfigParser_Parse failed");
         free(file);
         goto fail3;
@@ -271,10 +256,9 @@ int main (int argc, char **argv)
     free(file);
     
     // init module params
-    struct NCDModuleInitParams params = {
-        .reactor = &ss,
-        .manager = &manager
-    };
+    struct NCDModuleInitParams params;
+    params.reactor = &ss;
+    params.manager = &manager;
     
     // init modules
     size_t num_inited_modules = 0;
@@ -289,15 +273,12 @@ int main (int argc, char **argv)
     // init processes list
     LinkedList2_Init(&processes);
     
-    // init init job
-    BPending_Init(&init_job, BReactor_PendingGroup(&ss), init_job_handler, NULL);
-    
-    // init free job
-    BPending_Init(&free_job, BReactor_PendingGroup(&ss), free_job_handler, NULL);
-    
-    // start initializing processes
-    init_next = configuration;
-    BPending_Set(&init_job);
+    // init processes
+    struct NCDConfig_interfaces *conf = config_ast;
+    while (conf) {
+        process_new(conf);
+        conf = conf->next;
+    }
     
     // enter event loop
     BLog(BLOG_NOTICE, "entering event loop");
@@ -309,12 +290,6 @@ int main (int argc, char **argv)
         struct process *p = UPPER_OBJECT(n, struct process, list_node);
         process_free(p);
     }
-    
-    // free free job
-    BPending_Free(&free_job);
-    
-    // free init job
-    BPending_Free(&init_job);
 fail5:
     // free modules
     while (num_inited_modules > 0) {
@@ -325,7 +300,7 @@ fail5:
         num_inited_modules--;
     }
     // free configuration
-    NCDConfig_free_interfaces(configuration);
+    NCDConfig_free_interfaces(config_ast);
 fail3:
     // remove signal handler
     BSignal_Finish();
@@ -346,24 +321,6 @@ fail0:
     return 1;
 }
 
-void terminate (void)
-{
-    ASSERT(!terminating)
-    
-    BLog(BLOG_NOTICE, "tearing down");
-    
-    terminating = 1;
-    
-    if (LinkedList2_IsEmpty(&processes)) {
-        BReactor_Quit(&ss, 1);
-        return;
-    }
-    
-    // start free job
-    LinkedList2Iterator_InitForward(&free_it, &processes);
-    BPending_Set(&free_job);
-}
-
 void print_help (const char *name)
 {
     printf(
@@ -380,7 +337,8 @@ void print_help (const char *name)
         #endif
         "        [--loglevel <0-5/none/error/warning/notice/info/debug>]\n"
         "        [--channel-loglevel <channel-name> <0-5/none/error/warning/notice/info/debug>] ...\n"
-        "        --config-file <file>\n",
+        "        --config-file <file>\n"
+        "        [--retry-time <ms>]\n",
         name
     );
 }
@@ -408,6 +366,7 @@ int parse_arguments (int argc, char *argv[])
         options.loglevels[i] = -1;
     }
     options.config_file = NULL;
+    options.retry_time = DEFAULT_RETRY_TIME;
     
     for (int i = 1; i < argc; i++) {
         char *arg = argv[i];
@@ -492,6 +451,17 @@ int parse_arguments (int argc, char *argv[])
             options.config_file = argv[i + 1];
             i++;
         }
+        else if (!strcmp(arg, "--retry-time")) {
+            if (1 >= argc - i) {
+                fprintf(stderr, "%s: requires an argument\n", arg);
+                return 0;
+            }
+            if ((options.retry_time = atoi(argv[i + 1])) < 0) {
+                fprintf(stderr, "%s: wrong argument\n", arg);
+                return 0;
+            }
+            i++;
+        }
         else {
             fprintf(stderr, "unknown option: %s\n", arg);
             return 0;
@@ -514,8 +484,24 @@ void signal_handler (void *unused)
 {
     BLog(BLOG_NOTICE, "termination requested");
     
-    if (!terminating) {
-        terminate();
+    if (terminating) {
+        return;
+    }
+    
+    terminating = 1;
+    
+    if (LinkedList2_IsEmpty(&processes)) {
+        BReactor_Quit(&ss, 1);
+        return;
+    }
+    
+    // schedule work for all processes
+    LinkedList2Iterator it;
+    LinkedList2Iterator_InitForward(&it, &processes);
+    LinkedList2Node *n;
+    while (n = LinkedList2Iterator_Next(&it)) {
+        struct process *p = UPPER_OBJECT(n, struct process, list_node);
+        process_schedule_work(p);
     }
 }
 
@@ -537,6 +523,7 @@ int statement_init (struct statement *s, struct NCDConfig_statements *conf)
     // find module
     char *module_name = NCDConfig_concat_strings(conf->names);
     if (!module_name) {
+        BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
         goto fail0;
     }
     const struct NCDModule *m = find_module(module_name);
@@ -557,14 +544,15 @@ int statement_init (struct statement *s, struct NCDConfig_statements *conf)
     while (arg) {
         struct argument_elem *e = malloc(sizeof(*e));
         if (!e) {
-            goto fail1;
+            BLog(BLOG_ERROR, "malloc failed");
+            goto loop_fail0;
         }
         
         switch (arg->type) {
             case NCDCONFIG_ARG_STRING: {
                 if (!NCDValue_InitString(&e->val, arg->string)) {
-                    free(e);
-                    goto fail1;
+                    BLog(BLOG_ERROR, "NCDValue_InitString failed");
+                    goto loop_fail1;
                 }
                 
                 e->is_var = 0;
@@ -572,18 +560,14 @@ int statement_init (struct statement *s, struct NCDConfig_statements *conf)
             
             case NCDCONFIG_ARG_VAR: {
                 if (!(e->var.modname = strdup(arg->var->value))) {
-                    free(e);
-                    goto fail1;
+                    BLog(BLOG_ERROR, "strdup failed");
+                    goto loop_fail1;
                 }
                 
-                if (!arg->var->next) {
-                    e->var.varname = NULL;
-                } else {
-                    if (!(e->var.varname = NCDConfig_concat_strings(arg->var->next))) {
-                        free(e->var.modname);
-                        free(e);
-                        goto fail1;
-                    }
+                if (!(e->var.varname = (arg->var->next ? NCDConfig_concat_strings(arg->var->next) : strdup("")))) {
+                    BLog(BLOG_ERROR, "NCDConfig_concat_strings/strdup failed");
+                    free(e->var.modname);
+                    goto loop_fail1;
                 }
                 
                 e->is_var = 1;
@@ -598,6 +582,12 @@ int statement_init (struct statement *s, struct NCDConfig_statements *conf)
         prevptr = &e->next_arg;
         
         arg = arg->next;
+        continue;
+        
+    loop_fail1:
+        free(e);
+    loop_fail0:
+        goto fail1;
     }
     
     // init name
@@ -605,6 +595,7 @@ int statement_init (struct statement *s, struct NCDConfig_statements *conf)
         s->name = NULL;
     } else {
         if (!(s->name = strdup(conf->name))) {
+            BLog(BLOG_ERROR, "strdup failed");
             goto fail1;
         }
     }
@@ -647,11 +638,13 @@ int process_new (struct NCDConfig_interfaces *conf)
     // allocate strucure
     struct process *p = malloc(sizeof(*p));
     if (!p) {
+        BLog(BLOG_ERROR, "malloc failed");
         goto fail0;
     }
     
     // init name
     if (!(p->name = strdup(conf->name))) {
+        BLog(BLOG_ERROR, "strdup failed");
         goto fail1;
     }
     
@@ -659,11 +652,15 @@ int process_new (struct NCDConfig_interfaces *conf)
     size_t num_st = 0;
     struct NCDConfig_statements *st = conf->statements;
     while (st) {
+        if (num_st == SIZE_MAX) {
+            BLog(BLOG_ERROR, "too many statements");
+            goto fail2;
+        }
         num_st++;
         st = st->next;
     }
     
-    // statements array
+    // allocate statements array
     if (!(p->statements = BAllocArray(num_st, sizeof(p->statements[0])))) {
         goto fail2;
     }
@@ -697,15 +694,19 @@ int process_new (struct NCDConfig_interfaces *conf)
     p->fp = 0;
     
     // init timer
-    BTimer_Init(&p->wait_timer, RETRY_TIME, (BTimer_handler)process_wait_timer_handler, p);
+    BTimer_Init(&p->wait_timer, 0, (BTimer_handler)process_wait_timer_handler, p);
     
     // init advance job
     BPending_Init(&p->advance_job, BReactor_PendingGroup(&ss), (BPending_handler)process_advance_job_handler, p);
     
+    // init work job
+    BPending_Init(&p->work_job, BReactor_PendingGroup(&ss), (BPending_handler)process_work_job_handler, p);
+    
     // insert to processes list
     LinkedList2_Append(&processes, &p->list_node);
     
-    process_work(p);
+    // schedule work
+    BPending_Set(&p->work_job);
     
     return 1;
     
@@ -716,6 +717,7 @@ fail2:
 fail1:
     free(p);
 fail0:
+    BLog(BLOG_ERROR, "failed to initialize process %s", conf->name);
     return 0;
 }
 
@@ -727,6 +729,9 @@ void process_free (struct process *p)
     // remove from processes list
     LinkedList2_Remove(&processes, &p->list_node);
     
+    // free work job
+    BPending_Free(&p->work_job);
+    
     // free advance job
     BPending_Free(&p->advance_job);
     
@@ -755,9 +760,9 @@ size_t process_rap (struct process *p)
 void process_free_statements (struct process *p)
 {
     // free statments
-    for (size_t i = 0; i < p->num_statements; i++) {
-        struct process_statement *ps = &p->statements[i];
-        statement_free(&ps->s);
+    while (p->num_statements > 0) {
+        statement_free(&p->statements[p->num_statements - 1].s);
+        p->num_statements--;
     }
     
     // free stataments array
@@ -796,16 +801,26 @@ void process_log (struct process *p, int level, const char *fmt, ...)
     va_end(vl);
 }
 
-void process_work (struct process *p)
+void process_schedule_work (struct process *p)
 {
     process_assert_pointers(p);
     
-    // stop timer in case we were WAITING
+    // stop timer
     BReactor_RemoveTimer(&ss, &p->wait_timer);
     
-    // stop advance job (if we need to advance, we will re-schedule it)
+    // stop advance job
     BPending_Unset(&p->advance_job);
     
+    // schedule work
+    BPending_Set(&p->work_job);
+}
+
+void process_work_job_handler (struct process *p)
+{
+    process_assert_pointers(p);
+    ASSERT(!BTimer_IsRunning(&p->wait_timer))
+    ASSERT(!BPending_IsSet(&p->advance_job))
+    
     if (terminating) {
         if (p->fp == 0) {
             // finished retreating
@@ -815,41 +830,65 @@ void process_work (struct process *p)
             if (LinkedList2_IsEmpty(&processes)) {
                 BReactor_Quit(&ss, 1);
             }
-            
-            return;
-        }
-        
-        // order the last living statement to die, if needed
-        struct process_statement *ps = &p->statements[p->fp - 1];
-        if (ps->state != SSTATE_DYING) {
-            process_statement_log(ps, BLOG_INFO, "killing");
-            
-            // order it to die
-            NCDModuleInst_Event(&ps->inst, NCDMODULE_TOEVENT_DIE);
-            
-            // set statement state DYING
-            ps->state = SSTATE_DYING;
-            
-            // update AP
-            if (p->ap > ps->i) {
-                p->ap = ps->i;
+        } else {
+            // order the last living statement to die, if needed
+            struct process_statement *ps = &p->statements[p->fp - 1];
+            ASSERT(ps->state != SSTATE_FORGOTTEN)
+            if (ps->state != SSTATE_DYING) {
+                process_statement_log(ps, BLOG_INFO, "killing");
+                
+                // order it to die
+                NCDModuleInst_Event(&ps->inst, NCDMODULE_TOEVENT_DIE);
+                
+                // set statement state DYING
+                ps->state = SSTATE_DYING;
+                
+                // update AP
+                if (p->ap > ps->i) {
+                    p->ap = ps->i;
+                }
             }
+            
+            process_assert_pointers(p);
         }
         
-        process_assert_pointers(p);
-        
         return;
     }
     
     if (p->ap == p->fp) {
-        // schedule advance
-        if (!(p->ap > 0 && p->statements[p->ap - 1].state == SSTATE_CHILD)) {
-            BPending_Set(&p->advance_job);
-        }
-        
-        // report clean
-        if (p->ap > 0) {
-            NCDModuleInst_Event(&p->statements[p->ap - 1].inst, NCDMODULE_TOEVENT_CLEAN);
+        if (p->ap == process_rap(p)) {
+            if (p->ap == p->num_statements) {
+                // all statements are up
+                process_log(p, BLOG_INFO, "victory");
+            } else {
+                struct process_statement *ps = &p->statements[p->ap];
+                ASSERT(ps->state == SSTATE_FORGOTTEN)
+                
+                // clear expired error
+                if (ps->have_error && ps->error_until <= btime_gettime()) {
+                    ps->have_error = 0;
+                }
+                
+                if (ps->have_error) {
+                    // next statement has error, wait
+                    process_statement_log(ps, BLOG_INFO, "waiting after error");
+                    BReactor_SetTimerAbsolute(&ss, &p->wait_timer, ps->error_until);
+                } else {
+                    // schedule advance
+                    BPending_Set(&p->advance_job);
+                }
+            }
+        } else {
+            ASSERT(p->ap > 0)
+            ASSERT(p->ap <= p->num_statements)
+            
+            struct process_statement *ps = &p->statements[p->ap - 1];
+            ASSERT(ps->state == SSTATE_CHILD)
+            
+            process_statement_log(ps, BLOG_INFO, "clean");
+            
+            // report clean
+            NCDModuleInst_Event(&ps->inst, NCDMODULE_TOEVENT_CLEAN);
         }
     } else {
         // order the last living statement to die, if needed
@@ -870,33 +909,17 @@ void process_work (struct process *p)
 
 void process_advance_job_handler (struct process *p)
 {
-    ASSERT(p->ap == p->fp)
-    ASSERT(!(p->ap > 0 && p->statements[p->ap - 1].state == SSTATE_CHILD))
-    ASSERT(!terminating)
-    
-    // advance
-    process_advance(p);
-}
-
-void process_advance (struct process *p)
-{
+    process_assert_pointers(p);
     ASSERT(p->ap == p->fp)
     ASSERT(p->ap == process_rap(p))
-    
-    if (p->ap == p->num_statements) {
-        process_log(p, BLOG_INFO, "victory");
-        
-        process_assert_pointers(p);
-        return;
-    }
+    ASSERT(p->ap < p->num_statements)
+    ASSERT(!p->statements[p->ap].have_error)
+    ASSERT(!BPending_IsSet(&p->work_job))
+    ASSERT(!BTimer_IsRunning(&p->wait_timer))
+    ASSERT(!terminating)
     
     struct process_statement *ps = &p->statements[p->ap];
-    
-    // check if we need to wait
-    if (ps->have_error && ps->error_until > btime_gettime()) {
-        process_wait(p);
-        return;
-    }
+    ASSERT(ps->state == SSTATE_FORGOTTEN)
     
     process_statement_log(ps, BLOG_INFO, "initializing");
     
@@ -906,12 +929,10 @@ void process_advance (struct process *p)
     // build arguments
     struct argument_elem *arg = ps->s.first_arg;
     while (arg) {
+        // resolve argument
         NCDValue v;
-        
         if (arg->is_var) {
-            const char *real_varname = (arg->var.varname ? arg->var.varname : "");
-            
-            if (!process_resolve_variable(p, p->ap, arg->var.modname, real_varname, &v)) {
+            if (!process_resolve_variable(p, p->ap, arg->var.modname, arg->var.varname, &v)) {
                 process_statement_log(ps, BLOG_ERROR, "failed to resolve variable");
                 goto fail1;
             }
@@ -936,16 +957,12 @@ void process_advance (struct process *p)
     snprintf(ps->logprefix, sizeof(ps->logprefix), "process %s: statement %zu: module: ", p->name, ps->i);
     
     // initialize module instance
-    if (!NCDModuleInst_Init(
-        &ps->inst, ps->s.name, ps->s.module, &ps->inst_args, ps->logprefix, &ss, &manager,
+    NCDModuleInst_Init(
+        &ps->inst, ps->s.module, &ps->inst_args, ps->logprefix, &ss, &manager,
         (NCDModule_handler_event)process_statement_instance_handler_event,
-        (NCDModule_handler_died)process_statement_instance_handler_died,
         (NCDModule_handler_getvar)process_statement_instance_handler_getvar,
         ps
-    )) {
-        process_statement_log(ps, BLOG_ERROR, "failed to initialize");
-        goto fail1;
-    }
+    );
     
     // set statement state CHILD
     ps->state = SSTATE_CHILD;
@@ -957,62 +974,56 @@ void process_advance (struct process *p)
     p->fp++;
     
     process_assert_pointers(p);
-    
     return;
     
 fail1:
     NCDValue_Free(&ps->inst_args);
-    process_statement_set_error(ps);
-    process_wait(p);
-}
-
-void process_wait (struct process *p)
-{
-    ASSERT(p->ap == p->fp)
-    ASSERT(p->ap == process_rap(p))
-    ASSERT(p->ap < p->num_statements)
-    ASSERT(p->statements[p->ap].have_error)
-    
-    process_statement_log(&p->statements[p->ap], BLOG_INFO, "waiting after error");
     
-    // set timer
-    BReactor_SetTimerAbsolute(&ss, &p->wait_timer, p->statements[p->ap].error_until);
+    // mark error
+    process_statement_set_error(ps);
     
-    process_assert_pointers(p);
+    // schedule work to start the timer
+    BPending_Set(&p->work_job);
 }
 
 void process_wait_timer_handler (struct process *p)
 {
+    process_assert_pointers(p);
     ASSERT(p->ap == p->fp)
     ASSERT(p->ap == process_rap(p))
     ASSERT(p->ap < p->num_statements)
     ASSERT(p->statements[p->ap].have_error)
+    ASSERT(!BPending_IsSet(&p->work_job))
+    ASSERT(!BPending_IsSet(&p->advance_job))
+    ASSERT(!terminating)
     
     process_log(p, BLOG_INFO, "retrying");
     
     // clear error
     p->statements[p->ap].have_error = 0;
     
-    process_advance(p);
+    // schedule work
+    BPending_Set(&p->work_job);
 }
 
 int process_resolve_variable (struct process *p, size_t pos, const char *modname, const char *varname, NCDValue *out)
 {
+    process_assert_pointers(p);
     ASSERT(pos >= 0)
     ASSERT(pos <= process_rap(p))
     ASSERT(modname)
     ASSERT(varname)
     
     // find referred-to statement
-    struct process_statement *rps;
-    size_t i;
-    for (i = pos; i > 0; i--) {
-        rps = &p->statements[i - 1];
-        if (rps->s.name && !strcmp(rps->s.name, modname)) {
+    struct process_statement *rps = NULL;
+    for (size_t i = pos; i > 0; i--) {
+        struct process_statement *ps = &p->statements[i - 1];
+        if (ps->s.name && !strcmp(ps->s.name, modname)) {
+            rps = ps;
             break;
         }
     }
-    if (i == 0) {
+    if (!rps) {
         process_log(p, BLOG_ERROR, "unknown statement name in variable: %s.%s", modname, varname);
         return 0;
     }
@@ -1041,14 +1052,18 @@ void process_statement_set_error (struct process_statement *ps)
     ASSERT(ps->state == SSTATE_FORGOTTEN)
     
     ps->have_error = 1;
-    ps->error_until = btime_gettime() + RETRY_TIME;
+    ps->error_until = btime_add(btime_gettime(), options.retry_time);
 }
 
 void process_statement_instance_handler_event (struct process_statement *ps, int event)
 {
-    ASSERT(ps->state == SSTATE_CHILD || ps->state == SSTATE_ADULT)
+    ASSERT(ps->state == SSTATE_CHILD || ps->state == SSTATE_ADULT || ps->state == SSTATE_DYING)
     
     struct process *p = ps->p;
+    process_assert_pointers(p);
+    
+    // schedule work
+    process_schedule_work(p);
     
     switch (event) {
         case NCDMODULE_EVENT_UP: {
@@ -1073,56 +1088,47 @@ void process_statement_instance_handler_event (struct process_statement *ps, int
                 p->ap = ps->i + 1;
             }
         } break;
+        
+        case NCDMODULE_EVENT_DEAD: {
+            int is_error = NCDModuleInst_HaveError(&ps->inst);
+            
+            if (is_error) {
+                process_statement_log(ps, BLOG_ERROR, "died with error");
+            } else {
+                process_statement_log(ps, BLOG_INFO, "died");
+            }
+            
+            // free instance
+            NCDModuleInst_Free(&ps->inst);
+            
+            // free instance arguments
+            NCDValue_Free(&ps->inst_args);
+            
+            // set state FORGOTTEN
+            ps->state = SSTATE_FORGOTTEN;
+            
+            // set error
+            if (is_error) {
+                process_statement_set_error(ps);
+            }
+            
+            // update AP
+            if (p->ap > ps->i) {
+                p->ap = ps->i;
+            }
+            
+            // update FP
+            while (p->fp > 0 && p->statements[p->fp - 1].state == SSTATE_FORGOTTEN) {
+                p->fp--;
+            }
+        } break;
     }
-    
-    process_work(p);
-    return;
-}
-
-void process_statement_instance_handler_died (struct process_statement *ps, int is_error)
-{
-    ASSERT(ps->state == SSTATE_CHILD || ps->state == SSTATE_ADULT || ps->state == SSTATE_DYING)
-    
-    struct process *p = ps->p;
-    
-    // free instance
-    NCDModuleInst_Free(&ps->inst);
-    
-    // free instance arguments
-    NCDValue_Free(&ps->inst_args);
-    
-    // set state FORGOTTEN
-    ps->state = SSTATE_FORGOTTEN;
-    
-    // set error
-    if (is_error) {
-        process_statement_set_error(ps);
-    } else {
-        ps->have_error = 0;
-    }
-    
-    // update AP
-    if (p->ap > ps->i) {
-        p->ap = ps->i;
-    }
-    
-    // update FP
-    while (p->fp > 0 && p->statements[p->fp - 1].state == SSTATE_FORGOTTEN) {
-        p->fp--;
-    }
-    
-    process_statement_log(ps, BLOG_INFO, "died");
-    
-    if (is_error) {
-        process_statement_log(ps, BLOG_ERROR, "with error");
-    }
-    
-    process_work(p);
-    return;
 }
 
 int process_statement_instance_handler_getvar (struct process_statement *ps, const char *modname, const char *varname, NCDValue *out)
 {
+    ASSERT(ps->state != SSTATE_FORGOTTEN)
+    
     if (ps->i > process_rap(ps->p)) {
         process_statement_log(ps, BLOG_ERROR, "tried to resolve variable %s.%s but it's dirty", modname, varname);
         return 0;
@@ -1130,40 +1136,3 @@ int process_statement_instance_handler_getvar (struct process_statement *ps, con
     
     return process_resolve_variable(ps->p, ps->i, modname, varname, out);
 }
-
-void init_job_handler (void *unused)
-{
-    ASSERT(!terminating)
-    
-    if (!init_next) {
-        // initialized all processes
-        return;
-    }
-    
-    struct NCDConfig_interfaces *conf = init_next;
-    
-    // schedule next
-    init_next = init_next->next;
-    BPending_Set(&init_job);
-    
-    // init process
-    process_new(conf);
-}
-
-void free_job_handler (void *unused)
-{
-    ASSERT(terminating)
-    
-    LinkedList2Node *n = LinkedList2Iterator_Next(&free_it);
-    if (!n) {
-        // done initiating shutdown for all processes
-        return;
-    }
-    struct process *p = UPPER_OBJECT(n, struct process, list_node);
-    
-    // schedule next
-    BPending_Set(&free_job);
-    
-    process_work(p);
-    return;
-}

+ 1 - 1
ncd/ncd.h

@@ -24,4 +24,4 @@
 #define PROGRAM_NAME "ncd"
 
 // how long to wait after an error before retrying
-#define RETRY_TIME 5000
+#define DEFAULT_RETRY_TIME 5000