Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
oai
freediameter
Commits
37f3b35c
Commit
37f3b35c
authored
Dec 16, 2010
by
Sebastien Decugis
Browse files
Split interface file in modules
parent
12b195dd
Changes
13
Hide whitespace changes
Inline
Side-by-side
doc/dbg_interactive.py.sample
View file @
37f3b35c
...
...
@@ -65,18 +65,40 @@ r = fd_event_send(cvar.fd_g_config.cnf_main_ev, FDEV_DUMP_EXT, 0, None)
# Display the local Diameter Identity:
print "Local Diameter Identity:", cvar.fd_g_config.cnf_diamid
# Display realm, without using the low-level functions (skip proxy classe definitions):
# Display realm, using the low-level functions (skip proxy classe definitions):
print "Realm:", _fDpy.fd_config_cnf_diamrlm_get(_fDpy.cvar.fd_g_config)
############# Lists ############
l1 = fd_list() # The creator has an implicit fd_list_init call
# Note: we use different names from the C API here, for usability.
l1 = fd_list() # Will be our sentinel
l2 = fd_list()
fd_list_insert_after(l1, l2)
l1.dump()
del l2 # The destructor has an implicit fd_list_unlink call
l3 = fd_list()
l1.isempty()
l1.insert_next(l2) # l1 -> l2
l1.isempty()
l1.insert_prev(l3) # l1 -> l2 -> l3 (circular list)
l1.dump()
l3.detach() # l1 -> l2
l4=fd_list()
l5=fd_list()
l3.insert_next(l4) # l3 -> l4
l3.insert_next(l5) # l3 -> l5 -> l4
l1.concat(l3) # l1 -> l2 -> l5 -> l4
elements = l1.enum_as() # default: enumerates as fd_list. Warning: this a copy, changing the python list has no effect on the underlying list.
for li in elements:
li.dump()
del elements
del l2
del l3
del l4
del l5
l1.isempty() # The destructor has an implicit fd_list_unlink call
del l1
...
...
@@ -213,7 +235,7 @@ s1 = session()
s1.getsid()
s2 = session("this.is.a.full.session.id")
r,s3,isnew = fd_sess_fromsid("this.is.a.full.session.id")
s4 = session("host.id", "opt.part")
s4 = session("host.id", "opt
ional
.part")
s4.settimeout(30) # the python wrapper takes a number of seconds as parameter for simplicity
s4.dump()
...
...
@@ -240,51 +262,142 @@ rd.remove("p2.testbed.aaa")
rd.error("p3.testbed.aaa", "relay.testbed.aaa", 3002)
list = rd.extract(-1)
list[0].dump()
for c in list.enum_as("struct rtd_candidate *"):
print "%s (%s): %s" % (c.diamid, c.realm, c.score)
############# Messages, AVPs ############
## AVP
# Create empty (as for messages, pass None or a dictionary object as 1st param, and flags as optional 2nd param)
blank_avp = avp()
del blank_avp
######################### old stuff (need update) ######################
oh = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host")) # Octet String
vi = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Vendor-Id")) # U32
vsai = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id")) # Grouped
# Set values
val = avp_value()
val.u32 = 123
vi.setval(None) # this cleans a previous value (not needed)
vi.setval(val)
val.os = "my.origin.host"
oh.setval(val)
vsai.add_child(vi) # call as add_child(vi, 1) to add the new AVP at the beginning, default is at the end
## Messages
# Create empty
a_msg = msg()
a_msg.dump()
del a_msg
# It is also possible to pass MSGFL_* flags in second parameter (ALLOC_ETEID is default)
msg_no_eid = msg(None, 0)
msg_no_eid.dump()
del msg_no_eid
# Create from dictionary
dwr_dict = cvar.fd_g_config.cnf_dict.search ( DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request" )
dwr = msg(dwr_dict)
dwr.dump()
# Create msg from a binary buffer (then you should call parse_dict and parse_rules methods)
dwr2 = msg("\x01\x00\x00\x14\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xf0\x00\x01")
# Create answer from request (optional parameters: dictionary to use, and flags):
dwr3 = msg(cvar.fd_g_config.cnf_dict.search ( DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request" ))
dwa3 = dwr3.create_answer()
dwr3cpy = dwa3.get_query()
## Other functions with AVPs & messages
# Add the AVPs in the message
dwr.add_child(oh)
oh.add_next(vsai) # equivalent to add_child on the parent
# Create a network byte buffer from the message
dwr.bufferize()
# Get first child AVP (fast)
avp = dwr.first_child()
# then:
avp = avp.get_next() # when last AVP, returns None
# Get all 1st level children (slower) -- warning, changes to the python list will not be reflected on the underlying message. read-only use.
dwr.children()
# example use:
for a in dwr.children()
a.dump(0) # 0 means: dump only this object, do not walk the tree
# Search the first AVP of a given type
oh_dict = cvar.fd_g_config.cnf_dict.search( DICT_AVP, AVP_BY_NAME, "Origin-Host")
oh = dwr.search( oh_dict )
# After adding AVPs, the length in the message header is outdated, refresh as follow:
dwr.update_length()
# Get dictionary model for a message or avp
dwr.model()
oh.model().dump()
# Messages
gdict = fd_config_cnf_dict_get(cvar.fd_g_config)
pobj = new_dict_object_pptr()
fd_dict_search ( gdict, DICT_COMMAND, CMD_BY_NAME, char_to_void("Capabilities-Exchange-Request"), pobj, -1 )
cerdict = dict_object_pptr_value(pobj)
fd_dict_search ( gdict, DICT_AVP, AVP_BY_NAME, char_to_void("Origin-Host"), pobj, -1 )
ohdict = dict_object_pptr_value(pobj)
delete_dict_object_pptr(pobj)
pmsg = new_msg_pptr()
fd_msg_new(cerdict, MSGFL_ALLOC_ETEID, pmsg)
msg = msg_pptr_value(pmsg);
pavp = new_avp_pptr()
fd_msg_avp_new(ohdict, 0, pavp)
avp = avp_pptr_value(pavp);
fd_msg_avp_add(msg, MSG_BRW_FIRST_CHILD, avp)
fd_msg_dump_walk(0, msg)
pahdr = new_avp_hdr_pptr()
fd_msg_avp_hdr(avp, pahdr)
ahdr = avp_hdr_pptr_value(pahdr)
delete_avp_hdr_pptr(pahdr)
avp_hdr_avp_code_get(ahdr)
os = new_avp_value_os()
avp_value_os_fromstr(os, fd_config_cnf_diamid_get(cvar.fd_g_config))
val = new_avp_value()
avp_value_os_set(val, os)
delete_avp_value_os(os)
fd_msg_avp_setvalue(avp, val)
delete_avp_value(val)
r,buf = fd_msg_bufferize_py(msg)
fd_msg_free(msg)
delete_avp_pptr(pavp)
# Retrieve the header of messages & avp:
dwr_hdr = dwr.header()
dwr_hdr.msg_version
dwr_hdr.msg_hbhid
oh_hdr = oh.header()
hex(oh_hdr.avp_flags)
oh_hdr.avp_vendor
oh_hdr.avp_value.os.dump() # The initial avp value must be set with setval(), but then this accessor is allowed.
# Get or set the routing data
rd = rt_data()
dwr.set_rtd(rd)
rd = dwr.get_rtd()
# Test if message is routable
dwr.is_routable()
# Which peer the message was received from (when received from network)
dwr.source()
# The session corresponding to this message (returns None when no Session-Id AVP is included)
dwr.get_session()
# Parse a buffer
buf = "\x01\x00\x00@\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00N\x10\x00\x00\x00\x00\x01\x08@\x00\x00\x16my.origin.host\x00\x00\x00\x00\x01\x04@\x00\x00\x14\x00\x00\x01\n@\x00\x00\x0c\x00\x00\x00{"
mydwr = msg(buf)
# Resolve objects in the dictionary. Return value is None or a struct pei_error in case of problem.
mydwr.parse_dict() # if not using the fD global dict, pass it as parameter
err = mydwr.parse_rules()
err.pei_errcode
# Grouped AVPs are browsed with same methods as messages:
gavp = dwr.children()[1]
gavp.first_child().dump()
gavp.children()
######################### old stuff (need update) ######################
# Create a new peer_info structure and add the peer to the framework.
...
...
extensions/dbg_interactive/CMakeLists.txt
View file @
37f3b35c
...
...
@@ -16,7 +16,16 @@ SET(CMAKE_SWIG_FLAGS -castmode -threads)
SET
(
SWIG_MODULE_fDpy_EXTRA_DEPS
${
CMAKE_BINARY_DIR
}
/include/freeDiameter/freeDiameter-host.h
${
CMAKE_SOURCE_DIR
}
/include/freeDiameter/libfreeDiameter.h
${
CMAKE_SOURCE_DIR
}
/include/freeDiameter/freeDiameter.h
)
${
CMAKE_SOURCE_DIR
}
/include/freeDiameter/freeDiameter.h
lists.i
dictionary.i
sessions.i
routing.i
messages.i
dispatch.i
queues.i
peers.i
)
SET_SOURCE_FILES_PROPERTIES
(
dbg_interactive.i PROPERTIES SWIG_MODULE_NAME fDpy
)
# The following code is inspired from SWIG_ADD_MODULE, but we do only what we need
...
...
extensions/dbg_interactive/dbg_interactive.i
View file @
37f3b35c
...
...
@@ -46,42 +46,23 @@
/* Include standard types & functions used in freeDiameter headers */
%
include
<
stdint
.
i
>
%
include
<
cdata
.
i
>
//
%include <cdata.i>
%
include
<
cstring
.
i
>
%
include
<
typemaps
.
i
>
/* Some functions are not available through the wrapper */
%
ignore
fd_lib_init
;
%
ignore
fd_lib_fini
;
/* -- the following functions are better accessed differently, but we leave their definitions just in case
%ignore fd_dict_init;
%ignore fd_dict_fini;
%ignore fd_sess_handler_create_internal;
%ignore fd_sess_handler_destroy;
%ignore fd_sess_new;
%ignore fd_sess_getsid;
%ignore fd_sess_destroy;
%ignore fd_sess_reclaim;
%ignore fd_sess_state_store_internal;
%ignore fd_sess_state_retrieve_internal;
%ignore fd_rtd_init;
%ignore fd_rtd_free;
%ignore fd_rtd_candidate_add;
%ignore fd_rtd_candidate_del;
%ignore fd_rtd_candidate_extract;
%ignore fd_rtd_error_add;
*/
/* Inline functions seems to give problems to SWIG -- just remove the inline definition */
%
define
__inline__
%
enddef
/* Make some global-variables read-only (mainly to avoid warnings) */
%
immutable
fd_g_config
;
%
immutable
peer_state_str
;
/* Create a generic error handling mechanism so that functions can provoke an exception */
/*****************
* Exceptions *
*****************/
%
{
/* This is not thread-safe etc. but it should work /most of the time/. */
static
int
wrapper_errno
;
...
...
@@ -127,8 +108,9 @@ static char * wrapper_error_txt; /* if NULL, use strerror(errno) */
Some types & typemaps for usability
***********************************/
%
apply
(
char
*
STRING
,
size_t
LENGTH
)
{
(
char
*
string
,
size_t
len
)
}
;
%
apply
(
char
*
STRING
,
size_t
LENGTH
)
{
(
char
*
string
,
size_t
len
)
}
;
/* fd_hash */
/* Generic typemap for functions that create something */
%
typemap
(
in
,
numinputs
=
0
,
noblock
=
1
)
SWIGTYPE
**
OUTPUT
(
void
*
temp
=
NULL
)
{
$
1
=
(
void
*
)
&
temp;
}
...
...
@@ -136,12 +118,7 @@ static char * wrapper_error_txt; /* if NULL, use strerror(errno) */
%
append_output
(
SWIG_NewPointerObj
(
*
$
1
,
$
*
1
_descriptor
,
0
))
;
}
%
apply
int
*
OUTPUT
{
enum
dict_object_type
*
type
}
;
%
apply
(
char
*
STRING
,
size_t
LENGTH
)
{
(
char
*
sid
,
size_t
len
)
}
;
%
apply
SWIGTYPE
**
OUTPUT
{
struct
session
**
session
}
;
%
apply
int
*
OUTPUT
{
int
*
new
}
;
/* Callbacks defined in python */
/* To allow passing callback functions defined in python */
%
typemap
(
in
)
PyObject
*
PyCb
{
if
(
!
PyCallable_Check
(
$
input
))
{
PyErr_SetString
(
PyExc_TypeError
,
"Need a callable object!"
)
;
...
...
@@ -151,7 +128,6 @@ static char * wrapper_error_txt; /* if NULL, use strerror(errno) */
}
/*********************************************************
Now, create wrappers for (almost) all objects from fD API
*********************************************************/
...
...
@@ -159,567 +135,17 @@ static char * wrapper_error_txt; /* if NULL, use strerror(errno) */
%
include
"freeDiameter/libfreeDiameter.h"
%
include
"freeDiameter/freeDiameter.h"
/* Most of the functions from the API are not directly usable "as is".
See the specific following files and the dbg_interactive.py.sample file
for more usable python-style versions.
*/
%
include
"lists.i"
%
include
"dictionary.i"
%
include
"sessions.i"
%
include
"routing.i"
%
include
"messages.i"
%
include
"dispatch.i"
%
include
"queues.i"
/**********************************************************/
/* The remaining of this file allows easier manipulation of
the structures and functions of fD by providing wrapper-specific
extensions to the freeDiameter API.
/****** LISTS *********/
%
extend
fd_list
{
/* allow a parameter in the constructor, and perform the fd_list_init operation */
fd_list
(
void
*
o
=
NULL
)
{
struct
fd_list
*
li
;
li
=
(
struct
fd_list
*
)
malloc
(
sizeof
(
struct
fd_list
))
;
if
(
!
li
)
{
DI_ERROR_MALLOC
;
return
NULL
;
}
fd_list_init
(
li
,
o
)
;
return
li
;
}
/* Unlink before freeing */
~
fd_list
()
{
fd_list_unlink
(
$
self
)
;
free
(
$
self
)
;
}
/* For debug, show the values of the list */
void
dump
()
{
fd_log_debug
(
"list: %p
\n
"
,
$
self
)
;
fd_log_debug
(
" - next: %p
\n
"
,
$
self-
>
next
)
;
fd_log_debug
(
" - prev: %p
\n
"
,
$
self-
>
prev
)
;
fd_log_debug
(
" - head: %p
\n
"
,
$
self-
>
head
)
;
fd_log_debug
(
" - o : %p
\n
"
,
$
self-
>
o
)
;
}
}
;
/****** DICTIONARY *********/
struct
dictionary
{
}
;
%
extend
dictionary
{
dictionary
()
{
struct
dictionary
*
r
=
NULL
;
int
ret
=
fd_dict_init
(
&
r);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
NULL
;
}
return
r
;
}
~
dictionary
()
{
struct
dictionary
*
d
=
self
;
int
ret
=
fd_dict_fini
(
&
d);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
}
return
;
}
void
dump
()
{
fd_dict_dump
(
$
self
)
;
}
PyObject
*
vendors_list
()
{
uint32_t
*
list
=
NULL
,
*
li
;
PyObject
*
ret
;
SWIG_PYTHON_THREAD_BEGIN_BLOCK
;
ret
=
PyList_New
(
0
)
;
list
=
fd_dict_get_vendorid_list
(
$
self
)
;
for
(
li
=
list
;
*
li
!=
0
;
li
++
)
{
PyList_Append
(
ret
,
PyInt_FromLong
((
long
)
*
li
))
;
}
free
(
list
)
;
SWIG_PYTHON_THREAD_END_BLOCK
;
return
ret
;
}
struct
dict_object
*
new_obj
(
enum
dict_object_type
type
,
void
*
data
,
struct
dict_object
*
parent
=
NULL
)
{
struct
dict_object
*
obj
=
NULL
;
int
ret
=
fd_dict_new
(
$
self
,
type
,
data
,
parent
,
&
obj);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
NULL
;
}
return
obj
;
}
struct
dict_object
*
search
(
enum
dict_object_type
type
,
int
criteria
,
int
what_by_val
)
{
struct
dict_object
*
obj
=
NULL
;
int
ret
=
fd_dict_search
(
$
self
,
type
,
criteria
,
&
what_by_val,
&
obj,
ENOENT
);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
NULL
;
}
return
obj
;
}
struct
dict_object
*
search
(
enum
dict_object_type
type
,
int
criteria
,
char
*
what_by_string
)
{
struct
dict_object
*
obj
=
NULL
;
int
ret
=
fd_dict_search
(
$
self
,
type
,
criteria
,
what_by_string
,
&
obj,
ENOENT
);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
NULL
;
}
return
obj
;
}
struct
dict_object
*
search
(
enum
dict_object_type
type
,
int
criteria
,
void
*
what
)
{
struct
dict_object
*
obj
=
NULL
;
int
ret
=
fd_dict_search
(
$
self
,
type
,
criteria
,
what
,
&
obj,
ENOENT
);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
NULL
;
}
return
obj
;
}
struct
dict_object
*
error_cmd
()
{
struct
dict_object
*
obj
=
NULL
;
int
ret
=
fd_dict_get_error_cmd
(
$
self
,
&
obj
);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
NULL
;
}
return
obj
;
}
}
struct
dict_object
{
}
;
%
extend
dict_object
{
dict_object
()
{
DI_ERROR
(
EINVAL
,
PyExc_SyntaxError
,
"dict_object cannot be created directly. Use fd_dict_new()."
)
;
return
NULL
;
}
~
dict_object
()
{
DI_ERROR
(
EINVAL
,
PyExc_SyntaxError
,
"dict_object cannot be destroyed directly. Destroy the parent dictionary."
)
;
return
;
}
void
dump
()
{
fd_dict_dump_object
(
$
self
)
;
}
enum
dict_object_type
gettype
()
{
enum
dict_object_type
t
;
int
ret
=
fd_dict_gettype
(
$
self
,
&
t);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
0
;
}
return
t
;
}
struct
dictionary
*
getdict
()
{
struct
dictionary
*
d
;
int
ret
=
fd_dict_getdict
(
$
self
,
&
d
);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
NULL
;
}
return
d
;
}
/* Since casting the pointer requires intelligence, we do it here instead of giving it to SWIG */
PyObject
*
getval
()
{
/* first, get the type */
enum
dict_object_type
t
;
int
ret
=
fd_dict_gettype
(
$
self
,
&
t);
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
return
NULL
;
}
switch
(
t
)
{
%
define
%
GETVAL_CASE
(
TYPE
,
STRUCT
)
case
TYPE
:
{
PyObject
*
v
=
NULL
;
struct
STRUCT
*
data
=
NULL
;
data
=
malloc
(
sizeof
(
struct
STRUCT
))
;
if
(
!
data
)
{
DI_ERROR_MALLOC
;
return
NULL
;
}
ret
=
fd_dict_getval
(
$
self
,
data
)
;
if
(
ret
!=
0
)
{
DI_ERROR
(
ret
,
NULL
,
NULL
)
;
free
(
data
)
;
return
NULL
;
}
SWIG_PYTHON_THREAD_BEGIN_BLOCK
;
v
=
SWIG_NewPointerObj
((
void
*
)
data
,
SWIGTYPE_p_
##
STRUCT
,
SWIG_POINTER_OWN
)
;
Py_XINCREF
(
v
)
;
SWIG_PYTHON_THREAD_END_BLOCK
;
return
v
;
}
break
%
enddef
%
GETVAL_CASE
(
DICT_VENDOR
,
dict_vendor_data
)
;
%
GETVAL_CASE
(
DICT_APPLICATION
,
dict_application_data
)
;
%
GETVAL_CASE
(
DICT_TYPE
,
dict_type_data
)
;
%
GETVAL_CASE
(
DICT_ENUMVAL
,
dict_enumval_data
)
;
%
GETVAL_CASE
(
DICT_AVP
,
dict_avp_data
)
;
%
GETVAL_CASE
(
DICT_COMMAND
,
dict_cmd_data
)
;
%
GETVAL_CASE
(
DICT_RULE
,
dict_rule_data
)
;
default
:
DI_ERROR
(
EINVAL
,
PyExc_SystemError
,
"Internal error: Got invalid object type"
)
;
}
return
NULL
;
}
}
/* The following wrapper leaks memory each time an union avp_value is assigned an octet string.
TODO: fix this leak by better understanding SWIG...
-- the alternative is to uncomment the "free" statements bellow, but then it is easy to
create a segmentation fault by assigning first an integer, then an octetstring.
*/
%
extend
avp_value
{
/* The following hack in the proxy file allows assigning the octet string directly like this:
avp_value.os = "blabla"
*/
%
pythoncode
{
__swig_setmethods__
[
"os"
]
=
_fDpy
.
avp_value_os_set
if
_newclass
:
os
=
_swig_property
(
_fDpy
.
avp_value_os_get
,
_fDpy
.
avp_value_os_set
)
}
void
os_set
(
char
*
STRING
,
size_t
LENGTH
)
{
/* free($self->os.data); -- do not free, in case the previous value was not an OS */
$
self-
>
os
.
data
=
malloc
(
LENGTH
)
;
if
(
!
$
self-
>
os
.
data
)
{
DI_ERROR_MALLOC
;
return
;
}
memcpy
(
$
self-
>
os
.
data
,
STRING
,
LENGTH
)
;
$
self-
>
os
.
len
=
LENGTH
;
}
void
os_set
(
avp_value_os
*
os
)
{
/* free($self->os.data); -- do not free, in case the previous value was not an OS */
$
self-
>
os
.
data
=
malloc
(
os-
>
len
)
;
if
(
!
$
self-
>
os
.
data
)
{
DI_ERROR_MALLOC
;
return
;
}
memcpy
(
$
self-
>
os
.
data
,
os-
>
data
,
os-
>
len
)
;
$
self-
>
os
.
len
=
os-
>
len
;
}
}
;
%
extend
avp_value_os
{
void
dump
()
{
%
#
define
LEN_MAX
20
int
i
,
n
=
LEN_MAX
;
if
(
$
self-
>
len
<
LEN_MAX
)
n
=
$
self-
>
len
;
fd_log_debug
(
"l:%u, v:["
,
$
self-
>
len
)
;
for
(
i
=
0
;
i
<
n
;
i
++
)
fd_log_debug
(
"%02.2X"
,
$
self-
>
data
[
i
])
;
fd_log_debug
(
"] '%.*s%s'
\n
"
,
n
,
$
self-
>
data
,
n
==
LEN_MAX
?
"..."
:
""
)
;
}
}
/****** SESSIONS *********/
%
{
/* store the python callback function here */
static
PyObject
*
py_cleanup_cb
=
NULL
;
/* call it (might be called from a different thread than the interpreter, when session times out) */
static
void
call_the_python_cleanup_callback
(
session_state
*
state
,
char
*
sid
)
{
PyObject
*
result
;
if
(
!
py_cleanup_cb
)
return
;
/* Call the function */
SWIG_PYTHON_THREAD_BEGIN_BLOCK
;
result
=
PyEval_CallFunction
(
py_cleanup_cb
,
"(Os)"
,
state
,
sid
)
;
Py_XDECREF
(
result
)
;
SWIG_PYTHON_THREAD_END_BLOCK
;
return
;
}
%
}