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

+ 1 - 0
blog_channels.txt

@@ -26,6 +26,7 @@ ncd_ref 4
 ncd_index 4
 ncd_alias 4
 ncd_process_manager 4
+ncd_ondemand 4
 ncd_net_backend_waitdevice 4
 ncd_net_backend_waitlink 4
 ncd_net_backend_badvpn 4

+ 4 - 0
generated/blog_channel_ncd_ondemand.h

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

+ 64 - 63
generated/blog_channels_defines.h

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

+ 1 - 0
generated/blog_channels_list.h

@@ -26,6 +26,7 @@
 {.name = "ncd_index", .loglevel = 4},
 {.name = "ncd_alias", .loglevel = 4},
 {.name = "ncd_process_manager", .loglevel = 4},
+{.name = "ncd_ondemand", .loglevel = 4},
 {.name = "ncd_net_backend_waitdevice", .loglevel = 4},
 {.name = "ncd_net_backend_waitlink", .loglevel = 4},
 {.name = "ncd_net_backend_badvpn", .loglevel = 4},

+ 1 - 0
ncd/CMakeLists.txt

@@ -60,6 +60,7 @@ add_executable(badvpn-ncd
     modules/index.c
     modules/alias.c
     modules/process_manager.c
+    modules/ondemand.c
     modules/net_backend_waitdevice.c
     modules/net_backend_waitlink.c
     modules/net_backend_badvpn.c

+ 2 - 0
ncd/modules/modules.h

@@ -50,6 +50,7 @@ extern const struct NCDModuleGroup ncdmodule_ref;
 extern const struct NCDModuleGroup ncdmodule_index;
 extern const struct NCDModuleGroup ncdmodule_alias;
 extern const struct NCDModuleGroup ncdmodule_process_manager;
+extern const struct NCDModuleGroup ncdmodule_ondemand;
 extern const struct NCDModuleGroup ncdmodule_net_backend_waitdevice;
 extern const struct NCDModuleGroup ncdmodule_net_backend_waitlink;
 extern const struct NCDModuleGroup ncdmodule_net_backend_badvpn;
@@ -96,6 +97,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_index,
     &ncdmodule_alias,
     &ncdmodule_process_manager,
+    &ncdmodule_ondemand,
     &ncdmodule_net_backend_waitdevice,
     &ncdmodule_net_backend_waitlink,
     &ncdmodule_net_backend_badvpn,

+ 400 - 0
ncd/modules/ondemand.c

@@ -0,0 +1,400 @@
+/**
+ * @file ondemand.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
+ * 
+ * On-demand process manager.
+ * 
+ * Synopsis:
+ *   ondemand(string template_name, list args)
+ * 
+ * Description:
+ *   Manages an on-demand template process using a process template named
+ *   template_name.
+ *   On deinitialization, if the process is running, reqests its termination
+ *   and waits for it to terminate.
+ * 
+ * Synopsis:
+ *   ondemand::demand()
+ * 
+ * Description:
+ *   Demands the availability of an on-demand template process.
+ *   This statement is in UP state if and only if the template process of the
+ *   corresponding ondemand object is completely up.
+ * 
+ * Variables:
+ *   Exposes variables and objects from the template process corresponding to
+ *   the ondemand object.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/offset.h>
+#include <misc/debug.h>
+#include <structure/LinkedList1.h>
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_ondemand.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct ondemand {
+    NCDModuleInst *i;
+    char *template_name;
+    NCDValue *args;
+    LinkedList1 demands_list;
+    int dying;
+    int have_process;
+    NCDModuleProcess process;
+    int process_terminating;
+    int process_up;
+};
+
+struct demand {
+    NCDModuleInst *i;
+    struct ondemand *od;
+    LinkedList1Node demands_list_node;
+};
+
+static int ondemand_start_process (struct ondemand *o);
+static void ondemand_terminate_process (struct ondemand *o);
+static void ondemand_process_handler (struct ondemand *o, int event);
+static void ondemand_free (struct ondemand *o);
+static void demand_free (struct demand *o);
+
+static int ondemand_start_process (struct ondemand *o)
+{
+    ASSERT(!o->dying)
+    ASSERT(!o->have_process)
+    
+    // copy arguments
+    NCDValue args;
+    if (!NCDValue_InitCopy(&args, o->args)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
+        goto fail0;
+    }
+    
+    // start process
+    if (!NCDModuleProcess_Init(&o->process, o->i, o->template_name, args, o, (NCDModuleProcess_handler_event)ondemand_process_handler)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
+        NCDValue_Free(&args);
+        goto fail0;
+    }
+    
+    // set have process
+    o->have_process = 1;
+    
+    // set process not terminating
+    o->process_terminating = 0;
+    
+    // set process not up
+    o->process_up = 0;
+    
+    return 1;
+    
+fail0:
+    return 0;
+}
+
+static void ondemand_terminate_process (struct ondemand *o)
+{
+    ASSERT(o->have_process)
+    ASSERT(!o->process_terminating)
+    
+    // request termination
+    NCDModuleProcess_Terminate(&o->process);
+    
+    // set process terminating
+    o->process_terminating = 1;
+    
+    if (o->process_up) {
+        // set process down
+        o->process_up = 0;
+        
+        // signal demands down
+        for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) {
+            struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node);
+            ASSERT(demand->od == o)
+            NCDModuleInst_Backend_Down(demand->i);
+        }
+    }
+}
+
+static void ondemand_process_handler (struct ondemand *o, int event)
+{
+    ASSERT(o->have_process)
+    
+    switch (event) {
+        case NCDMODULEPROCESS_EVENT_UP: {
+            ASSERT(!o->process_terminating)
+            ASSERT(!o->process_up)
+            
+            // set process up
+            o->process_up = 1;
+            
+            // signal demands up
+            for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) {
+                struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node);
+                ASSERT(demand->od == o)
+                NCDModuleInst_Backend_Up(demand->i);
+            }
+        } break;
+        
+        case NCDMODULEPROCESS_EVENT_DOWN: {
+            ASSERT(!o->process_terminating)
+            ASSERT(o->process_up)
+            
+            // continue process
+            NCDModuleProcess_Continue(&o->process);
+            
+            // set process down
+            o->process_up = 0;
+            
+            // signal demands down
+            for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) {
+                struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node);
+                ASSERT(demand->od == o)
+                NCDModuleInst_Backend_Down(demand->i);
+            }
+        } break;
+        
+        case NCDMODULEPROCESS_EVENT_TERMINATED: {
+            ASSERT(o->process_terminating)
+            ASSERT(!o->process_up)
+            
+            // free process
+            NCDModuleProcess_Free(&o->process);
+            
+            // set have no process
+            o->have_process = 0;
+            
+            // if dying, die finally
+            if (o->dying) {
+                ondemand_free(o);
+                return;
+            }
+            
+            // if demands arrivied, restart process
+            if (!LinkedList1_IsEmpty(&o->demands_list)) {
+                ondemand_start_process(o);
+            }
+        } break;
+    }
+}
+
+static void ondemand_func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct ondemand *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 *arg_template_name;
+    NCDValue *arg_args;
+    if (!NCDValue_ListRead(i->args, 2, &arg_template_name, &arg_args)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(arg_template_name) != NCDVALUE_STRING || NCDValue_Type(arg_args) != NCDVALUE_LIST) {
+        ModuleLog(i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    o->template_name = NCDValue_StringValue(arg_template_name);
+    o->args = arg_args;
+    
+    // init demands list
+    LinkedList1_Init(&o->demands_list);
+    
+    // set not dying
+    o->dying = 0;
+    
+    // set have no process
+    o->have_process = 0;
+    
+    // signal up
+    NCDModuleInst_Backend_Up(i);
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void ondemand_free (struct ondemand *o)
+{
+    ASSERT(!o->have_process)
+    NCDModuleInst *i = o->i;
+    
+    // die demands
+    while (!LinkedList1_IsEmpty(&o->demands_list)) {
+        struct demand *demand = UPPER_OBJECT(LinkedList1_GetFirst(&o->demands_list), struct demand, demands_list_node);
+        ASSERT(demand->od == o)
+        demand_free(demand);
+    }
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void ondemand_func_die (void *vo)
+{
+    struct ondemand *o = vo;
+    ASSERT(!o->dying)
+    
+    // if not have process, die right away
+    if (!o->have_process) {
+        ondemand_free(o);
+        return;
+    }
+    
+    // set dying
+    o->dying = 1;
+    
+    // request process termination if not already
+    if (!o->process_terminating) {
+        ondemand_terminate_process(o);
+    }
+}
+
+static void demand_func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct demand *o = malloc(sizeof(*o));
+    if (!o) {
+        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
+        goto fail0;
+    }
+    NCDModuleInst_Backend_SetUser(i, o);
+    
+    // init arguments
+    o->i = i;
+    
+    // read arguments
+    if (!NCDValue_ListRead(i->args, 0)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    
+    // set ondemand
+    o->od = i->method_object->inst_user;
+    
+    // add to ondemand's demands list
+    LinkedList1_Append(&o->od->demands_list, &o->demands_list_node);
+    
+    // start process if needed
+    if (!o->od->have_process) {
+        ASSERT(!o->od->dying)
+        
+        if (!ondemand_start_process(o->od)) {
+            goto fail2;
+        }
+    }
+    
+    // if process is up, signal up
+    if (o->od->process_up) {
+        NCDModuleInst_Backend_Up(i);
+    }
+    
+    return;
+    
+fail2:
+    LinkedList1_Remove(&o->od->demands_list, &o->demands_list_node);
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void demand_free (struct demand *o)
+{
+    NCDModuleInst *i = o->i;
+    
+    // remove from ondemand's demands list
+    LinkedList1_Remove(&o->od->demands_list, &o->demands_list_node);
+    
+    // request process termination if no longer needed
+    if (o->od->have_process && !o->od->process_terminating && LinkedList1_IsEmpty(&o->od->demands_list)) {
+        ondemand_terminate_process(o->od);
+    }
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Dead(i);
+}
+
+static void demand_func_die (void *vo)
+{
+    struct demand *o = vo;
+    
+    demand_free(o);
+}
+
+static int demand_func_getvar (void *vo, const char *varname, NCDValue *out)
+{
+    struct demand *o = vo;
+    ASSERT(o->od->have_process)
+    ASSERT(o->od->process_up)
+    
+    return NCDModuleProcess_GetVar(&o->od->process, varname, out);
+}
+
+static NCDModuleInst * demand_func_getobj (void *vo, const char *objname)
+{
+    struct demand *o = vo;
+    ASSERT(o->od->have_process)
+    ASSERT(o->od->process_up)
+    
+    return NCDModuleProcess_GetObj(&o->od->process, objname);
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "ondemand",
+        .func_new = ondemand_func_new,
+        .func_die = ondemand_func_die
+    }, {
+        .type = "ondemand::demand",
+        .func_new = demand_func_new,
+        .func_die = demand_func_die,
+        .func_getvar = demand_func_getvar,
+        .func_getobj = demand_func_getobj
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_ondemand = {
+    .modules = modules
+};