42f0849d55
git-svn-id: https://unbound.nlnetlabs.nl/svn/trunk@4236 be551aaa-1e26-0410-a405-d3ace91eadb9
414 lines
11 KiB
C
414 lines
11 KiB
C
/*
|
|
* pythonmod.c: unbound module C wrapper
|
|
*
|
|
* Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
|
|
* Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
|
|
*
|
|
* This software is open source.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of the organization nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
/**
|
|
* \file
|
|
* Python module for unbound. Calls python script.
|
|
*/
|
|
|
|
/* ignore the varargs unused warning from SWIGs internal vararg support */
|
|
#ifdef __GNUC__
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
#ifndef __clang__
|
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
#endif
|
|
#endif
|
|
|
|
#include "config.h"
|
|
#include "sldns/sbuffer.h"
|
|
|
|
#undef _POSIX_C_SOURCE
|
|
#undef _XOPEN_SOURCE
|
|
#include <Python.h>
|
|
|
|
#include "pythonmod/pythonmod.h"
|
|
#include "util/module.h"
|
|
#include "util/config_file.h"
|
|
#include "pythonmod_utils.h"
|
|
|
|
#ifdef S_SPLINT_S
|
|
typedef struct PyObject PyObject;
|
|
typedef struct PyThreadState PyThreadState;
|
|
typedef void* PyGILState_STATE;
|
|
#endif
|
|
|
|
/**
|
|
* Global state for the module.
|
|
*/
|
|
struct pythonmod_env {
|
|
|
|
/** Python script filename. */
|
|
const char* fname;
|
|
|
|
/** Python main thread */
|
|
PyThreadState* mainthr;
|
|
/** Python module. */
|
|
PyObject* module;
|
|
|
|
/** Module init function */
|
|
PyObject* func_init;
|
|
/** Module deinit function */
|
|
PyObject* func_deinit;
|
|
/** Module operate function */
|
|
PyObject* func_operate;
|
|
/** Module super_inform function */
|
|
PyObject* func_inform;
|
|
|
|
/** Python dictionary. */
|
|
PyObject* dict;
|
|
|
|
/** Module data. */
|
|
PyObject* data;
|
|
|
|
/** Module qstate. */
|
|
struct module_qstate* qstate;
|
|
};
|
|
|
|
/**
|
|
* Per query state for the iterator module.
|
|
*/
|
|
struct pythonmod_qstate {
|
|
|
|
/** Module per query data. */
|
|
PyObject* data;
|
|
};
|
|
|
|
/* Generated */
|
|
#ifndef S_SPLINT_S
|
|
#include "pythonmod/interface.h"
|
|
#endif
|
|
|
|
int pythonmod_init(struct module_env* env, int id)
|
|
{
|
|
/* Initialize module */
|
|
FILE* script_py = NULL;
|
|
PyObject* py_init_arg, *res;
|
|
PyGILState_STATE gil;
|
|
int init_standard = 1;
|
|
|
|
struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
|
|
if (!pe)
|
|
{
|
|
log_err("pythonmod: malloc failure");
|
|
return 0;
|
|
}
|
|
|
|
env->modinfo[id] = (void*) pe;
|
|
|
|
/* Initialize module */
|
|
pe->fname = env->cfg->python_script;
|
|
if(pe->fname==NULL || pe->fname[0]==0) {
|
|
log_err("pythonmod: no script given.");
|
|
return 0;
|
|
}
|
|
|
|
/* Initialize Python libraries */
|
|
if (!Py_IsInitialized())
|
|
{
|
|
#if PY_MAJOR_VERSION >= 3
|
|
wchar_t progname[8];
|
|
mbstowcs(progname, "unbound", 8);
|
|
#else
|
|
char *progname = "unbound";
|
|
#endif
|
|
Py_SetProgramName(progname);
|
|
Py_NoSiteFlag = 1;
|
|
#if PY_MAJOR_VERSION >= 3
|
|
PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
|
|
#endif
|
|
Py_Initialize();
|
|
PyEval_InitThreads();
|
|
SWIG_init();
|
|
pe->mainthr = PyEval_SaveThread();
|
|
}
|
|
|
|
gil = PyGILState_Ensure();
|
|
|
|
/* Initialize Python */
|
|
PyRun_SimpleString("import sys \n");
|
|
PyRun_SimpleString("sys.path.append('.') \n");
|
|
if(env->cfg->directory && env->cfg->directory[0]) {
|
|
char wdir[1524];
|
|
snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
|
|
env->cfg->directory);
|
|
PyRun_SimpleString(wdir);
|
|
}
|
|
PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
|
|
PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
|
|
PyRun_SimpleString("import distutils.sysconfig \n");
|
|
PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
|
|
if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
|
|
{
|
|
log_err("pythonmod: cannot initialize core module: unboundmodule.py");
|
|
PyGILState_Release(gil);
|
|
return 0;
|
|
}
|
|
|
|
/* Check Python file load */
|
|
if ((script_py = fopen(pe->fname, "r")) == NULL)
|
|
{
|
|
log_err("pythonmod: can't open file %s for reading", pe->fname);
|
|
PyGILState_Release(gil);
|
|
return 0;
|
|
}
|
|
|
|
/* Load file */
|
|
pe->module = PyImport_AddModule("__main__");
|
|
pe->dict = PyModule_GetDict(pe->module);
|
|
pe->data = Py_None;
|
|
Py_INCREF(pe->data);
|
|
PyModule_AddObject(pe->module, "mod_env", pe->data);
|
|
|
|
/* TODO: deallocation of pe->... if an error occurs */
|
|
|
|
if (PyRun_SimpleFile(script_py, pe->fname) < 0)
|
|
{
|
|
log_err("pythonmod: can't parse Python script %s", pe->fname);
|
|
PyGILState_Release(gil);
|
|
return 0;
|
|
}
|
|
|
|
fclose(script_py);
|
|
|
|
if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
|
|
{
|
|
init_standard = 0;
|
|
if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
|
|
{
|
|
log_err("pythonmod: function init is missing in %s", pe->fname);
|
|
PyGILState_Release(gil);
|
|
return 0;
|
|
}
|
|
}
|
|
if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
|
|
{
|
|
log_err("pythonmod: function deinit is missing in %s", pe->fname);
|
|
PyGILState_Release(gil);
|
|
return 0;
|
|
}
|
|
if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
|
|
{
|
|
log_err("pythonmod: function operate is missing in %s", pe->fname);
|
|
PyGILState_Release(gil);
|
|
return 0;
|
|
}
|
|
if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
|
|
{
|
|
log_err("pythonmod: function inform_super is missing in %s", pe->fname);
|
|
PyGILState_Release(gil);
|
|
return 0;
|
|
}
|
|
|
|
if (init_standard)
|
|
{
|
|
py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
|
|
}
|
|
else
|
|
{
|
|
py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
|
|
SWIGTYPE_p_config_file, 0);
|
|
}
|
|
res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
|
|
if (PyErr_Occurred())
|
|
{
|
|
log_err("pythonmod: Exception occurred in function init");
|
|
PyErr_Print();
|
|
Py_XDECREF(res);
|
|
Py_XDECREF(py_init_arg);
|
|
PyGILState_Release(gil);
|
|
return 0;
|
|
}
|
|
|
|
Py_XDECREF(res);
|
|
Py_XDECREF(py_init_arg);
|
|
PyGILState_Release(gil);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void pythonmod_deinit(struct module_env* env, int id)
|
|
{
|
|
struct pythonmod_env* pe = env->modinfo[id];
|
|
if(pe == NULL)
|
|
return;
|
|
|
|
/* Free Python resources */
|
|
if(pe->module != NULL)
|
|
{
|
|
PyObject* res;
|
|
PyGILState_STATE gil = PyGILState_Ensure();
|
|
|
|
/* Deinit module */
|
|
res = PyObject_CallFunction(pe->func_deinit, "i", id);
|
|
if (PyErr_Occurred()) {
|
|
log_err("pythonmod: Exception occurred in function deinit");
|
|
PyErr_Print();
|
|
}
|
|
/* Free result if any */
|
|
Py_XDECREF(res);
|
|
/* Free shared data if any */
|
|
Py_XDECREF(pe->data);
|
|
PyGILState_Release(gil);
|
|
|
|
PyEval_RestoreThread(pe->mainthr);
|
|
Py_Finalize();
|
|
pe->mainthr = NULL;
|
|
}
|
|
pe->fname = NULL;
|
|
free(pe);
|
|
|
|
/* Module is deallocated in Python */
|
|
env->modinfo[id] = NULL;
|
|
}
|
|
|
|
void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
|
|
{
|
|
struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
|
|
struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
|
|
PyObject* py_qstate, *py_sqstate, *res;
|
|
PyGILState_STATE gil = PyGILState_Ensure();
|
|
|
|
log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
|
|
log_query_info(VERB_ALGO, "super is", &super->qinfo);
|
|
|
|
py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
|
|
py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
|
|
|
|
res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
|
|
py_sqstate, pq->data);
|
|
|
|
if (PyErr_Occurred())
|
|
{
|
|
log_err("pythonmod: Exception occurred in function inform_super");
|
|
PyErr_Print();
|
|
qstate->ext_state[id] = module_error;
|
|
}
|
|
else if ((res == NULL) || (!PyObject_IsTrue(res)))
|
|
{
|
|
log_err("pythonmod: python returned bad code in inform_super");
|
|
qstate->ext_state[id] = module_error;
|
|
}
|
|
|
|
Py_XDECREF(res);
|
|
Py_XDECREF(py_sqstate);
|
|
Py_XDECREF(py_qstate);
|
|
|
|
PyGILState_Release(gil);
|
|
}
|
|
|
|
void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
|
|
int id, struct outbound_entry* ATTR_UNUSED(outbound))
|
|
{
|
|
struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
|
|
struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
|
|
PyObject* py_qstate, *res;
|
|
PyGILState_STATE gil = PyGILState_Ensure();
|
|
|
|
if ( pq == NULL)
|
|
{
|
|
/* create qstate */
|
|
pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
|
|
|
|
/* Initialize per query data */
|
|
pq->data = Py_None;
|
|
Py_INCREF(pq->data);
|
|
}
|
|
|
|
/* Call operate */
|
|
py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
|
|
res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
|
|
py_qstate, pq->data);
|
|
if (PyErr_Occurred())
|
|
{
|
|
log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
|
|
PyErr_Print();
|
|
qstate->ext_state[id] = module_error;
|
|
}
|
|
else if ((res == NULL) || (!PyObject_IsTrue(res)))
|
|
{
|
|
log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
|
|
qstate->ext_state[id] = module_error;
|
|
}
|
|
Py_XDECREF(res);
|
|
Py_XDECREF(py_qstate);
|
|
|
|
PyGILState_Release(gil);
|
|
}
|
|
|
|
void pythonmod_clear(struct module_qstate* qstate, int id)
|
|
{
|
|
struct pythonmod_qstate* pq;
|
|
if (qstate == NULL)
|
|
return;
|
|
|
|
pq = (struct pythonmod_qstate*)qstate->minfo[id];
|
|
verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%lX", id,
|
|
(unsigned long int)pq);
|
|
if(pq != NULL)
|
|
{
|
|
PyGILState_STATE gil = PyGILState_Ensure();
|
|
Py_DECREF(pq->data);
|
|
PyGILState_Release(gil);
|
|
/* Free qstate */
|
|
free(pq);
|
|
}
|
|
|
|
qstate->minfo[id] = NULL;
|
|
}
|
|
|
|
size_t pythonmod_get_mem(struct module_env* env, int id)
|
|
{
|
|
struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
|
|
verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%lX", id,
|
|
(unsigned long int)pe);
|
|
if(!pe)
|
|
return 0;
|
|
return sizeof(*pe);
|
|
}
|
|
|
|
/**
|
|
* The module function block
|
|
*/
|
|
static struct module_func_block pythonmod_block = {
|
|
"python",
|
|
&pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
|
|
&pythonmod_clear, &pythonmod_get_mem
|
|
};
|
|
|
|
struct module_func_block* pythonmod_get_funcblock(void)
|
|
{
|
|
return &pythonmod_block;
|
|
}
|