blob: 06b963711e85c1e2e28d33f7c4116f7d51dc86af [file] [log] [blame]
/*
* target_python.c
*
* Target language specific functions for openwsman swig plugins
*
* Here: Python
*/
/*****************************************************************************
* Copyright (C) 2008 Novell Inc. All rights reserved.
* Copyright (C) 2008 SUSE Linux Products GmbH. All rights reserved.
*
* 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 Novell Inc. nor of SUSE Linux Products GmbH 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 Novell Inc. OR SUSE Linux Products GmbH OR
* THE 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.
*****************************************************************************/
#include <Python.h>
#define PLUGIN_FILE "pywsmanplugin"
#define PLUGIN_MODULE "WsmanPlugin"
static PyThreadState* pluginMainPyThreadState = NULL;
/*
* get Python exception trace -> char
*
*/
#define TB_ERROR(str) {tbstr = str; goto cleanup;}
static char *
get_exc_trace()
{
char *tbstr = NULL;
PyObject *iostrmod = NULL;
PyObject *tbmod = NULL;
PyObject *iostr = NULL;
PyObject *obstr = NULL;
PyObject *args = NULL;
PyObject *newstr = NULL;
PyObject *func = NULL;
char* rv = NULL;
PyObject *type, *value, *traceback;
TARGET_THREAD_BEGIN_BLOCK;
PyErr_Fetch(&type, &value, &traceback);
debug("** type %p, value %p, traceback %p", type, value, traceback);
PyErr_Print();
PyErr_Clear();
PyErr_NormalizeException(&type, &value, &traceback);
debug("** type %p, value %p, traceback %p", type, value, traceback);
iostrmod = PyImport_ImportModule("StringIO");
if (iostrmod==NULL)
TB_ERROR("can't import StringIO");
iostr = PyObject_CallMethod(iostrmod, "StringIO", NULL);
if (iostr==NULL)
TB_ERROR("cStringIO.StringIO() failed");
tbmod = PyImport_ImportModule("traceback");
if (tbmod==NULL)
TB_ERROR("can't import traceback");
obstr = PyObject_CallMethod(tbmod, "print_exception",
"(OOOOO)",
type ? type : Py_None,
value ? value : Py_None,
traceback ? traceback : Py_None,
Py_None,
iostr);
if (obstr==NULL)
{
PyErr_Print();
TB_ERROR("traceback.print_exception() failed");
}
Py_DecRef(obstr);
obstr = PyObject_CallMethod(iostr, "getvalue", NULL);
if (obstr==NULL)
TB_ERROR("getvalue() failed.");
if (!PyString_Check(obstr))
TB_ERROR("getvalue() did not return a string");
debug("%s", PyString_AsString(obstr));
args = PyTuple_New(2);
PyTuple_SetItem(args, 0, string2target("\n"));
PyTuple_SetItem(args, 1, string2target("<br>"));
func = PyObject_GetAttrString(obstr, "replace");
//newstr = PyObject_CallMethod(obstr, "replace", args);
newstr = PyObject_CallObject(func, args);
tbstr = PyString_AsString(newstr);
rv = fmtstr("plugin:%s", tbstr);
cleanup:
PyErr_Restore(type, value, traceback);
if (rv == NULL)
{
rv = tbstr ? tbstr : "";
}
Py_DecRef(func);
Py_DecRef(args);
Py_DecRef(newstr);
Py_DecRef(iostr);
Py_DecRef(obstr);
Py_DecRef(iostrmod);
Py_DecRef(tbmod);
TARGET_THREAD_END_BLOCK;
return rv;
}
/*
* Global Python initializer
*
* load the Python interpreter
* init threads
*/
static int
PyGlobalInitialize()
{
/* debug("<%d/0x%x> PyGlobalInitialize() called", getpid(), pthread_self()); */
if (_TARGET_INIT)
{
/* debug("<%d/0x%x> PyGlobalInitialize() returning: already initialized", getpid(), pthread_self()); */
return 0;
}
_TARGET_INIT=1;//true
debug("<%d/0x%x> Python: Loading", getpid(), pthread_self());
Py_SetProgramName(PLUGIN_FILE);
Py_Initialize();
SWIGEXPORT void SWIG_init(void);
SWIG_init();
pluginMainPyThreadState = PyGILState_GetThisThreadState();
PyEval_ReleaseThread(pluginMainPyThreadState);
debug("<%d/0x%x> PyGlobalInitialize() succeeded", getpid(), pthread_self());
return 0;
}
/*---------------------------------------------------------------*/
/*
* TargetCall
*
* ** must be called while holding the threads lock **
*/
static int
TargetCall(WsXmlDocH doc, PyObject* instance,
const char* opname, int nargs, ...)
{
va_list vargs;
PyObject *pyargs = NULL;
PyObject *pyfunc = NULL;
PyObject *result = NULL;
WsmanStatus status;
wsman_status_init(&status);
pyargs = PyTuple_New(nargs);
pyfunc = PyObject_GetAttrString(instance, opname);
if (pyfunc == NULL)
{
PyErr_Print();
PyErr_Clear();
char* str = fmtstr("Python module does not contain \"%s\"", opname);
debug("%s", str);
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
free(str);
goto cleanup;
}
if (! PyCallable_Check(pyfunc))
{
char* str = fmtstr("Python module attribute \"%s\" is not callable",
opname);
debug("%s", str);
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
free(str);
goto cleanup;
}
va_start(vargs, nargs);
int i;
for (i = 0; i < nargs; ++i)
{
PyObject* arg = va_arg(vargs, PyObject*);
if (arg == NULL)
{
arg = Py_None;
Py_IncRef(arg);
}
PyTuple_SET_ITEM(pyargs, i, arg);
}
va_end(vargs);
result = PyObject_CallObject(pyfunc, pyargs);
if (PyErr_Occurred())
{
status.fault_code = WSMAN_INTERNAL_ERROR;
status.fault_detail_code = 0;
PyErr_Clear();
goto cleanup;
}
if (! PyTuple_Check(result) ||
(PyTuple_Size(result) != 2 && PyTuple_Size(result) != 1))
{
TARGET_THREAD_BEGIN_ALLOW;
status.fault_msg = fmtstr("Python function \"%s\" didn't return a two-tuple", opname);
status.fault_code = WSMAN_INTERNAL_ERROR;
status.fault_detail_code = 0;
TARGET_THREAD_END_ALLOW;
goto cleanup;
}
PyObject* code = PyTuple_GetItem(result, 0);
PyObject* detail = Py_None;
if (PyTuple_Size(result) == 2)
{
detail = PyTuple_GetItem(result, 1);
}
if (! PyInt_Check(code) || (! PyInt_Check(detail) && detail != Py_None))
{
TARGET_THREAD_BEGIN_ALLOW;
status.fault_msg = fmtstr("Python function \"%s\" didn't return a {<int>, <int>) two-tuple", opname);
status.fault_code = WSMAN_INTERNAL_ERROR;
status.fault_detail_code = 0;
TARGET_THREAD_END_ALLOW;
goto cleanup;
}
status.fault_code = PyInt_AsLong(code);
if (detail == Py_None)
{
status.fault_code = WSMAN_INTERNAL_ERROR;
status.fault_detail_code = 0;
}
else
{
status.fault_detail_code = PyInt_AsLong(detail);
}
cleanup:
if (status.fault_code != WSMAN_RC_OK)
wsman_generate_fault( doc, status.fault_code, status.fault_detail_code, status.fault_msg );
Py_DecRef(pyargs);
Py_DecRef(pyfunc);
Py_DecRef(result);
return status.fault_code != WSMAN_RC_OK;
}
/*
* import PLUGIN_FILE
* local (per plugin) Python initializer
* keeps track of reference count
*/
static int
TargetInitialize(void *self, void **data)
{
int rc = 0;
WsmanStatus status;
wsman_status_init(&status);
/* Set _PLUGIN_INIT, protected by _PLUGIN_INIT_MUTEX
* so we call Py_Finalize() only once.
*/
if (pthread_mutex_lock(&_PLUGIN_INIT_MUTEX))
{
perror("Can't lock _PLUGIN_INIT_MUTEX");
abort();
}
/* load Python */
rc = PyGlobalInitialize();
if (rc != 0)
{
pthread_mutex_unlock(&_PLUGIN_INIT_MUTEX);
return rc;
}
debug("<%d/0x%x> TargetInitialize(Python) called", getpid(), pthread_self());
TARGET_THREAD_BEGIN_BLOCK;
/*
* import PLUGIN_FILE
*/
if (_TARGET_MODULE == NULL)
{
_TARGET_MODULE = PyImport_ImportModule(PLUGIN_FILE);
if (_TARGET_MODULE == NULL)
{
char *trace = get_exc_trace();
debug("<%d/0x%x> Python: import %s failed", getpid(), pthread_self(), PLUGIN_FILE);
message(trace);
PyErr_Clear();
TARGET_THREAD_END_BLOCK;
debug("<%d/0x%x> %s", getpid(), pthread_self(), trace);
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
pthread_mutex_unlock(&_PLUGIN_INIT_MUTEX);
return -1;
}
*data = _TARGET_MODULE;
}
pthread_mutex_unlock(&_PLUGIN_INIT_MUTEX);
debug("<%d/0x%x> Python: _TARGET_MODULE at %p", getpid(), pthread_self(), _TARGET_MODULE);
TARGET_THREAD_END_BLOCK;
debug("<%d/0x%x> TargetInitialize(Python) succeeded", getpid(), pthread_self());
return 0;
}
/*
* TargetCleanup
*/
static void
TargetCleanup(void *self, void *data)
{
/* Decrement _PLUGIN_COUNT, protected by _PLUGIN_INIT_MUTEX
* call Py_Finalize when _PLUGIN_COUNT drops to zero
*/
if (pthread_mutex_lock(&_PLUGIN_INIT_MUTEX))
{
perror("Can't lock _PLUGIN_INIT_MUTEX");
abort();
}
if (--_PLUGIN_COUNT > 0)
{
pthread_mutex_unlock(&_PLUGIN_INIT_MUTEX);
return;
}
TARGET_THREAD_BEGIN_BLOCK;
Py_DecRef(_TARGET_MODULE);
TARGET_THREAD_END_BLOCK;
PyEval_AcquireLock();
PyThreadState_Swap(pluginMainPyThreadState);
if (_TARGET_INIT) // if Python is initialized and _PLUGIN_COUNT == 0, call Py_Finalize
{
debug("Calling Py_Finalize()");
Py_Finalize();
_TARGET_INIT=0; // false
}
pthread_mutex_unlock(&_PLUGIN_INIT_MUTEX);
}
/*
* TargetEndpoints
*/
static list_t *
TargetEndpoints( void *self, void *data )
{
int len, i;
PyObject *instance = (PyObject *)data;
PyObject *pyfunc, *pynamespaces = NULL;
WsmanStatus status;
wsman_status_init(&status);
debug("TargetEndpoints(Python), data %p, instance %p", data, instance);
/*
* Get namespaces
*/
list_t *namespaces = list_create(LISTCOUNT_T_MAX);
pyfunc = PyObject_GetAttrString(instance, "namespaces");
if (pyfunc == NULL)
{
PyErr_Print();
PyErr_Clear();
debug("Python module does not contain \"namespaces\"");
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
goto cleanup;
}
if (! PyCallable_Check(pyfunc))
{
debug("Python module attribute \"namespaces\" is not callable");
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
goto cleanup;
}
pynamespaces = PyObject_CallObject(pyfunc, NULL);
if (PyErr_Occurred())
{
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
PyErr_Clear();
goto cleanup;
}
if (! PyTuple_Check(pynamespaces))
{
TARGET_THREAD_BEGIN_ALLOW;
debug("Python function \"namespaces\" didn't return a two-tuple");
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
TARGET_THREAD_END_ALLOW;
goto cleanup;
}
len = PyTuple_Size(pynamespaces);
for (i = 0; i < len; ++i) {
lnode_t *node;
PyObject *ns, *prefix;
PyObject* elem = PyTuple_GetItem(pynamespaces, i);
if (! PyTuple_Check(elem)
|| ! (PyTuple_Size(elem) == 2)) {
TARGET_THREAD_BEGIN_ALLOW;
debug("Python function \"namespaces\" didn't return a list of two-tuple");
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
TARGET_THREAD_END_ALLOW;
goto cleanup;
}
ns = PyTuple_GetItem(elem, 0);
prefix = PyTuple_GetItem(elem, 1);
if (!PyString_Check(ns)
|| !PyString_Check(prefix)) {
TARGET_THREAD_BEGIN_ALLOW;
debug("Python function \"namespaces\" didn't return a list of [<string>,<string>] tuples");
status.fault_code = WSA_ENDPOINT_UNAVAILABLE;
status.fault_detail_code = 0;
TARGET_THREAD_END_ALLOW;
goto cleanup;
}
WsSupportedNamespaces *sup_ns = (WsSupportedNamespaces *)u_malloc(sizeof(WsSupportedNamespaces));
sup_ns->ns = PyString_AsString(ns);
sup_ns->class_prefix = PyString_AsString(prefix);
node = lnode_create(ns);
list_append(namespaces, node);
}
cleanup:
if (pyfunc) Py_DecRef(pyfunc);
if (pynamespaces) Py_DecRef(pynamespaces);
return namespaces;
}