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
log_py_err(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 	/* when using PyErr_Restore there is no need to Py_XDECREF for
249 	 * these 3 pointers. */
250 	Py_XDECREF(exc_typ);
251 	Py_XDECREF(exc_val);
252 	Py_XDECREF(exc_tb);
253 }
254 
pythonmod_init(struct module_env * env,int id)255 int pythonmod_init(struct module_env* env, int id)
256 {
257    int py_mod_idx = py_mod_count++;
258 
259    /* Initialize module */
260    FILE* script_py = NULL;
261    PyObject* py_init_arg, *res;
262    PyGILState_STATE gil;
263    int init_standard = 1, i = 0;
264 #if PY_MAJOR_VERSION < 3
265    PyObject* PyFileObject = NULL;
266 #endif
267    struct config_strlist* cfg_item = env->cfg->python_script;
268 
269    struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
270    if (!pe)
271    {
272       log_err("pythonmod: malloc failure");
273       return 0;
274    }
275 
276    env->modinfo[id] = (void*) pe;
277 
278    /* Initialize module */
279    pe->fname=NULL; i = 0;
280    while (cfg_item!=NULL) {
281       if (py_mod_idx==i++) {
282          pe->fname=cfg_item->str;
283          break;
284       }
285       cfg_item = cfg_item->next;
286    }
287    if(pe->fname==NULL || pe->fname[0]==0) {
288       log_err("pythonmod[%d]: no script given.", py_mod_idx);
289       return 0;
290    }
291 
292    /* Initialize Python libraries */
293    if (py_mod_count==1 && !Py_IsInitialized())
294    {
295 #if PY_MAJOR_VERSION >= 3
296       wchar_t progname[8];
297       mbstowcs(progname, "unbound", 8);
298 #else
299       char *progname = "unbound";
300 #endif
301       Py_SetProgramName(progname);
302       Py_NoSiteFlag = 1;
303 #if PY_MAJOR_VERSION >= 3
304       PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
305 #endif
306       Py_Initialize();
307 #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION <= 6)
308       /* initthreads only for python 3.6 and older */
309       PyEval_InitThreads();
310 #endif
311       SWIG_init();
312       mainthr = PyEval_SaveThread();
313    }
314 
315    gil = PyGILState_Ensure();
316 
317    if (py_mod_count==1) {
318       /* Initialize Python */
319       PyRun_SimpleString("import sys \n");
320       PyRun_SimpleString("sys.path.append('.') \n");
321       if(env->cfg->directory && env->cfg->directory[0]) {
322          char wdir[1524];
323          snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
324          env->cfg->directory);
325          PyRun_SimpleString(wdir);
326       }
327       PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
328       PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
329       PyRun_SimpleString("import distutils.sysconfig \n");
330       PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
331       if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
332       {
333          log_err("pythonmod: cannot initialize core module: unboundmodule.py");
334          PyGILState_Release(gil);
335          return 0;
336       }
337    }
338 
339    /* Check Python file load */
340    /* uses python to open the file, this works on other platforms,
341     * eg. Windows, to open the file in the correct mode for python */
342 #if PY_MAJOR_VERSION < 3
343    PyFileObject = PyFile_FromString((char*)pe->fname, "r");
344    script_py = PyFile_AsFile(PyFileObject);
345 #else
346    script_py = fopen(pe->fname, "r");
347 #endif
348    if (script_py == NULL)
349    {
350       log_err("pythonmod: can't open file %s for reading", pe->fname);
351       PyGILState_Release(gil);
352       return 0;
353    }
354 
355    /* Load file */
356    pe->module = PyImport_AddModule("__main__");
357    pe->dict = PyModule_GetDict(pe->module);
358    pe->data = PyDict_New();
359    Py_XINCREF(pe->data);
360    PyModule_AddObject(pe->module, "mod_env", pe->data);
361 
362    /* TODO: deallocation of pe->... if an error occurs */
363 
364    if (PyRun_SimpleFile(script_py, pe->fname) < 0) {
365 #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9)
366       /* for python before 3.9 */
367       log_err("pythonmod: can't parse Python script %s", pe->fname);
368       /* print the error to logs too, run it again */
369       fseek(script_py, 0, SEEK_SET);
370       /* we don't run the file, like this, because then side-effects
371        *    s = PyRun_File(script_py, pe->fname, Py_file_input,
372        *        PyModule_GetDict(PyImport_AddModule("__main__")), pe->dict);
373        * could happen (again). Instead we parse the file again to get
374        * the error string in the logs, for when the daemon has stderr
375        * removed.  SimpleFile run already printed to stderr, for then
376        * this is called from unbound-checkconf or unbound -dd the user
377        * has a nice formatted error.
378       */
379       /* ignore the NULL return of _node, it is NULL due to the parse failure
380        * that we are expecting */
381       (void)PyParser_SimpleParseFile(script_py, pe->fname, Py_file_input);
382 #else
383       /* for python 3.9 and newer */
384       char* fstr = NULL;
385       size_t flen = 0;
386       log_err("pythonmod: can't parse Python script %s", pe->fname);
387       /* print the error to logs too, run it again */
388       fseek(script_py, 0, SEEK_END);
389       flen = (size_t)ftell(script_py);
390       fstr = malloc(flen+1);
391       if(!fstr) {
392 	      log_err("malloc failure to print parse error");
393 	      PyGILState_Release(gil);
394 	      fclose(script_py);
395 	      return 0;
396       }
397       fseek(script_py, 0, SEEK_SET);
398       if(fread(fstr, flen, 1, script_py) < 1) {
399 	      log_err("file read failed to print parse error: %s: %s",
400 		pe->fname, strerror(errno));
401 	      PyGILState_Release(gil);
402 	      fclose(script_py);
403 	      free(fstr);
404 	      return 0;
405       }
406       fstr[flen] = 0;
407       /* we compile the string, but do not run it, to stop side-effects */
408       /* ignore the NULL return of _node, it is NULL due to the parse failure
409        * that we are expecting */
410       (void)Py_CompileString(fstr, pe->fname, Py_file_input);
411 #endif
412       log_py_err();
413       PyGILState_Release(gil);
414       fclose(script_py);
415 #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9)
416       /* no cleanup needed for python before 3.9 */
417 #else
418       /* cleanup for python 3.9 and newer */
419       free(fstr);
420 #endif
421       return 0;
422    }
423 #if PY_MAJOR_VERSION < 3
424    Py_XDECREF(PyFileObject);
425 #else
426    fclose(script_py);
427 #endif
428 
429    if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
430    {
431       init_standard = 0;
432       if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
433       {
434          log_err("pythonmod: function init is missing in %s", pe->fname);
435          PyGILState_Release(gil);
436          return 0;
437       }
438    }
439    if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
440    {
441       log_err("pythonmod: function deinit is missing in %s", pe->fname);
442       PyGILState_Release(gil);
443       return 0;
444    }
445    if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
446    {
447       log_err("pythonmod: function operate is missing in %s", pe->fname);
448       PyGILState_Release(gil);
449       return 0;
450    }
451    if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
452    {
453       log_err("pythonmod: function inform_super is missing in %s", pe->fname);
454       PyGILState_Release(gil);
455       return 0;
456    }
457 
458    if (init_standard)
459    {
460       py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
461    }
462    else
463    {
464       py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
465         SWIGTYPE_p_config_file, 0);
466    }
467    res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
468    if (PyErr_Occurred())
469    {
470       log_err("pythonmod: Exception occurred in function init");
471       log_py_err();
472       Py_XDECREF(res);
473       Py_XDECREF(py_init_arg);
474       PyGILState_Release(gil);
475       return 0;
476    }
477 
478    Py_XDECREF(res);
479    Py_XDECREF(py_init_arg);
480    PyGILState_Release(gil);
481 
482    return 1;
483 }
484 
pythonmod_deinit(struct module_env * env,int id)485 void pythonmod_deinit(struct module_env* env, int id)
486 {
487    struct pythonmod_env* pe = env->modinfo[id];
488    if(pe == NULL)
489       return;
490 
491    /* Free Python resources */
492    if(pe->module != NULL)
493    {
494       PyObject* res;
495       PyGILState_STATE gil = PyGILState_Ensure();
496 
497       /* Deinit module */
498       res = PyObject_CallFunction(pe->func_deinit, "i", id);
499       if (PyErr_Occurred()) {
500          log_err("pythonmod: Exception occurred in function deinit");
501          log_py_err();
502       }
503       /* Free result if any */
504       Py_XDECREF(res);
505       /* Free shared data if any */
506       Py_XDECREF(pe->data);
507       PyGILState_Release(gil);
508 
509       if(--py_mod_count==0) {
510          PyEval_RestoreThread(mainthr);
511          Py_Finalize();
512          mainthr = NULL;
513       }
514    }
515    pe->fname = NULL;
516    free(pe);
517 
518    /* Module is deallocated in Python */
519    env->modinfo[id] = NULL;
520 }
521 
pythonmod_inform_super(struct module_qstate * qstate,int id,struct module_qstate * super)522 void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
523 {
524    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
525    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
526    PyObject* py_qstate, *py_sqstate, *res;
527    PyGILState_STATE gil = PyGILState_Ensure();
528 
529    log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
530    log_query_info(VERB_ALGO, "super is", &super->qinfo);
531 
532    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
533    py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
534 
535    res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
536 	py_sqstate, pq->data);
537 
538    if (PyErr_Occurred())
539    {
540       log_err("pythonmod: Exception occurred in function inform_super");
541       log_py_err();
542       qstate->ext_state[id] = module_error;
543    }
544    else if ((res == NULL)  || (!PyObject_IsTrue(res)))
545    {
546       log_err("pythonmod: python returned bad code in inform_super");
547       qstate->ext_state[id] = module_error;
548    }
549 
550    Py_XDECREF(res);
551    Py_XDECREF(py_sqstate);
552    Py_XDECREF(py_qstate);
553 
554    PyGILState_Release(gil);
555 }
556 
pythonmod_operate(struct module_qstate * qstate,enum module_ev event,int id,struct outbound_entry * ATTR_UNUSED (outbound))557 void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
558 	int id, struct outbound_entry* ATTR_UNUSED(outbound))
559 {
560    struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
561    struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
562    PyObject* py_qstate, *res;
563    PyGILState_STATE gil = PyGILState_Ensure();
564 
565    if ( pq == NULL)
566    {
567       /* create qstate */
568       pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
569       if(!pq) {
570 		log_err("pythonmod_operate: malloc failure for qstate");
571 		PyGILState_Release(gil);
572 		return;
573       }
574 
575       /* Initialize per query data */
576       pq->data = PyDict_New();
577       if(!pq->data) {
578 		log_err("pythonmod_operate: malloc failure for query data dict");
579 		PyGILState_Release(gil);
580 		return;
581       }
582    }
583 
584    /* Call operate */
585    py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
586    res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
587 	py_qstate, pq->data);
588    if (PyErr_Occurred())
589    {
590       log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
591       log_py_err();
592       qstate->ext_state[id] = module_error;
593    }
594    else if ((res == NULL)  || (!PyObject_IsTrue(res)))
595    {
596       log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
597       qstate->ext_state[id] = module_error;
598    }
599    Py_XDECREF(res);
600    Py_XDECREF(py_qstate);
601 
602    PyGILState_Release(gil);
603 }
604 
pythonmod_clear(struct module_qstate * qstate,int id)605 void pythonmod_clear(struct module_qstate* qstate, int id)
606 {
607    struct pythonmod_qstate* pq;
608    if (qstate == NULL)
609       return;
610 
611    pq = (struct pythonmod_qstate*)qstate->minfo[id];
612    verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%p", id, pq);
613    if(pq != NULL)
614    {
615       PyGILState_STATE gil = PyGILState_Ensure();
616       Py_DECREF(pq->data);
617       PyGILState_Release(gil);
618       /* Free qstate */
619       free(pq);
620    }
621 
622    qstate->minfo[id] = NULL;
623 }
624 
pythonmod_get_mem(struct module_env * env,int id)625 size_t pythonmod_get_mem(struct module_env* env, int id)
626 {
627    struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
628    verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%p", id, pe);
629    if(!pe)
630       return 0;
631    return sizeof(*pe);
632 }
633 
634 /**
635  * The module function block
636  */
637 static struct module_func_block pythonmod_block = {
638    "python",
639    &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
640    &pythonmod_clear, &pythonmod_get_mem
641 };
642 
pythonmod_get_funcblock(void)643 struct module_func_block* pythonmod_get_funcblock(void)
644 {
645    return &pythonmod_block;
646 }
647