mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-25 10:54:45 +00:00
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@5626 d0543943-73ff-0310-b7d9-9358b9ac24b2
335 lines
10 KiB
C++
335 lines
10 KiB
C++
#include "freeswitch_python.h"
|
|
|
|
#define sanity_check(x) do { if (!session) { switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_ERROR, "session is not initalized\n"); return x;}} while(0)
|
|
#define init_vars() do { caller_profile.source = "mod_python"; swapstate = S_SWAPPED_IN; } while(0)
|
|
|
|
PySession::PySession() : CoreSession()
|
|
{
|
|
init_vars();
|
|
}
|
|
|
|
PySession::PySession(char *uuid) : CoreSession(uuid)
|
|
{
|
|
init_vars();
|
|
}
|
|
|
|
PySession::PySession(switch_core_session_t *new_session) : CoreSession(new_session)
|
|
{
|
|
init_vars();
|
|
}
|
|
|
|
|
|
void PySession::setDTMFCallback(PyObject *pyfunc, char *funcargs)
|
|
{
|
|
sanity_check();
|
|
|
|
if (!PyCallable_Check(pyfunc)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "DTMF function is not a python function.\n");
|
|
return;
|
|
}
|
|
Py_XINCREF(pyfunc);
|
|
CoreSession::setDTMFCallback((void *) pyfunc, funcargs);
|
|
|
|
|
|
}
|
|
|
|
void PySession::setHangupHook(PyObject *pyfunc) {
|
|
|
|
if (!PyCallable_Check(pyfunc)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Hangup hook is not a python function.\n");
|
|
return;
|
|
}
|
|
|
|
// without this Py_XINCREF, there will be segfaults. basically the python
|
|
// interpreter will not know that it should not GC this object.
|
|
// callback example: http://docs.python.org/ext/callingPython.html
|
|
Py_XINCREF(pyfunc);
|
|
CoreSession::setHangupHook((void *) pyfunc);
|
|
|
|
}
|
|
|
|
|
|
void PySession::check_hangup_hook() {
|
|
PyObject *func;
|
|
PyObject *result;
|
|
char *resultStr;
|
|
bool did_swap_in = false;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "check_hangup_hook called\n");
|
|
|
|
if (!session) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No valid session\n");
|
|
return;
|
|
}
|
|
|
|
// The did_swap_in boolean was added to fix the following problem:
|
|
// Design flaw - we swap in threadstate based on the assumption that thread state
|
|
// is currently _swapped out_ when this hangup hook is called. However, nothing known to
|
|
// guarantee that, and if thread state is already swapped in when this is invoked,
|
|
// bad things will happen.
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "check hangup hook end_allow_threads\n");
|
|
did_swap_in = end_allow_threads();
|
|
|
|
if (on_hangup == NULL) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "on_hangup is null\n");
|
|
return;
|
|
}
|
|
|
|
func = (PyObject *) on_hangup;
|
|
|
|
// TODO: to match js implementation, should pass the _python_ PySession
|
|
// object instance wrapping this C++ PySession instance. but how do we do that?
|
|
// for now, pass the uuid since its better than nothing
|
|
PyObject* func_arg = Py_BuildValue("(s)", uuid);
|
|
|
|
result = PyEval_CallObject(func, func_arg);
|
|
Py_XDECREF(func_arg);
|
|
|
|
if (result) {
|
|
resultStr = (char *) PyString_AsString(result);
|
|
// currently just ignore the result
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to call python hangup callback\n");
|
|
PyErr_Print();
|
|
PyErr_Clear();
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "check hangup hook begin_allow_threads\n");
|
|
if (did_swap_in) {
|
|
begin_allow_threads();
|
|
}
|
|
|
|
Py_XDECREF(result);
|
|
|
|
}
|
|
|
|
switch_status_t PySession::run_dtmf_callback(void *input,
|
|
switch_input_type_t itype) {
|
|
|
|
PyObject *func, *arglist;
|
|
PyObject *pyresult;
|
|
PyObject *headerdict;
|
|
|
|
char *resultStr;
|
|
char *funcargs;
|
|
switch_file_handle_t *fh = NULL;
|
|
bool did_swap_in = false;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "run_dtmf_callback\n");
|
|
|
|
|
|
if (!cb_state.function) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "cb_state->function is null\n");
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
func = (PyObject *) cb_state.function;
|
|
if (!func) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "cb_state->function is null\n");
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "cb_state->function is NOT null\n");
|
|
}
|
|
if (!PyCallable_Check(func)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "function not callable\n");
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
funcargs = (char *) cb_state.funcargs;
|
|
|
|
if (itype == SWITCH_INPUT_TYPE_DTMF) {
|
|
|
|
arglist = Py_BuildValue("(sis)", input, itype, funcargs);
|
|
}
|
|
else if (itype == SWITCH_INPUT_TYPE_EVENT) {
|
|
// DUNNO if this is correct in the case we have an event
|
|
// will be of type switch_event_t *event;
|
|
// http://www.freeswitch.org/docs/structswitch__event.html
|
|
switch_event_t *event = (switch_event_t *) input;
|
|
arglist = Py_BuildValue("({s:s}is)",
|
|
"body", event->body,
|
|
itype,
|
|
funcargs);
|
|
|
|
// build a dictionary with all the headers
|
|
|
|
switch_event_header_t *hp;
|
|
headerdict = PyDict_New();
|
|
for (hp = event->headers; hp; hp = hp->next) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding event header to result");
|
|
|
|
// TODO: create PyObject pointers for name and value
|
|
// and explicitly decref them. all ref counting stuff is
|
|
// a mess and needs to be tested and looked at closer.
|
|
PyDict_SetItem(headerdict,
|
|
Py_BuildValue("s", hp->name),
|
|
Py_BuildValue("s", hp->value));
|
|
|
|
}
|
|
|
|
// add it to the main event dictionary (first arg in list)
|
|
// under key 'headers'
|
|
PyObject *dict = PyTuple_GetItem(arglist, 0);
|
|
PyDict_SetItemString(dict,
|
|
"headers",
|
|
headerdict);
|
|
|
|
Py_XDECREF(headerdict);
|
|
|
|
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown input type: %d\n", itype);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
|
|
if (!arglist) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error building arglist");
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "run_dtmf_callback end_allow_threads\n");
|
|
did_swap_in = end_allow_threads();
|
|
|
|
pyresult = PyEval_CallObject(func, arglist);
|
|
|
|
|
|
Py_XDECREF(arglist); // Trash arglist
|
|
|
|
if (pyresult && pyresult != Py_None) {
|
|
resultStr = (char *) PyString_AsString(pyresult);
|
|
switch_status_t cbresult = process_callback_result(resultStr, &cb_state, session);
|
|
return cbresult;
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Python callback\n returned None");
|
|
PyErr_Print();
|
|
PyErr_Clear();
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "run_dtmf_callback begin_allow_threads\n");
|
|
if (did_swap_in) {
|
|
begin_allow_threads();
|
|
}
|
|
|
|
Py_XDECREF(pyresult);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
bool PySession::begin_allow_threads(void) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::begin_allow_threads() called\n");
|
|
|
|
if (!session) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No valid session\n");
|
|
return false;
|
|
}
|
|
|
|
// swap out threadstate and store in instance variable
|
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
|
PyThreadState *swapin_tstate = (PyThreadState *) switch_channel_get_private(channel, "SwapInThreadState");
|
|
// so lets assume the thread state was swapped in when the python script was started,
|
|
// therefore swapin_tstate will be NULL (because there is nothing to swap in, since its
|
|
// _already_ swapped in.)
|
|
if (swapin_tstate == NULL) {
|
|
// currently swapped in
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Threadstate swap-out!\n");
|
|
swapin_tstate = PyEval_SaveThread();
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "swapin_tstate: %p\n", swapin_tstate);
|
|
// give future swapper-inners something to actually swap in
|
|
switch_channel_set_private(channel, "SwapInThreadState", (void *) swapin_tstate);
|
|
cb_state.threadState = threadState; // TODO: get rid of this
|
|
args.buf = &cb_state;
|
|
ap = &args;
|
|
return true;
|
|
|
|
}
|
|
else {
|
|
// currently swapped out
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
bool PySession::end_allow_threads(void) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::end_allow_threads() called\n");
|
|
// swap in threadstate from instance variable saved earlier
|
|
if (!session) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No valid session\n");
|
|
return false;
|
|
}
|
|
|
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
|
PyThreadState *swapin_tstate = (PyThreadState *) switch_channel_get_private(channel, "SwapInThreadState");
|
|
if (swapin_tstate == NULL) {
|
|
// currently swapped in
|
|
return false;
|
|
}
|
|
else {
|
|
// currently swapped out
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Threadstate swap-in!\n");
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "swapin_tstate: %p\n", swapin_tstate);
|
|
PyEval_RestoreThread(swapin_tstate);
|
|
// dont give any swapper-inners the opportunity to do a double swap
|
|
switch_channel_set_private(channel, "SwapInThreadState", NULL);
|
|
return true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void PySession::hangup(char *cause) {
|
|
|
|
|
|
// since we INCREF'd this function pointer earlier (so the py gc didnt reclaim it)
|
|
// we have to DECREF it, or else the PySession dtor will never get called and
|
|
// a zombie channel will be left over using up resources
|
|
|
|
if (cb_state.function != NULL) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "xdecref on cb_state_function\n");
|
|
PyObject * func = (PyObject *) cb_state.function;
|
|
Py_XDECREF(func);
|
|
}
|
|
else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "cb_state.function is null\n");
|
|
}
|
|
|
|
|
|
CoreSession::hangup(cause);
|
|
|
|
}
|
|
|
|
|
|
PySession::~PySession() {
|
|
// Should we do any cleanup here?
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::~PySession started\n");
|
|
|
|
if (on_hangup) {
|
|
PyObject * func = (PyObject *) on_hangup;
|
|
Py_XDECREF(func);
|
|
}
|
|
|
|
|
|
if (cb_state.function != NULL) {
|
|
PyObject * func = (PyObject *) cb_state.function;
|
|
Py_XDECREF(func);
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::~PySession finished\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|