xref: /netbsd-src/external/bsd/unbound/dist/pythonmod/pythonmod.c (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
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 #ifndef __clang__
45 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
46 #endif
47 #endif
48 
49 #include "config.h"
50 #include "sldns/sbuffer.h"
51 
52 #undef _POSIX_C_SOURCE
53 #undef _XOPEN_SOURCE
54 #include <Python.h>
55 
56 #include "pythonmod/pythonmod.h"
57 #include "util/module.h"
58 #include "util/config_file.h"
59 #include "pythonmod_utils.h"
60 
61 #ifdef S_SPLINT_S
62 typedef struct PyObject PyObject;
63 typedef struct PyThreadState PyThreadState;
64 typedef void* PyGILState_STATE;
65 #endif
66 
67 /**
68  * Global state for the module.
69  */
70 struct pythonmod_env {
71 
72 	/** Python script filename. */
73 	const char* fname;
74 
75 	/** Python main thread */
76 	PyThreadState* mainthr;
77 	/** Python module. */
78 	PyObject* module;
79 
80 	/** Module init function */
81 	PyObject* func_init;
82 	/** Module deinit function */
83 	PyObject* func_deinit;
84 	/** Module operate function */
85 	PyObject* func_operate;
86 	/** Module super_inform function */
87 	PyObject* func_inform;
88 
89 	/** Python dictionary. */
90 	PyObject* dict;
91 
92 	/** Module data. */
93 	PyObject* data;
94 
95 	/** Module qstate. */
96 	struct module_qstate* qstate;
97 };
98 
99 /**
100  * Per query state for the iterator module.
101  */
102 struct pythonmod_qstate {
103 
104 	/** Module per query data. */
105 	PyObject* data;
106 };
107 
108 /* Generated */
109 #ifndef S_SPLINT_S
110 #include "pythonmod/interface.h"
111 #endif
112 
113 /** log python error */
114 static void
115 log_py_err(void)
116 {
117 	char *result = NULL;
118 	const char* iomod = "cStringIO";
119 	PyObject *modStringIO = NULL;
120 	PyObject *modTB = NULL;
121 	PyObject *obFuncStringIO = NULL;
122 	PyObject *obStringIO = NULL;
123 	PyObject *obFuncTB = NULL;
124 	PyObject *argsTB = NULL;
125 	PyObject *obResult = NULL;
126 	PyObject *ascstr = NULL;
127 	PyObject *exc_typ, *exc_val, *exc_tb;
128 
129 	/* Fetch the error state now before we cruch it */
130 	/* exc val contains the error message
131 	 * exc tb contains stack traceback and other info. */
132 	PyErr_Fetch(&exc_typ, &exc_val, &exc_tb);
133 	PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb);
134 
135 	/* Import the modules we need - cStringIO and traceback */
136 	modStringIO = PyImport_ImportModule("cStringIO");
137 	if (modStringIO==NULL) {
138 		/* python 1.4 and before */
139 		modStringIO = PyImport_ImportModule("StringIO");
140 		iomod = "StringIO";
141 	}
142 	if (modStringIO==NULL) {
143 		/* python 3 */
144 		modStringIO = PyImport_ImportModule("io");
145 		iomod = "io";
146 	}
147 	if (modStringIO==NULL) {
148 		log_err("pythonmod: cannot print exception, "
149 			"cannot ImportModule cStringIO or StringIO or io");
150 		goto cleanup;
151 	}
152 	modTB = PyImport_ImportModule("traceback");
153 	if (modTB==NULL) {
154 		log_err("pythonmod: cannot print exception, "
155 			"cannot ImportModule traceback");
156 		goto cleanup;
157 	}
158 
159 	/* Construct a cStringIO object */
160 	obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO");
161 	if (obFuncStringIO==NULL) {
162 		log_err("pythonmod: cannot print exception, "
163 			"cannot GetAttrString %s.StringIO", iomod);
164 		goto cleanup;
165 	}
166 	obStringIO = PyObject_CallObject(obFuncStringIO, NULL);
167 	if (obStringIO==NULL) {
168 		log_err("pythonmod: cannot print exception, "
169 			"cannot call %s.StringIO()", iomod);
170 		goto cleanup;
171 	}
172 
173 	/* Get the traceback.print_exception function, and call it. */
174 	obFuncTB = PyObject_GetAttrString(modTB, "print_exception");
175 	if (obFuncTB==NULL) {
176 		log_err("pythonmod: cannot print exception, "
177 			"cannot GetAttrString traceback.print_exception");
178 		goto cleanup;
179 	}
180 	argsTB = Py_BuildValue("OOOOO", (exc_typ ? exc_typ : Py_None),
181 		(exc_val ? exc_val : Py_None), (exc_tb  ? exc_tb  : Py_None),
182 		Py_None, obStringIO);
183 	if (argsTB==NULL) {
184 		log_err("pythonmod: cannot print exception, "
185 			"cannot BuildValue for print_exception");
186 		goto cleanup;
187 	}
188 
189 	obResult = PyObject_CallObject(obFuncTB, argsTB);
190 	if (obResult==NULL) {
191 		PyErr_Print();
192 		log_err("pythonmod: cannot print exception, "
193 			"call traceback.print_exception() failed");
194 		goto cleanup;
195 	}
196 
197 	/* Now call the getvalue() method in the StringIO instance */
198 	Py_DECREF(obFuncStringIO);
199 	obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue");
200 	if (obFuncStringIO==NULL) {
201 		log_err("pythonmod: cannot print exception, "
202 			"cannot GetAttrString StringIO.getvalue");
203 		goto cleanup;
204 	}
205 	Py_DECREF(obResult);
206 	obResult = PyObject_CallObject(obFuncStringIO, NULL);
207 	if (obResult==NULL) {
208 		log_err("pythonmod: cannot print exception, "
209 			"call StringIO.getvalue() failed");
210 		goto cleanup;
211 	}
212 
213 	/* And it should be a string all ready to go - duplicate it. */
214 	if (!PyString_Check(obResult) && !PyUnicode_Check(obResult)) {
215 		log_err("pythonmod: cannot print exception, "
216 			"StringIO.getvalue() result did not String_Check"
217 			" or Unicode_Check");
218 		goto cleanup;
219 	}
220 	if(PyString_Check(obResult)) {
221 		result = PyString_AsString(obResult);
222 	} else {
223 		ascstr = PyUnicode_AsASCIIString(obResult);
224 		result = PyBytes_AsString(ascstr);
225 	}
226 	log_err("pythonmod: python error: %s", result);
227 
228 cleanup:
229 	Py_XDECREF(modStringIO);
230 	Py_XDECREF(modTB);
231 	Py_XDECREF(obFuncStringIO);
232 	Py_XDECREF(obStringIO);
233 	Py_XDECREF(obFuncTB);
234 	Py_XDECREF(argsTB);
235 	Py_XDECREF(obResult);
236 	Py_XDECREF(ascstr);
237 
238 	/* clear the exception, by not restoring it */
239 	/* Restore the exception state */
240 	/* PyErr_Restore(exc_typ, exc_val, exc_tb); */
241 }
242 
243 int pythonmod_init(struct module_env* env, int id)
244 {
245    /* Initialize module */
246    FILE* script_py = NULL;
247    PyObject* py_init_arg, *res;
248    PyGILState_STATE gil;
249    int init_standard = 1;
250 #if PY_MAJOR_VERSION < 3
251    PyObject* PyFileObject = NULL;
252 #endif
253 
254    struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
255    if (!pe)
256    {
257       log_err("pythonmod: malloc failure");
258       return 0;
259    }
260 
261    env->modinfo[id] = (void*) pe;
262 
263    /* Initialize module */
264    pe->fname = env->cfg->python_script;
265    if(pe->fname==NULL || pe->fname[0]==0) {
266       log_err("pythonmod: no script given.");
267       return 0;
268    }
269 
270    /* Initialize Python libraries */
271    if (!Py_IsInitialized())
272    {
273 #if PY_MAJOR_VERSION >= 3
274       wchar_t progname[8];
275       mbstowcs(progname, "unbound", 8);
276 #else
277       char *progname = "unbound";
278 #endif
279       Py_SetProgramName(progname);
280       Py_NoSiteFlag = 1;
281 #if PY_MAJOR_VERSION >= 3
282       PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
283 #endif
284       Py_Initialize();
285       PyEval_InitThreads();
286       SWIG_init();
287       pe->mainthr = PyEval_SaveThread();
288    }
289 
290    gil = PyGILState_Ensure();
291 
292    /* Initialize Python */
293    PyRun_SimpleString("import sys \n");
294    PyRun_SimpleString("sys.path.append('.') \n");
295    if(env->cfg->directory && env->cfg->directory[0]) {
296       char wdir[1524];
297       snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
298       env->cfg->directory);
299       PyRun_SimpleString(wdir);
300    }
301    PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
302    PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
303    PyRun_SimpleString("import distutils.sysconfig \n");
304    PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
305    if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
306    {
307       log_err("pythonmod: cannot initialize core module: unboundmodule.py");
308       PyGILState_Release(gil);
309       return 0;
310    }
311 
312    /* Check Python file load */
313    /* uses python to open the file, this works on other platforms,
314     * eg. Windows, to open the file in the correct mode for python */
315 #if PY_MAJOR_VERSION < 3
316    PyFileObject = PyFile_FromString((char*)pe->fname, "r");
317    script_py = PyFile_AsFile(PyFileObject);
318 #else
319    script_py = _Py_fopen(pe->fname, "r");
320 #endif
321    if (script_py == NULL)
322    {
323       log_err("pythonmod: can't open file %s for reading", pe->fname);
324       PyGILState_Release(gil);
325       return 0;
326    }
327 
328    /* Load file */
329    pe->module = PyImport_AddModule("__main__");
330    pe->dict = PyModule_GetDict(pe->module);
331    pe->data = Py_None;
332    Py_INCREF(pe->data);
333    PyModule_AddObject(pe->module, "mod_env", pe->data);
334 
335    /* TODO: deallocation of pe->... if an error occurs */
336 
337    if (PyRun_SimpleFile(script_py, pe->fname) < 0) {
338       log_err("pythonmod: can't parse Python script %s", pe->fname);
339       /* print the error to logs too, run it again */
340       fseek(script_py, 0, SEEK_SET);
341       /* we don't run the file, like this, because then side-effects
342        *    s = PyRun_File(script_py, pe->fname, Py_file_input,
343        *        PyModule_GetDict(PyImport_AddModule("__main__")), pe->dict);
344        * could happen (again). Instead we parse the file again to get
345        * the error string in the logs, for when the daemon has stderr
346        * removed.  SimpleFile run already printed to stderr, for then
347        * this is called from unbound-checkconf or unbound -dd the user
348        * has a nice formatted error.
349       */
350       /* ignore the NULL return of _node, it is NULL due to the parse failure
351        * that we are expecting */
352       (void)PyParser_SimpleParseFile(script_py, pe->fname, Py_file_input);
353       log_py_err();
354       PyGILState_Release(gil);
355       return 0;
356    }
357 #if PY_MAJOR_VERSION < 3
358    Py_XDECREF(PyFileObject);
359 #else
360    fclose(script_py);
361 #endif
362 
363    if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
364    {
365       init_standard = 0;
366       if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
367       {
368          log_err("pythonmod: function init is missing in %s", pe->fname);
369          PyGILState_Release(gil);
370          return 0;
371       }
372    }
373    if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
374    {
375       log_err("pythonmod: function deinit is missing in %s", pe->fname);
376       PyGILState_Release(gil);
377       return 0;
378    }
379    if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
380    {
381       log_err("pythonmod: function operate is missing in %s", pe->fname);
382       PyGILState_Release(gil);
383       return 0;
384    }
385    if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
386    {
387       log_err("pythonmod: function inform_super is missing in %s", pe->fname);
388       PyGILState_Release(gil);
389       return 0;
390    }
391 
392    if (init_standard)
393    {
394       py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
395    }
396    else
397    {
398       py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
399         SWIGTYPE_p_config_file, 0);
400    }
401    res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
402    if (PyErr_Occurred())
403    {
404       log_err("pythonmod: Exception occurred in function init");
405       log_py_err();
406       Py_XDECREF(res);
407       Py_XDECREF(py_init_arg);
408       PyGILState_Release(gil);
409       return 0;
410    }
411 
412    Py_XDECREF(res);
413    Py_XDECREF(py_init_arg);
414    PyGILState_Release(gil);
415 
416    return 1;
417 }
418 
419 void pythonmod_deinit(struct module_env* env, int id)
420 {
421    struct pythonmod_env* pe = env->modinfo[id];
422    if(pe == NULL)
423       return;
424 
425    /* Free Python resources */
426    if(pe->module != NULL)
427    {
428       PyObject* res;
429       PyGILState_STATE gil = PyGILState_Ensure();
430 
431       /* Deinit module */
432       res = PyObject_CallFunction(pe->func_deinit, "i", id);
433       if (PyErr_Occurred()) {
434          log_err("pythonmod: Exception occurred in function deinit");
435          log_py_err();
436       }
437       /* Free result if any */
438       Py_XDECREF(res);
439       /* Free shared data if any */
440       Py_XDECREF(pe->data);
441       PyGILState_Release(gil);
442 
443       PyEval_RestoreThread(pe->mainthr);
444       Py_Finalize();
445       pe->mainthr = NULL;
446    }
447    pe->fname = NULL;
448    free(pe);
449 
450    /* Module is deallocated in Python */
451    env->modinfo[id] = NULL;
452 }
453 
454 void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
455 {
456    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
457    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
458    PyObject* py_qstate, *py_sqstate, *res;
459    PyGILState_STATE gil = PyGILState_Ensure();
460 
461    log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
462    log_query_info(VERB_ALGO, "super is", &super->qinfo);
463 
464    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
465    py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
466 
467    res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
468 	py_sqstate, pq->data);
469 
470    if (PyErr_Occurred())
471    {
472       log_err("pythonmod: Exception occurred in function inform_super");
473       log_py_err();
474       qstate->ext_state[id] = module_error;
475    }
476    else if ((res == NULL)  || (!PyObject_IsTrue(res)))
477    {
478       log_err("pythonmod: python returned bad code in inform_super");
479       qstate->ext_state[id] = module_error;
480    }
481 
482    Py_XDECREF(res);
483    Py_XDECREF(py_sqstate);
484    Py_XDECREF(py_qstate);
485 
486    PyGILState_Release(gil);
487 }
488 
489 void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
490 	int id, struct outbound_entry* ATTR_UNUSED(outbound))
491 {
492    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
493    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
494    PyObject* py_qstate, *res;
495    PyGILState_STATE gil = PyGILState_Ensure();
496 
497    if ( pq == NULL)
498    {
499       /* create qstate */
500       pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
501 
502       /* Initialize per query data */
503       pq->data = Py_None;
504       Py_INCREF(pq->data);
505    }
506 
507    /* Call operate */
508    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
509    res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
510 	py_qstate, pq->data);
511    if (PyErr_Occurred())
512    {
513       log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
514       log_py_err();
515       qstate->ext_state[id] = module_error;
516    }
517    else if ((res == NULL)  || (!PyObject_IsTrue(res)))
518    {
519       log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
520       qstate->ext_state[id] = module_error;
521    }
522    Py_XDECREF(res);
523    Py_XDECREF(py_qstate);
524 
525    PyGILState_Release(gil);
526 }
527 
528 void pythonmod_clear(struct module_qstate* qstate, int id)
529 {
530    struct pythonmod_qstate* pq;
531    if (qstate == NULL)
532       return;
533 
534    pq = (struct pythonmod_qstate*)qstate->minfo[id];
535    verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%p", id, pq);
536    if(pq != NULL)
537    {
538       PyGILState_STATE gil = PyGILState_Ensure();
539       Py_DECREF(pq->data);
540       PyGILState_Release(gil);
541       /* Free qstate */
542       free(pq);
543    }
544 
545    qstate->minfo[id] = NULL;
546 }
547 
548 size_t pythonmod_get_mem(struct module_env* env, int id)
549 {
550    struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
551    verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%p", id, pe);
552    if(!pe)
553       return 0;
554    return sizeof(*pe);
555 }
556 
557 /**
558  * The module function block
559  */
560 static struct module_func_block pythonmod_block = {
561    "python",
562    &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
563    &pythonmod_clear, &pythonmod_get_mem
564 };
565 
566 struct module_func_block* pythonmod_get_funcblock(void)
567 {
568    return &pythonmod_block;
569 }
570