Ver código fonte

ncd: modules: add dynamic_depend

ambrop7 14 anos atrás
pai
commit
1a05502bc1

+ 1 - 0
blog_channels.txt

@@ -7,6 +7,7 @@ ncd_var 4
 ncd_list 4
 ncd_depend 4
 ncd_multidepend 4
+ncd_dynamic_depend 4
 ncd_concat 4
 ncd_concatv 4
 ncd_if 4

+ 4 - 0
generated/blog_channel_ncd_dynamic_depend.h

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

+ 91 - 90
generated/blog_channels_defines.h

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

+ 1 - 0
generated/blog_channels_list.h

@@ -7,6 +7,7 @@
 {.name = "ncd_list", .loglevel = 4},
 {.name = "ncd_depend", .loglevel = 4},
 {.name = "ncd_multidepend", .loglevel = 4},
+{.name = "ncd_dynamic_depend", .loglevel = 4},
 {.name = "ncd_concat", .loglevel = 4},
 {.name = "ncd_concatv", .loglevel = 4},
 {.name = "ncd_if", .loglevel = 4},

+ 1 - 0
ncd/CMakeLists.txt

@@ -42,6 +42,7 @@ add_executable(badvpn-ncd
     modules/list.c
     modules/depend.c
     modules/multidepend.c
+    modules/dynamic_depend.c
     modules/concat.c
     modules/concatv.c
     modules/if.c

+ 539 - 0
ncd/modules/dynamic_depend.c

@@ -0,0 +1,539 @@
+/**
+ * @file dynamic_depend.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
+ * 
+ * Dynamic dependencies module.
+ * 
+ * Synopsis: dynamic_provide(string name, order_value)
+ * Synopsis: dynamic_depend(string name)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/offset.h>
+#include <misc/debug.h>
+#include <structure/LinkedList0.h>
+#include <structure/BAVL.h>
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_dynamic_depend.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct provide;
+
+struct name {
+    char *name;
+    BAVLNode names_tree_node;
+    BAVL provides_tree;
+    LinkedList0 waiting_depends_list;
+    struct provide *cur_p;
+    LinkedList0 cur_bound_depends_list;
+    int cur_resetting;
+};
+
+struct provide {
+    NCDModuleInst *i;
+    struct name *n;
+    NCDValue *order_value;
+    BAVLNode provides_tree_node;
+    int dying;
+};
+
+struct depend {
+    NCDModuleInst *i;
+    struct name *n;
+    int is_bound;
+    LinkedList0Node depends_list_node;
+};
+
+static BAVL names_tree;
+
+static void provide_free (struct provide *o);
+static void depend_free (struct depend *o);
+
+static int stringptr_comparator (void *user, char **v1, char **v2)
+{
+    int cmp = strcmp(*v1, *v2);
+    if (cmp < 0) {
+        return -1;
+    }
+    if (cmp > 0) {
+        return 1;
+    }
+    return 0;
+}
+
+static int valueptr_comparator (void *user, NCDValue **v1, NCDValue **v2)
+{
+    return NCDValue_Compare(*v1, *v2);
+}
+
+static struct name * find_name (const char *name)
+{
+    BAVLNode *tn = BAVL_LookupExact(&names_tree, &name);
+    if (!tn) {
+        return NULL;
+    }
+    
+    struct name *n = UPPER_OBJECT(tn, struct name, names_tree_node);
+    ASSERT(!strcmp(n->name, name))
+    
+    return n;
+}
+
+static struct name * name_init (NCDModuleInst *i, const char *name)
+{
+    ASSERT(!find_name(name))
+    
+    // allocate structure
+    struct name *o = malloc(sizeof(*o));
+    if (!o) {
+        ModuleLog(i, BLOG_ERROR, "malloc failed");
+        goto fail0;
+    }
+    
+    // copy name
+    if (!(o->name = strdup(name))) {
+        ModuleLog(i, BLOG_ERROR, "strdup failed");
+        goto fail1;
+    }
+    
+    // insert to names tree
+    ASSERT_EXECUTE(BAVL_Insert(&names_tree, &o->names_tree_node, NULL))
+    
+    // init provides tree
+    BAVL_Init(&o->provides_tree, OFFSET_DIFF(struct provide, order_value, provides_tree_node), (BAVL_comparator)valueptr_comparator, NULL);
+    
+    // init waiting depends list
+    LinkedList0_Init(&o->waiting_depends_list);
+    
+    // set no current provide
+    o->cur_p = NULL;
+    
+    return o;
+    
+fail1:
+    free(o);
+fail0:
+    return NULL;
+}
+
+static void name_free (struct name *o)
+{
+    ASSERT(BAVL_IsEmpty(&o->provides_tree))
+    ASSERT(LinkedList0_IsEmpty(&o->waiting_depends_list))
+    ASSERT(!o->cur_p)
+    
+    // remove from names tree
+    BAVL_Remove(&names_tree, &o->names_tree_node);
+    
+    // free name
+    free(o->name);
+    
+    // free structure
+    free(o);
+}
+
+static struct provide * name_get_first_provide (struct name *o)
+{
+    BAVLNode *tn = BAVL_GetFirst(&o->provides_tree);
+    if (!tn) {
+        return NULL;
+    }
+    
+    struct provide *p = UPPER_OBJECT(tn, struct provide, provides_tree_node);
+    ASSERT(p->n == o)
+    
+    return p;
+}
+
+static void name_new_current (struct name *o)
+{
+    ASSERT(!o->cur_p)
+    ASSERT(!BAVL_IsEmpty(&o->provides_tree))
+    
+    // set current provide
+    o->cur_p = name_get_first_provide(o);
+    
+    // init bound depends list
+    LinkedList0_Init(&o->cur_bound_depends_list);
+    
+    // set not resetting
+    o->cur_resetting = 0;
+    
+    // bind waiting depends
+    while (!LinkedList0_IsEmpty(&o->waiting_depends_list)) {
+        struct depend *d = UPPER_OBJECT(LinkedList0_GetFirst(&o->waiting_depends_list), struct depend, depends_list_node);
+        ASSERT(d->n == o)
+        ASSERT(!d->is_bound)
+        
+        // remove from waiting depends list
+        LinkedList0_Remove(&o->waiting_depends_list, &d->depends_list_node);
+        
+        // set bound
+        d->is_bound = 1;
+        
+        // add to bound depends list
+        LinkedList0_Prepend(&o->cur_bound_depends_list, &d->depends_list_node);
+        
+        // signal up
+        NCDModuleInst_Backend_Up(d->i);
+    }
+}
+
+static void name_free_if_unused (struct name *o)
+{
+    if (BAVL_IsEmpty(&o->provides_tree) && LinkedList0_IsEmpty(&o->waiting_depends_list)) {
+        name_free(o);
+    }
+}
+
+static void name_continue_resetting (struct name *o)
+{
+    ASSERT(o->cur_p)
+    ASSERT(o->cur_resetting)
+    
+    // still have bound depends?
+    if (!LinkedList0_IsEmpty(&o->cur_bound_depends_list)) {
+        return;
+    }
+    
+    struct provide *old_p = o->cur_p;
+    
+    // set no current provide
+    o->cur_p = NULL;
+    
+    // free old current provide if it's dying
+    if (old_p->dying) {
+        provide_free(old_p);
+    }
+    
+    if (!BAVL_IsEmpty(&o->provides_tree)) {
+        // get new current provide
+        name_new_current(o);
+    } else {
+        // free name if unused
+        name_free_if_unused(o);
+    }
+}
+
+static void name_start_resetting (struct name *o)
+{
+    ASSERT(o->cur_p)
+    ASSERT(!o->cur_resetting)
+    
+    // set resetting
+    o->cur_resetting = 1;
+    
+    // signal bound depends down
+    for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->cur_bound_depends_list); ln; ln = LinkedList0Node_Next(ln)) {
+        struct depend *d = UPPER_OBJECT(ln, struct depend, depends_list_node);
+        ASSERT(d->n == o)
+        ASSERT(d->is_bound)
+        NCDModuleInst_Backend_Down(d->i);
+    }
+    
+    // if there were no bound depends, continue right away
+    name_continue_resetting(o);
+}
+
+static int func_globalinit (struct NCDModuleInitParams params)
+{
+    // init names tree
+    BAVL_Init(&names_tree, OFFSET_DIFF(struct name, name, names_tree_node), (BAVL_comparator)stringptr_comparator, NULL);
+    
+    return 1;
+}
+
+static void provide_func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct provide *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 *name_arg;
+    if (!NCDValue_ListRead(i->args, 2, &name_arg, &o->order_value)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(name_arg) != NCDVALUE_STRING) {
+        ModuleLog(i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    char *name_str = NCDValue_StringValue(name_arg);
+    
+    // find name, create new if needed
+    struct name *n = find_name(name_str);
+    if (!n && !(n = name_init(i, name_str))) {
+        goto fail1;
+    }
+    
+    // set name
+    o->n = n;
+    
+    // check for order value conflict
+    if (BAVL_LookupExact(&n->provides_tree, &o->order_value)) {
+        ModuleLog(i, BLOG_ERROR, "order value already exists");
+        goto fail1;
+    }
+    
+    // add to name's provides tree
+    ASSERT_EXECUTE(BAVL_Insert(&n->provides_tree, &o->provides_tree_node, NULL))
+    
+    // set not dying
+    o->dying = 0;
+    
+    // signal up
+    NCDModuleInst_Backend_Up(i);
+    
+    // should this be the current provide?
+    if (o == name_get_first_provide(n)) {
+        if (!n->cur_p) {
+            name_new_current(n);
+        }
+        else if (!n->cur_resetting) {
+            name_start_resetting(n);
+        }
+    }
+    
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void provide_free (struct provide *o)
+{
+    struct name *n = o->n;
+    ASSERT(o->dying)
+    ASSERT(o != n->cur_p)
+    
+    NCDModuleInst *i = o->i;
+    
+    // remove from name's provides tree
+    BAVL_Remove(&n->provides_tree, &o->provides_tree_node);
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void provide_func_die (void *vo)
+{
+    struct provide *o = vo;
+    struct name *n = o->n;
+    ASSERT(!o->dying)
+    
+    // set dying
+    o->dying = 1;
+    
+    // if this is not the current provide, die right away
+    if (o != n->cur_p) {
+        // free provide
+        provide_free(o);
+        
+        // free name if unused
+        name_free_if_unused(n);
+        return;
+    }
+    
+    ASSERT(!n->cur_resetting)
+    
+    // start resetting
+    name_start_resetting(n);
+}
+
+static void depend_func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct depend *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 *name_arg;
+    if (!NCDValue_ListRead(i->args, 1, &name_arg)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(name_arg) != NCDVALUE_STRING) {
+        ModuleLog(i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    char *name_str = NCDValue_StringValue(name_arg);
+    
+    // find name, create new if needed
+    struct name *n = find_name(name_str);
+    if (!n && !(n = name_init(i, name_str))) {
+        goto fail1;
+    }
+    
+    // set name
+    o->n = n;
+    
+    if (n->cur_p && !n->cur_resetting) {
+        // set bound
+        o->is_bound = 1;
+        
+        // add to bound depends list
+        LinkedList0_Prepend(&n->cur_bound_depends_list, &o->depends_list_node);
+        
+        // signal up
+        NCDModuleInst_Backend_Up(i);
+    } else {
+        // set not bound
+        o->is_bound = 0;
+        
+        // add to waiting depends list
+        LinkedList0_Prepend(&n->waiting_depends_list, &o->depends_list_node);
+    }
+    
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void depend_func_die (void *vo)
+{
+    struct depend *o = vo;
+    NCDModuleInst *i = o->i;
+    struct name *n = o->n;
+    
+    if (o->is_bound) {
+        ASSERT(n->cur_p)
+        
+        // remove from bound depends list
+        LinkedList0_Remove(&n->cur_bound_depends_list, &o->depends_list_node);
+        
+        // continue resetting
+        if (n->cur_resetting) {
+            name_continue_resetting(n);
+        }
+    } else {
+        // remove from waiting depends list
+        LinkedList0_Remove(&n->waiting_depends_list, &o->depends_list_node);
+        
+        // free name if unused
+        name_free_if_unused(n);
+    }
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void depend_func_clean (void *vo)
+{
+    struct depend *o = vo;
+    struct name *n = o->n;
+    ASSERT(!o->is_bound || n->cur_p)
+    
+    if (!(o->is_bound && n->cur_resetting)) {
+        return;
+    }
+    
+    // remove from bound depends list
+    LinkedList0_Remove(&n->cur_bound_depends_list, &o->depends_list_node);
+    
+    // set not bound
+    o->is_bound = 0;
+    
+    // add to waiting depends list
+    LinkedList0_Prepend(&n->waiting_depends_list, &o->depends_list_node);
+    
+    // continue resetting
+    name_continue_resetting(n);
+}
+
+static int depend_func_getvar (void *vo, const char *varname, NCDValue *out)
+{
+    struct depend *o = vo;
+    struct name *n = o->n;
+    ASSERT(!o->is_bound || n->cur_p)
+    
+    if (!o->is_bound) {
+        return 0;
+    }
+    
+    return NCDModuleInst_Backend_GetVar(n->cur_p->i, varname, out);
+}
+
+static NCDModuleInst * depend_func_getobj (void *vo, const char *objname)
+{
+    struct depend *o = vo;
+    struct name *n = o->n;
+    ASSERT(!o->is_bound || n->cur_p)
+    
+    if (!o->is_bound) {
+        return NULL;
+    }
+    
+    return NCDModuleInst_Backend_GetObj(n->cur_p->i, objname);
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "dynamic_provide",
+        .func_new = provide_func_new,
+        .func_die = provide_func_die
+    }, {
+        .type = "dynamic_depend",
+        .func_new = depend_func_new,
+        .func_die = depend_func_die,
+        .func_clean = depend_func_clean,
+        .func_getvar = depend_func_getvar,
+        .func_getobj = depend_func_getobj,
+        .can_resolve_when_down = 1
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_dynamic_depend = {
+    .func_globalinit = func_globalinit,
+    .modules = modules
+};

+ 2 - 0
ncd/modules/modules.h

@@ -31,6 +31,7 @@ extern const struct NCDModuleGroup ncdmodule_var;
 extern const struct NCDModuleGroup ncdmodule_list;
 extern const struct NCDModuleGroup ncdmodule_depend;
 extern const struct NCDModuleGroup ncdmodule_multidepend;
+extern const struct NCDModuleGroup ncdmodule_dynamic_depend;
 extern const struct NCDModuleGroup ncdmodule_concat;
 extern const struct NCDModuleGroup ncdmodule_concatv;
 extern const struct NCDModuleGroup ncdmodule_if;
@@ -82,6 +83,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_list,
     &ncdmodule_depend,
     &ncdmodule_multidepend,
+    &ncdmodule_dynamic_depend,
     &ncdmodule_concat,
     &ncdmodule_concatv,
     &ncdmodule_if,