]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Support multiple python module instances
authorPhilipp Serr <episource@gmx.de>
Sun, 22 Jan 2017 10:58:28 +0000 (11:58 +0100)
committerPhilipp Serr <episource@gmx.de>
Sat, 2 Mar 2019 13:32:48 +0000 (14:32 +0100)
This commit adds proper support for multiple instances of the python
module: When more than one instance is added to the module list, the
first instance loads the first script specified in the `python:`
configuration section. The second instance loads the second script,
and so on.

When there are more module instances in the module list than there are
scripts in the `python:` section, an error is raised during
initialization and unbound won't start. When more scripts than module
instances are provided, the surplus scripts are ignored.

pythonmod/interface.i
pythonmod/pythonmod.c
util/config_file.c
util/config_file.h
util/configparser.c
util/configparser.y

index 5f2559bacffa80e3c78033fac5cbef696a56326a..465a9184dd653c4c79a8b1562d7ac7c2781e2a5d 100644 (file)
@@ -1026,7 +1026,7 @@ struct config_file {
    char* control_key_file;
    char* control_cert_file;
    int do_daemonize;
-   char* python_script;
+   struct config_strlist* python_script;
 };
 
 /* ************************************************************************************ *
index a668ecc23cc70da904078e116dc6198315f3e3fb..a098c326e0d1bf7f3eaaea25a5eafa4b65e6c1f5 100644 (file)
@@ -64,6 +64,15 @@ typedef struct PyThreadState PyThreadState;
 typedef void* PyGILState_STATE;
 #endif
 
+/**
+ *  counter for python module instances
+ *  incremented by pythonmod_init(...)
+ */
+int py_mod_count = 0;
+
+/** Python main thread */
+PyThreadState* mainthr;
+
 /**
  * Global state for the module.
  */
@@ -72,8 +81,6 @@ struct pythonmod_env {
        /** Python script filename. */
        const char* fname;
 
-       /** Python main thread */
-       PyThreadState* mainthr;
        /** Python module. */
        PyObject* module;
 
@@ -242,11 +249,15 @@ cleanup:
 
 int pythonmod_init(struct module_env* env, int id)
 {
+   int py_mod_idx = py_mod_count++;
+    
    /* Initialize module */
    FILE* script_py = NULL;
    PyObject* py_init_arg, *res;
    PyGILState_STATE gil;
-   int init_standard = 1;
+   int init_standard = 1, i = 0;
+   
+   struct config_strlist* cfg_item = env->cfg->python_script;
 
    struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
    if (!pe)
@@ -258,14 +269,21 @@ int pythonmod_init(struct module_env* env, int id)
    env->modinfo[id] = (void*) pe;
 
    /* Initialize module */
-   pe->fname = env->cfg->python_script;
+   pe->fname=NULL; i = 0;
+   while (cfg_item!=NULL) {
+      if (py_mod_idx==i++) {
+         pe->fname=cfg_item->str;
+         break;
+      }
+      cfg_item = cfg_item->next;
+   }
    if(pe->fname==NULL || pe->fname[0]==0) {
-      log_err("pythonmod: no script given.");
+      log_err("pythonmod[%d]: no script given.", py_mod_idx);
       return 0;
    }
 
    /* Initialize Python libraries */
-   if (!Py_IsInitialized())
+   if (py_mod_count==1 && !Py_IsInitialized()) 
    {
 #if PY_MAJOR_VERSION >= 3
       wchar_t progname[8];
@@ -281,29 +299,31 @@ int pythonmod_init(struct module_env* env, int id)
       Py_Initialize();
       PyEval_InitThreads();
       SWIG_init();
-      pe->mainthr = PyEval_SaveThread();
+      mainthr = PyEval_SaveThread();
    }
 
    gil = PyGILState_Ensure();
 
-   /* Initialize Python */
-   PyRun_SimpleString("import sys \n");
-   PyRun_SimpleString("sys.path.append('.') \n");
-   if(env->cfg->directory && env->cfg->directory[0]) {
-      char wdir[1524];
-      snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
-      env->cfg->directory);
-      PyRun_SimpleString(wdir);
-   }
-   PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
-   PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
-   PyRun_SimpleString("import distutils.sysconfig \n");
-   PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
-   if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
-   {
-      log_err("pythonmod: cannot initialize core module: unboundmodule.py");
-      PyGILState_Release(gil);
-      return 0;
+   if (py_mod_count==1) {
+      /* Initialize Python */
+      PyRun_SimpleString("import sys \n");
+      PyRun_SimpleString("sys.path.append('.') \n");
+      if(env->cfg->directory && env->cfg->directory[0]) {
+         char wdir[1524];
+         snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
+         env->cfg->directory);
+         PyRun_SimpleString(wdir);
+      }
+      PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
+      PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
+      PyRun_SimpleString("import distutils.sysconfig \n");
+      PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
+      if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
+      {
+         log_err("pythonmod: cannot initialize core module: unboundmodule.py");
+         PyGILState_Release(gil);
+         return 0;
+      }
    }
 
    /* Check Python file load */
@@ -341,6 +361,7 @@ int pythonmod_init(struct module_env* env, int id)
       (void)PyParser_SimpleParseFile(script_py, pe->fname, Py_file_input);
       log_py_err();
       PyGILState_Release(gil);
+      fclose(script_py);
       return 0;
    }
    fclose(script_py);
@@ -425,9 +446,11 @@ void pythonmod_deinit(struct module_env* env, int id)
       Py_XDECREF(pe->data);
       PyGILState_Release(gil);
 
