icinga2/lib/python/pythonlanguage.cpp

440 lines
11 KiB
C++
Raw Normal View History

2013-02-14 14:58:26 +01:00
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
2013-02-14 14:58:26 +01:00
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
2013-03-18 11:02:18 +01:00
#include "python/pythonlanguage.h"
#include "python/pythoninterpreter.h"
2013-03-27 07:27:44 +01:00
#include "base/scriptfunction.h"
2013-03-18 11:02:18 +01:00
#include "base/dynamictype.h"
#include "base/objectlock.h"
#include "base/application.h"
#include "base/array.h"
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
2013-02-14 14:58:26 +01:00
using namespace icinga;
2013-02-16 07:49:36 +01:00
PythonInterpreter *PythonLanguage::m_CurrentInterpreter;
2013-02-14 14:58:26 +01:00
REGISTER_SCRIPTLANGUAGE("Python", PythonLanguage);
2013-02-16 07:49:36 +01:00
PyMethodDef PythonLanguage::m_NativeMethodDef[] = {
{ "RegisterFunction", &PythonLanguage::PyRegisterFunction, METH_VARARGS, NULL },
2013-03-18 12:36:02 +01:00
{ NULL, NULL, 0, NULL } /* sentinel */
2013-02-16 07:49:36 +01:00
};
2013-02-14 14:58:26 +01:00
PythonLanguage::PythonLanguage(void)
2013-02-16 12:36:25 +01:00
: ScriptLanguage(), m_Initialized(false)
{ }
void PythonLanguage::InitializeOnce(void)
2013-02-14 14:58:26 +01:00
{
2013-03-02 09:07:47 +01:00
ObjectLock olock(this);
2013-02-16 12:36:25 +01:00
if (m_Initialized)
return;
2013-02-14 14:58:26 +01:00
Py_Initialize();
PyEval_InitThreads();
Py_SetProgramName(Application::GetArgV()[0]);
PySys_SetArgv(Application::GetArgC(), Application::GetArgV());
// See http://docs.python.org/2/c-api/init.html for an explanation.
PyRun_SimpleString("import sys; sys.path.pop(0)\n");
2013-02-14 14:58:26 +01:00
m_MainThreadState = PyThreadState_Get();
2013-02-16 11:28:34 +01:00
m_TracebackModule = PyImport_ImportModule("traceback");
2013-02-16 07:49:36 +01:00
m_NativeModule = Py_InitModule("ire", m_NativeMethodDef);
2013-02-14 14:58:26 +01:00
(void) PyThreadState_Swap(NULL);
2013-02-14 14:58:26 +01:00
PyEval_ReleaseLock();
String name;
2013-03-18 11:02:18 +01:00
BOOST_FOREACH(boost::tie(name, boost::tuples::ignore), ScriptFunctionRegistry::GetInstance()->GetItems()) {
RegisterNativeFunction(name);
}
2013-03-15 11:51:35 +01:00
ScriptFunctionRegistry::GetInstance()->OnRegistered.connect(boost::bind(&PythonLanguage::RegisterNativeFunction, this, _1));
ScriptFunctionRegistry::GetInstance()->OnUnregistered.connect(boost::bind(&PythonLanguage::UnregisterNativeFunction, this, _1));
2013-02-16 12:36:25 +01:00
m_Initialized = true;
2013-02-14 14:58:26 +01:00
}
PythonLanguage::~PythonLanguage(void)
{
/* Due to how we're destructing objects it might not be safe to
* call Py_Finalize() when the Icinga instance is being shut
* down - so don't bother calling it. */
2013-02-14 14:58:26 +01:00
}
ScriptInterpreter::Ptr PythonLanguage::CreateInterpreter(const Script::Ptr& script)
{
2013-02-16 12:36:25 +01:00
InitializeOnce();
return make_shared<PythonInterpreter>(GetSelf(), script);
2013-02-14 14:58:26 +01:00
}
PyThreadState *PythonLanguage::GetMainThreadState(void) const
{
2013-03-02 09:07:47 +01:00
ObjectLock olock(this);
2013-02-14 14:58:26 +01:00
return m_MainThreadState;
}
2013-02-15 18:27:21 +01:00
PyObject *PythonLanguage::MarshalToPython(const Value& value)
{
String svalue;
switch (value.GetType()) {
case ValueEmpty:
Py_INCREF(Py_None);
return Py_None;
case ValueNumber:
return PyFloat_FromDouble(value);
case ValueString:
svalue = value;
return PyString_FromString(svalue.CStr());
case ValueObject:
if (value.IsObjectType<DynamicObject>()) {
DynamicObject::Ptr dobj = value;
String type = dobj->GetType()->GetName();
String name = dobj->GetName();
PyObject *ptype = PyString_FromString(type.CStr());
if (ptype == NULL)
return NULL;
PyObject *pname = PyString_FromString(name.CStr());
if (pname == NULL) {
Py_DECREF(ptype);
return NULL;
}
PyObject *result = PyTuple_New(2);
if (result == NULL) {
Py_DECREF(ptype);
Py_DECREF(pname);
return NULL;
}
(void) PyTuple_SetItem(result, 0, ptype);
(void) PyTuple_SetItem(result, 1, pname);
return result;
} else if (value.IsObjectType<Dictionary>()) {
Dictionary::Ptr dict = value;
2013-03-02 09:07:47 +01:00
ObjectLock olock(dict);
PyObject *pdict = PyDict_New();
String key;
Value value;
2013-03-18 11:02:18 +01:00
BOOST_FOREACH(boost::tie(key, value), dict) {
PyObject *dv = MarshalToPython(value);
PyDict_SetItemString(pdict, key.CStr(), dv);
Py_DECREF(dv);
}
return pdict;
} else if (value.IsObjectType<Array>()) {
Array::Ptr arr = value;
ObjectLock olock(arr);
PyObject *plist = PyList_New(0);
BOOST_FOREACH(const Value& value, arr) {
PyObject *dv = MarshalToPython(value);
PyList_Append(plist, dv);
Py_DECREF(dv);
}
return plist;
}
Py_INCREF(Py_None);
return Py_None;
default:
2013-03-18 11:02:18 +01:00
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected variant type."));
}
}
2013-02-16 07:49:36 +01:00
Value PythonLanguage::MarshalFromPython(PyObject *value)
{
if (value == Py_None) {
return Empty;
} else if (PyDict_Check(value)) {
Dictionary::Ptr dict = make_shared<Dictionary>();
PyObject *dk, *dv;
Py_ssize_t pos = 0;
while (PyDict_Next(value, &pos, &dk, &dv)) {
String ik = PyString_AsString(dk);
Value iv = MarshalFromPython(dv);
dict->Set(ik, iv);
}
return dict;
} else if (PyList_Check(value)) {
Array::Ptr arr = make_shared<Array>();
for (Py_ssize_t pos = 0; pos < PyList_Size(value); pos++) {
PyObject *dv = PyList_GetItem(value, pos);
Value iv = MarshalFromPython(dv);
arr->Add(iv);
}
return arr;
2013-02-16 07:49:36 +01:00
} else if (PyTuple_Check(value) && PyTuple_Size(value) == 2) {
PyObject *ptype, *pname;
ptype = PyTuple_GetItem(value, 0);
if (ptype == NULL || !PyString_Check(ptype))
2013-03-18 11:02:18 +01:00
BOOST_THROW_EXCEPTION(std::invalid_argument("Tuple must contain two strings."));
2013-02-16 07:49:36 +01:00
String type = PyString_AsString(ptype);
pname = PyTuple_GetItem(value, 1);
if (pname == NULL || !PyString_Check(pname))
2013-03-18 11:02:18 +01:00
BOOST_THROW_EXCEPTION(std::invalid_argument("Tuple must contain two strings."));
2013-02-16 07:49:36 +01:00
String name = PyString_AsString(pname);
DynamicType::Ptr dtype = DynamicType::GetByName(type);
if (!dtype)
BOOST_THROW_EXCEPTION(std::invalid_argument("Type '" + type + "' does not exist."));
DynamicObject::Ptr object = dtype->GetObject(name);
2013-02-16 07:49:36 +01:00
if (!object)
2013-03-18 11:02:18 +01:00
BOOST_THROW_EXCEPTION(std::invalid_argument("Object '" + name + "' of type '" + type + "' does not exist."));
2013-02-16 07:49:36 +01:00
return object;
} else if (PyFloat_Check(value)) {
return PyFloat_AsDouble(value);
} else if (PyInt_Check(value)) {
return PyInt_AsLong(value);
} else if (PyString_Check(value)) {
return PyString_AsString(value);
} else {
return Empty;
}
}
2013-02-16 11:28:34 +01:00
String PythonLanguage::ExceptionInfoToString(PyObject *type, PyObject *exc, PyObject *tb) const
{
2013-03-02 09:07:47 +01:00
ObjectLock olock(this);
2013-02-16 11:28:34 +01:00
PyObject *tb_dict = PyModule_GetDict(m_TracebackModule);
PyObject *format_exception = PyDict_GetItemString(tb_dict, "format_exception");
if (!PyCallable_Check(format_exception))
return "Failed to format exception information.";
PyObject *result = PyObject_CallFunctionObjArgs(format_exception, type, exc, tb, NULL);
2013-02-16 11:28:34 +01:00
Py_DECREF(format_exception);
Py_DECREF(tb_dict);
if (!result || !PyList_Check(result)) {
2013-02-16 11:28:34 +01:00
Py_XDECREF(result);
return "format_exception() returned something that is not a list.";
2013-02-16 11:28:34 +01:00
}
String msg;
for (Py_ssize_t i = 0; i < PyList_Size(result); i++) {
PyObject *li = PyList_GetItem(result, i);
if (!li || !PyString_Check(li)) {
Py_DECREF(result);
return "format_exception() returned something that is not a list of strings.";
}
msg += PyString_AsString(li);
}
2013-02-16 11:28:34 +01:00
Py_DECREF(result);
return msg;
}
2013-02-16 07:49:36 +01:00
PyObject *PythonLanguage::PyCallNativeFunction(PyObject *self, PyObject *args)
{
assert(PyString_Check(self));
char *name = PyString_AsString(self);
2013-03-15 13:29:02 +01:00
ScriptFunction::Ptr function = ScriptFunctionRegistry::GetInstance()->GetItem(name);
2013-03-18 11:02:18 +01:00
std::vector<Value> arguments;
if (args != NULL) {
if (PyTuple_Check(args)) {
for (Py_ssize_t i = 0; i < PyTuple_Size(args); i++) {
PyObject *arg = PyTuple_GetItem(args, i);
2013-02-15 18:27:21 +01:00
arguments.push_back(MarshalFromPython(arg));
}
} else {
2013-02-15 18:27:21 +01:00
arguments.push_back(MarshalFromPython(args));
}
}
2013-02-16 07:49:36 +01:00
PyThreadState *tstate = PyEval_SaveThread();
Value result;
try {
2013-03-27 07:27:44 +01:00
result = function->Invoke(arguments);
} catch (const std::exception& ex) {
2013-02-16 07:49:36 +01:00
PyEval_RestoreThread(tstate);
String message = DiagnosticInformation(ex);
PyErr_SetString(PyExc_RuntimeError, message.CStr());
return NULL;
}
2013-02-16 07:49:36 +01:00
PyEval_RestoreThread(tstate);
return MarshalToPython(result);
}
/**
* Registers a native function.
*
* @param name The name of the native function.
*/
void PythonLanguage::RegisterNativeFunction(const String& name)
{
2013-03-02 09:07:47 +01:00
ObjectLock olock(this);
2013-02-16 07:49:36 +01:00
PyThreadState *tstate = PyThreadState_Swap(m_MainThreadState);
PyObject *pname = PyString_FromString(name.CStr());
PyMethodDef *md = new PyMethodDef;
md->ml_name = strdup(name.CStr());
2013-02-16 07:49:36 +01:00
md->ml_meth = &PythonLanguage::PyCallNativeFunction;
md->ml_flags = METH_VARARGS;
md->ml_doc = NULL;
PyObject *pfunc = PyCFunction_NewEx(md, pname, m_NativeModule);
(void) PyModule_AddObject(m_NativeModule, name.CStr(), pfunc);
2013-02-16 07:49:36 +01:00
(void) PyThreadState_Swap(tstate);
}
/**
* Unregisters a native function.
*
* @param name The name of the native function.
*/
void PythonLanguage::UnregisterNativeFunction(const String& name)
{
2013-03-02 09:07:47 +01:00
ObjectLock olock(this);
2013-02-16 07:49:36 +01:00
PyThreadState *tstate = PyThreadState_Swap(m_MainThreadState);
PyObject *pdict = PyModule_GetDict(m_NativeModule);
PyObject *pname = PyString_FromString(name.CStr());
PyCFunctionObject *pfunc = (PyCFunctionObject *)PyDict_GetItem(pdict, pname);
if (pfunc && PyCFunction_Check(pfunc)) {
/* Eww. */
free(const_cast<char *>(pfunc->m_ml->ml_name));
delete pfunc->m_ml;
}
(void) PyDict_DelItem(pdict, pname);
Py_DECREF(pname);
2013-02-16 07:49:36 +01:00
(void) PyThreadState_Swap(tstate);
}
PyObject *PythonLanguage::PyRegisterFunction(PyObject *, PyObject *args)
2013-02-16 07:49:36 +01:00
{
char *name;
PyObject *object;
if (!PyArg_ParseTuple(args, "sO", &name, &object))
return NULL;
PythonInterpreter *interp = GetCurrentInterpreter();
if (interp == NULL) {
PyErr_SetString(PyExc_RuntimeError, "GetCurrentInterpreter() returned NULL.");
return NULL;
}
if (!PyCallable_Check(object)) {
PyErr_SetString(PyExc_RuntimeError, "Function object is not callable.");
return NULL;
}
2013-02-18 14:40:24 +01:00
interp->RegisterPythonFunction(name, object);
2013-02-16 07:49:36 +01:00
Py_INCREF(Py_None);
return Py_None;
}
/**
* Retrieves the current interpreter object. Caller must hold the GIL.
*
* @returns The current interpreter.
*/
PythonInterpreter *PythonLanguage::GetCurrentInterpreter(void)
{
return m_CurrentInterpreter;
}
/**
* Sets the current interpreter. Caller must hold the GIL.
*
* @param interpreter The interpreter.
*/
void PythonLanguage::SetCurrentInterpreter(PythonInterpreter *interpreter)
{
m_CurrentInterpreter = interpreter;
}