]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - pythonmod/pythonmod.c
Vendor import of Unbound 1.9.0.
[FreeBSD/FreeBSD.git] / pythonmod / pythonmod.c
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
251    struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
252    if (!pe)
253    {
254       log_err("pythonmod: malloc failure");
255       return 0;
256    }
257
258    env->modinfo[id] = (void*) pe;
259
260    /* Initialize module */
261    pe->fname = env->cfg->python_script;
262    if(pe->fname==NULL || pe->fname[0]==0) {
263       log_err("pythonmod: no script given.");
264       return 0;
265    }
266
267    /* Initialize Python libraries */
268    if (!Py_IsInitialized())
269    {
270 #if PY_MAJOR_VERSION >= 3
271       wchar_t progname[8];
272       mbstowcs(progname, "unbound", 8);
273 #else
274       char *progname = "unbound";
275 #endif
276       Py_SetProgramName(progname);
277       Py_NoSiteFlag = 1;
278 #if PY_MAJOR_VERSION >= 3
279       PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
280 #endif
281       Py_Initialize();
282       PyEval_InitThreads();
283       SWIG_init();
284       pe->mainthr = PyEval_SaveThread();
285    }
286
287    gil = PyGILState_Ensure();
288
289    /* Initialize Python */
290    PyRun_SimpleString("import sys \n");
291    PyRun_SimpleString("sys.path.append('.') \n");
292    if(env->cfg->directory && env->cfg->directory[0]) {
293       char wdir[1524];
294       snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
295       env->cfg->directory);
296       PyRun_SimpleString(wdir);
297    }
298    PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
299    PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
300    PyRun_SimpleString("import distutils.sysconfig \n");
301    PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
302    if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
303    {
304       log_err("pythonmod: cannot initialize core module: unboundmodule.py");
305       PyGILState_Release(gil);
306       return 0;
307    }
308
309    /* Check Python file load */
310    if ((script_py = fopen(pe->fname, "r")) == NULL)
311    {
312       log_err("pythonmod: can't open file %s for reading", pe->fname);
313       PyGILState_Release(gil);
314       return 0;
315    }
316
317    /* Load file */
318    pe->module = PyImport_AddModule("__main__");
319    pe->dict = PyModule_GetDict(pe->module);
320    pe->data = Py_None;
321    Py_INCREF(pe->data);
322    PyModule_AddObject(pe->module, "mod_env", pe->data);
323
324    /* TODO: deallocation of pe->... if an error occurs */
325
326    if (PyRun_SimpleFile(script_py, pe->fname) < 0) {
327       log_err("pythonmod: can't parse Python script %s", pe->fname);
328       /* print the error to logs too, run it again */
329       fseek(script_py, 0, SEEK_SET);
330       /* we don't run the file, like this, because then side-effects
331        *    s = PyRun_File(script_py, pe->fname, Py_file_input, 
332        *        PyModule_GetDict(PyImport_AddModule("__main__")), pe->dict);
333        * could happen (again). Instead we parse the file again to get
334        * the error string in the logs, for when the daemon has stderr
335        * removed.  SimpleFile run already printed to stderr, for then
336        * this is called from unbound-checkconf or unbound -dd the user
337        * has a nice formatted error.
338       */
339       /* ignore the NULL return of _node, it is NULL due to the parse failure
340        * that we are expecting */
341       (void)PyParser_SimpleParseFile(script_py, pe->fname, Py_file_input);
342       log_py_err();
343       PyGILState_Release(gil);
344       return 0;
345    }
346    fclose(script_py);
347
348    if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
349    {
350       init_standard = 0;
351       if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
352       {
353          log_err("pythonmod: function init is missing in %s", pe->fname);
354          PyGILState_Release(gil);
355          return 0;
356       }
357    }
358    if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
359    {
360       log_err("pythonmod: function deinit is missing in %s", pe->fname);
361       PyGILState_Release(gil);
362       return 0;
363    }
364    if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
365    {
366       log_err("pythonmod: function operate is missing in %s", pe->fname);
367       PyGILState_Release(gil);
368       return 0;
369    }
370    if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
371    {
372       log_err("pythonmod: function inform_super is missing in %s", pe->fname);
373       PyGILState_Release(gil);
374       return 0;
375    }
376
377    if (init_standard)
378    {
379       py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
380    }
381    else
382    {
383       py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
384         SWIGTYPE_p_config_file, 0);
385    }
386    res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
387    if (PyErr_Occurred())
388    {
389       log_err("pythonmod: Exception occurred in function init");
390       log_py_err();
391       Py_XDECREF(res);
392       Py_XDECREF(py_init_arg);
393       PyGILState_Release(gil);
394       return 0;
395    }
396
397    Py_XDECREF(res);
398    Py_XDECREF(py_init_arg);
399    PyGILState_Release(gil);
400
401    return 1;
402 }
403
404 void pythonmod_deinit(struct module_env* env, int id)
405 {
406    struct pythonmod_env* pe = env->modinfo[id];
407    if(pe == NULL)
408       return;
409
410    /* Free Python resources */
411    if(pe->module != NULL)
412    {
413       PyObject* res;
414       PyGILState_STATE gil = PyGILState_Ensure();
415
416       /* Deinit module */
417       res = PyObject_CallFunction(pe->func_deinit, "i", id);
418       if (PyErr_Occurred()) {
419          log_err("pythonmod: Exception occurred in function deinit");
420          log_py_err();
421       }
422       /* Free result if any */
423       Py_XDECREF(res);
424       /* Free shared data if any */
425       Py_XDECREF(pe->data);
426       PyGILState_Release(gil);
427
428       PyEval_RestoreThread(pe->mainthr);
429       Py_Finalize();
430       pe->mainthr = NULL;
431    }
432    pe->fname = NULL;
433    free(pe);
434
435    /* Module is deallocated in Python */
436    env->modinfo[id] = NULL;
437 }
438
439 void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
440 {
441    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
442    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
443    PyObject* py_qstate, *py_sqstate, *res;
444    PyGILState_STATE gil = PyGILState_Ensure();
445
446    log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
447    log_query_info(VERB_ALGO, "super is", &super->qinfo);
448
449    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
450    py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
451
452    res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
453         py_sqstate, pq->data);
454
455    if (PyErr_Occurred())
456    {
457       log_err("pythonmod: Exception occurred in function inform_super");
458       log_py_err();
459       qstate->ext_state[id] = module_error;
460    }
461    else if ((res == NULL)  || (!PyObject_IsTrue(res)))
462    {
463       log_err("pythonmod: python returned bad code in inform_super");
464       qstate->ext_state[id] = module_error;
465    }
466
467    Py_XDECREF(res);
468    Py_XDECREF(py_sqstate);
469    Py_XDECREF(py_qstate);
470
471    PyGILState_Release(gil);
472 }
473
474 void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
475         int id, struct outbound_entry* ATTR_UNUSED(outbound))
476 {
477    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
478    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
479    PyObject* py_qstate, *res;
480    PyGILState_STATE gil = PyGILState_Ensure();
481
482    if ( pq == NULL)
483    {
484       /* create qstate */
485       pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
486
487       /* Initialize per query data */
488       pq->data = Py_None;
489       Py_INCREF(pq->data);
490    }
491
492    /* Call operate */
493    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
494    res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
495         py_qstate, pq->data);
496    if (PyErr_Occurred())
497    {
498       log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
499       log_py_err();
500       qstate->ext_state[id] = module_error;
501    }
502    else if ((res == NULL)  || (!PyObject_IsTrue(res)))
503    {
504       log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
505       qstate->ext_state[id] = module_error;
506    }
507    Py_XDECREF(res);
508    Py_XDECREF(py_qstate);
509
510    PyGILState_Release(gil);
511 }
512
513 void pythonmod_clear(struct module_qstate* qstate, int id)
514 {
515    struct pythonmod_qstate* pq;
516    if (qstate == NULL)
517       return;
518
519    pq = (struct pythonmod_qstate*)qstate->minfo[id];
520    verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%lX", id,
521         (unsigned long int)pq);
522    if(pq != NULL)
523    {
524       PyGILState_STATE gil = PyGILState_Ensure();
525       Py_DECREF(pq->data);
526       PyGILState_Release(gil);
527       /* Free qstate */
528       free(pq);
529    }
530
531    qstate->minfo[id] = NULL;
532 }
533
534 size_t pythonmod_get_mem(struct module_env* env, int id)
535 {
536    struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
537    verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%lX", id,
538         (unsigned long int)pe);
539    if(!pe)
540       return 0;
541    return sizeof(*pe);
542 }
543
544 /**
545  * The module function block
546  */
547 static struct module_func_block pythonmod_block = {
548    "python",
549    &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
550    &pythonmod_clear, &pythonmod_get_mem
551 };
552
553 struct module_func_block* pythonmod_get_funcblock(void)
554 {
555    return &pythonmod_block;
556 }