-      PyEval_RestoreThread(pe->mainthr);
-      Py_Finalize();
-      pe->mainthr = NULL;
+      if(--py_mod_count==0) {
+         PyEval_RestoreThread(mainthr);
+         Py_Finalize();
+         mainthr = NULL;
+      }
    }
    pe->fname = NULL;
    free(pe);
index 9b60254d7b4e8467a5c11d401726298b8f093333..8d6bdf5eaddee4a15aff07b25faff010adf75728 100644 (file)
@@ -602,7 +602,7 @@ int config_set_option(struct config_file* cfg, const char* opt,
        else S_STR("control-key-file:", control_key_file)
        else S_STR("control-cert-file:", control_cert_file)
        else S_STR("module-config:", module_conf)
-       else S_STR("python-script:", python_script)
+       else S_STRLIST("python-script:", python_script)
        else S_YNO("disable-dnssec-lame-check:", disable_dnssec_lame_check)
 #ifdef CLIENT_SUBNET
        /* Can't set max subnet prefix here, since that value is used when
@@ -1054,7 +1054,7 @@ config_get_option(struct config_file* cfg, const char* opt,
        else O_YNO(opt, "unblock-lan-zones", unblock_lan_zones)
        else O_YNO(opt, "insecure-lan-zones", insecure_lan_zones)
        else O_DEC(opt, "max-udp-size", max_udp_size)
-       else O_STR(opt, "python-script", python_script)
+       else O_LST(opt, "python-script", python_script)
        else O_YNO(opt, "disable-dnssec-lame-check", disable_dnssec_lame_check)
        else O_DEC(opt, "ip-ratelimit", ip_ratelimit)
        else O_DEC(opt, "ratelimit", ratelimit)
@@ -1420,6 +1420,7 @@ config_delete(struct config_file* cfg)
        free(cfg->dnstap_version);
        config_deldblstrlist(cfg->ratelimit_for_domain);
        config_deldblstrlist(cfg->ratelimit_below_domain);
+       config_delstrlist(cfg->python_script);
 #ifdef USE_IPSECMOD
        free(cfg->ipsecmod_hook);
        config_delstrlist(cfg->ipsecmod_whitelist);
@@ -1630,6 +1631,31 @@ cfg_strlist_insert(struct config_strlist** head, char* item)
        return 1;
 }
 
+int
+cfg_strlist_append_ex(struct config_strlist** head, char* item)
+{
+       struct config_strlist *s;
+       if(!item || !head)
+               return 0;
+       s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist));
+       if(!s)
+               return 0;
+       s->str = item;
+       s->next = NULL;
+       
+       if (*head==NULL) {
+               *head = s;
+       } else {
+               struct config_strlist *last = *head;
+               while (last->next!=NULL) {
+                   last = last->next;
+               }
+               last->next = s;
+       }
+       
+       return 1;  
+}
+
 int 
 cfg_str2list_insert(struct config_str2list** head, char* item, char* i2)
 {
index 3cffdbff93868b7d8cfecb97020f9116dcacaed4..68d1c911cf3247a71c1620800c86cd2e7e333b79 100644 (file)
@@ -433,7 +433,7 @@ struct config_file {
        char* control_cert_file;
 
        /** Python script file */
-       char* python_script;
+       struct config_strlist* python_script;
 
        /** Use systemd socket activation. */
        int use_systemd;
@@ -820,6 +820,14 @@ char* config_collate_cat(struct config_strlist* list);
  */
 int cfg_strlist_append(struct config_strlist_head* list, char* item);
 
+/**
+ * Searches the end of a string list and appends the given text.
+ * @param head: pointer to strlist head variable.
+ * @param item: new item. malloced by caller. if NULL the insertion fails.
+ * @return true on success.
+ */
+int cfg_strlist_append_ex(struct config_strlist** head, char* item);
+
 /**
  * Find string in strlist.
  * @param head: pointer to strlist head variable.
index 9674aa283dc528b6e6d1699dfe6f6dad725f33c9..2b5ed3f682c4a61d47b78f3022d2a9ad413c0c42 100644 (file)
@@ -5716,8 +5716,8 @@ yyreduce:
 #line 2721 "./util/configparser.y" /* yacc.c:1648  */
     {
                OUTYY(("P(python-script:%s)\n", (yyvsp[0].str)));
-               free(cfg_parser->cfg->python_script);
-               cfg_parser->cfg->python_script = (yyvsp[0].str);
+               if(!cfg_strlist_append_ex(&cfg_parser->cfg->python_script, (yyvsp[0].str)))
+                       yyerror("out of memory");
        }
 #line 5723 "util/configparser.c" /* yacc.c:1648  */
     break;
index 5f52f4d7784782d905b8762cf31480ee526cb25d..9ca2c35ec25434a5de8b8754371c5b81c2816151 100644 (file)
@@ -2720,8 +2720,8 @@ content_py: py_script
 py_script: VAR_PYTHON_SCRIPT STRING_ARG
        {
                OUTYY(("P(python-script:%s)\n", $2));
-               free(cfg_parser->cfg->python_script);
-               cfg_parser->cfg->python_script = $2;
+               if(!cfg_strlist_append_ex(&cfg_parser->cfg->python_script, $2))
+                       yyerror("out of memory");
        }
 server_disable_dnssec_lame_check: VAR_DISABLE_DNSSEC_LAME_CHECK STRING_ARG
        {