"Fossies" - the Fresh Open Source Software Archive

Member "netxms-3.8.166/src/libnxpython/manager.cpp" (23 Feb 2021, 11007 Bytes) of package /linux/misc/netxms-3.8.166.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "manager.cpp" see the Fossies "Dox" file reference documentation.

    1 /*
    2 ** NetXMS - Network Management System
    3 ** Copyright (C) 2003-2018 Raden Solutions
    4 **
    5 ** This program is free software; you can redistribute it and/or modify
    6 ** it under the terms of the GNU General Public License as published by
    7 ** the Free Software Foundation; either version 2 of the License, or
    8 ** (at your option) any later version.
    9 **
   10 ** This program is distributed in the hope that it will be useful,
   11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 ** GNU General Public License for more details.
   14 **
   15 ** You should have received a copy of the GNU General Public License
   16 ** along with this program; if not, write to the Free Software
   17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   18 **
   19 ** File: manager.cpp
   20 **
   21 **/
   22 
   23 #include "libnxpython.h"
   24 #include <nxqueue.h>
   25 
   26 #define DEBUG_TAG _T("python")
   27 
   28 /**
   29  * "netxms" module initialization function
   30  */
   31 PyObject *PyInit_netxms();
   32 
   33 /**
   34  * Extra modules
   35  */
   36 static NXPYTHON_MODULE_INFO *s_extraModules = NULL;
   37 
   38 /**
   39  * Manager request queue
   40  */
   41 static Queue s_managerRequestQueue;
   42 
   43 /**
   44  * Manager request
   45  */
   46 struct PythonManagerRequest
   47 {
   48    int command;
   49    PyThreadState *interpreter;
   50    CONDITION condition;
   51 
   52    PythonManagerRequest()
   53    {
   54       command = 0;   // create interpreter
   55       interpreter = NULL;
   56       condition = ConditionCreate(true);
   57    }
   58 
   59    PythonManagerRequest(PyThreadState *pi)
   60    {
   61       command = 1;   // destroy interpreter
   62       interpreter = pi;
   63       condition = ConditionCreate(true);
   64    }
   65 
   66    ~PythonManagerRequest()
   67    {
   68       ConditionDestroy(condition);
   69    }
   70 
   71    void wait()
   72    {
   73       ConditionWait(condition, INFINITE);
   74    }
   75 
   76    void notify()
   77    {
   78       ConditionSet(condition);
   79    }
   80 };
   81 
   82 /**
   83  * Manager thread
   84  */
   85 static THREAD_RESULT THREAD_CALL PythonManager(void *arg)
   86 {
   87    PyImport_AppendInittab("netxms", PyInit_netxms);
   88    if (s_extraModules != NULL)
   89    {
   90       for(int i = 0; s_extraModules[i].name != NULL; i++)
   91       {
   92          PyImport_AppendInittab(s_extraModules[i].name, s_extraModules[i].initFunction);
   93       }
   94    }
   95    Py_InitializeEx(0);
   96    char version[128];
   97    strlcpy(version, Py_GetVersion(), 128);
   98    char *p = strchr(version, ' ');
   99    if (p != NULL)
  100       *p = 0;
  101 
  102    PyEval_InitThreads();
  103    PyThreadState *mainPyThread = PyEval_SaveThread();
  104    ConditionSet(static_cast<CONDITION>(arg));
  105    nxlog_debug_tag(DEBUG_TAG, 1, _T("Embedded Python interpreter (version %hs) initialized"), version);
  106 
  107    while(true)
  108    {
  109       PythonManagerRequest *request = static_cast<PythonManagerRequest*>(s_managerRequestQueue.getOrBlock());
  110       if (request == INVALID_POINTER_VALUE)
  111          break;
  112 
  113       PyEval_AcquireThread(mainPyThread);
  114       switch(request->command)
  115       {
  116          case 0:  // create interpreter
  117             request->interpreter = Py_NewInterpreter();
  118             if (request->interpreter != NULL)
  119             {
  120                PyRun_SimpleString(
  121                        "import importlib.abc\n"
  122                        "import importlib.machinery\n"
  123                        "import sys\n"
  124                        "\n"
  125                        "class Finder(importlib.abc.MetaPathFinder):\n"
  126                        "    def find_spec(self, fullname, path, target=None):\n"
  127                        "        if fullname in sys.builtin_module_names:\n"
  128                        "            return importlib.machinery.ModuleSpec(\n"
  129                        "                fullname,\n"
  130                        "                importlib.machinery.BuiltinImporter,\n"
  131                        "            )\n"
  132                        "\n"
  133                        "sys.meta_path.append(Finder())\n"
  134                    );
  135 
  136                PyThreadState_Swap(mainPyThread);
  137                nxlog_debug_tag(DEBUG_TAG, 6, _T("Created Python interpreter instance %p"), request->interpreter);
  138             }
  139             else
  140             {
  141                nxlog_debug_tag(DEBUG_TAG, 6, _T("Cannot create Python interpreter instance"));
  142             }
  143             break;
  144          case 1:  // destroy interpreter
  145             PyThreadState_Swap(request->interpreter);
  146             Py_EndInterpreter(request->interpreter);
  147             PyThreadState_Swap(mainPyThread);
  148             nxlog_debug_tag(DEBUG_TAG, 6, _T("Destroyed Python interpreter instance %p"), request->interpreter);
  149             break;
  150       }
  151       PyEval_ReleaseThread(mainPyThread);
  152       request->notify();
  153    }
  154 
  155    PyEval_RestoreThread(mainPyThread);
  156    Py_Finalize();
  157    nxlog_debug_tag(DEBUG_TAG, 1, _T("Embedded Python interpreter terminated"));
  158    return THREAD_OK;
  159 }
  160 
  161 /**
  162  * Create Python sub-interpreter
  163  */
  164 PythonInterpreter *PythonInterpreter::create()
  165 {
  166    PythonManagerRequest r;
  167    s_managerRequestQueue.put(&r);
  168    r.wait();
  169    return (r.interpreter != NULL) ? new PythonInterpreter(r.interpreter) : NULL;
  170 }
  171 
  172 /**
  173  * Internal constructor
  174  */
  175 PythonInterpreter::PythonInterpreter(PyThreadState *s)
  176 {
  177    m_threadState = s;
  178    m_mainModule = NULL;
  179 }
  180 
  181 /**
  182  * Destructor
  183  */
  184 PythonInterpreter::~PythonInterpreter()
  185 {
  186    if (m_mainModule != NULL)
  187    {
  188       PyEval_AcquireThread(m_threadState);
  189       Py_DECREF(m_mainModule);
  190       m_mainModule = NULL;
  191       PyEval_ReleaseThread(m_threadState);
  192    }
  193    PythonManagerRequest r(m_threadState);
  194    s_managerRequestQueue.put(&r);
  195    r.wait();
  196 }
  197 
  198 /**
  199  * Log execution error
  200  */
  201 void PythonInterpreter::logExecutionError(const TCHAR *format)
  202 {
  203    PyObject *type, *value, *traceback;
  204    PyErr_Fetch(&type, &value, &traceback);
  205    PyObject *text = (value != NULL) ? PyObject_Str(value) : NULL;
  206 #ifdef UNICODE
  207    nxlog_debug_tag(DEBUG_TAG, 6, format, (text != NULL) ? PyUnicode_AsUnicode(text) : L"error text not provided");
  208 #else
  209    nxlog_debug_tag(DEBUG_TAG, 6, format, (text != NULL) ? PyUnicode_AsUTF8(text) : "error text not provided");
  210 #endif
  211    if (text != NULL)
  212       Py_DECREF(text);
  213    if (type != NULL)
  214       Py_DECREF(type);
  215    if (value != NULL)
  216       Py_DECREF(value);
  217    if (traceback != NULL)
  218       Py_DECREF(traceback);
  219 }
  220 
  221 /**
  222  * Load and execute module from given source
  223  */
  224 PyObject *PythonInterpreter::loadModule(const char *source, const char *moduleName, const char *fileName)
  225 {
  226    PyObject *module = NULL;
  227    PyEval_AcquireThread(m_threadState);
  228 
  229    PyObject *code = Py_CompileString(source, (fileName != NULL) ? fileName : ":memory:", Py_file_input);
  230    if (code != NULL)
  231    {
  232       module = PyImport_ExecCodeModuleEx(moduleName, code, fileName);
  233       if (module == NULL)
  234       {
  235          TCHAR format[256];
  236          _sntprintf(format, 256, _T("Cannot import module %hs (%%s)"), moduleName);
  237          logExecutionError(format);
  238       }
  239       Py_DECREF(code);
  240    }
  241    else
  242    {
  243       logExecutionError(_T("Source compilation failed (%s)"));
  244    }
  245 
  246    PyEval_ReleaseThread(m_threadState);
  247    return module;
  248 }
  249 
  250 /**
  251  * Load source code from file and execute
  252  */
  253 PyObject *PythonInterpreter::loadModuleFromFile(const TCHAR *fileName, const char *moduleName)
  254 {
  255    char *source = LoadFileAsUTF8String(fileName);
  256    if (source == NULL)
  257       return NULL;
  258 #ifdef UNICODE
  259    char *utfFileName = UTF8StringFromWideString(fileName);
  260    PyObject *module = loadModule(source, moduleName, utfFileName);
  261    free(utfFileName);
  262 #else
  263    bool success = loadModule(source, moduleName, fileName);
  264 #endif
  265    free(source);
  266    return module;
  267 }
  268 
  269 /**
  270  * Load main module
  271  */
  272 bool PythonInterpreter::loadMainModule(const char *source, const char *fileName)
  273 {
  274    bool success = false;
  275    PyEval_AcquireThread(m_threadState);
  276 
  277    if (m_mainModule != NULL)
  278    {
  279       Py_DECREF(m_mainModule);
  280       m_mainModule = NULL;
  281    }
  282 
  283    PyObject *code = Py_CompileString(source, (fileName != NULL) ? fileName : ":memory:", Py_file_input);
  284    if (code != NULL)
  285    {
  286       m_mainModule = PyImport_ExecCodeModuleEx("__main__", code, fileName);
  287       if (m_mainModule != NULL)
  288       {
  289          success = true;
  290       }
  291       else
  292       {
  293          logExecutionError(_T("Cannot create __main__ module (%s)"));
  294       }
  295       Py_DECREF(code);
  296    }
  297    else
  298    {
  299       logExecutionError(_T("Source compilation failed (%s)"));
  300    }
  301 
  302    PyEval_ReleaseThread(m_threadState);
  303    return success;
  304 }
  305 
  306 /**
  307  * Load main module from file
  308  */
  309 bool PythonInterpreter::loadMainModuleFromFile(const TCHAR *fileName)
  310 {
  311    char *source = LoadFileAsUTF8String(fileName);
  312    if (source == NULL)
  313       return false;
  314 #ifdef UNICODE
  315    char *utfFileName = UTF8StringFromWideString(fileName);
  316    bool success = loadMainModule(source, utfFileName);
  317    free(utfFileName);
  318 #else
  319    bool success = loadMainModule(source, fileName);
  320 #endif
  321    free(source);
  322    return success;
  323 }
  324 
  325 /**
  326  * Call function in main module with C arguments
  327  */
  328 PyObject *PythonInterpreter::call(const char *name, const char *format, ...)
  329 {
  330    va_list args;
  331    va_start(args, format);
  332    PyObject *result = callv(m_mainModule, name, format, args);
  333    va_end(args);
  334    return result;
  335 }
  336 
  337 /**
  338  * Call function with C arguments
  339  */
  340 PyObject *PythonInterpreter::call(PyObject *module, const char *name, const char *format, ...)
  341 {
  342    if (module == NULL)
  343       return NULL;
  344 
  345    va_list args;
  346    va_start(args, format);
  347    PyObject *result = callv(module, name, format, args);
  348    va_end(args);
  349    return result;
  350 }
  351 
  352 /**
  353  * Call function with C arguments
  354  */
  355 PyObject *PythonInterpreter::callv(PyObject *module, const char *name, const char *format, va_list args)
  356 {
  357    if (module == NULL)
  358       return NULL;
  359 
  360    PyObject *result = NULL;
  361    PyEval_AcquireThread(m_threadState);
  362 
  363    PyObject *func = PyObject_GetAttrString(module, name);
  364    if ((func != NULL) && PyCallable_Check(func))
  365    {
  366       PyObject *pargs = (format != NULL) ? Py_VaBuildValue(format, args) : NULL;
  367       result = PyObject_CallObject(func, pargs);
  368       if (result == NULL)
  369       {
  370          logExecutionError(_T("Function call failed (%s)"));
  371       }
  372       if (pargs != NULL)
  373          Py_DECREF(pargs);
  374    }
  375    else
  376    {
  377       nxlog_debug_tag(DEBUG_TAG, 6, _T("Function %hs does not exist"), name);
  378    }
  379 
  380    PyEval_ReleaseThread(m_threadState);
  381    return result;
  382 }
  383 
  384 /**
  385  * Decrement object reference
  386  */
  387 void PythonInterpreter::decref(PyObject *object)
  388 {
  389    PyEval_AcquireThread(m_threadState);
  390    Py_DECREF(object);
  391    PyEval_ReleaseThread(m_threadState);
  392 }
  393 
  394 /**
  395  * Manager thread handle
  396  */
  397 static THREAD s_managerThread = INVALID_THREAD_HANDLE;
  398 
  399 /**
  400  * Initialize embedded Python
  401  */
  402 void LIBNXPYTHON_EXPORTABLE InitializeEmbeddedPython(NXPYTHON_MODULE_INFO *modules)
  403 {
  404    s_extraModules = modules;
  405    CONDITION initCompleted = ConditionCreate(true);
  406    s_managerThread = ThreadCreateEx(PythonManager, 0, initCompleted);
  407    ConditionWait(initCompleted, INFINITE);
  408    ConditionDestroy(initCompleted);
  409 }
  410 
  411 /**
  412  * Shutdown embedded Python
  413  */
  414 void LIBNXPYTHON_EXPORTABLE ShutdownEmbeddedPython()
  415 {
  416    s_managerRequestQueue.put(INVALID_POINTER_VALUE);
  417    ThreadJoin(s_managerThread);
  418 }
  419 
  420 /**
  421  * Create Python interpreter
  422  */
  423 PythonInterpreter LIBNXPYTHON_EXPORTABLE *CreatePythonInterpreter()
  424 {
  425    return PythonInterpreter::create();
  426 }