From: Philipp Serr Date: Sun, 22 Jan 2017 10:58:28 +0000 (+0100) Subject: Support multiple python module instances X-Git-Tag: release-1.9.3rc1~29^2^2^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b248654aabe5e03c9502ad2c8991514cbcd897d3;p=thirdparty%2Funbound.git Support multiple python module instances 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. --- diff --git a/pythonmod/interface.i b/pythonmod/interface.i index 5f2559bac..465a9184d 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -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; }; /* ************************************************************************************ * diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index a668ecc23..a098c326e 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -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); diff --git a/util/config_file.c b/util/config_file.c index 9b60254d7..8d6bdf5ea 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -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) { diff --git a/util/config_file.h b/util/config_file.h index 3cffdbff9..68d1c911c 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -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. diff --git a/util/configparser.c b/util/configparser.c index 9674aa283..2b5ed3f68 100644 --- a/util/configparser.c +++ b/util/configparser.c @@ -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; diff --git a/util/configparser.y b/util/configparser.y index 5f52f4d77..9ca2c35ec 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -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 {