]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - pythonmod/pythonmod.c
import unbound 1.6.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 #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_init_arg, *res;
116    PyGILState_STATE gil;
117    int init_standard = 1;
118
119    struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
120    if (!pe) 
121    {
122       log_err("pythonmod: malloc failure");
123       return 0;
124    }
125
126    env->modinfo[id] = (void*) pe;
127
128    /* Initialize module */
129    pe->fname = env->cfg->python_script;
130    if(pe->fname==NULL || pe->fname[0]==0) {
131       log_err("pythonmod: no script given.");
132       return 0;
133    }
134
135    /* Initialize Python libraries */
136    if (!Py_IsInitialized()) 
137    {
138 #if PY_MAJOR_VERSION >= 3
139       wchar_t progname[8];
140       mbstowcs(progname, "unbound", 8);
141 #else
142       char *progname = "unbound";
143 #endif
144       Py_SetProgramName(progname);
145       Py_NoSiteFlag = 1;
146 #if PY_MAJOR_VERSION >= 3
147       PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
148 #endif
149       Py_Initialize();
150       PyEval_InitThreads();
151       SWIG_init();
152       pe->mainthr = PyEval_SaveThread();
153    }
154
155    gil = PyGILState_Ensure();
156
157    /* Initialize Python */
158    PyRun_SimpleString("import sys \n");
159    PyRun_SimpleString("sys.path.append('.') \n");
160    if(env->cfg->directory && env->cfg->directory[0]) {
161       char wdir[1524];
162       snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
163       env->cfg->directory);
164       PyRun_SimpleString(wdir);
165    }
166    PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
167    PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
168    PyRun_SimpleString("import distutils.sysconfig \n");
169    PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
170    if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
171    {
172       log_err("pythonmod: cannot initialize core module: unboundmodule.py"); 
173       PyGILState_Release(gil);
174       return 0;
175    }
176
177    /* Check Python file load */
178    if ((script_py = fopen(pe->fname, "r")) == NULL) 
179    {
180       log_err("pythonmod: can't open file %s for reading", pe->fname);
181       PyGILState_Release(gil);
182       return 0;
183    }
184
185    /* Load file */
186    pe->module = PyImport_AddModule("__main__");
187    pe->dict = PyModule_GetDict(pe->module);
188    pe->data = Py_None;
189    Py_INCREF(pe->data);
190    PyModule_AddObject(pe->module, "mod_env", pe->data);
191
192    /* TODO: deallocation of pe->... if an error occurs */
193   
194    if (PyRun_SimpleFile(script_py, pe->fname) < 0) 
195    {
196       log_err("pythonmod: can't parse Python script %s", pe->fname);
197       PyGILState_Release(gil);
198       return 0;
199    }
200
201    fclose(script_py);
202
203    if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
204    {
205       init_standard = 0;
206       if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL) 
207       {
208          log_err("pythonmod: function init is missing in %s", pe->fname);
209          PyGILState_Release(gil);
210          return 0;
211       }
212    }
213    if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL) 
214    {
215       log_err("pythonmod: function deinit is missing in %s", pe->fname);
216       PyGILState_Release(gil);
217       return 0;
218    }
219    if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL) 
220    {
221       log_err("pythonmod: function operate is missing in %s", pe->fname);
222       PyGILState_Release(gil);
223       return 0;
224    }
225    if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL) 
226    {
227       log_err("pythonmod: function inform_super is missing in %s", pe->fname);
228       PyGILState_Release(gil);
229       return 0;
230    }
231
232    if (init_standard)
233    {
234       py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
235    }
236    else
237    {
238       py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
239         SWIGTYPE_p_config_file, 0);
240    }
241    res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
242    if (PyErr_Occurred()) 
243    {
244       log_err("pythonmod: Exception occurred in function init");
245       PyErr_Print();
246       Py_XDECREF(res);
247       Py_XDECREF(py_init_arg);
248       PyGILState_Release(gil);
249       return 0;
250    }
251
252    Py_XDECREF(res);
253    Py_XDECREF(py_init_arg);
254    PyGILState_Release(gil);
255
256    return 1;
257 }
258
259 void pythonmod_deinit(struct module_env* env, int id)
260 {
261    struct pythonmod_env* pe = env->modinfo[id];
262    if(pe == NULL)
263       return;
264
265    /* Free Python resources */
266    if(pe->module != NULL)
267    {
268       PyObject* res;
269       PyGILState_STATE gil = PyGILState_Ensure();
270
271       /* Deinit module */
272       res = PyObject_CallFunction(pe->func_deinit, "i", id);
273       if (PyErr_Occurred()) {
274          log_err("pythonmod: Exception occurred in function deinit");
275          PyErr_Print();
276       }
277       /* Free result if any */
278       Py_XDECREF(res);
279       /* Free shared data if any */
280       Py_XDECREF(pe->data);
281       PyGILState_Release(gil);
282
283       PyEval_RestoreThread(pe->mainthr);
284       Py_Finalize();
285       pe->mainthr = NULL;
286    }
287    pe->fname = NULL;
288    free(pe);
289
290    /* Module is deallocated in Python */
291    env->modinfo[id] = NULL;
292 }
293
294 void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
295 {
296    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
297    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
298    PyObject* py_qstate, *py_sqstate, *res;
299    PyGILState_STATE gil = PyGILState_Ensure();
300
301    log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
302    log_query_info(VERB_ALGO, "super is", &super->qinfo);
303
304    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
305    py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
306
307    res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate, 
308         py_sqstate, pq->data);
309
310    if (PyErr_Occurred()) 
311    {
312       log_err("pythonmod: Exception occurred in function inform_super");
313       PyErr_Print();
314       qstate->ext_state[id] = module_error;
315    } 
316    else if ((res == NULL)  || (!PyObject_IsTrue(res))) 
317    {
318       log_err("pythonmod: python returned bad code in inform_super");
319       qstate->ext_state[id] = module_error;
320    } 
321
322    Py_XDECREF(res);
323    Py_XDECREF(py_sqstate);
324    Py_XDECREF(py_qstate);
325
326    PyGILState_Release(gil);
327 }
328
329 void pythonmod_operate(struct module_qstate* qstate, enum module_ev event, 
330         int id, struct outbound_entry* ATTR_UNUSED(outbound))
331 {
332    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
333    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
334    PyObject* py_qstate, *res;
335    PyGILState_STATE gil = PyGILState_Ensure();
336
337    if ( pq == NULL)
338    { 
339       /* create qstate */
340       pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
341       
342       /* Initialize per query data */
343       pq->data = Py_None;
344       Py_INCREF(pq->data);
345    }
346
347    /* Call operate */
348    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
349    res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event, 
350         py_qstate, pq->data);
351    if (PyErr_Occurred()) 
352    {
353       log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
354       PyErr_Print();
355       qstate->ext_state[id] = module_error;
356    } 
357    else if ((res == NULL)  || (!PyObject_IsTrue(res))) 
358    {
359       log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
360       qstate->ext_state[id] = module_error;
361    } 
362    Py_XDECREF(res);
363    Py_XDECREF(py_qstate);
364
365    PyGILState_Release(gil);
366 }
367
368 void pythonmod_clear(struct module_qstate* qstate, int id)
369 {
370    struct pythonmod_qstate* pq;
371    if (qstate == NULL)
372       return;
373
374    pq = (struct pythonmod_qstate*)qstate->minfo[id];
375    verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%lX", id, 
376         (unsigned long int)pq);
377    if(pq != NULL)
378    {
379       PyGILState_STATE gil = PyGILState_Ensure();
380       Py_DECREF(pq->data);
381       PyGILState_Release(gil);
382       /* Free qstate */
383       free(pq);
384    }
385
386    qstate->minfo[id] = NULL;
387 }
388
389 size_t pythonmod_get_mem(struct module_env* env, int id)
390 {
391    struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
392    verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%lX", id, 
393         (unsigned long int)pe);
394    if(!pe)
395       return 0;
396    return sizeof(*pe);
397 }
398
399 /**
400  * The module function block 
401  */
402 static struct module_func_block pythonmod_block = {
403    "python",
404    &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super, 
405    &pythonmod_clear, &pythonmod_get_mem
406 };
407
408 struct module_func_block* pythonmod_get_funcblock(void)
409 {
410    return &pythonmod_block;
411 }