Jelajahi Sumber

ncd: add multidepend module

ambrop7 15 tahun lalu
induk
melakukan
f8618b8b79

+ 1 - 0
blog_channels.txt

@@ -6,6 +6,7 @@ ncd 4
 ncd_var 4
 ncd_list 4
 ncd_depend 4
+ncd_multidepend 4
 ncd_concat 4
 ncd_concatlist 4
 ncd_if 4

+ 4 - 0
generated/blog_channel_ncd_multidepend.h

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

+ 46 - 45
generated/blog_channels_defines.h

@@ -6,48 +6,49 @@
 #define BLOG_CHANNEL_ncd_var 5
 #define BLOG_CHANNEL_ncd_list 6
 #define BLOG_CHANNEL_ncd_depend 7
-#define BLOG_CHANNEL_ncd_concat 8
-#define BLOG_CHANNEL_ncd_concatlist 9
-#define BLOG_CHANNEL_ncd_if 10
-#define BLOG_CHANNEL_ncd_strcmp 11
-#define BLOG_CHANNEL_ncd_logical 12
-#define BLOG_CHANNEL_ncd_ip_in_network 13
-#define BLOG_CHANNEL_ncd_run 14
-#define BLOG_CHANNEL_ncd_net_backend_physical 15
-#define BLOG_CHANNEL_ncd_net_backend_waitdevice 16
-#define BLOG_CHANNEL_ncd_net_backend_badvpn 17
-#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 18
-#define BLOG_CHANNEL_ncd_net_backend_rfkill 19
-#define BLOG_CHANNEL_ncd_net_dns 20
-#define BLOG_CHANNEL_ncd_net_iptables 21
-#define BLOG_CHANNEL_ncd_net_ipv4_addr 22
-#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 23
-#define BLOG_CHANNEL_ncd_net_ipv4_route 24
-#define BLOG_CHANNEL_StreamPeerIO 25
-#define BLOG_CHANNEL_DatagramPeerIO 26
-#define BLOG_CHANNEL_BReactor 27
-#define BLOG_CHANNEL_BSignal 28
-#define BLOG_CHANNEL_FragmentProtoAssembler 29
-#define BLOG_CHANNEL_BPredicate 30
-#define BLOG_CHANNEL_ServerConnection 31
-#define BLOG_CHANNEL_Listener 32
-#define BLOG_CHANNEL_DataProto 33
-#define BLOG_CHANNEL_FrameDecider 34
-#define BLOG_CHANNEL_BSocksClient 35
-#define BLOG_CHANNEL_BDHCPClientCore 36
-#define BLOG_CHANNEL_BDHCPClient 37
-#define BLOG_CHANNEL_NCDIfConfig 38
-#define BLOG_CHANNEL_BUnixSignal 39
-#define BLOG_CHANNEL_BProcess 40
-#define BLOG_CHANNEL_StreamSocketSink 41
-#define BLOG_CHANNEL_StreamSocketSource 42
-#define BLOG_CHANNEL_DatagramSocketSink 43
-#define BLOG_CHANNEL_DatagramSocketSource 44
-#define BLOG_CHANNEL_PRStreamSink 45
-#define BLOG_CHANNEL_PRStreamSource 46
-#define BLOG_CHANNEL_BSocketPRFileDesc 47
-#define BLOG_CHANNEL_PacketProtoDecoder 48
-#define BLOG_CHANNEL_DPRelay 49
-#define BLOG_CHANNEL_BThreadWork 50
-#define BLOG_CHANNEL_DPReceive 51
-#define BLOG_NUM_CHANNELS 52
+#define BLOG_CHANNEL_ncd_multidepend 8
+#define BLOG_CHANNEL_ncd_concat 9
+#define BLOG_CHANNEL_ncd_concatlist 10
+#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_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

+ 1 - 0
generated/blog_channels_list.h

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

+ 1 - 0
ncd/CMakeLists.txt

