xref: /netbsd-src/external/bsd/unbound/dist/pythonmod/pythonmod.c (revision d99781d3c9f97c53ce1999acc9e5bfadbeddfd17)
1 /*
2  * pythonmod.c: unbound module C wrapper
3  *
4  * Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
5  *                     Marek Vavrusa  (xvavru00 AT stud.fit.vutbr.cz)
6  *
7  * This software is open source.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  *    * Redistributions of source code must retain the above copyright notice,
14  *      this list of conditions and the following disclaimer.
15  *
16  *    * Redistributions in binary form must reproduce the above copyright notice,
17  *      this list of conditions and the following disclaimer in the documentation
18  *      and/or other materials provided with the distribution.
19  *
20  *    * Neither the name of the organization nor the names of its
21  *      contributors may be used to endorse or promote products derived from this
22  *      software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
28  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  */
36 /**
37  * \file
38  * Python module for unbound.  Calls python script.
39  */
40 
41 /* ignore the varargs unused warning from SWIGs internal vararg support */
42 #ifdef __GNUC__
43 #pragma GCC diagnostic ignored "-Wunused-parameter"
44 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
45 #endif
46 
47 #include "config.h"
48 #include "sldns/sbuffer.h"
49 
50 #undef _POSIX_C_SOURCE
51 #undef _XOPEN_SOURCE
52 #include <Python.h>
53 
54 #include "pythonmod/pythonmod.h"
55 #include "util/module.h"
56 #include "util/config_file.h"
57 #include "pythonmod_utils.h"
58 
59 #ifdef S_SPLINT_S
60 typedef struct PyObject PyObject;
61 typedef struct PyThreadState PyThreadState;
62 typedef void* PyGILState_STATE;
63 #endif
64 
65 /**
66  * Global state for the module.
67  */
68 struct pythonmod_env {
69 
70 	/** Python script filename. */
71 	const char* fname;
72 
73 	/** Python main thread */
74 	PyThreadState* mainthr;
75 	/** Python module. */
76 	PyObject* module;
77 
78 	/** Module init function */
79 	PyObject* func_init;
80 	/** Module deinit function */
81 	PyObject* func_deinit;
82 	/** Module operate function */
83 	PyObject* func_operate;
84 	/** Module super_inform function */
85 	PyObject* func_inform;
86 
87 	/** Python dictionary. */
88 	PyObject* dict;
89 
90 	/** Module data. */
91 	PyObject* data;
92 
93 	/** Module qstate. */
94 	struct module_qstate* qstate;
95 };
96 
97 /**
98  * Per query state for the iterator module.
99  */
100 struct pythonmod_qstate {
101 
102 	/** Module per query data. */
103 	PyObject* data;
104 };
105 
106 /* Generated */
107 #ifndef S_SPLINT_S
108 #include "pythonmod/interface.h"
109 #endif
110 
111 int pythonmod_init(struct module_env* env, int id)
112 {
113    /* Initialize module */
114    FILE* script_py = NULL;
115    PyObject* py_cfg, *res;
116    PyGILState_STATE gil;
117    struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
118    if (!pe)
119    {
120       log_err("pythonmod: malloc failure");
121       return 0;
122    }
123 
124    env->modinfo[id] = (void*) pe;
125 
126    /* Initialize module */
127    pe->fname = env->cfg->python_script;
128    if(pe->fname==NULL || pe->fname[0]==0) {
129       log_err("pythonmod: no script given.");
130       return 0;
131    }
132 
133    /* Initialize Python libraries */
134    if (!Py_IsInitialized())
135    {
136 #if PY_MAJOR_VERSION >= 3
137       wchar_t progname[8];
138       mbstowcs(progname, "unbound", 8);
139 #else
140       char *progname = "unbound";
141 #endif
142       Py_SetProgramName(progname);
143       Py_NoSiteFlag = 1;
144 #if PY_MAJOR_VERSION >= 3
145       PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
146 #endif
147       Py_Initialize();
148       PyEval_InitThreads();
149       SWIG_init();
150       pe->mainthr = PyEval_SaveThread();
151    }
152 
153    gil = PyGILState_Ensure();
154 
155    /* Initialize Python */
156    PyRun_SimpleString("import sys \n");
157    PyRun_SimpleString("sys.path.append('.') \n");
158    if(env->cfg->directory && env->cfg->directory[0]) {
159    	char wdir[1524];
160    	snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
161 		env->cfg->directory);
162    	PyRun_SimpleString(wdir);
163    }
164    PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
165    PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
166    PyRun_SimpleString("import distutils.sysconfig \n");
167    PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
168    if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
169    {
170       log_err("pythonmod: cannot initialize core module: unboundmodule.py");
171       PyGILState_Release(gil);
172       return 0;
173    }
174 
175    /* Check Python file load */
176    if ((script_py = fopen(pe->fname, "r")) == NULL)
177    {
178       log_err("pythonmod: can't open file %s for reading", pe->fname);
179       PyGILState_Release(gil);
180       return 0;
181    }
182 
183    /* Load file */
184    pe->module = PyImport_AddModule("__main__");
185    pe->dict = PyModule_GetDict(pe->module);
186    pe->data = Py_None;
187    Py_INCREF(pe->data);
188    PyModule_AddObject(pe->module, "mod_env", pe->data);
189 
190    /* TODO: deallocation of pe->... if an error occurs */
191 
192    if (PyRun_SimpleFile(script_py, pe->fname) < 0)
193    {
194       log_err("pythonmod: can't parse Python script %s", pe->fname);
195       PyGILState_Release(gil);
196       return 0;
197    }
198 
199    fclose(script_py);
200 
201    if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
202    {
203       log_err("pythonmod: function init is missing in %s", pe->fname);
204       PyGILState_Release(gil);
205       return 0;
206    }
207    if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
208    {
209       log_err("pythonmod: function deinit is missing in %s", pe->fname);
210       PyGILState_Release(gil);
211       return 0;
212    }
213    if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
214    {
215       log_err("pythonmod: function operate is missing in %s", pe->fname);
216       PyGILState_Release(gil);
217       return 0;
218    }
219    if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
220    {
221       log_err("pythonmod: function inform_super is missing in %s", pe->fname);
222       PyGILState_Release(gil);
223       return 0;
224    }
225 
226    py_cfg = SWIG_NewPointerObj((void*) env->cfg, SWIGTYPE_p_config_file, 0);
227    res = PyObject_CallFunction(pe->func_init, "iO", id, py_cfg);
228    if (PyErr_Occurred())
229    {
230       log_err("pythonmod: Exception occurred in function init");
231       PyErr_Print();
232    }
233 
234    Py_XDECREF(res);
235    Py_XDECREF(py_cfg);
236    PyGILState_Release(gil);
237 
238    return 1;
239 }
240 
241 void pythonmod_deinit(struct module_env* env, int id)
242 {
243    struct pythonmod_env* pe = env->modinfo[id];
244    if(pe == NULL)
245       return;
246 
247    /* Free Python resources */
248    if(pe->module != NULL)
249    {
250       PyObject* res;
251       PyGILState_STATE gil = PyGILState_Ensure();
252 
253       /* Deinit module */
254       res = PyObject_CallFunction(pe->func_deinit, "i", id);
255       if (PyErr_Occurred()) {
256          log_err("pythonmod: Exception occurred in function deinit");
257          PyErr_Print();
258       }
259       /* Free result if any */
260       Py_XDECREF(res);
261       /* Free shared data if any */
262       Py_XDECREF(pe->data);
263       PyGILState_Release(gil);
264 
265       PyEval_RestoreThread(pe->mainthr);
266       Py_Finalize();
267       pe->mainthr = NULL;
268    }
269    pe->fname = NULL;
270    free(pe);
271 
272    /* Module is deallocated in Python */
273    env->modinfo[id] = NULL;
274 }
275 
276 void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
277 {
278    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
279    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
280    PyObject* py_qstate, *py_sqstate, *res;
281    PyGILState_STATE gil = PyGILState_Ensure();
282 
283    log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
284    log_query_info(VERB_ALGO, "super is", &super->qinfo);
285 
286    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
287    py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
288 
289    res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
290 	py_sqstate, pq->data);
291 
292    if (PyErr_Occurred())
293    {
294       log_err("pythonmod: Exception occurred in function inform_super");
295       PyErr_Print();
296       qstate->ext_state[id] = module_error;
297    }
298    else if ((res == NULL)  || (!PyObject_IsTrue(res)))
299    {
300       log_err("pythonmod: python returned bad code in inform_super");
301       qstate->ext_state[id] = module_error;
302    }
303 
304    Py_XDECREF(res);
305    Py_XDECREF(py_sqstate);
306    Py_XDECREF(py_qstate);
307 
308    PyGILState_Release(gil);
309 }
310 
311 void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
312 	int id, struct outbound_entry* ATTR_UNUSED(outbound))
313 {
314    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
315    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
316    PyObject* py_qstate, *res;
317    PyGILState_STATE gil = PyGILState_Ensure();
318 
319    if ( pq == NULL)
320    {
321       /* create qstate */
322       pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
323 
324       /* Initialize per query data */
325       pq->data = Py_None;
326       Py_INCREF(pq->data);
327    }
328 
329    /* Call operate */
330    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
331    res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
332 	py_qstate, pq->data);
333    if (PyErr_Occurred())
334    {
335       log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
336       PyErr_Print();
337       qstate->ext_state[id] = module_error;
338    }
339    else if ((res == NULL)  || (!PyObject_IsTrue(res)))
340    {
341       log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
342       qstate->ext_state[id] = module_error;
343    }
344    Py_XDECREF(res);
345    Py_XDECREF(py_qstate);
346 
347    PyGILState_Release(gil);
348 }
349 
350 void pythonmod_clear(struct module_qstate* qstate, int id)
351 {
352    struct pythonmod_qstate* pq;
353    if (qstate == NULL)
354       return;
355 
356    pq = (struct pythonmod_qstate*)qstate->minfo[id];
357    verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%lX", id,
358    	(unsigned long int)pq);
359    if(pq != NULL)
360    {
361       PyGILState_STATE gil = PyGILState_Ensure();
362       Py_DECREF(pq->data);
363       PyGILState_Release(gil);
364       /* Free qstate */
365       free(pq);
366    }
367 
368    qstate->minfo[id] = NULL;
369 }
370 
371 size_t pythonmod_get_mem(struct module_env* env, int id)
372 {
373    struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
374    verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%lX", id,
375    	(unsigned long int)pe);
376    if(!pe)
377       return 0;
378    return sizeof(*pe);
379 }
380 
381 /**
382  * The module function block
383  */
384 static struct module_func_block pythonmod_block = {
385    "python",
386    &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
387    &pythonmod_clear, &pythonmod_get_mem
388 };
389 
390 struct module_func_block* pythonmod_get_funcblock(void)
391 {
392    return &pythonmod_block;
393 }
394