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