@@ -9,6 +9,7 @@ add_executable(badvpn-ncd
     modules/var.c
     modules/list.c
     modules/depend.c
+    modules/multidepend.c
     modules/concat.c
     modules/concatlist.c
     modules/if.c

+ 2 - 0
ncd/modules/modules.h

@@ -30,6 +30,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_concat;
 extern const struct NCDModuleGroup ncdmodule_concatlist;
 extern const struct NCDModuleGroup ncdmodule_if;
@@ -52,6 +53,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_var,
     &ncdmodule_list,
     &ncdmodule_depend,
+    &ncdmodule_multidepend,
     &ncdmodule_concat,
     &ncdmodule_concatlist,
     &ncdmodule_if,

+ 393 - 0
ncd/modules/multidepend.c

@@ -0,0 +1,393 @@
+/**
+ * @file multidepend.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
+ * 
+ * Multiple-option dependencies module.
+ * 
+ * Synopsis: multiprovide(string name)
+ * Arguments:
+ *   name - provider identifier
+ * 
+ * Synopsis: multidepend(list(string) names)
+ * Arguments:
+ *   names - list of provider identifiers. The dependency is satisfied by any
+ *     provide statement with a provider identifier contained in this list.
+ *     The order of provider identifiers in the list specifies priority
+ *     (higher priority first).
+ * Variables: Provides variables available from the corresponding provide,
+ *     ("modname.varname" or "modname").
+ */
+
+#include <stdlib.h>
+
+#include <misc/offset.h>
+#include <structure/LinkedList2.h>
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_multidepend.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct provide {
+    NCDModuleInst *i;
+    char *name;
+    LinkedList2Node provides_node;
+    LinkedList2 depends;
+    int dying;
+};
+
+struct depend {
+    NCDModuleInst *i;
+    NCDValue *names;
+    LinkedList2Node depends_node;
+    struct provide *provide;
+    LinkedList2Node provide_node;
+    int provide_collapsing;
+};
+
+LinkedList2 provides;
+LinkedList2 depends;
+
+static struct provide * find_provide (const char *name)
+{
+    LinkedList2Iterator it;
+    LinkedList2Iterator_InitForward(&it, &provides);
+    LinkedList2Node *n;
+    while (n = LinkedList2Iterator_Next(&it)) {
+        struct provide *p = UPPER_OBJECT(n, struct provide, provides_node);
+        if (!strcmp(p->name, name)) {
+            LinkedList2Iterator_Free(&it);
+            return p;
+        }
+    }
+    
+    return NULL;
+}
+
+static struct provide * depend_find_best_provide (struct depend *o)
+{
+    NCDValue *e = NCDValue_ListFirst(o->names);
+    while (e) {
+        struct provide *p = find_provide(NCDValue_StringValue(e));
+        if (p && !p->dying) {
+            return p;
+        }
+        e = NCDValue_ListNext(o->names, e);
+    }
+    
+    return NULL;
+}
+
+static void depend_update (struct depend *o)
+{
+    // if we're collapsing, do nothing
+    if (o->provide && o->provide_collapsing) {
+        return;
+    }
+    
+    // find best provide
+    struct provide *bp = depend_find_best_provide(o);
+    
+    // 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);
+        o->provide_collapsing = 1;
+        return;
+    }
+    
+    if (bp) {
+        // 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)
+{
+    // init provides list
+    LinkedList2_Init(&provides);
+    
+    // init depends list
+    LinkedList2_Init(&depends);
+    
+    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;
+    }
+    
+    // init arguments
+    o->i = i;
+    
+    // read arguments
+    NCDValue *name_arg;
+    if (!NCDValue_ListRead(o->i->args, 1, &name_arg)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(name_arg) != NCDVALUE_STRING) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    o->name = NCDValue_StringValue(name_arg);
+    
+    // check for existing provide with this name
+    if (find_provide(o->name)) {
+        ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists");
+        goto fail1;
+    }
+    
+    // insert to provides list
+    LinkedList2_Append(&provides, &o->provides_node);
+    
+    // init depends list
+    LinkedList2_Init(&o->depends);
+    
+    // set not dying
+    o->dying = 0;
+    
+    // update depends
+    LinkedList2Iterator it;
+    LinkedList2Iterator_InitForward(&it, &depends);
+    LinkedList2Node *n;
+    while (n = LinkedList2Iterator_Next(&it)) {
+        struct depend *d = UPPER_OBJECT(n, struct depend, depends_node);
+        depend_update(d);
+    }
+    
+    // signal up
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    
+    return o;
+    
+fail1:
+    free(o);
+fail0:
+    return NULL;
+}
+
+static void provide_func_free (void *vo)
+{
+    struct provide *o = vo;
+    ASSERT(LinkedList2_IsEmpty(&o->depends))
+    
+    // remove from provides list
+    LinkedList2_Remove(&provides, &o->provides_node);
+    
+    // free instance
+    free(o);
+}
+
+static void provide_func_die (void *vo)
+{
+    struct provide *o = vo;
+    ASSERT(!o->dying)
+    
+    // if we have no depends, die immediately
+    if (LinkedList2_IsEmpty(&o->depends)) {
+        NCDModuleInst_Backend_Died(o->i, 0);
+        return;
+    }
+    
+    // set dying
+    o->dying = 1;
+    
+    // start collapsing our depends
+    LinkedList2Iterator it;
+    LinkedList2Iterator_InitForward(&it, &o->depends);
+    LinkedList2Node *n;
+    while (n = LinkedList2Iterator_Next(&it)) {
+        struct depend *d = UPPER_OBJECT(n, struct depend, provide_node);
+        ASSERT(d->provide == o)
+        depend_update(d);
+    }
+}
+
+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;
+    }
+    
+    // init arguments
+    o->i = i;
+    
+    // read arguments
+    NCDValue *names_arg;
+    if (!NCDValue_ListRead(o->i->args, 1, &names_arg)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(names_arg) != NCDVALUE_LIST) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    o->names = names_arg;
+    
+    // check names list
+    NCDValue *e = NCDValue_ListFirst(o->names);
+    while (e) {
+        if (NCDValue_Type(e) != NCDVALUE_STRING) {
+            ModuleLog(o->i, BLOG_ERROR, "wrong type");
+            goto fail1;
+        }
+        e = NCDValue_ListNext(o->names, e);
+    }
+    
+    // insert to depends list
+    LinkedList2_Append(&depends, &o->depends_node);
+    
+    // set no provide
+    o->provide = NULL;
+    
+    // update
+    depend_update(o);
+    
+    return o;
+    
+fail1:
+    free(o);
+fail0:
+    return NULL;
+}
+
+static void depend_func_free (void *vo)
+{
+    struct depend *o = vo;
+    
+    if (o->provide) {
+        // remove from provide's list
+        LinkedList2_Remove(&o->provide->depends, &o->provide_node);
+        
+        // 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);
+        }
+    }
+    
+    // remove from depends list
+    LinkedList2_Remove(&depends, &o->depends_node);
+    
+    // free instance
+    free(o);
+}
+
+static void depend_func_clean (void *vo)
+{
+    struct depend *o = vo;
+    
+    if (!(o->provide && o->provide_collapsing)) {
+        return;
+    }
+    
+    // remove from provide's list
+    LinkedList2_Remove(&o->provide->depends, &o->provide_node);
+    
+    // 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);
+    }
+    
+    // set no provide
+    o->provide = NULL;
+    
+    // update
+    depend_update(o);
+}
+
+static int depend_func_getvar (void *vo, const char *name_orig, NCDValue *out)
+{
+    struct depend *o = vo;
+    ASSERT(o->provide)
+    ASSERT(!o->provide_collapsing)
+    
+    int ret = 0;
+    
+    char *name = strdup(name_orig);
+    if (!name) {
+        ModuleLog(o->i, BLOG_ERROR, "strdup failed");
+        goto fail0;
+    }
+    
+    const char *modname;
+    const char *varname;
+    
+    char *dot = strstr(name, ".");
+    if (!dot) {
+        modname = name;
+        varname = "";
+    } else {
+        *dot = '\0';
+        modname = name;
+        varname = dot + 1;
+    }
+    
+    ret = NCDModuleInst_Backend_GetVar(o->provide->i, modname, varname, out);
+    
+    free(name);
+fail0:
+    return ret;
+}
+
+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_clean = depend_func_clean,
+        .func_getvar = depend_func_getvar
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_multidepend = {
+    .func_globalinit = func_globalinit,
+    .modules = modules
+};