From 57e5d6d2740b543631834151d3a903830e365e0b Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Mon, 5 Aug 2024 12:25:37 +0200 Subject: [PATCH] bevent: add core bevent API (#3090) * bevent: add new bevent API * test: rename test/event.c -> test/bevent.c * test: add bevent test * bevent: rename bevent_get_enum() to bevent_get_type() --- include/baresip.h | 23 ++ src/bevent.c | 513 +++++++++++++++++++++++++++++++++++++++++++- test/CMakeLists.txt | 2 +- test/bevent.c | 132 ++++++++++++ test/event.c | 55 ----- test/main.c | 3 +- test/test.h | 3 +- 7 files changed, 669 insertions(+), 62 deletions(-) create mode 100644 test/bevent.c delete mode 100644 test/event.c diff --git a/include/baresip.h b/include/baresip.h index 5e594306ac..5b223f4a17 100644 --- a/include/baresip.h +++ b/include/baresip.h @@ -869,10 +869,15 @@ enum ua_event { UA_EVENT_MODULE, UA_EVENT_END_OF_FILE, UA_EVENT_CUSTOM, + UA_EVENT_SIPSESS_CONN, UA_EVENT_MAX, }; + +struct bevent; + + /** SIP auto answer method */ enum answer_method { ANSM_NONE = 0, @@ -884,6 +889,7 @@ enum answer_method { /** Defines the User-Agent event handler */ typedef void (ua_event_h)(struct ua *ua, enum ua_event ev, struct call *call, const char *prm, void *arg); +typedef void (bevent_h)(enum ua_event ev, struct bevent *event, void *arg); typedef void (options_resp_h)(int err, const struct sip_msg *msg, void *arg); typedef void (refer_resp_h)(int err, const struct sip_msg *msg, void *arg); @@ -1657,14 +1663,31 @@ void module_app_unload(void); int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, struct call *call, const char *prm); +int odict_encode_bevent(struct odict *od, struct bevent *event); int event_add_au_jb_stat(struct odict *od_parent, const struct call *call); int uag_event_register(ua_event_h *eh, void *arg); void uag_event_unregister(ua_event_h *eh); +int bevent_register(bevent_h *eh, void *arg); +void bevent_unregister(bevent_h *eh); void ua_event(struct ua *ua, enum ua_event ev, struct call *call, const char *fmt, ...); +int bevent_app_emit(enum ua_event ev, void *arg, const char *fmt, ...); +int bevent_ua_emit(enum ua_event ev, struct ua *ua, const char *fmt, ...); +int bevent_call_emit(enum ua_event ev, struct call *call, + const char *fmt, ...); +int bevent_sip_msg_emit(enum ua_event ev, const struct sip_msg *msg, + const char *fmt, ...); void module_event(const char *module, const char *event, struct ua *ua, struct call *call, const char *fmt, ...); const char *uag_event_str(enum ua_event ev); +struct call *bevent_get_call(const struct bevent *event); +struct ua *bevent_get_ua(const struct bevent *event); +const struct sip_msg *bevent_get_msg(const struct bevent *event); +void *bevent_get_apparg(const struct bevent *event); +enum ua_event bevent_get_type(const struct bevent *event); +const char *bevent_get_text(const struct bevent *event); +void bevent_set_error(struct bevent *event, int err); +void bevent_stop(struct bevent *event); /* diff --git a/src/bevent.c b/src/bevent.c index d840fe7f13..22c45e1b34 100644 --- a/src/bevent.c +++ b/src/bevent.c @@ -1,5 +1,5 @@ /** - * @file src/event.c Baresip event handling + * @file src/bevent.c Baresip event handling * * Copyright (C) 2017 Alfred E. Heggestad */ @@ -14,6 +14,20 @@ enum { }; +struct bevent { + enum ua_event ev; + const char *txt; + int err; + bool stop; + union { + struct ua *ua; + struct call *call; + const struct sip_msg *msg; + void *arg; + } u; +}; + + struct ua_eh { struct le le; ua_event_h *h; @@ -32,7 +46,90 @@ static void eh_destructor(void *arg) } -static const char *event_class_name(enum ua_event ev) +struct ehe { + struct le le; + bevent_h *h; + void *arg; +}; + + +static struct list ehel; /**< Event handlers (struct ehe) */ + + +static void bevent_emit_base(struct bevent *event); + + +static void ehe_destructor(void *arg) +{ + struct ehe *ehe = arg; + + list_unlink(&ehe->le); +} + + +enum bevent_class { + BEVENT_CLASS_UA, + BEVENT_CLASS_CALL, + BEVENT_CLASS_APP, + BEVENT_CLASS_SIP, + BEVENT_CLASS_UNDEFINED +}; + + +static enum bevent_class bevent_class(enum ua_event ev) +{ + switch (ev) { + + case UA_EVENT_REGISTERING: + case UA_EVENT_REGISTER_OK: + case UA_EVENT_REGISTER_FAIL: + case UA_EVENT_UNREGISTERING: + case UA_EVENT_FALLBACK_OK: + case UA_EVENT_FALLBACK_FAIL: + case UA_EVENT_REFER: + case UA_EVENT_CREATE: + case UA_EVENT_MWI_NOTIFY: + case UA_EVENT_CUSTOM: + return BEVENT_CLASS_UA; + + case UA_EVENT_SHUTDOWN: + case UA_EVENT_EXIT: + return BEVENT_CLASS_APP; + + case UA_EVENT_CALL_INCOMING: + case UA_EVENT_CALL_OUTGOING: + case UA_EVENT_CALL_RINGING: + case UA_EVENT_CALL_PROGRESS: + case UA_EVENT_CALL_ANSWERED: + case UA_EVENT_CALL_ESTABLISHED: + case UA_EVENT_CALL_CLOSED: + case UA_EVENT_CALL_TRANSFER: + case UA_EVENT_CALL_TRANSFER_FAILED: + case UA_EVENT_CALL_REDIRECT: + case UA_EVENT_CALL_DTMF_START: + case UA_EVENT_CALL_DTMF_END: + case UA_EVENT_CALL_RTPESTAB: + case UA_EVENT_CALL_RTCP: + case UA_EVENT_CALL_MENC: + case UA_EVENT_CALL_LOCAL_SDP: + case UA_EVENT_CALL_REMOTE_SDP: + case UA_EVENT_CALL_HOLD: + case UA_EVENT_CALL_RESUME: + case UA_EVENT_AUDIO_ERROR: + case UA_EVENT_END_OF_FILE: + case UA_EVENT_VU_RX: + case UA_EVENT_VU_TX: + case UA_EVENT_MODULE: + return BEVENT_CLASS_CALL; + case UA_EVENT_SIPSESS_CONN: + return BEVENT_CLASS_SIP; + default: + return BEVENT_CLASS_UNDEFINED; + } +} + + +static const char *ua_event_class_name(enum ua_event ev) { switch (ev) { @@ -82,6 +179,176 @@ static const char *event_class_name(enum ua_event ev) } +static const char *bevent_class_name(enum ua_event ev) +{ + enum bevent_class ec = bevent_class(ev); + switch (ec) { + case BEVENT_CLASS_UA: + return "ua"; + + case BEVENT_CLASS_CALL: + return "call"; + + case BEVENT_CLASS_APP: + return "application"; + + case BEVENT_CLASS_SIP: + return "sip"; + + default: + return "other"; + } +} + + +/** + * Returns the call object of a call event, which was generated by function + * bevent_call_emit(). If the event was generated by another emit function + * then NULL is returned + * + * @param event Baresip event + * + * @return The call object, or NULL + */ +struct call *bevent_get_call(const struct bevent *event) +{ + if (!event) + return NULL; + + if (bevent_class(event->ev) == BEVENT_CLASS_CALL) + return event->u.call; + + return NULL; +} + + +/** + * Returns the User-Agent of a UA or call event, which was generated by + * function bevent_ua_emit() or function bevent_call_emit(). If the event was + * generated by another emit function then NULL is returned + * + * @param event Baresip event + * + * @return The User-Agent, or NULL + */ +struct ua *bevent_get_ua(const struct bevent *event) +{ + struct call *call; + + if (!event) + return NULL; + + call = bevent_get_call(event); + if (call) + return call_get_ua(call); + + if (bevent_class(event->ev) == BEVENT_CLASS_UA) + return event->u.ua; + + return NULL; +} + + +/** + * Returns the sip_msg of a SIP message event, which was generated by function + * bevent_sip_msg_emit(). If the event was generated by another emit function + * then NULL is returned + * + * @param event Baresip event + * + * @return The SIP message, or NULL + */ +const struct sip_msg *bevent_get_msg(const struct bevent *event) +{ + if (!event) + return NULL; + + if (bevent_class(event->ev) == BEVENT_CLASS_SIP) + return event->u.msg; + + return NULL; +} + + +/** + * Returns the argument of an application event, which was generated by + * function bevent_app_emit(). If the event was generated by another emit + * function then NULL is returned + * + * @param event Baresip event + * + * @return Pointer to the application argument + */ +void *bevent_get_apparg(const struct bevent *event) +{ + if (!event) + return NULL; + + if (bevent_class(event->ev) == BEVENT_CLASS_APP) + return event->u.arg; + + return NULL; +} + + +/** + * Returns the event type + * + * @param event Baresip event + * + * @return the event type + */ +enum ua_event bevent_get_type(const struct bevent *event) +{ + return event ? event->ev : UA_EVENT_MAX; +} + + +/** + * Returns the event text + * + * @param event Baresip event + * + * @return the event text + */ +const char *bevent_get_text(const struct bevent *event) +{ + return event ? event->txt : ""; +} + + +/** + * Set error code of given event. This should be used in the event handler to + * inform baresip that an error occurred. An event with set error is not passed + * to further event handlers + * + * @param event Baresip event + * @param err Error code + */ +void bevent_set_error(struct bevent *event, int err) +{ + if (!event) + return; + + event->err = err; +} + + +/** + * Stops event processing of given event. This should be used in an event + * handler to stop passing the event to further event handlers + * + * @param event Baresip event + */ +void bevent_stop(struct bevent *event) +{ + if (!event) + return; + + event->stop = true; +} + + static int add_rtcp_stats(struct odict *od_parent, const struct rtcp_stats *rs) { struct odict *od = NULL, *tx = NULL, *rx = NULL; @@ -151,8 +418,10 @@ int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, return EINVAL; err |= odict_entry_add(od, "type", ODICT_STRING, event_str); - err |= odict_entry_add(od, "class", - ODICT_STRING, event_class_name(ev)); + if (!odict_lookup(od, "class")) { + err |= odict_entry_add(od, "class", + ODICT_STRING, ua_event_class_name(ev)); + } if (ua) { err |= odict_entry_add(od, "accountaor", @@ -240,6 +509,9 @@ int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, if (ev == UA_EVENT_CALL_RTCP) { struct stream *strm = NULL; + if (!prm) + goto out; + if (0 == str_casecmp(prm, "audio")) strm = audio_strm(call_audio(call)); else if (0 == str_casecmp(prm, "video")) @@ -256,6 +528,25 @@ int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, } +int odict_encode_bevent(struct odict *od, struct bevent *event) +{ + struct ua *ua = bevent_get_ua(event); + struct call *call = bevent_get_call(event); + int err; + + if (!od) + return EINVAL; + + err = odict_entry_add(od, "class", + ODICT_STRING, bevent_class_name(event->ev)); + if (err) + return err; + + /* For now we re-use the deprecated function */ + return event_encode_dict(od, ua, event->ev, call, event->txt); +} + + /** * Add audio buffer status * @@ -324,6 +615,57 @@ void uag_event_unregister(ua_event_h *h) } +/** + * Register an Event handler + * + * @param eh Event handler + * @param arg Handler argument + * + * @return 0 if success, otherwise errorcode + */ +int bevent_register(bevent_h *eh, void *arg) +{ + struct ehe *ehe; + + if (!eh) + return EINVAL; + + bevent_unregister(eh); + + ehe = mem_zalloc(sizeof(*ehe), ehe_destructor); + if (!ehe) + return ENOMEM; + + ehe->h = eh; + ehe->arg = arg; + + list_append(&ehel, &ehe->le, ehe); + + return 0; +} + + +/** + * Unregister an Event handler + * + * @param eh Event handler + */ +void bevent_unregister(bevent_h *eh) +{ + struct le *le; + + for (le = ehel.head; le; le = le->next) { + + struct ehe *ehe = le->data; + + if (ehe->h == eh) { + mem_deref(ehe); + break; + } + } +} + + /** * Send a User-Agent event to all UA event handlers * @@ -410,6 +752,168 @@ void module_event(const char *module, const char *event, struct ua *ua, } +static void bevent_emit_base(struct bevent *event) +{ + struct le *le; + le = ehel.head; + while (le) { + struct ehe *ehe = le->data; + le = le->next; + + ehe->h(event->ev, event, ehe->arg); + + if (event->stop) + return; + } +} + + +static int bevent_emit(struct bevent *event, const char *fmt, va_list ap) +{ + char *buf; + int err; + + if (!fmt) + err = re_sdprintf(&buf, ""); + else + err = re_vsdprintf(&buf, fmt, ap); + if (err) + return err; + + event->txt = buf; + event->err = 0; + bevent_emit_base(event); + if (event->err) { + err = event->err; + goto out; + } + + if (event->stop) + goto out; + +out: + mem_deref(buf); + return err; +} + + +/** + * Emit an application event + * + * @param ev User-agent event + * @param arg Application specific argument (optional) + * @param fmt Formatted arguments + * @param ... Variable arguments + * + * @return 0 if success, otherwise errorcode + */ +int bevent_app_emit(enum ua_event ev, void *arg, const char *fmt, ...) +{ + va_list ap; + struct bevent event = {.ev = ev}; + int err; + + if (bevent_class(ev) != BEVENT_CLASS_APP) + return EINVAL; + + event.u.arg = arg; + + va_start(ap, fmt); + err = bevent_emit(&event, fmt, ap); + va_end(ap); + + return err; +} + + +/** + * Emit a User-Agent event + * + * @param ev User-Agent event + * @param ua User-Agent + * @param fmt Formatted arguments + * @param ... Variable arguments + * + * @return 0 if success, otherwise errorcode + */ +int bevent_ua_emit(enum ua_event ev, struct ua *ua, const char *fmt, ...) +{ + struct bevent event = {.ev = ev}; + va_list ap; + int err; + + if (bevent_class(ev) != BEVENT_CLASS_UA) + return EINVAL; + + event.u.ua = ua; + + va_start(ap, fmt); + err = bevent_emit(&event, fmt, ap); + va_end(ap); + + return err; +} + + +/** + * Emit a Call event + * + * @param ev User-Agent event + * @param call Call object + * @param fmt Formatted arguments + * @param ... Variable arguments + * + * @return 0 if success, otherwise errorcode + */ +int bevent_call_emit(enum ua_event ev, struct call *call, const char *fmt, ...) +{ + struct bevent event = {.ev = ev}; + va_list ap; + int err; + + if (bevent_class(ev) != BEVENT_CLASS_CALL) + return EINVAL; + + event.u.call = call; + + va_start(ap, fmt); + err = bevent_emit(&event, fmt, ap); + va_end(ap); + + return err; +} + + +/** + * Emit a SIP message event + * + * @param ev User-Agent event + * @param msg SIP message + * @param fmt Formatted arguments + * @param ... Variable arguments + * + * @return 0 if success, otherwise errorcode + */ +int bevent_sip_msg_emit(enum ua_event ev, const struct sip_msg *msg, + const char *fmt, ...) +{ + struct bevent event = {.ev = ev}; + va_list ap; + int err; + + if (bevent_class(ev) != BEVENT_CLASS_SIP) + return EINVAL; + + event.u.msg = msg; + + va_start(ap, fmt); + err = bevent_emit(&event, fmt, ap); + va_end(ap); + + return err; +} + + /** * Get the name of the User-Agent event * @@ -431,6 +935,7 @@ const char *uag_event_str(enum ua_event ev) case UA_EVENT_CREATE: return "CREATE"; case UA_EVENT_SHUTDOWN: return "SHUTDOWN"; case UA_EVENT_EXIT: return "EXIT"; + case UA_EVENT_SIPSESS_CONN: return "SIPSESS_CONN"; case UA_EVENT_CALL_INCOMING: return "CALL_INCOMING"; case UA_EVENT_CALL_OUTGOING: return "CALL_OUTGOING"; case UA_EVENT_CALL_RINGING: return "CALL_RINGING"; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8c0a53bf93..d44fa51ed2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,7 +13,7 @@ add_executable(${PROJECT_NAME} call.c cmd.c contact.c - event.c + bevent.c jbuf.c menu.c message.c diff --git a/test/bevent.c b/test/bevent.c new file mode 100644 index 0000000000..b35d648632 --- /dev/null +++ b/test/bevent.c @@ -0,0 +1,132 @@ +/** + * @file test/bevent.c Baresip selftest -- event handling + * + * Copyright (C) 2010 Alfred E. Heggestad + */ +#include +#include +#include +#include "test.h" + + +struct fixture { + int cnt; + + enum ua_event expected_event; +}; + + +int test_event_encode(void) +{ + struct odict *od = NULL; + size_t i; + int err = 0; + + static const enum ua_event eventv[] = { + UA_EVENT_REGISTERING, + UA_EVENT_REGISTER_OK, + UA_EVENT_REGISTER_FAIL, + UA_EVENT_UNREGISTERING, + UA_EVENT_SHUTDOWN, + UA_EVENT_EXIT + /* .. more events .. */ + }; + + for (i=0; i= 2); + + /* verify the mandatory entries */ + entry = odict_lookup(od, "type"); + ASSERT_TRUE(entry != NULL); + ASSERT_EQ(ODICT_STRING, odict_entry_type(entry)); + ASSERT_STREQ(uag_event_str(ev), odict_entry_str(entry)); + + od = mem_deref(od); + } + + out: + mem_deref(od); + + return err; +} + + +static void event_handler(enum ua_event ev, struct bevent *event, void *arg) +{ + struct fixture *f = arg; + void *apparg = bevent_get_apparg(event); + struct ua *ua = bevent_get_ua(event); + struct call *call = bevent_get_call(event); + const struct sip_msg *msg = bevent_get_msg(event); + (void)ev; + + if (apparg && apparg != (void *) 0xdeadbeef) { + bevent_set_error(event, EINVAL); + } + else if (ua && ua != (void *) 0xdeadbeef) { + bevent_set_error(event, EINVAL); + } + else if (call && call != (void *) 0xdeadbeef) { + bevent_set_error(event, EINVAL); + } + else if (msg && msg != (void *) 0xdeadbeef) { + bevent_set_error(event, EINVAL); + } + else if (f->expected_event != bevent_get_type(event)) { + bevent_set_error(event, EINVAL); + } + else + ++f->cnt; +} + + +int test_event_register(void) +{ + int err = 0; + struct fixture f; + + memset(&f, 0, sizeof(f)); + + err = bevent_register(event_handler, &f); + TEST_ERR(err); + + f.expected_event = UA_EVENT_EXIT; + err = bevent_app_emit(UA_EVENT_EXIT, (void *) 0xdeadbeef, "%s", + "details"); + TEST_ERR(err); + + err = bevent_app_emit(UA_EVENT_SHUTDOWN, (void *) 0xdeadbeef, "%s", + "details"); + ASSERT_EQ(EINVAL, err); + + f.expected_event = UA_EVENT_REGISTER_OK; + err = bevent_ua_emit(UA_EVENT_REGISTER_OK, (struct ua *) 0xdeadbeef, + NULL); + TEST_ERR(err); + + f.expected_event = UA_EVENT_CALL_INCOMING; + err = bevent_call_emit(UA_EVENT_CALL_INCOMING, NULL, NULL); + TEST_ERR(err); + + f.expected_event = UA_EVENT_SIPSESS_CONN; + err = bevent_sip_msg_emit(UA_EVENT_SIPSESS_CONN, + (struct sip_msg *) 0xdeadbeef, NULL); + TEST_ERR(err); + + + ASSERT_EQ(4, f.cnt); +out: + bevent_unregister(event_handler); + return err; +} diff --git a/test/event.c b/test/event.c deleted file mode 100644 index d7b99a7967..0000000000 --- a/test/event.c +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @file test/event.c Baresip selftest -- event handling - * - * Copyright (C) 2010 Alfred E. Heggestad - */ -#include -#include -#include -#include "test.h" - - -int test_event(void) -{ - struct odict *od = NULL; - size_t i; - int err = 0; - - static const enum ua_event eventv[] = { - UA_EVENT_REGISTERING, - UA_EVENT_REGISTER_OK, - UA_EVENT_REGISTER_FAIL, - UA_EVENT_UNREGISTERING, - UA_EVENT_SHUTDOWN, - UA_EVENT_EXIT - /* .. more events .. */ - }; - - for (i=0; i= 2); - - /* verify the mandatory entries */ - entry = odict_lookup(od, "type"); - ASSERT_TRUE(entry != NULL); - ASSERT_EQ(ODICT_STRING, odict_entry_type(entry)); - ASSERT_STREQ(uag_event_str(ev), odict_entry_str(entry)); - - od = mem_deref(od); - } - - out: - mem_deref(od); - - return err; -} diff --git a/test/main.c b/test/main.c index c47a349089..223c4fa3ef 100644 --- a/test/main.c +++ b/test/main.c @@ -59,7 +59,8 @@ static const struct test tests[] = { TEST(test_cmd), TEST(test_cmd_long), TEST(test_contact), - TEST(test_event), + TEST(test_event_encode), + TEST(test_event_register), TEST(test_jbuf), TEST(test_jbuf_adaptive), TEST(test_jbuf_adaptive_video), diff --git a/test/test.h b/test/test.h index 0fc4fcdd4f..fbb8b7276e 100644 --- a/test/test.h +++ b/test/test.h @@ -233,7 +233,8 @@ int test_call_sni(void); int test_cmd(void); int test_cmd_long(void); int test_contact(void); -int test_event(void); +int test_event_encode(void); +int test_event_register(void); int test_jbuf(void); int test_jbuf_adaptive(void); int test_jbuf_adaptive_video(void);