<!-- Kamailio Pull Request Template -->
<!-- IMPORTANT: - for detailed contributing guidelines, read: https://github.com/kamailio/kamailio/blob/master/.github/CONTRIBUTING.md - pull requests must be done to master branch, unless they are backports of fixes from master branch to a stable branch - backports to stable branches must be done with 'git cherry-pick -x ...' - code is contributed under BSD for core and main components (tm, sl, auth, tls) - code is contributed GPLv2 or a compatible license for the other components - GPL code is contributed with OpenSSL licensing exception -->
#### Pre-Submission Checklist <!-- Go over all points below, and after creating the PR, tick all the checkboxes that apply --> <!-- All points should be verified, otherwise, read the CONTRIBUTING guidelines from above--> <!-- If you're unsure about any of these, don't hesitate to ask on sr-dev mailing list --> - [ ] Commit message has the format required by CONTRIBUTING guide - [ ] Commits are split per component (core, individual modules, libs, utils, ...) - [ ] Each component has a single commit (if not, squash them into one commit) - [ ] No commits to README files for modules (changes must be done to docbook files in `doc/` subfolder, the README file is autogenerated)
#### Type Of Change - [ ] Small bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds new functionality) - [ ] Breaking change (fix or feature that would change existing functionality)
#### Checklist: <!-- Go over all points below, and after creating the PR, tick the checkboxes that apply --> - [ ] PR should be backported to stable branches - [ ] Tested changes locally - [ ] Related to issue #XXXX (replace XXXX with an open issue number)
#### Description <!-- Describe your changes in detail -->
You can view, comment on, or merge this pull request online at:
https://github.com/kamailio/kamailio/pull/3473
-- Commit Summary --
* rtpengine: support receiving dtmf events from rtpengine and raise an event
-- File Changes --
M src/modules/rtpengine/doc/rtpengine.xml (7) M src/modules/rtpengine/doc/rtpengine_admin.xml (117) M src/modules/rtpengine/rtpengine.c (403)
-- Patch Links --
https://github.com/kamailio/kamailio/pull/3473.patch https://github.com/kamailio/kamailio/pull/3473.diff
Hi,
Anything else is needed from my end or is it just waiting for spare time?
@rfuchs may want to check it.
@rfuchs commented on this pull request.
A bit unsure about how this is supposed to work, maybe I'm just dense?
-static int fixup_set_id(void **param, int param_no) +static void rtpengine_dtmf_events_loop(int rank)
The `rank` argument seems unused?
@@ -2147,6 +2401,9 @@ static int child_init(int rank)
/* probe rtpengines only in first worker */ if(build_rtpp_socks(0, 1)) return -1; + + if (rtpengine_dtmf_event_sock.len > 0) + rtpengine_dtmf_events_loop(rank);
This enters into an infinite read loop during child_init(). Doesn't this block the startup of all child processes?
@miconda commented on this pull request.
@@ -2147,6 +2401,9 @@ static int child_init(int rank)
/* probe rtpengines only in first worker */ if(build_rtpp_socks(0, 1)) return -1; + + if (rtpengine_dtmf_event_sock.len > 0) + rtpengine_dtmf_events_loop(rank);
It seems to block the child process with rank 1 (PROC_SIPINIT), which should not be done, because it can create problems for other modules such as usrloc.
A new process has to be started -- in mod init, it has to be registered with:
``` register_procs(1); cfg_register_child(1); ```
And in child init created with fork_process(), something like:
``` if(rank == PROC_MAIN) { /* fork worker process */ newpid = fork_process(PROC_RPC, "RTPENGINE DTMF WORKER", 1); if(newpid < 0) { LM_ERR("failed to fork worker process %d\n", i); return -1; } else if(newpid == 0) { if(cfg_child_init()) return -1; /* child - this will loop forever */ rtpengine_dtmf_events_loop(); } else { /* parent process*/ } return 0; } ```
@joeygo commented on this pull request.
-static int fixup_set_id(void **param, int param_no) +static void rtpengine_dtmf_events_loop(int rank)
You are right. I removed it.
@joeygo commented on this pull request.
@@ -2147,6 +2401,9 @@ static int child_init(int rank)
/* probe rtpengines only in first worker */ if(build_rtpp_socks(0, 1)) return -1; + + if (rtpengine_dtmf_event_sock.len > 0) + rtpengine_dtmf_events_loop(rank);
Thanks for the example. I fixed the code.
@joeygo pushed 1 commit.
7c2d64ffa57bb8ebe6ce46311ca98a100b09a997 Register a new worker process for dtmf event listener
@rfuchs commented on this pull request.
- s_port.len = rtpengine_dtmf_event_sock.s + rtpengine_dtmf_event_sock.len - s_port.s;
+ + if (s_port.len <= 0 || str2int(&s_port, &port) < 0 || port > 65535) { + LM_ERR("failed to initialize dtmf event listener because port is invalid %.*s\n", rtpengine_dtmf_event_sock.len, rtpengine_dtmf_event_sock.s); + return; + } + rtpengine_dtmf_event_sock.len -= s_port.len + 1; + trim(&rtpengine_dtmf_event_sock); + rtpengine_dtmf_event_sock.s[rtpengine_dtmf_event_sock.len] = '\0'; + + memset(&udp_addr, 0, sizeof(udp_addr)); + + if (rtpengine_dtmf_event_sock.s[0] == '[') { + udp_addr.sin6.sin6_family = AF_INET6; + udp_addr.sin6.sin6_port = htons(port); + ret = inet_pton(AF_INET, rtpengine_dtmf_event_sock.s, &udp_addr.sin6.sin6_addr);
Must be AF_INET6 here
ret = inet_pton(AF_INET, rtpengine_dtmf_event_sock.s, &udp_addr.sin.sin_addr);
+ } + + if (ret != 1) { + LM_ERR("failed to initialize dtmf event listener because address could not be created for %s\n", rtpengine_dtmf_event_sock.s); + return; + } + + rtpengine_dtmf_event_fd = socket(udp_addr.s.sa_family, SOCK_DGRAM, 0); + + if(rtpengine_dtmf_event_fd < 0) { + LM_ERR("can't create socket\n"); + return; + } + + if (bind(rtpengine_dtmf_event_fd, (struct sockaddr*)&udp_addr.s, sizeof(udp_addr.s)) < 0) {
sizeof(udp_addr.s) is sizeof(struct sockaddr) which I think may be too small for INET6. sizeof(udp_addr) would be a better option
}
+ + if (dtmf_event_rt == -1) { + LM_NOTICE("nothing to do - nobody is listening!\n"); + goto end; + } + + p = shm_malloc(ret + 1); + if (!p) { + LM_ERR("could not allocate %d for buffer %.*s\n", ret, ret, buffer); + goto end; + } + memcpy(p, buffer, ret); + p[ret] = '\0'; + + if (rtpengine_raise_dtmf_event(p, ret) < 0) {
I think this leaks the `p` buffer if the return value == 0
+end: + close(rtpengine_dtmf_event_fd); +} + +static int rtpengine_raise_dtmf_event(char *buffer, int len) { + srjson_doc_t jdoc; + srjson_t *it = NULL; + struct sip_msg *fmsg = NULL; + struct run_act_ctx ctx; + int rtb; + + LM_DBG("executing event_route[rtpengine:dtmf-event] (%d)\n", dtmf_event_rt); + LM_DBG("dispatching buffer: %s\n", buffer); + + srjson_InitDoc(&jdoc, NULL);
jdoc needs to be freed somewhere
@@ -261,7 +262,7 @@ static int add_rtpp_node_info(
void *ptrs, struct rtpp_node *crt_rtpp, struct rtpp_set *rtpp_list); static int rtpp_test_ping(struct rtpp_node *node);
-static void rtpengine_dtmf_events_loop(int rank); +static void rtpengine_dtmf_events_loop();
nit: for better standards compliance, be explicit with `(void)`
- }
+ + if(rank == PROC_MAIN) { + if(rtpengine_dtmf_event_sock.len > 0) { + LM_DBG("Register RTPENGINE DTMF WORKER %d\n", mypid); + /* fork worker process */ + mypid = fork_process(PROC_RPC, "RTPENGINE DTMF WORKER", 1); + if(mypid < 0) { + LM_ERR("failed to fork RTPENGINE DTMF WORKER process %d\n", mypid); + return -1; + } else if(mypid == 0) { + if(cfg_child_init()) + return -1; + /* this will loop forever */ + rtpengine_dtmf_events_loop(); + }
I suppose there should be an exit() at the end here? Although I'm not too familiar with how the Kamailio internals are handling this
@joeygo commented on this pull request.
- s_port.len = rtpengine_dtmf_event_sock.s + rtpengine_dtmf_event_sock.len - s_port.s;
+ + if (s_port.len <= 0 || str2int(&s_port, &port) < 0 || port > 65535) { + LM_ERR("failed to initialize dtmf event listener because port is invalid %.*s\n", rtpengine_dtmf_event_sock.len, rtpengine_dtmf_event_sock.s); + return; + } + rtpengine_dtmf_event_sock.len -= s_port.len + 1; + trim(&rtpengine_dtmf_event_sock); + rtpengine_dtmf_event_sock.s[rtpengine_dtmf_event_sock.len] = '\0'; + + memset(&udp_addr, 0, sizeof(udp_addr)); + + if (rtpengine_dtmf_event_sock.s[0] == '[') { + udp_addr.sin6.sin6_family = AF_INET6; + udp_addr.sin6.sin6_port = htons(port); + ret = inet_pton(AF_INET, rtpengine_dtmf_event_sock.s, &udp_addr.sin6.sin6_addr);
Fixed.
@joeygo commented on this pull request.
}
+ + if (dtmf_event_rt == -1) { + LM_NOTICE("nothing to do - nobody is listening!\n"); + goto end; + } + + p = shm_malloc(ret + 1); + if (!p) { + LM_ERR("could not allocate %d for buffer %.*s\n", ret, ret, buffer); + goto end; + } + memcpy(p, buffer, ret); + p[ret] = '\0'; + + if (rtpengine_raise_dtmf_event(p, ret) < 0) {
Fixed.
@joeygo commented on this pull request.
+end: + close(rtpengine_dtmf_event_fd); +} + +static int rtpengine_raise_dtmf_event(char *buffer, int len) { + srjson_doc_t jdoc; + srjson_t *it = NULL; + struct sip_msg *fmsg = NULL; + struct run_act_ctx ctx; + int rtb; + + LM_DBG("executing event_route[rtpengine:dtmf-event] (%d)\n", dtmf_event_rt); + LM_DBG("dispatching buffer: %s\n", buffer); + + srjson_InitDoc(&jdoc, NULL);
Fixed.
@joeygo commented on this pull request.
@@ -261,7 +262,7 @@ static int add_rtpp_node_info(
void *ptrs, struct rtpp_node *crt_rtpp, struct rtpp_set *rtpp_list); static int rtpp_test_ping(struct rtpp_node *node);
-static void rtpengine_dtmf_events_loop(int rank); +static void rtpengine_dtmf_events_loop();
Fixed.
@joeygo commented on this pull request.
- }
+ + if(rank == PROC_MAIN) { + if(rtpengine_dtmf_event_sock.len > 0) { + LM_DBG("Register RTPENGINE DTMF WORKER %d\n", mypid); + /* fork worker process */ + mypid = fork_process(PROC_RPC, "RTPENGINE DTMF WORKER", 1); + if(mypid < 0) { + LM_ERR("failed to fork RTPENGINE DTMF WORKER process %d\n", mypid); + return -1; + } else if(mypid == 0) { + if(cfg_child_init()) + return -1; + /* this will loop forever */ + rtpengine_dtmf_events_loop(); + }
Maybe @miconda can comment on that?
@miconda commented on this pull request.
- }
+ + if(rank == PROC_MAIN) { + if(rtpengine_dtmf_event_sock.len > 0) { + LM_DBG("Register RTPENGINE DTMF WORKER %d\n", mypid); + /* fork worker process */ + mypid = fork_process(PROC_RPC, "RTPENGINE DTMF WORKER", 1); + if(mypid < 0) { + LM_ERR("failed to fork RTPENGINE DTMF WORKER process %d\n", mypid); + return -1; + } else if(mypid == 0) { + if(cfg_child_init()) + return -1; + /* this will loop forever */ + rtpengine_dtmf_events_loop(); + }
I think we do not have explicitly `exit` after the functions doing infinite loops in the special child process. But it could be a good practice to kill the process (and by that kamailio instance) if the function that should do infinite loop exits due to some errors before it enters the infinite loop.
@joeygo pushed 1 commit.
2ddab46263db2ae94038e672c79faaa769d9c306 rtpengine: Fix rfuchs's PR comments
@joeygo pushed 1 commit.
3df99a84a33d1dcdb064f96e6edd36b75f9e7166 rtpengine: fix build failed on specific distribution
@rfuchs commented on this pull request.
+ fmsg = faked_msg_next(); + rtb = get_route_type(); + set_route_type(REQUEST_ROUTE); + init_run_actions_ctx(&ctx); + run_top_route(event_rt.rlist[dtmf_event_rt], fmsg, &ctx); + set_route_type(rtb); + if(ctx.run_flags & DROP_R_F) { + LM_ERR("exit due to 'drop' in event route\n"); + goto error; + } + + return 0; + +error: + srjson_DestroyDoc(&jdoc);
This also needs to be done on success, no?
@joeygo commented on this pull request.
+ fmsg = faked_msg_next(); + rtb = get_route_type(); + set_route_type(REQUEST_ROUTE); + init_run_actions_ctx(&ctx); + run_top_route(event_rt.rlist[dtmf_event_rt], fmsg, &ctx); + set_route_type(rtb); + if(ctx.run_flags & DROP_R_F) { + LM_ERR("exit due to 'drop' in event route\n"); + goto error; + } + + return 0; + +error: + srjson_DestroyDoc(&jdoc);
You are right. Fixed.
@joeygo pushed 1 commit.
9d563b820b82bdda49e8c4ad5ef0e82e09d8d535 rtpengine: release json document object
@joeygo commented on this pull request.
- }
+ + if(rank == PROC_MAIN) { + if(rtpengine_dtmf_event_sock.len > 0) { + LM_DBG("Register RTPENGINE DTMF WORKER %d\n", mypid); + /* fork worker process */ + mypid = fork_process(PROC_RPC, "RTPENGINE DTMF WORKER", 1); + if(mypid < 0) { + LM_ERR("failed to fork RTPENGINE DTMF WORKER process %d\n", mypid); + return -1; + } else if(mypid == 0) { + if(cfg_child_init()) + return -1; + /* this will loop forever */ + rtpengine_dtmf_events_loop(); + }
I don't think it is right to kill the entire kamailio process due to an exit in such function. I print error logs in case of failure in the function. isn't it enough? If you prefer that I'll kill the process just let me know and I'll do that.
LGTM now
Hi @miconda, Is there anything else I should do or join wait for someone to merge the PR?
Thanks
Merged #3473 into master.
Done, thanks!
Hi @miconda, I was wondering, if I wish to back-port this to 5.7.1, what is the right way of doing so?
Thanks, Joey.
This is a new feature, the stable branches can get backports only for bug fixes or documentation improvements.