Fri Jan 16 13:37:43 CST 2009 Pekka Pessi <first.last@nokia.com>
* nua: fixed problem handling re-SUBSCRIBE when it creates new dialog git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@11836 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
10c67f8219
commit
587408b8ce
|
@ -1 +1 @@
|
|||
Wed Feb 11 11:07:52 CST 2009
|
||||
Wed Feb 11 11:08:29 CST 2009
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
#include <fnmatch.h>
|
||||
#endif
|
||||
|
||||
#include "test_s2.h"
|
||||
|
||||
static char const * const default_patterns[] = { "*", NULL };
|
||||
static char const * const *test_patterns = default_patterns;
|
||||
|
||||
|
@ -80,6 +82,10 @@ int main(int argc, char *argv[])
|
|||
Suite *suite = suite_create("Unit tests for Sofia-SIP UA Engine");
|
||||
SRunner *runner;
|
||||
|
||||
s2_tester = "check_nua";
|
||||
if (getenv("CHECK_NUA_VERBOSE"))
|
||||
s2_start_stop = strtoul(getenv("CHECK_NUA_VERBOSE"), NULL, 10);
|
||||
|
||||
if (getenv("CHECK_NUA_CASES")) {
|
||||
size_t i;
|
||||
char *s, **patterns;
|
||||
|
|
|
@ -10,6 +10,7 @@ void check_nua_tcase_add_test(TCase *, TFun, char const *name);
|
|||
|
||||
void check_session_cases(Suite *suite);
|
||||
void check_register_cases(Suite *suite);
|
||||
void check_simple_cases(Suite *suite);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -54,12 +54,13 @@ static nua_t *nua;
|
|||
|
||||
static void register_setup(void)
|
||||
{
|
||||
nua = s2_nua_setup(TAG_END());
|
||||
nua = s2_nua_setup("register", TAG_END());
|
||||
}
|
||||
|
||||
static void register_pingpong_setup(void)
|
||||
{
|
||||
nua = s2_nua_setup(TPTAG_PINGPONG(20000),
|
||||
nua = s2_nua_setup("register with pingpong",
|
||||
TPTAG_PINGPONG(20000),
|
||||
TPTAG_KEEPALIVE(10000),
|
||||
TAG_END());
|
||||
tport_set_params(s2->tcp.tport, TPTAG_PONG2PING(1), TAG_END());
|
||||
|
@ -68,6 +69,7 @@ static void register_pingpong_setup(void)
|
|||
|
||||
static void register_teardown(void)
|
||||
{
|
||||
s2_teardown_started("register");
|
||||
nua_shutdown(nua);
|
||||
fail_unless(s2_check_event(nua_r_shutdown, 200));
|
||||
s2_nua_teardown();
|
||||
|
|
|
@ -63,9 +63,8 @@ static struct dialog *dialog = NULL;
|
|||
|
||||
static void call_setup(void)
|
||||
{
|
||||
s2_case("0.1.1", "Setup for Call Tests", "");
|
||||
|
||||
nua = s2_nua_setup(SIPTAG_ORGANIZATION_STR("Pussy Galore's Flying Circus"),
|
||||
nua = s2_nua_setup("call",
|
||||
SIPTAG_ORGANIZATION_STR("Pussy Galore's Flying Circus"),
|
||||
NUTAG_OUTBOUND("no-options-keepalive, no-validate"),
|
||||
TAG_END());
|
||||
|
||||
|
@ -85,7 +84,7 @@ static void call_setup(void)
|
|||
|
||||
static void call_teardown(void)
|
||||
{
|
||||
s2_case("0.1.2", "Teardown Call Test Setup", "");
|
||||
s2_teardown_started("call");
|
||||
|
||||
mark_point();
|
||||
|
||||
|
@ -3078,6 +3077,7 @@ static void options_setup(void)
|
|||
|
||||
static void options_teardown(void)
|
||||
{
|
||||
s2_teardown_started("options");
|
||||
call_teardown();
|
||||
}
|
||||
|
||||
|
|
|
@ -1973,8 +1973,7 @@ int nua_base_server_report(nua_server_request_t *sr, tagi_t const *tags)
|
|||
static void nua_client_request_destroy(nua_client_request_t *cr);
|
||||
static int nua_client_init_request0(nua_client_request_t *cr);
|
||||
static int nua_client_request_try(nua_client_request_t *cr);
|
||||
static int nua_client_request_sendmsg(nua_client_request_t *cr,
|
||||
msg_t *msg, sip_t *sip);
|
||||
static int nua_client_request_sendmsg(nua_client_request_t *cr);
|
||||
static void nua_client_restart_after(su_root_magic_t *magic,
|
||||
su_timer_t *timer,
|
||||
nua_client_request_t *cr);
|
||||
|
@ -2416,23 +2415,6 @@ int nua_client_init_request0(nua_client_request_t *cr)
|
|||
(sip_header_t *)sip->sip_from) < 0) {
|
||||
return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
|
||||
}
|
||||
|
||||
if (cr->cr_dialog) {
|
||||
ds->ds_leg = nta_leg_tcreate(nua->nua_nta,
|
||||
nua_stack_process_request, nh,
|
||||
SIPTAG_CALL_ID(sip->sip_call_id),
|
||||
SIPTAG_FROM(sip->sip_from),
|
||||
SIPTAG_TO(sip->sip_to),
|
||||
SIPTAG_CSEQ(sip->sip_cseq),
|
||||
TAG_END());
|
||||
if (!ds->ds_leg)
|
||||
return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
|
||||
|
||||
if (!sip->sip_from->a_tag &&
|
||||
sip_from_tag(msg_home(msg), sip->sip_from,
|
||||
nta_leg_tag(ds->ds_leg, NULL)) < 0)
|
||||
return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ds->ds_route)
|
||||
|
@ -2540,15 +2522,17 @@ int nua_client_resend_request(nua_client_request_t *cr,
|
|||
|
||||
if (nua_client_request_queue(cr))
|
||||
return 0;
|
||||
|
||||
if (nua_dialog_is_reporting(cr->cr_owner->nh_ds))
|
||||
return 0;
|
||||
|
||||
return nua_client_request_try(cr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** Create a request message and send it.
|
||||
/** Send a request message.
|
||||
*
|
||||
* If an error occurs, send error event to the application.
|
||||
*
|
||||
|
@ -2558,26 +2542,11 @@ int nua_client_resend_request(nua_client_request_t *cr,
|
|||
static
|
||||
int nua_client_request_try(nua_client_request_t *cr)
|
||||
{
|
||||
int error = -1;
|
||||
msg_t *msg = msg_copy(cr->cr_msg);
|
||||
sip_t *sip = sip_object(msg);
|
||||
|
||||
cr->cr_answer_recv = 0, cr->cr_offer_sent = 0;
|
||||
cr->cr_offer_recv = 0, cr->cr_answer_sent = 0;
|
||||
|
||||
if (msg && sip) {
|
||||
error = nua_client_request_sendmsg(cr, msg, sip);
|
||||
if (!error)
|
||||
return 0;
|
||||
|
||||
if (error == -1)
|
||||
msg_destroy(msg);
|
||||
}
|
||||
int error = nua_client_request_sendmsg(cr);
|
||||
|
||||
if (error < 0)
|
||||
error = nua_client_response(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL);
|
||||
|
||||
assert(error > 0);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -2585,13 +2554,10 @@ int nua_client_request_try(nua_client_request_t *cr)
|
|||
*
|
||||
* @retval 0 if request is pending
|
||||
* @retval >=1 if error event has been sent
|
||||
* @retval -1 if error occurred but event has not been sent,
|
||||
and @a msg has not been destroyed
|
||||
* @retval -2 if error occurred, event has not been sent,
|
||||
* but @a msg has been destroyed
|
||||
* @retval < 0 if no error event has been sent
|
||||
*/
|
||||
static
|
||||
int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip)
|
||||
int nua_client_request_sendmsg(nua_client_request_t *cr)
|
||||
{
|
||||
nua_handle_t *nh = cr->cr_owner;
|
||||
nua_dialog_state_t *ds = nh->nh_ds;
|
||||
|
@ -2599,9 +2565,36 @@ int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip)
|
|||
char const *name = cr->cr_method_name;
|
||||
url_string_t const *url = (url_string_t *)cr->cr_target;
|
||||
nta_leg_t *leg;
|
||||
msg_t *msg;
|
||||
sip_t *sip;
|
||||
int error;
|
||||
|
||||
assert(cr->cr_orq == NULL);
|
||||
|
||||
cr->cr_offer_sent = cr->cr_answer_recv = 0;
|
||||
cr->cr_offer_recv = cr->cr_answer_sent = 0;
|
||||
|
||||
if (!ds->ds_leg && cr->cr_dialog) {
|
||||
ds->ds_leg = nta_leg_tcreate(nh->nh_nua->nua_nta,
|
||||
nua_stack_process_request, nh,
|
||||
SIPTAG_CALL_ID(cr->cr_sip->sip_call_id),
|
||||
SIPTAG_FROM(cr->cr_sip->sip_from),
|
||||
SIPTAG_TO(cr->cr_sip->sip_to),
|
||||
SIPTAG_CSEQ(cr->cr_sip->sip_cseq),
|
||||
TAG_END());
|
||||
if (!ds->ds_leg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cr->cr_sip->sip_from && ds->ds_leg) {
|
||||
if (cr->cr_sip->sip_from->a_tag == NULL) {
|
||||
if (sip_from_tag(msg_home(cr->cr_msg), cr->cr_sip->sip_from,
|
||||
nta_leg_tag(ds->ds_leg, NULL)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cr->cr_retry_count++;
|
||||
|
||||
if (ds->ds_leg)
|
||||
|
@ -2609,6 +2602,11 @@ int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip)
|
|||
else
|
||||
leg = nh->nh_nua->nua_dhandle->nh_ds->ds_leg; /* Default leg */
|
||||
|
||||
msg = msg_copy(cr->cr_msg), sip = sip_object(msg);
|
||||
|
||||
if (msg == NULL)
|
||||
return -1;
|
||||
|
||||
if (nua_dialog_is_established(ds)) {
|
||||
while (sip->sip_route)
|
||||
sip_route_remove(msg, sip);
|
||||
|
@ -2639,8 +2637,10 @@ int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip)
|
|||
* generated based on the dialog information and added to the request.
|
||||
* If the dialog has a route, it is added to the request, too.
|
||||
*/
|
||||
if (nta_msg_request_complete(msg, leg, method, name, url) < 0)
|
||||
if (nta_msg_request_complete(msg, leg, method, name, url) < 0) {
|
||||
msg_destroy(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**@MaxForwards header (with default value set by NTATAG_MAX_FORWARDS()) is
|
||||
* also added now, if it does not exist.
|
||||
|
@ -2716,16 +2716,23 @@ int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip)
|
|||
cr->cr_contactize &&
|
||||
!cr->cr_has_contact &&
|
||||
!ds->ds_ltarget,
|
||||
!ds->ds_route) < 0)
|
||||
!ds->ds_route) < 0) {
|
||||
msg_destroy(msg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
cr->cr_wait_for_cred = 0;
|
||||
|
||||
if (cr->cr_methods->crm_send)
|
||||
return cr->cr_methods->crm_send(cr, msg, sip, NULL);
|
||||
error = cr->cr_methods->crm_send(cr, msg, sip, NULL);
|
||||
else
|
||||
error = nua_base_client_request(cr, msg, sip, NULL);
|
||||
|
||||
return nua_base_client_request(cr, msg, sip, NULL);
|
||||
if (error != 0 && error != -2)
|
||||
msg_destroy(msg);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**Add tags to request message and send it,
|
||||
|
@ -2793,9 +2800,10 @@ int nua_base_client_request(nua_client_request_t *cr, msg_t *msg, sip_t *sip,
|
|||
}
|
||||
|
||||
/** Callback for nta client transaction */
|
||||
int nua_client_orq_response(nua_client_request_t *cr,
|
||||
nta_outgoing_t *orq,
|
||||
sip_t const *sip)
|
||||
int
|
||||
nua_client_orq_response(nua_client_request_t *cr,
|
||||
nta_outgoing_t *orq,
|
||||
sip_t const *sip)
|
||||
{
|
||||
int status;
|
||||
char const *phrase;
|
||||
|
@ -3090,11 +3098,8 @@ int nua_client_restart(nua_client_request_t *cr,
|
|||
int status, char const *phrase)
|
||||
{
|
||||
nua_handle_t *nh = cr->cr_owner;
|
||||
nua_dialog_state_t *ds = nh->nh_ds;
|
||||
nta_outgoing_t *orq;
|
||||
int error = -1, terminated, graceful;
|
||||
msg_t *msg = NULL;
|
||||
sip_t *sip = NULL;
|
||||
|
||||
if (cr->cr_retry_count > NH_PGET(nh, retry_count))
|
||||
return 0;
|
||||
|
@ -3102,32 +3107,10 @@ int nua_client_restart(nua_client_request_t *cr,
|
|||
orq = cr->cr_orq, cr->cr_orq = NULL; assert(orq);
|
||||
terminated = cr->cr_terminated, cr->cr_terminated = 0;
|
||||
graceful = cr->cr_graceful, cr->cr_graceful = 0;
|
||||
cr->cr_offer_sent = cr->cr_answer_recv = 0;
|
||||
cr->cr_offer_recv = cr->cr_answer_sent = 0;
|
||||
|
||||
if (!ds->ds_leg && cr->cr_dialog) {
|
||||
sip_t const *sip = cr->cr_sip;
|
||||
ds->ds_leg = nta_leg_tcreate(nh->nh_nua->nua_nta,
|
||||
nua_stack_process_request, nh,
|
||||
SIPTAG_CALL_ID(sip->sip_call_id),
|
||||
SIPTAG_FROM(sip->sip_from),
|
||||
SIPTAG_TO(sip->sip_to),
|
||||
SIPTAG_CSEQ(sip->sip_cseq),
|
||||
TAG_END());
|
||||
}
|
||||
|
||||
if (ds->ds_leg || !cr->cr_dialog) {
|
||||
msg = msg_copy(cr->cr_msg);
|
||||
sip = sip_object(msg);
|
||||
}
|
||||
|
||||
if (msg && sip) {
|
||||
cr->cr_restarting = 1;
|
||||
error = nua_client_request_sendmsg(cr, msg, sip);
|
||||
cr->cr_restarting = 0;
|
||||
if (error !=0 && error != -2)
|
||||
msg_destroy(msg);
|
||||
}
|
||||
cr->cr_restarting = 1;
|
||||
error = nua_client_request_sendmsg(cr);
|
||||
cr->cr_restarting = 0;
|
||||
|
||||
if (error) {
|
||||
cr->cr_graceful = graceful;
|
||||
|
|
|
@ -52,6 +52,9 @@
|
|||
#include <limits.h>
|
||||
#include <time.h>
|
||||
|
||||
char const *s2_tester = "s2_tester";
|
||||
int s2_start_stop;
|
||||
|
||||
/* -- Module types ------------------------------------------------------ */
|
||||
|
||||
struct tp_magic_s
|
||||
|
@ -207,6 +210,19 @@ int s2_check_callstate(enum nua_callstate state)
|
|||
return retval;
|
||||
}
|
||||
|
||||
int s2_check_substate(struct event *e, enum nua_substate state)
|
||||
{
|
||||
int retval = 0;
|
||||
tagi_t const *tagi;
|
||||
|
||||
tagi = tl_find(e->data->e_tags, nutag_substate);
|
||||
if (tagi) {
|
||||
retval = (tag_value_t)state == tagi->t_value;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
s2_nua_callback(nua_event_t event,
|
||||
int status, char const *phrase,
|
||||
|
@ -371,6 +387,22 @@ s2_check_request(sip_method_t method, char const *name)
|
|||
return m != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
s2_save_uas_dialog(struct dialog *d, sip_t *sip)
|
||||
{
|
||||
if (d && !d->local) {
|
||||
assert(sip->sip_request);
|
||||
d->local = sip_from_dup(d->home, sip->sip_to);
|
||||
if (d->local->a_tag == NULL)
|
||||
sip_from_tag(d->home, d->local, s2_generate_tag(d->home));
|
||||
d->remote = sip_to_dup(d->home, sip->sip_from);
|
||||
d->call_id = sip_call_id_dup(d->home, sip->sip_call_id);
|
||||
d->rseq = sip->sip_cseq->cs_seq;
|
||||
/* d->route = sip_route_dup(d->home, sip->sip_record_route); */
|
||||
d->target = sip_contact_dup(d->home, sip->sip_contact);
|
||||
}
|
||||
}
|
||||
|
||||
struct message *
|
||||
s2_respond_to(struct message *m, struct dialog *d,
|
||||
int status, char const *phrase,
|
||||
|
@ -386,6 +418,8 @@ s2_respond_to(struct message *m, struct dialog *d,
|
|||
assert(m); assert(m->msg); assert(m->tport);
|
||||
assert(100 <= status && status < 700);
|
||||
|
||||
s2_save_uas_dialog(d, m->sip);
|
||||
|
||||
ta_start(ta, tag, value);
|
||||
|
||||
reply = s2_msg(0); sip = sip_object(reply); home = msg_home(reply);
|
||||
|
@ -414,13 +448,7 @@ s2_respond_to(struct message *m, struct dialog *d,
|
|||
}
|
||||
}
|
||||
|
||||
if (d && !d->local) {
|
||||
d->local = sip_from_dup(d->home, sip->sip_to);
|
||||
d->remote = sip_to_dup(d->home, sip->sip_from);
|
||||
d->call_id = sip_call_id_dup(d->home, sip->sip_call_id);
|
||||
d->rseq = sip->sip_cseq->cs_seq;
|
||||
/* d->route = sip_route_dup(d->home, sip->sip_record_route); */
|
||||
d->target = sip_contact_dup(d->home, m->sip->sip_contact);
|
||||
if (d && !d->contact) {
|
||||
d->contact = sip_contact_dup(d->home, sip->sip_contact);
|
||||
}
|
||||
|
||||
|
@ -806,6 +834,9 @@ void s2_case(char const *number,
|
|||
char const *description)
|
||||
{
|
||||
_s2case = number;
|
||||
|
||||
if (s2_start_stop)
|
||||
printf("%s - starting %s %s\n", s2_tester, number, title);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
@ -875,10 +906,14 @@ tp_stack_class_t const s2_stack[1] =
|
|||
}};
|
||||
|
||||
/** Basic setup for test cases */
|
||||
void s2_setup_base(char const *hostname)
|
||||
void s2_setup_base(char const *label, char const *hostname)
|
||||
{
|
||||
assert(s2 == NULL);
|
||||
|
||||
if (s2_start_stop > 1) {
|
||||
printf("%s - setup %s test case\n", s2_tester, label ? label : "next");
|
||||
}
|
||||
|
||||
su_init();
|
||||
|
||||
s2 = su_home_new(sizeof *s2);
|
||||
|
@ -897,6 +932,7 @@ void s2_setup_base(char const *hostname)
|
|||
|
||||
s2->hostname = hostname;
|
||||
s2->tid = (unsigned long)time(NULL) * 510633671UL;
|
||||
|
||||
}
|
||||
|
||||
SOFIAPUBVAR su_log_t nua_log[];
|
||||
|
@ -1489,22 +1525,43 @@ void s2_dns_domain(char const *domain, int use_naptr,
|
|||
va_end(va0);
|
||||
}
|
||||
|
||||
static char const *s2_teardown_label = NULL;
|
||||
|
||||
void
|
||||
s2_teardown_started(char const *label)
|
||||
{
|
||||
if (!s2_teardown_label) {
|
||||
s2_teardown_label = label;
|
||||
if (s2_start_stop > 1) {
|
||||
printf("%s - tearing down %s test case\n", s2_tester, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
s2_teardown(void)
|
||||
{
|
||||
s2 = NULL;
|
||||
su_deinit();
|
||||
|
||||
if (s2_start_stop > 1) {
|
||||
printf("%s - %s test case tore down\n", s2_tester,
|
||||
s2_teardown_label ? s2_teardown_label : "previous");
|
||||
}
|
||||
|
||||
s2_teardown_label = NULL;
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
|
||||
#include <sofia-sip/sresolv.h>
|
||||
|
||||
nua_t *s2_nua_setup(tag_type_t tag, tag_value_t value, ...)
|
||||
nua_t *s2_nua_setup(char const *label,
|
||||
tag_type_t tag, tag_value_t value, ...)
|
||||
{
|
||||
ta_list ta;
|
||||
|
||||
s2_setup_base(NULL);
|
||||
s2_setup_base(label, NULL);
|
||||
s2_setup_dns();
|
||||
|
||||
s2_setup_logs(0);
|
||||
|
@ -1618,4 +1675,3 @@ void s2_register_teardown(void)
|
|||
s2->registration->nh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,8 @@ struct dialog
|
|||
msg_t *invite; /* latest invite sent */
|
||||
};
|
||||
|
||||
extern char const *s2_tester;
|
||||
extern int s2_start_stop;
|
||||
extern struct tester *s2;
|
||||
extern tp_stack_class_t const s2_stack[1];
|
||||
|
||||
|
@ -144,8 +146,12 @@ struct message *s2_next_request(void);
|
|||
struct message *s2_wait_for_request(sip_method_t method, char const *name);
|
||||
int s2_check_request(sip_method_t method, char const *name);
|
||||
|
||||
int s2_check_substate(struct event *e, enum nua_substate state);
|
||||
|
||||
#define SIP_METHOD_UNKNOWN sip_method_unknown, NULL
|
||||
|
||||
void s2_save_uas_dialog(struct dialog *d, sip_t *sip);
|
||||
|
||||
struct message *s2_respond_to(struct message *m, struct dialog *d,
|
||||
int status, char const *phrase,
|
||||
tag_type_t tag, tag_value_t value, ...);
|
||||
|
@ -161,15 +167,16 @@ int s2_save_register(struct message *m);
|
|||
|
||||
void s2_flush_all(void);
|
||||
|
||||
void s2_setup_base(char const *hostname);
|
||||
void s2_setup_base(char const *label, char const *hostname);
|
||||
void s2_setup_logs(int level);
|
||||
void s2_setup_tport(char const * const *protocols,
|
||||
tag_type_t tag, tag_value_t value, ...);
|
||||
void s2_teardown(void);
|
||||
|
||||
nua_t *s2_nua_setup(tag_type_t tag, tag_value_t value, ...);
|
||||
void s2_nua_teardown(void);
|
||||
nua_t *s2_nua_setup(char const *label, tag_type_t tag, tag_value_t value, ...);
|
||||
|
||||
void s2_teardown_started(char const *label);
|
||||
void s2_nua_teardown(void);
|
||||
void s2_register_setup(void);
|
||||
void s2_register_teardown(void);
|
||||
|
||||
|
|
Loading…
Reference in New Issue