diff --git a/README.md b/README.md index 82cc1cb87..759ddd922 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ option and which are reproduced below: -G, --graphite-interval=INT Graphite data statistics send interval --graphite-prefix=STRING Graphite prefix for every line -T, --tos=INT TOS value to set on streams + --control-tos=INT TOS value to set on control-ng interface -o, --timeout=SECS RTP timeout -s, --silent-timeout=SECS RTP timeout for muted -a, --final-timeout=SECS Call timeout @@ -192,6 +193,7 @@ option and which are reproduced below: --log-facility=daemon|local0|... Syslog facility to use for logging --log-facility-cdr=local0|... Syslog facility to use for logging CDRs --log-facility-rtcp=local0|... Syslog facility to use for logging RTCP data (take care of traffic amount) + --log-format=default|parsable Log prefix format -E, --log-stderr Log on stderr instead of syslog -x, --xmlrpc-format=INT XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only --num-threads=INT Number of worker threads to create @@ -217,6 +219,24 @@ The options are described in more detail below. If called with this option, the *rtpengine* daemon will simply print its version number and exit. +* --config-file + + Specifies the location of a config file to be used. The config file is an *.ini* style config file, + with all command-line options listed here also being valid options in the config file. For all + command-line options, the long name version instead of the single-character version + (e.g. `table` instead of just `t`) must be used in the config file. For boolean options that are + either present or not (e.g. `no-fallback`), a boolean value (either `true` or `false`) must be + used in the config file. If an option is given in both the config file and at the command line, + the command-line value overrides the value from the config file. Options that can be specified + multiple times on the command line must be given only once in the config file, with the multiple + values separated by semicolons (see section *Interfaces configuration* below for an example). + +* --config-section + + Specifies the *.ini* style section to be used in the config file. Multiple sections can be + present in the config file, but only one can be used at a time. The default value is `rtpengine`. + A config file section is started in the config file using square brackets (e.g. `[rtpengine]`). + * -t, --table Takes an integer argument and specifies which kernel table to use for in-kernel packet forwarding. See @@ -231,46 +251,7 @@ The options are described in more detail below. * -i, --interface Specifies a local network interface for RTP. At least one must be given, but multiple can be specified. - The format of the value is `[NAME/]IP[!IP]` with `IP` being either an IPv4 address or an IPv6 address. - - The second IP address after the exclamation point is optional and can be used if the address to advertise - in outgoing SDP bodies should be different from the actual local address. This can be useful in certain - cases, such as your SIP proxy being behind NAT. For example, `--interface=10.65.76.2!192.0.2.4` means - that 10.65.76.2 is the actual local address on the server, but outgoing SDP bodies should advertise - 192.0.2.4 as the address that endpoints should talk to. Note that you may have to escape the exlamation - point from your shell, e.g. using `\!`. - - Giving an interface a name (separated from the address by a slash) is optional; if omitted, the name - `default` is used. Names are useful to create logical interfaces which consist of one or more local - addresses. It is then possible to instruct *rtpengine* to use particular interfaces when processing - an SDP message, to use different local addresses when talking to different endpoints. The most common use - case for this is to bridge between one or more private IP networks and the public internet. - - For example, if clients coming from a private IP network must communicate their RTP with the local - address 10.35.2.75, while clients coming from the public internet must communicate with your other - local address 192.0.2.67, you could create one logical interface `pub` and a second one `priv` by - using `--interface=pub/192.0.2.67 --interface=priv/10.35.2.75`. You can then use the `direction` - option to tell *rtpengine* which local address to use for which endpoints (either `pub` or `priv`). - - If multiple logical interfaces are configured, but the `direction` option isn't given in a - particular call, then the first interface given on the command line will be used. - - It is possible to specify multiple addresses for the same logical interface (the same name). Most - commonly this would be one IPv4 addrsess and one IPv6 address, for example: - `--interface=192.168.63.1 --interface=fe80::800:27ff:fe00:0`. In this example, no interface name - is given, therefore both addresses will be added to a logical interface named `default`. You would use - the `address family` option to tell *rtpengine* which address to use in a particular case. - - It is also possible to have multiple addresses of the same family in a logical network interface. In - this case, the first address (of a particular family) given for an interface will be the primary address - used by *rtpengine* for most purposes. Any additional addresses will be advertised as additional ICE - candidates with increasingly lower priority. This is useful on multi-homed systems and allows endpoints - to choose the best possible path to reach the RTP proxy. If ICE is not being used, then additional - addresses will go unused. - - If you're not using the NG protocol but rather the legacy UDP protocol used by the *rtpproxy* module, - the interfaces must be named `internal` and `external` corresponding to the `i` and `e` flags if you - wish to use network bridging in this mode. + See the section *Interfaces configuration* just below for details. * -l, --listen-tcp, -u, --listen-udp, -n, --listen-ng @@ -312,6 +293,11 @@ The options are described in more detail below. Takes an integer as argument and if given, specifies the TOS value that should be set in outgoing packets. The default is to leave the TOS field untouched. A typical value is 184 (*Expedited Forwarding*). +* --control-tos + + Takes an integer as argument and if given, specifies the TOS value that should be set in the control-ng + interface packets. The default is to leave the TOS field untouched. + * -o, --timeout Takes the number of seconds as argument after which a media stream should be considered dead if no media @@ -364,6 +350,13 @@ The options are described in more detail below. Same as --log-facility with the difference that only RTCP data is written to this log facility. Be careful with this parameter since there may be a lot of information written to it. +* --log-format=default|parsable + + Selects between multiple log output styles. The default is to prefix log lines with a description + of the relevant entity, such as `[CALLID]` or `[CALLID port 12345]`. The `parsable` output style + is similar, but makes the ID easier to parse by enclosing it in quotes, such as `[ID="CALLID"]` + or `[ID="CALLID" port="12345"]`. + * -E, --log-stderr Log to stderr instead of syslog. Only useful in combination with `--foreground`. @@ -611,6 +604,80 @@ A typical command line (enabling both UDP and NG protocols) thus may look like: --listen-udp=127.0.0.1:22222 --listen-ng=127.0.0.1:2223 --tos=184 \ --pidfile=/var/run/rtpengine.pid + +Interfaces configuration +------------------------ + +The command-line options `-i` or `--interface=`, or equivalently the `interface=` config file option, +specify local network interfaces for RTP. At least one must be given, but multiple can be specified. +The format of the value is `[NAME/]IP[!IP]` with `IP` being either an IPv4 address or an IPv6 address. + +To configure multiple interfaces using the command-line options, simply present multiple `-i` or +`--interface=` options. When using the config file, only use a single `interface=` line, but specify +multiple values separated by semicolons (e.g. `interface = internal/12.23.34.45;external/23.34.45.54`). + +The second IP address after the exclamation point is optional and can be used if the address to advertise +in outgoing SDP bodies should be different from the actual local address. This can be useful in certain +cases, such as your SIP proxy being behind NAT. For example, `--interface=10.65.76.2!192.0.2.4` means +that 10.65.76.2 is the actual local address on the server, but outgoing SDP bodies should advertise +192.0.2.4 as the address that endpoints should talk to. Note that you may have to escape the exlamation +point from your shell when using command-line options, e.g. using `\!`. + +Giving an interface a name (separated from the address by a slash) is optional; if omitted, the name +`default` is used. Names are useful to create logical interfaces which consist of one or more local +addresses. It is then possible to instruct *rtpengine* to use particular interfaces when processing +an SDP message, to use different local addresses when talking to different endpoints. The most common use +case for this is to bridge between one or more private IP networks and the public internet. + +For example, if clients coming from a private IP network must communicate their RTP with the local +address 10.35.2.75, while clients coming from the public internet must communicate with your other +local address 192.0.2.67, you could create one logical interface `pub` and a second one `priv` by +using `--interface=pub/192.0.2.67 --interface=priv/10.35.2.75`. You can then use the `direction` +option to tell *rtpengine* which local address to use for which endpoints (either `pub` or `priv`). + +If multiple logical interfaces are configured, but the `direction` option isn't given in a +particular call, then the first interface given on the command line will be used. + +It is possible to specify multiple addresses for the same logical interface (the same name). Most +commonly this would be one IPv4 addrsess and one IPv6 address, for example: +`--interface=192.168.63.1 --interface=fe80::800:27ff:fe00:0`. In this example, no interface name +is given, therefore both addresses will be added to a logical interface named `default`. You would use +the `address family` option to tell *rtpengine* which address to use in a particular case. + +It is also possible to have multiple addresses of the same family in a logical network interface. In +this case, the first address (of a particular family) given for an interface will be the primary address +used by *rtpengine* for most purposes. Any additional addresses will be advertised as additional ICE +candidates with increasingly lower priority. This is useful on multi-homed systems and allows endpoints +to choose the best possible path to reach the RTP proxy. If ICE is not being used, then additional +addresses will go unused, even though ports would still get allocated on those interfaces. + +Another option is to give interface names in the format `BASE:SUFFIX`. This allows interfaces to be +used in a round-robin fashion, useful for load-balancing the port ranges of multiple interfaces. +For example, consider the following configuration: +`--interface=pub:1/192.0.2.67 --interface=pub:2/10.35.2.75`. These two interfaces can still be +referenced directly by name (e.g. `direction=pub:1`), but it is now also possible to reference only +the base name (i.e. `direction=pub`). If the base name is used, one of the two interfaces is selected +in a round-robin fashion, and only if the interface actually has enough open ports available. This +makes it possible to effectively increase the number of available media ports across multiple IP +addresses. There is no limit on how many interfaces can share the same base name. + +It is possible to combine the `BASE:SUFFIX` notation with specifying multiple addresses for the same +interface name. An advanced example could be (using config file notation, and omitting actual +network addresses): + + interface = pub:1/IPv4 pub:1/IPv4 pub:1/IPv6 pub:2/IPv4 pub:2/IPv6 pub:3/IPv6 pub:4/IPv4 + +In this example, when `direction=pub` is IPv4 is needed as a primary address, either `pub:1`, `pub:2`, +or `pub:4` might be selected. When `pub:1` is selected, one IPv4 and one IPv6 address will be used +as additional ICE alternatives. For `pub:2`, only one IPv6 is used as ICE alternative, and for `pub:4` +no alternatives would be used. When IPv6 is needed as a primary address, either `pub:1`, `pub:2`, or +`pub:3` might be selected. If at any given time not enough ports are available on any interface, +it will not be selected by the round-robin algorithm. + +If you're not using the NG protocol but rather the legacy UDP protocol used by the *rtpproxy* module, +the interfaces must be named `internal` and `external` corresponding to the `i` and `e` flags if you +wish to use network bridging in this mode. + In-kernel Packet Forwarding --------------------------- @@ -641,7 +708,8 @@ actually add the required rule to the tables. In short, the prerequisites for in-kernel packet forwarding are: 1. The `xt_RTPENGINE` kernel module must be loaded. -2. An `iptables` and/or `ip6tables` rule must be present in the `INPUT` chain to send packets +2. An `iptables` and/or `ip6tables` rule must be present in the `INPUT` chain (or in a custom user-defined + chain which is then called by the `INPUT` chain) to send packets to the `RTPENGINE` target. This rule should be limited to UDP packets, but otherwise there are no restrictions. 3. The `rtpengine` daemon must be running. @@ -730,6 +798,15 @@ like `--dport 30000:40000`. If the kernel module receives a packet that it doesn to an active media stream, it will simply ignore it and hand it back to the network stack for normal processing. +The `RTPENGINE` rule need not necessarily be present directly in the `INPUT` chain. It can also be in a +user-defined chain which is then referenced by the `INPUT` chain, like so: + + iptables -N rtpengine + iptables -I INPUT -p udp -j rtpengine + iptables -I rtpengine -j RTPENGINE --id 42 + +This can be a useful setup if certain firewall scripts are being used. + Summary ------- @@ -745,7 +822,7 @@ A typical start-up sequence including in-kernel forwarding might look like this: echo 'del 0' > /proc/rtpengine/control # start daemon - /usr/sbin/rtpengine --table=0 --ip=10.64.73.31 --ip6=2001:db8::4f3:3d \ + /usr/sbin/rtpengine --table=0 --interface=10.64.73.31 --interface=2001:db8::4f3:3d \ --listen-ng=127.0.0.1:2223 --tos=184 --pidfile=/var/run/rtpengine.pid --no-fallback Running Multiple Instances @@ -767,9 +844,9 @@ then the start-up sequence might look like this: echo 'del 0' > /proc/rtpengine/control echo 'del 1' > /proc/rtpengine/control - /usr/sbin/rtpengine --table=0 --ip=10.64.73.31 \ + /usr/sbin/rtpengine --table=0 --interface=10.64.73.31 \ --listen-ng=127.0.0.1:2223 --tos=184 --pidfile=/var/run/rtpengine-10.pid --no-fallback - /usr/sbin/rtpengine --table=1 --ip=192.168.65.73 \ + /usr/sbin/rtpengine --table=1 --interface=192.168.65.73 \ --listen-ng=127.0.0.1:2224 --tos=184 --pidfile=/var/run/rtpengine-192.pid --no-fallback With this setup, the SIP proxy can choose which instance of *rtpengine* to talk to and thus which local @@ -934,6 +1011,10 @@ Optionally included keys are: Identical to setting `record call` to `on` (see below). + - `no rtcp attribute` + + Omit the `a=rtcp` line from the outgoing SDP. + * `replace` @@ -970,24 +1051,12 @@ Optionally included keys are: However, this mechanism for selecting the address family is now obsolete and the `address family` dictionary key should be used instead. - A direction keyword is *round-robin-calls*. If this is received, a round robin algorithm runs for - choosing the logical interface for the current stream(e.g. audio, video). - The algorithm checks that all local interfaces of the tried logical interface have free ports for - call streams. If a logical interface fails the check, the next one is tried. If there is no logical - interface found with this property, it fallbacks to the default behaviour (e.g. return first logical - interface in --interface list even if no free ports are available). The attribute is ignored for - answers() because the logical interface was already selected at offers(). - Naming an interface "round-robin-calls" and trying to select it using direction will - __run the above algorithm__! - - Round robin for both legs of the stream: - { ..., "direction": [ "round-robin-calls", "round-robin-calls" ], ... } - - Round robin for first leg and and select "pub" for the second leg of the stream: - { ..., "direction": [ "round-robin-calls", "pub" ], ... } - - Round robin for first leg and and default behaviour for the second leg of the stream: - { ..., "direction": [ "round-robin-calls" ], ... } + For legacy support, the special direction keyword `round-robin-calls` can be used to invoke the + round-robin interface selection algorithm described in the section *Interfaces configuration*. + If this special keyword is used, the round-robin selection will run over all configured + interfaces, whether or not they are configured using the `BASE:SUFFIX` interface name notation. + This special keyword is provided only for legacy support and should be considered obsolete. + It will be removed in future versions. * `received from` @@ -1137,6 +1206,32 @@ Optionally included keys are: See the `--recording-dir` option above. +* `codec` + + Contains a dictionary controlling various aspects of codecs (or RTP payload types). + The following keys are understood: + + * `strip` + + Contains a list of strings. Each string is the name of a codec or RTP payload + type that should be removed from the SDP. Codec names are case sensitive, and + can be either from the list of codecs explicitly defined by the SDP through + an `a=rtpmap` attribute, or can be from the list of RFC-defined codecs. Examples + are `PCMU`, `opus`, or `telephone-event`. + + As a special keyword, `all` can be used to remove all codecs, except the ones + that should explicitly offered (see below). Note that it is an error to strip + all codecs and leave none that could be offered. In this case, the original + list of codecs will be left unchanged. + + * `offer` + + Contains a list of strings. Each string is the name of a codec that should be + included in the list of codecs offered. This is primarily useful to block all + codecs (`strip -> all`) except the ones given in the `offer` whitelist. + Currently codecs that were not present in the original list of codecs + offered by the client will be ignored. + An example of a complete `offer` request dictionary could be (SDP body abbreviated): diff --git a/daemon/Makefile b/daemon/Makefile index a93bb134a..21c5870c0 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -55,7 +55,7 @@ include ../lib/lib.Makefile SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \ bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \ crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c socket.c \ - media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c + media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c tcp_listener.c LIBSRCS= loglib.c auxlib.c rtplib.c OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o) diff --git a/daemon/aux.c b/daemon/aux.c index 459f781a6..c01c8c027 100644 --- a/daemon/aux.c +++ b/daemon/aux.c @@ -34,8 +34,8 @@ static cond_t threads_cond = COND_STATIC_INIT; static struct thread_buf __thread t_bufs[NUM_THREAD_BUFS]; static int __thread t_buf_idx; -__thread struct timeval g_now; -volatile int g_shutdown; +__thread struct timeval rtpe_now; +volatile int rtpe_shutdown; #ifdef NEED_ATOMIC64_MUTEX mutex_t __atomic64_mutex = MUTEX_STATIC_INIT; diff --git a/daemon/aux.h b/daemon/aux.h index 1b15dfe4c..4feb1250b 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -60,13 +60,10 @@ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \ #define NUM_THREAD_BUFS 8 -#define ALGORITHM_DEFAULT "" -#define ALGORITHM_ROUND_ROBIN_CALLS "round-robin-calls" - /*** GLOBALS ***/ -extern __thread struct timeval g_now; -extern volatile int g_shutdown; +extern __thread struct timeval rtpe_now; +extern volatile int rtpe_shutdown; @@ -186,7 +183,8 @@ INLINE int strmemcmp(const void *mem, int len, const char *str) { } INLINE void random_string(unsigned char *buf, int len) { - assert(RAND_bytes(buf, len) == 1); + int ret = RAND_bytes(buf, len); + assert(ret == 1); } INLINE long unsigned int ssl_random() { long unsigned int ret; diff --git a/daemon/call.c b/daemon/call.c index 54a1da980..a3f60af96 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -41,6 +41,8 @@ #include "cdr.h" #include "statistics.h" #include "ssrc.h" +#include "main.h" +#include "graphite.h" /* also serves as array index for callstream->peers[] */ @@ -115,12 +117,19 @@ const struct transport_protocol transport_protocols[] = { }; const int num_transport_protocols = G_N_ELEMENTS(transport_protocols); +/* XXX rework these */ +struct stats rtpe_statsps; +struct stats rtpe_stats; + +rwlock_t rtpe_callhash_lock; +GHashTable *rtpe_callhash; + /* ********** */ static void __monologue_destroy(struct call_monologue *monologue); static int monologue_destroy(struct call_monologue *ml); -static struct timeval add_ongoing_calls_dur_in_interval(struct callmaster *m, - struct timeval *interval_start, struct timeval *interval_duration); +static struct timeval add_ongoing_calls_dur_in_interval(struct timeval *interval_start, + struct timeval *interval_duration); /* called with call->master_lock held in R */ static int call_timer_delete_monologues(struct call *c) { @@ -138,7 +147,7 @@ static int call_timer_delete_monologues(struct call *c) { if (!ml->deleted) continue; - if (ml->deleted > poller_now) { + if (ml->deleted > rtpe_now.tv_sec) { if (!min_deleted || ml->deleted < min_deleted) min_deleted = ml->deleted; continue; @@ -162,12 +171,11 @@ out: -/* called with callmaster->hashlock held */ +/* called with hashlock held */ static void call_timer_iterator(gpointer data, gpointer user_data) { struct call *c = data; struct iterator_helper *hlp = user_data; GList *it; - struct callmaster *cm; unsigned int check; int good = 0; struct packet_stream *ps; @@ -180,11 +188,10 @@ static void call_timer_iterator(gpointer data, gpointer user_data) { rwlock_lock_r(&c->master_lock); log_info_call(c); - cm = c->callmaster; - rwlock_lock_r(&cm->conf.config_lock); + rwlock_lock_r(&rtpe_config.config_lock); // final timeout applicable to all calls (own and foreign) - if (cm->conf.final_timeout && poller_now >= (c->created.tv_sec + cm->conf.final_timeout)) { + if (rtpe_config.final_timeout && rtpe_now.tv_sec >= (c->created.tv_sec + rtpe_config.final_timeout)) { ilog(LOG_INFO, "Closing call due to final timeout"); tmp_t_reason = FINAL_TIMEOUT; for (it = c->monologues.head; it; it = it->next) { @@ -201,11 +208,11 @@ static void call_timer_iterator(gpointer data, gpointer user_data) { goto out; } - if (c->deleted && poller_now >= c->deleted + if (c->deleted && rtpe_now.tv_sec >= c->deleted && c->last_signal <= c->deleted) goto delete; - if (c->ml_deleted && poller_now >= c->ml_deleted) { + if (c->ml_deleted && rtpe_now.tv_sec >= c->ml_deleted) { if (call_timer_delete_monologues(c)) goto delete; } @@ -239,14 +246,14 @@ no_sfd: if (good) goto next; - check = cm->conf.timeout; + check = rtpe_config.timeout; tmp_t_reason = TIMEOUT; if (!MEDIA_ISSET(ps->media, RECV) || !sfd || !PS_ISSET(ps, FILLED)) { - check = cm->conf.silent_timeout; + check = rtpe_config.silent_timeout; tmp_t_reason = SILENT_TIMEOUT; } - if (poller_now - atomic64_get(timestamp) < check) + if (rtpe_now.tv_sec - atomic64_get(timestamp) < check) good = 1; next: @@ -277,7 +284,7 @@ delete: goto out; out: - rwlock_unlock_r(&cm->conf.config_lock); + rwlock_unlock_r(&rtpe_config.config_lock); rwlock_unlock_r(&c->master_lock); log_info_clear(); } @@ -330,7 +337,7 @@ retry: for (i = 0; i < 100; i++) close(i); - if (!ilog_stderr) { + if (!rtpe_config.common.log_stderr) { openlog("rtpengine/child", LOG_PID | LOG_NDELAY, LOG_DAEMON); } ilog(LOG_INFO, "Initiating XMLRPC call for tag "STR_FORMAT"", STR_FMT(tag)); @@ -376,19 +383,18 @@ fault: g_slice_free1(sizeof(*xh), xh); } -void kill_calls_timer(GSList *list, struct callmaster *m) { +void kill_calls_timer(GSList *list, const char *url) { struct call *ca; GList *csl; struct call_monologue *cm; - const char *url, *url_prefix, *url_suffix; + const char *url_prefix, *url_suffix; struct xmlrpc_helper *xh = NULL; char url_buf[128]; if (!list) return; - /* if m is NULL, it's the scheduled deletions, otherwise it's the timeouts */ - url = m ? m->conf.b2b_url : NULL; + /* if url is NULL, it's the scheduled deletions, otherwise it's the timeouts */ if (url) { xh = g_slice_alloc(sizeof(*xh)); xh->c = g_string_chunk_new(64); @@ -401,7 +407,7 @@ void kill_calls_timer(GSList *list, struct callmaster *m) { else url_suffix = g_string_chunk_insert(xh->c, url); xh->tags_urls = NULL; - xh->fmt = m->conf.fmt; + xh->fmt = rtpe_config.fmt; } while (list) { @@ -420,7 +426,7 @@ void kill_calls_timer(GSList *list, struct callmaster *m) { else snprintf(url_buf, sizeof(url_buf), "%s", url_suffix); - switch (m->conf.fmt) { + switch (rtpe_config.fmt) { case XF_SEMS: for (csl = ca->monologues.head; csl; csl = csl->next) { cm = csl->data; @@ -458,10 +464,9 @@ destroy: else \ d = ke->stats.x - ks_val; \ atomic64_add(&ps->stats.x, d); \ - atomic64_add(&m->statsps.x, d); \ + atomic64_add(&rtpe_statsps.x, d); \ } while (0) -static void callmaster_timer(void *ptr) { - struct callmaster *m = ptr; +static void call_timer(void *ptr) { struct iterator_helper hlp; GList *i, *l, *calls = NULL; struct rtpengine_list_entry *ke; @@ -477,26 +482,26 @@ static void callmaster_timer(void *ptr) { hlp.addr_sfd = g_hash_table_new(g_endpoint_hash, g_endpoint_eq); /* obtain the call list and make a copy from it so not to hold the lock */ - rwlock_lock_r(&m->hashlock); - l = g_hash_table_get_values(m->callhash); + rwlock_lock_r(&rtpe_callhash_lock); + l = g_hash_table_get_values(rtpe_callhash); if (l) { calls = g_list_copy(l); g_list_free(l); g_list_foreach(calls, call_obj_get, NULL); } - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); if (calls) { g_list_foreach(calls, call_timer_iterator, &hlp); g_list_free_full(calls, call_obj_put); } - atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, bytes); - atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, packets); - atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, errors); + atomic64_local_copy_zero_struct(&tmpstats, &rtpe_statsps, bytes); + atomic64_local_copy_zero_struct(&tmpstats, &rtpe_statsps, packets); + atomic64_local_copy_zero_struct(&tmpstats, &rtpe_statsps, errors); - atomic64_set(&m->stats.bytes, atomic64_get_na(&tmpstats.bytes)); - atomic64_set(&m->stats.packets, atomic64_get_na(&tmpstats.packets)); - atomic64_set(&m->stats.errors, atomic64_get_na(&tmpstats.errors)); + atomic64_set(&rtpe_stats.bytes, atomic64_get_na(&tmpstats.bytes)); + atomic64_set(&rtpe_stats.packets, atomic64_get_na(&tmpstats.packets)); + atomic64_set(&rtpe_stats.errors, atomic64_get_na(&tmpstats.errors)); i = kernel_list(); while (i) { @@ -521,7 +526,7 @@ static void callmaster_timer(void *ptr) { if (ke->stats.packets != atomic64_get(&ps->kernel_stats.packets)) - atomic64_set(&ps->last_packet, poller_now); + atomic64_set(&ps->last_packet, rtpe_now.tv_sec); ps->stats.in_tos_tclass = ke->stats.in_tos; @@ -581,7 +586,7 @@ static void callmaster_timer(void *ptr) { rwlock_unlock_r(&sfd->call->master_lock); if (update) { - redis_update_onekey(ps->call, m->conf.redis_write); + redis_update_onekey(ps->call, rtpe_redis_write); } next: @@ -599,54 +604,20 @@ next: g_hash_table_destroy(hlp.addr_sfd); kill_calls_timer(hlp.del_scheduled, NULL); - kill_calls_timer(hlp.del_timeout, m); + kill_calls_timer(hlp.del_timeout, rtpe_config.b2b_url); } #undef DS -struct callmaster *callmaster_new(struct poller *p) { - struct callmaster *c; - const char *errptr; - int erroff; - - c = obj_alloc0("callmaster", sizeof(*c), NULL); - - c->callhash = g_hash_table_new(str_hash, str_equal); - if (!c->callhash) - goto fail; - c->poller = p; - rwlock_init(&c->hashlock); - - c->info_re = pcre_compile("^([^:,]+)(?::(.*?))?(?:$|,)", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL); - if (!c->info_re) - goto fail; - c->info_ree = pcre_study(c->info_re, 0, &errptr); - - c->streams_re = pcre_compile("^([\\d.]+):(\\d+)(?::(.*?))?(?:$|,)", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL); - if (!c->streams_re) - goto fail; - c->streams_ree = pcre_study(c->streams_re, 0, &errptr); - - poller_add_timer(p, callmaster_timer, &c->obj); - - mutex_init(&c->totalstats.total_average_lock); - mutex_init(&c->totalstats_interval.total_average_lock); - mutex_init(&c->totalstats_interval.managed_sess_lock); - mutex_init(&c->totalstats_interval.total_calls_duration_lock); - - c->totalstats.started = poller_now; - //c->totalstats_interval.managed_sess_min = 0; // already zeroed - //c->totalstats_interval.managed_sess_max = 0; +int call_init() { + rtpe_callhash = g_hash_table_new(str_hash, str_equal); + if (!rtpe_callhash) + return -1; + rwlock_init(&rtpe_callhash_lock); - mutex_init(&c->totalstats_lastinterval_lock); - mutex_init(&c->cngs_lock); - c->cngs_hash = g_hash_table_new(g_sockaddr_hash, g_sockaddr_eq); + poller_add_timer(rtpe_poller, call_timer, NULL); - return c; - -fail: - obj_put(c); - return NULL; + return 0; } @@ -682,7 +653,7 @@ static struct call_media *__get_media(struct call_monologue *ml, GList **it, con med->call = ml->call; med->index = sp->index; call_str_cpy(ml->call, &med->type, &sp->type); - med->rtp_payload_types = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __payload_type_free); + med->codecs = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __payload_type_free); g_queue_push_tail(&ml->medias, med); @@ -844,7 +815,7 @@ struct packet_stream *__packet_stream_new(struct call *call) { mutex_init(&stream->in_lock); mutex_init(&stream->out_lock); stream->call = call; - atomic64_set_na(&stream->last_packet, poller_now); + atomic64_set_na(&stream->last_packet, rtpe_now.tv_sec); stream->rtp_stats = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __rtp_stats_free); recording_init_stream(stream); @@ -979,7 +950,7 @@ void __rtp_stats_update(GHashTable *dst, GHashTable *src) { struct rtp_payload_type *pt; GList *values, *l; - /* "src" is a call_media->rtp_payload_types table, while "dst" is a + /* "src" is a call_media->codecs table, while "dst" is a * packet_stream->rtp_stats table */ values = g_hash_table_get_values(src); @@ -1017,7 +988,7 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru a->rtp_sink = b; PS_SET(a, RTP); /* XXX technically not correct, could be udptl too */ - __rtp_stats_update(a->rtp_stats, A->rtp_payload_types); + __rtp_stats_update(a->rtp_stats, A->codecs); if (sp) { __fill_stream(a, &sp->rtp_endpoint, port_off, sp); @@ -1360,7 +1331,7 @@ static void __tos_change(struct call *call, const struct sdp_ng_flags *flags) { return; if (!flags || flags->tos > 255) - new_tos = call->callmaster->conf.default_tos; + new_tos = rtpe_config.default_tos; else new_tos = flags->tos; @@ -1381,7 +1352,9 @@ static void __init_interface(struct call_media *media, const str *ifname, int nu goto get; if (!ifname || !ifname->s) return; - if (!str_cmp_str(&media->logical_intf->name, ifname) || !str_cmp(ifname, ALGORITHM_ROUND_ROBIN_CALLS)) + if (!str_cmp_str(&media->logical_intf->name, ifname)) + return; + if (g_hash_table_lookup(media->logical_intf->rr_specs, ifname)) return; get: media->logical_intf = get_logical_interface(ifname, media->desired_family, num_ports); @@ -1446,18 +1419,56 @@ static void __dtls_logic(const struct sdp_ng_flags *flags, MEDIA_SET(other_media, DTLS); } -static void __rtp_payload_types(struct call_media *media, GQueue *types) { - struct rtp_payload_type *pt; +static void __rtp_payload_type_add(struct call_media *media, struct rtp_payload_type *pt) { struct call *call = media->call; + /* we must duplicate the contents */ + call_str_cpy(call, &pt->encoding_with_params, &pt->encoding_with_params); + call_str_cpy(call, &pt->encoding, &pt->encoding); + call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters); + call_str_cpy(call, &pt->format_parameters, &pt->format_parameters); + g_hash_table_replace(media->codecs, &pt->payload_type, pt); + g_queue_push_tail(&media->codecs_prefs, pt); +} + +static void __rtp_payload_types(struct call_media *media, GQueue *types, GHashTable *strip, + const GQueue *offer) +{ + struct rtp_payload_type *pt; + static const str str_all = STR_CONST_INIT("all"); + GHashTable *removed = g_hash_table_new_full(str_hash, str_equal, NULL, __payload_type_free); + int remove_all = 0; + + // start fresh + g_queue_clear(&media->codecs_prefs); + + if (strip && g_hash_table_lookup(strip, &str_all)) + remove_all = 1; /* we steal the entire list to avoid duplicate allocs */ while ((pt = g_queue_pop_head(types))) { - /* but we must duplicate the contents */ - call_str_cpy(call, &pt->encoding_with_params, &pt->encoding_with_params); - call_str_cpy(call, &pt->encoding, &pt->encoding); - call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters); - g_hash_table_replace(media->rtp_payload_types, &pt->payload_type, pt); + // codec stripping + if (strip) { + if (remove_all || g_hash_table_lookup(strip, &pt->encoding)) { + g_hash_table_replace(removed, &pt->encoding, pt); + continue; + } + } + __rtp_payload_type_add(media, pt); } + + if (offer) { + // now restore codecs that have been removed, but should be offered + for (GList *l = offer->head; l; l = l->next) { + str *codec = l->data; + pt = g_hash_table_lookup(removed, codec); + if (!pt) + continue; + g_hash_table_steal(removed, codec); + __rtp_payload_type_add(media, pt); + } + } + + g_hash_table_destroy(removed); } static void __ice_start(struct call_media *media) { @@ -1471,33 +1482,6 @@ static void __ice_start(struct call_media *media) { ice_agent_init(&media->ice_agent, media); } -static int get_algorithm_num_ports(GQueue *streams, char *algorithm) { - unsigned int algorithm_ports = 0; - struct stream_params *sp; - GList *media_iter; - - if (algorithm == NULL) { - return 0; - } - - for (media_iter = streams->head; media_iter; media_iter = media_iter->next) { - sp = media_iter->data; - - if (!str_cmp(&sp->direction[0], algorithm)) { - algorithm_ports += sp->consecutive_ports; - } - - if (!str_cmp(&sp->direction[1], algorithm)) { - algorithm_ports += sp->consecutive_ports; - } - } - - // XXX only do *=2 for RTP streams? - algorithm_ports *= 2; - - return algorithm_ports; -} - static void __endpoint_loop_protect(struct stream_params *sp, struct call_media *media) { struct intf_address intf_addr; @@ -1529,7 +1513,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, GList *media_iter, *ml_media, *other_ml_media; struct call_media *media, *other_media; unsigned int num_ports; - unsigned int rr_calls_ports; struct call_monologue *monologue; struct endpoint_map *em; struct call *call; @@ -1544,12 +1527,9 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, monologue = other_ml->active_dialogue; call = monologue->call; - call->last_signal = poller_now; + call->last_signal = rtpe_now.tv_sec; call->deleted = 0; - // get the total number of ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm - rr_calls_ports = get_algorithm_num_ports(streams, ALGORITHM_ROUND_ROBIN_CALLS); - __C_DBG("this="STR_FORMAT" other="STR_FORMAT, STR_FMT(&monologue->tag), STR_FMT(&other_ml->tag)); __tos_change(call, flags); @@ -1559,7 +1539,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, for (media_iter = streams->head; media_iter; media_iter = media_iter->next) { sp = media_iter->data; __C_DBG("processing media stream #%u", sp->index); - __C_DBG("free ports needed for round-robin-calls, left for this call %u", rr_calls_ports); /* first, check for existence of call_media struct on both sides of * the dialogue */ @@ -1612,7 +1591,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, MEDIA_SET(other_media, SDES); } - __rtp_payload_types(media, &sp->rtp_payload_types); + __rtp_payload_types(media, &sp->rtp_payload_types, flags->codec_strip, &flags->codec_offer); /* send and recv are from our POV */ bf_copy_same(&media->media_flags, &sp->sp_flags, @@ -1639,9 +1618,15 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, media->desired_family = sp->desired_family; } + /* determine number of consecutive ports needed locally. + * XXX only do *=2 for RTP streams? */ + num_ports = sp->consecutive_ports; + num_ports *= 2; + + /* local interface selection */ - __init_interface(media, &sp->direction[1], rr_calls_ports); - __init_interface(other_media, &sp->direction[0], rr_calls_ports); + __init_interface(media, &sp->direction[1], num_ports); + __init_interface(other_media, &sp->direction[0], num_ports); if (media->logical_intf == NULL || other_media->logical_intf == NULL) { goto error_intf; @@ -1658,12 +1643,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, MEDIA_SET(other_media, INITIALIZED); - /* determine number of consecutive ports needed locally. - * XXX only do *=2 for RTP streams? */ - num_ports = sp->consecutive_ports; - num_ports *= 2; - - if (!sp->rtp_endpoint.port) { /* Zero port: stream has been rejected. * RFC 3264, chapter 6: @@ -1686,11 +1665,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, em = __get_endpoint_map(media, num_ports, &sp->rtp_endpoint, flags); if (!em) { goto error_ports; - } else { - // update the ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm - if (str_cmp(&sp->direction[1], ALGORITHM_ROUND_ROBIN_CALLS) == 0) { - rr_calls_ports -= num_ports; - } } __num_media_streams(media, num_ports); @@ -1702,11 +1676,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, * when the answer comes. */ if (__wildcard_endpoint_map(other_media, num_ports)) goto error_ports; - - // update the ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm - if (str_cmp(&sp->direction[0], ALGORITHM_ROUND_ROBIN_CALLS) == 0) { - rr_calls_ports -= num_ports; - } } init: @@ -1768,35 +1737,35 @@ const struct rtp_payload_type *__rtp_stats_codec(struct call_media *m) { if (atomic64_get(&rtp_s->packets) == 0) goto out; - rtp_pt = rtp_payload_type(rtp_s->payload_type, m->rtp_payload_types); + rtp_pt = rtp_payload_type(rtp_s->payload_type, m->codecs); out: g_list_free(values); return rtp_pt; /* may be NULL */ } -void add_total_calls_duration_in_interval(struct callmaster *cm, - struct timeval *interval_tv) { - struct timeval ongoing_calls_dur = add_ongoing_calls_dur_in_interval(cm, - &cm->latest_graphite_interval_start, interval_tv); +void add_total_calls_duration_in_interval(struct timeval *interval_tv) { + struct timeval ongoing_calls_dur = add_ongoing_calls_dur_in_interval( + &rtpe_latest_graphite_interval_start, interval_tv); - mutex_lock(&cm->totalstats_interval.total_calls_duration_lock); - timeval_add(&cm->totalstats_interval.total_calls_duration_interval, - &cm->totalstats_interval.total_calls_duration_interval, + mutex_lock(&rtpe_totalstats_interval.total_calls_duration_lock); + timeval_add(&rtpe_totalstats_interval.total_calls_duration_interval, + &rtpe_totalstats_interval.total_calls_duration_interval, &ongoing_calls_dur); - mutex_unlock(&cm->totalstats_interval.total_calls_duration_lock); + mutex_unlock(&rtpe_totalstats_interval.total_calls_duration_lock); } -static struct timeval add_ongoing_calls_dur_in_interval(struct callmaster *m, - struct timeval *interval_start, struct timeval *interval_duration) { +static struct timeval add_ongoing_calls_dur_in_interval(struct timeval *interval_start, + struct timeval *interval_duration) +{ GHashTableIter iter; gpointer key, value; struct timeval call_duration, res = {0}; struct call *call; struct call_monologue *ml; - rwlock_lock_r(&m->hashlock); - g_hash_table_iter_init(&iter, m->callhash); + rwlock_lock_r(&rtpe_callhash_lock); + g_hash_table_iter_init(&iter, rtpe_callhash); while (g_hash_table_iter_next(&iter, &key, &value)) { call = (struct call*) value; @@ -1806,20 +1775,18 @@ static struct timeval add_ongoing_calls_dur_in_interval(struct callmaster *m, if (timercmp(interval_start, &ml->started, >)) { timeval_add(&res, &res, interval_duration); } else { - timeval_subtract(&call_duration, &g_now, &ml->started); + timeval_subtract(&call_duration, &rtpe_now, &ml->started); timeval_add(&res, &res, &call_duration); } } - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); return res; } /* called lock-free, but must hold a reference to the call */ void call_destroy(struct call *c) { - struct callmaster *m; struct packet_stream *ps=0; struct stream_fd *sfd; - struct poller *p; GList *l; int ret; struct call_monologue *ml; @@ -1831,14 +1798,11 @@ void call_destroy(struct call *c) { return; } - m = c->callmaster; - p = m->poller; - - rwlock_lock_w(&m->hashlock); - ret = (g_hash_table_lookup(m->callhash, &c->callid) == c); + rwlock_lock_w(&rtpe_callhash_lock); + ret = (g_hash_table_lookup(rtpe_callhash, &c->callid) == c); if (ret) - g_hash_table_remove(m->callhash, &c->callid); - rwlock_unlock_w(&m->hashlock); + g_hash_table_remove(rtpe_callhash, &c->callid); + rwlock_unlock_w(&rtpe_callhash_lock); // if call not found in callhash => previously deleted if (!ret) @@ -1850,7 +1814,7 @@ void call_destroy(struct call *c) { statistics_update_foreignown_dec(c); if (IS_OWN_CALL(c)) { - redis_delete(c, m->conf.redis_write); + redis_delete(c, rtpe_redis_write); } rwlock_lock_w(&c->master_lock); @@ -1870,8 +1834,8 @@ void call_destroy(struct call *c) { ml->label.s ? " (label '" : "", STR_FMT(ml->label.s ? &ml->label : &STR_EMPTY), ml->label.s ? "')" : "", - (unsigned int) (poller_now - ml->created) / 60, - (unsigned int) (poller_now - ml->created) % 60, + (unsigned int) (rtpe_now.tv_sec - ml->created) / 60, + (unsigned int) (rtpe_now.tv_sec - ml->created) % 60, STR_FMT(&ml->viabranch), ml->active_dialogue ? ml->active_dialogue->tag.len : 6, ml->active_dialogue ? ml->active_dialogue->tag.s : "(none)"); @@ -1910,9 +1874,9 @@ void call_destroy(struct call *c) { atomic64_get(&ps->stats.packets), atomic64_get(&ps->stats.bytes), atomic64_get(&ps->stats.errors), - g_now.tv_sec - atomic64_get(&ps->last_packet)); + rtpe_now.tv_sec - atomic64_get(&ps->last_packet)); - statistics_update_totals(m,ps); + statistics_update_totals(ps); } @@ -1964,7 +1928,7 @@ no_stats_output: while (c->stream_fds.head) { sfd = g_queue_pop_head(&c->stream_fds); - poller_del_item(p, sfd->socket.fd); + poller_del_item(rtpe_poller, sfd->socket.fd); obj_put(sfd); } @@ -2036,7 +2000,8 @@ static void __call_free(void *p) { crypto_params_cleanup(&md->sdes_out.params); g_queue_clear(&md->streams); g_queue_clear(&md->endpoint_maps); - g_hash_table_destroy(md->rtp_payload_types); + g_hash_table_destroy(md->codecs); + g_queue_clear(&md->codecs_prefs); g_slice_free1(sizeof(*md), md); } @@ -2062,58 +2027,57 @@ static void __call_free(void *p) { assert(c->stream_fds.head == NULL); } -static struct call *call_create(const str *callid, struct callmaster *m) { +static struct call *call_create(const str *callid) { struct call *c; ilog(LOG_NOTICE, "Creating new call"); c = obj_alloc0("call", sizeof(*c), __call_free); - c->callmaster = m; mutex_init(&c->buffer_lock); call_buffer_init(&c->buffer); rwlock_init(&c->master_lock); c->tags = g_hash_table_new(str_hash, str_equal); c->viabranches = g_hash_table_new(str_hash, str_equal); call_str_cpy(c, &c->callid, callid); - c->created = g_now; + c->created = rtpe_now; c->dtls_cert = dtls_cert(); - c->tos = m->conf.default_tos; + c->tos = rtpe_config.default_tos; c->ssrc_hash = create_ssrc_hash(); return c; } /* returns call with master_lock held in W */ -struct call *call_get_or_create(const str *callid, struct callmaster *m, enum call_type type) { +struct call *call_get_or_create(const str *callid, enum call_type type) { struct call *c; restart: - rwlock_lock_r(&m->hashlock); - c = g_hash_table_lookup(m->callhash, callid); + rwlock_lock_r(&rtpe_callhash_lock); + c = g_hash_table_lookup(rtpe_callhash, callid); if (!c) { - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); /* completely new call-id, create call */ - c = call_create(callid, m); - rwlock_lock_w(&m->hashlock); - if (g_hash_table_lookup(m->callhash, callid)) { + c = call_create(callid); + rwlock_lock_w(&rtpe_callhash_lock); + if (g_hash_table_lookup(rtpe_callhash, callid)) { /* preempted */ - rwlock_unlock_w(&m->hashlock); + rwlock_unlock_w(&rtpe_callhash_lock); obj_put(c); goto restart; } - g_hash_table_insert(m->callhash, &c->callid, obj_get(c)); + g_hash_table_insert(rtpe_callhash, &c->callid, obj_get(c)); if (type == CT_FOREIGN_CALL) /* foreign call*/ c->foreign_call = 1; - statistics_update_foreignown_inc(m,c); + statistics_update_foreignown_inc(c); rwlock_lock_w(&c->master_lock); - rwlock_unlock_w(&m->hashlock); + rwlock_unlock_w(&rtpe_callhash_lock); } else { obj_hold(c); rwlock_lock_w(&c->master_lock); - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); } log_info_call(c); @@ -2121,29 +2085,29 @@ restart: } /* returns call with master_lock held in W, or NULL if not found */ -struct call *call_get(const str *callid, struct callmaster *m) { +struct call *call_get(const str *callid) { struct call *ret; - rwlock_lock_r(&m->hashlock); - ret = g_hash_table_lookup(m->callhash, callid); + rwlock_lock_r(&rtpe_callhash_lock); + ret = g_hash_table_lookup(rtpe_callhash, callid); if (!ret) { - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); return NULL; } rwlock_lock_w(&ret->master_lock); obj_hold(ret); - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); log_info_call(ret); return ret; } /* returns call with master_lock held in W, or possibly NULL iff opmode == OP_ANSWER */ -struct call *call_get_opmode(const str *callid, struct callmaster *m, enum call_opmode opmode) { +struct call *call_get_opmode(const str *callid, enum call_opmode opmode) { if (opmode == OP_OFFER) - return call_get_or_create(callid, m, CT_OWN_CALL); - return call_get(callid, m); + return call_get_or_create(callid, CT_OWN_CALL); + return call_get(callid); } /* must be called with call->master_lock held in W */ @@ -2154,7 +2118,7 @@ struct call_monologue *__monologue_create(struct call *call) { ret = uid_slice_alloc0(ret, &call->monologues); ret->call = call; - ret->created = poller_now; + ret->created = rtpe_now.tv_sec; ret->other_tags = g_hash_table_new(str_hash, str_equal); g_queue_init(&ret->medias); @@ -2412,7 +2376,7 @@ struct call_monologue *call_get_mono_dialogue(struct call *call, const str *from } -int call_delete_branch(struct callmaster *m, const str *callid, const str *branch, +int call_delete_branch(const str *callid, const str *branch, const str *fromtag, const str *totag, bencode_item_t *output, int delete_delay) { struct call *c; @@ -2422,9 +2386,9 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc GList *i; if (delete_delay < 0) - delete_delay = m->conf.delete_delay; + delete_delay = rtpe_config.delete_delay; - c = call_get(callid, m); + c = call_get(callid); if (!c) { ilog(LOG_INFO, "Call-ID to delete not found"); goto err; @@ -2478,7 +2442,7 @@ do_delete: ilog(LOG_INFO, "Scheduling deletion of call branch '"STR_FORMAT"' " "(via-branch '"STR_FORMAT"') in %d seconds", STR_FMT(&ml->tag), STR_FMT0(branch), delete_delay); - ml->deleted = poller_now + delete_delay; + ml->deleted = rtpe_now.tv_sec + delete_delay; if (!c->ml_deleted || c->ml_deleted > ml->deleted) c->ml_deleted = ml->deleted; } @@ -2493,7 +2457,7 @@ do_delete: del_all: if (delete_delay > 0) { ilog(LOG_INFO, "Scheduling deletion of entire call in %d seconds", delete_delay); - c->deleted = poller_now + delete_delay; + c->deleted = rtpe_now.tv_sec + delete_delay; rwlock_unlock_w(&c->master_lock); } else { @@ -2522,63 +2486,18 @@ out: } -static void callmaster_get_all_calls_interator(void *key, void *val, void *ptr) { +static void call_get_all_calls_interator(void *key, void *val, void *ptr) { GQueue *q = ptr; g_queue_push_tail(q, obj_get_o(val)); } -void callmaster_get_all_calls(struct callmaster *m, GQueue *q) { - rwlock_lock_r(&m->hashlock); - g_hash_table_foreach(m->callhash, callmaster_get_all_calls_interator, q); - rwlock_unlock_r(&m->hashlock); - -} - - -#if 0 -// unused -// simplifty redis_write <> redis if put back into use -static void calls_dump_iterator(void *key, void *val, void *ptr) { - struct call *c = val; - struct callmaster *m = c->callmaster; - - if (m->conf.redis_write) { - redis_update(c, m->conf.redis_write); - } else if (m->conf.redis) { - redis_update(c, m->conf.redis); - } -} - -void calls_dump_redis(struct callmaster *m) { - if (!m->conf.redis) - return; - - ilog(LOG_DEBUG, "Start dumping all call data to Redis...\n"); - redis_wipe(m->conf.redis); - g_hash_table_foreach(m->callhash, calls_dump_iterator, NULL); - ilog(LOG_DEBUG, "Finished dumping all call data to Redis\n"); -} - -void calls_dump_redis_read(struct callmaster *m) { - if (!m->conf.redis_read) - return; +void call_get_all_calls(GQueue *q) { + rwlock_lock_r(&rtpe_callhash_lock); + g_hash_table_foreach(rtpe_callhash, call_get_all_calls_interator, q); + rwlock_unlock_r(&rtpe_callhash_lock); - ilog(LOG_DEBUG, "Start dumping all call data to read Redis...\n"); - redis_wipe(m->conf.redis_read); - g_hash_table_foreach(m->callhash, calls_dump_iterator, NULL); - ilog(LOG_DEBUG, "Finished dumping all call data to read Redis\n"); } -void calls_dump_redis_write(struct callmaster *m) { - if (!m->conf.redis_write) - return; - - ilog(LOG_DEBUG, "Start dumping all call data to write Redis...\n"); - redis_wipe(m->conf.redis_write); - g_hash_table_foreach(m->callhash, calls_dump_iterator, NULL); - ilog(LOG_DEBUG, "Finished dumping all call data to write Redis\n"); -} -#endif const struct transport_protocol *transport_protocol(const str *s) { int i; diff --git a/daemon/call.h b/daemon/call.h index 73602e688..f6d88fd86 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -61,11 +61,6 @@ enum transport_protocol_index { __PROTO_LAST, }; -enum xmlrpc_format { - XF_SEMS = 0, - XF_CALLID, -}; - enum call_stream_state { CSS_UNKNOWN = 0, CSS_SHUTDOWN, @@ -327,7 +322,9 @@ struct call_media { GQueue streams; /* normally RTP + RTCP */ GQueue endpoint_maps; - GHashTable *rtp_payload_types; + + GHashTable *codecs; + GQueue codecs_prefs; volatile unsigned int media_flags; }; @@ -355,8 +352,6 @@ struct call_monologue { struct call { struct obj obj; - struct callmaster *callmaster; /* RO */ - mutex_t buffer_lock; call_buffer_t buffer; @@ -387,78 +382,31 @@ struct call { struct recording *recording; }; -struct callmaster_config { - /* everything below protected by config_lock */ - rwlock_t config_lock; - int max_sessions; - unsigned int timeout; - unsigned int silent_timeout; - unsigned int final_timeout; - - unsigned int delete_delay; - struct redis *redis; - struct redis *redis_write; - struct redis *redis_notify; - struct event_base *redis_notify_event_base; - GQueue *redis_subscribed_keyspaces; - struct redisAsyncContext *redis_notify_async_context; - unsigned int redis_expires_secs; - char *b2b_url; - unsigned char default_tos; - enum xmlrpc_format fmt; - endpoint_t graphite_ep; - int graphite_interval; - - int redis_num_threads; -}; -struct callmaster { - struct obj obj; - rwlock_t hashlock; - GHashTable *callhash; - - /* XXX rework these */ - struct stats statsps; /* per second stats, running timer */ - struct stats stats; /* copied from statsps once a second */ - struct totalstats totalstats; - struct totalstats totalstats_interval; - mutex_t totalstats_lastinterval_lock; - struct totalstats totalstats_lastinterval; - - /* control_ng_stats stuff */ - mutex_t cngs_lock; - GHashTable *cngs_hash; - - struct poller *poller; - pcre *info_re; - pcre_extra *info_ree; - pcre *streams_re; - pcre_extra *streams_ree; - - struct callmaster_config conf; - struct timeval latest_graphite_interval_start; -}; +extern rwlock_t rtpe_callhash_lock; +extern GHashTable *rtpe_callhash; + +extern struct stats rtpe_statsps; /* per second stats, running timer */ +extern struct stats rtpe_stats; /* copied from statsps once a second */ + -struct callmaster *callmaster_new(struct poller *); -void callmaster_get_all_calls(struct callmaster *m, GQueue *q); +int call_init(void); +void call_get_all_calls(GQueue *q); -//void calls_dump_redis(struct callmaster *); -//void calls_dump_redis_read(struct callmaster *); -//void calls_dump_redis_write(struct callmaster *); struct call_monologue *__monologue_create(struct call *call); void __monologue_tag(struct call_monologue *ml, const str *tag); void __monologue_viabranch(struct call_monologue *ml, const str *viabranch); struct packet_stream *__packet_stream_new(struct call *call); -struct call *call_get_or_create(const str *callid, struct callmaster *m, enum call_type); -struct call *call_get_opmode(const str *callid, struct callmaster *m, enum call_opmode opmode); +struct call *call_get_or_create(const str *callid, enum call_type); +struct call *call_get_opmode(const str *callid, enum call_opmode opmode); struct call_monologue *call_get_mono_dialogue(struct call *call, const str *fromtag, const str *totag, const str *viabranch); -struct call *call_get(const str *callid, struct callmaster *m); +struct call *call_get(const str *callid); int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, const struct sdp_ng_flags *flags); -int call_delete_branch(struct callmaster *m, const str *callid, const str *branch, +int call_delete_branch(const str *callid, const str *branch, const str *fromtag, const str *totag, bencode_item_t *output, int delete_delay); void call_destroy(struct call *); enum call_stream_state call_stream_state_machine(struct packet_stream *); @@ -469,7 +417,7 @@ int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address int *len, const struct local_intf *ifa, int keep_unspec); const struct transport_protocol *transport_protocol(const str *s); -void add_total_calls_duration_in_interval(struct callmaster *cm, struct timeval *interval_tv); +void add_total_calls_duration_in_interval(struct timeval *interval_tv); void __payload_type_free(void *p); void __rtp_stats_update(GHashTable *dst, GHashTable *src); diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index af3191c82..c6d760dc1 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -22,8 +22,15 @@ #include "recording.h" #include "rtplib.h" #include "ssrc.h" +#include "tcp_listener.h" +#include "streambuf.h" +#include "main.h" +static pcre *info_re; +static pcre_extra *info_ree; +static pcre *streams_re; +static pcre_extra *streams_ree; int trust_address_def; int dtls_passive_def; @@ -135,7 +142,7 @@ fail: return -1; } -static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_opmode opmode, const char* addr, +static str *call_update_lookup_udp(char **out, enum call_opmode opmode, const char* addr, const endpoint_t *sin) { struct call *c; @@ -152,7 +159,7 @@ static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_o if (opmode == OP_ANSWER) str_swap(&fromtag, &totag); - c = call_get_opmode(&callid, m, opmode); + c = call_get_opmode(&callid, opmode); if (!c) { ilog(LOG_WARNING, "["STR_FORMAT"] Got UDP LOOKUP for unknown call-id", STR_FMT(&callid)); @@ -188,7 +195,7 @@ static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_o sp.index, sp.index, out[RE_UDP_COOKIE], SAF_UDP); rwlock_unlock_w(&c->master_lock); - redis_update_onekey(c, m->conf.redis_write); + redis_update_onekey(c, rtpe_redis_write); gettimeofday(&(monologue->started), NULL); @@ -212,11 +219,11 @@ out: return ret; } -str *call_update_udp(char **out, struct callmaster *m, const char* addr, const endpoint_t *sin) { - return call_update_lookup_udp(out, m, OP_OFFER, addr, sin); +str *call_update_udp(char **out, const char* addr, const endpoint_t *sin) { + return call_update_lookup_udp(out, OP_OFFER, addr, sin); } -str *call_lookup_udp(char **out, struct callmaster *m) { - return call_update_lookup_udp(out, m, OP_ANSWER, NULL, NULL); +str *call_lookup_udp(char **out) { + return call_update_lookup_udp(out, OP_ANSWER, NULL, NULL); } @@ -228,8 +235,8 @@ static int info_parse_func(char **a, void **ret, void *p) { return -1; } -static void info_parse(const char *s, GHashTable *ih, struct callmaster *m) { - pcre_multi_match(m->info_re, m->info_ree, s, 2, info_parse_func, ih, NULL); +static void info_parse(const char *s, GHashTable *ih) { + pcre_multi_match(info_re, info_ree, s, 2, info_parse_func, ih, NULL); } @@ -266,10 +273,10 @@ fail: } -static void streams_parse(const char *s, struct callmaster *m, GQueue *q) { +static void streams_parse(const char *s, GQueue *q) { int i; i = 0; - pcre_multi_match(m->streams_re, m->streams_ree, s, 3, streams_parse_func, &i, q); + pcre_multi_match(streams_re, streams_ree, s, 3, streams_parse_func, &i, q); } /* XXX move these somewhere else */ @@ -291,7 +298,7 @@ static void streams_free(GQueue *q) { -static str *call_request_lookup_tcp(char **out, struct callmaster *m, enum call_opmode opmode) { +static str *call_request_lookup_tcp(char **out, enum call_opmode opmode) { struct call *c; struct call_monologue *monologue; GQueue s = G_QUEUE_INIT; @@ -300,14 +307,14 @@ static str *call_request_lookup_tcp(char **out, struct callmaster *m, enum call_ str_init(&callid, out[RE_TCP_RL_CALLID]); infohash = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); - c = call_get_opmode(&callid, m, opmode); + c = call_get_opmode(&callid, opmode); if (!c) { ilog(LOG_WARNING, "["STR_FORMAT"] Got LOOKUP for unknown call-id", STR_FMT(&callid)); goto out; } - info_parse(out[RE_TCP_RL_INFO], infohash, m); - streams_parse(out[RE_TCP_RL_STREAMS], m, &s); + info_parse(out[RE_TCP_RL_INFO], infohash); + streams_parse(out[RE_TCP_RL_STREAMS], &s); str_init(&fromtag, g_hash_table_lookup(infohash, "fromtag")); if (!fromtag.s) { ilog(LOG_WARNING, "No from-tag in message"); @@ -336,7 +343,7 @@ out2: rwlock_unlock_w(&c->master_lock); streams_free(&s); - redis_update_onekey(c, m->conf.redis_write); + redis_update_onekey(c, rtpe_redis_write); ilog(LOG_INFO, "Returning to SIP proxy: "STR_FORMAT"", STR_FMT0(ret)); obj_put(c); @@ -346,14 +353,14 @@ out: return ret; } -str *call_request_tcp(char **out, struct callmaster *m) { - return call_request_lookup_tcp(out, m, OP_OFFER); +str *call_request_tcp(char **out) { + return call_request_lookup_tcp(out, OP_OFFER); } -str *call_lookup_tcp(char **out, struct callmaster *m) { - return call_request_lookup_tcp(out, m, OP_ANSWER); +str *call_lookup_tcp(char **out) { + return call_request_lookup_tcp(out, OP_ANSWER); } -str *call_delete_udp(char **out, struct callmaster *m) { +str *call_delete_udp(char **out) { str callid, branch, fromtag, totag; __C_DBG("got delete for callid '%s' and viabranch '%s'", @@ -364,12 +371,12 @@ str *call_delete_udp(char **out, struct callmaster *m) { str_init(&fromtag, out[RE_UDP_DQ_FROMTAG]); str_init(&totag, out[RE_UDP_DQ_TOTAG]); - if (call_delete_branch(m, &callid, &branch, &fromtag, &totag, NULL, -1)) + if (call_delete_branch(&callid, &branch, &fromtag, &totag, NULL, -1)) return str_sprintf("%s E8\n", out[RE_UDP_COOKIE]); return str_sprintf("%s 0\n", out[RE_UDP_COOKIE]); } -str *call_query_udp(char **out, struct callmaster *m) { +str *call_query_udp(char **out) { struct call *c; str *ret, callid, fromtag, totag; struct call_stats stats; @@ -380,7 +387,7 @@ str *call_query_udp(char **out, struct callmaster *m) { str_init(&fromtag, out[RE_UDP_DQ_FROMTAG]); str_init(&totag, out[RE_UDP_DQ_TOTAG]); - c = call_get_opmode(&callid, m, OP_OTHER); + c = call_get_opmode(&callid, OP_OTHER); if (!c) { ilog(LOG_INFO, "["STR_FORMAT"] Call-ID to query not found", STR_FMT(&callid)); goto err; @@ -390,12 +397,12 @@ str *call_query_udp(char **out, struct callmaster *m) { rwlock_unlock_w(&c->master_lock); - rwlock_lock_r(&m->conf.config_lock); + rwlock_lock_r(&rtpe_config.config_lock); ret = str_sprintf("%s %lld "UINT64F" "UINT64F" "UINT64F" "UINT64F"\n", out[RE_UDP_COOKIE], - (long long int) m->conf.silent_timeout - (poller_now - stats.last_packet), + (long long int) rtpe_config.silent_timeout - (rtpe_now.tv_sec - stats.last_packet), atomic64_get_na(&stats.totals[0].packets), atomic64_get_na(&stats.totals[1].packets), atomic64_get_na(&stats.totals[2].packets), atomic64_get_na(&stats.totals[3].packets)); - rwlock_unlock_r(&m->conf.config_lock); + rwlock_unlock_r(&rtpe_config.config_lock); goto out; err: @@ -410,43 +417,41 @@ out: return ret; } -void call_delete_tcp(char **out, struct callmaster *m) { +void call_delete_tcp(char **out) { str callid; str_init(&callid, out[RE_TCP_D_CALLID]); - call_delete_branch(m, &callid, NULL, NULL, NULL, NULL, -1); + call_delete_branch(&callid, NULL, NULL, NULL, NULL, -1); } -static void call_status_iterator(struct call *c, struct control_stream *s) { +static void call_status_iterator(struct call *c, struct streambuf_stream *s) { // GList *l; // struct callstream *cs; // struct peer *p; // struct streamrelay *r1, *r2; // struct streamrelay *rx1, *rx2; -// struct callmaster *m; // char addr1[64], addr2[64], addr3[64]; -// m = c->callmaster; // mutex_lock(&c->master_lock); - control_stream_printf(s, "session "STR_FORMAT" - - - - %lli\n", + streambuf_printf(s->outbuf, "session "STR_FORMAT" - - - - %lli\n", STR_FMT(&c->callid), - timeval_diff(&g_now, &c->created) / 1000000); + timeval_diff(&rtpe_now, &c->created) / 1000000); /* XXX restore function */ // mutex_unlock(&c->master_lock); } -void calls_status_tcp(struct callmaster *m, struct control_stream *s) { +void calls_status_tcp(struct streambuf_stream *s) { GQueue q = G_QUEUE_INIT; struct call *c; - callmaster_get_all_calls(m, &q); + call_get_all_calls(&q); - control_stream_printf(s, "proxy %u "UINT64F"/%i/%i\n", + streambuf_printf(s->outbuf, "proxy %u "UINT64F"/%i/%i\n", g_queue_get_length(&q), - atomic64_get(&m->stats.bytes), 0, 0); + atomic64_get(&rtpe_stats.bytes), 0, 0); while (q.head) { c = g_queue_pop_head(&q); @@ -476,13 +481,11 @@ INLINE void call_bencode_hold_ref(struct call *c, bencode_item_t *bi) { bencode_buffer_destroy_add(bi->buffer, call_release_ref, obj_get(c)); } -INLINE void str_hyphenate(bencode_item_t *it) { +INLINE void str_hyphenate(str *s_ori) { str s; - if (!bencode_get_str(it, &s)) - return; + s = *s_ori; while (s.len) { - str_chr_str(&s, &s, ' '); - if (!s.s || !s.len) + if (!str_chr_str(&s, &s, ' ')) break; *s.s = '-'; str_shift(&s, 1); @@ -495,90 +498,142 @@ INLINE char *bencode_get_alt(bencode_item_t *i, const char *one, const char *two return bencode_dictionary_get_str(i, two, out); } -INLINE void ng_sdes_option(struct sdp_ng_flags *out, bencode_item_t *it, unsigned int strip) { - str s; - - if (!bencode_get_str(it, &s)) - return; - str_shift(&s, strip); - - if (!str_cmp(&s, "no") || !str_cmp(&s, "off") || !str_cmp(&s, "disabled") - || !str_cmp(&s, "disable")) +INLINE void ng_sdes_option(struct sdp_ng_flags *out, str *s, void *dummy) { + if (!str_cmp(s, "no") || !str_cmp(s, "off") || !str_cmp(s, "disabled") + || !str_cmp(s, "disable")) out->sdes_off = 1; - else if (!str_cmp(&s, "unencrypted_srtp") || !str_cmp(&s, "UNENCRYPTED_SRTP")) + else if (!str_cmp(s, "unencrypted_srtp") || !str_cmp(s, "UNENCRYPTED_SRTP")) out->sdes_unencrypted_srtp = 1; - else if (!str_cmp(&s, "unencrypted_srtcp") || !str_cmp(&s, "UNENCRYPTED_SRTCP")) + else if (!str_cmp(s, "unencrypted_srtcp") || !str_cmp(s, "UNENCRYPTED_SRTCP")) out->sdes_unencrypted_srtcp = 1; - else if (!str_cmp(&s, "unauthenticated_srtp") || !str_cmp(&s, "UNAUTHENTICATED_SRTP")) + else if (!str_cmp(s, "unauthenticated_srtp") || !str_cmp(s, "UNAUTHENTICATED_SRTP")) out->sdes_unauthenticated_srtp = 1; - else if (!str_cmp(&s, "encrypted_srtp") || !str_cmp(&s, "ENCRYPTED_SRTP")) + else if (!str_cmp(s, "encrypted_srtp") || !str_cmp(s, "ENCRYPTED_SRTP")) out->sdes_encrypted_srtp = 1; - else if (!str_cmp(&s, "encrypted_srtcp") || !str_cmp(&s, "ENCRYPTED_SRTCP")) + else if (!str_cmp(s, "encrypted_srtcp") || !str_cmp(s, "ENCRYPTED_SRTCP")) out->sdes_encrypted_srtcp = 1; - else if (!str_cmp(&s, "authenticated_srtp") || !str_cmp(&s, "AUTHENTICATED_SRTP")) + else if (!str_cmp(s, "authenticated_srtp") || !str_cmp(s, "AUTHENTICATED_SRTP")) out->sdes_authenticated_srtp = 1; else ilog(LOG_WARN, "Unknown 'SDES' flag encountered: '"STR_FORMAT"'", - STR_FMT(&s)); + STR_FMT(s)); } -static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) { + +static void call_ng_flags_list(struct sdp_ng_flags *out, bencode_item_t *input, const char *key, + void (*callback)(struct sdp_ng_flags *, str *, void *), void *parm) +{ bencode_item_t *list, *it; + str s; + if ((list = bencode_dictionary_get_expect(input, key, BENCODE_LIST))) { + for (it = list->child; it; it = it->sibling) { + if (!bencode_get_str(it, &s)) + continue; + callback(out, &s, parm); + } + } +} +static void call_ng_flags_rtcp_mux(struct sdp_ng_flags *out, str *s, void *dummy) { + if (!str_cmp(s, "offer")) + out->rtcp_mux_offer = 1; + else if (!str_cmp(s, "require")) + out->rtcp_mux_require = 1; + else if (!str_cmp(s, "demux")) + out->rtcp_mux_demux = 1; + else if (!str_cmp(s, "accept")) + out->rtcp_mux_accept = 1; + else if (!str_cmp(s, "reject")) + out->rtcp_mux_reject = 1; + else + ilog(LOG_WARN, "Unknown 'rtcp-mux' flag encountered: '" STR_FORMAT "'", + STR_FMT(s)); +} +static void call_ng_flags_replace(struct sdp_ng_flags *out, str *s, void *dummy) { + str_hyphenate(s); + if (!str_cmp(s, "origin")) + out->replace_origin = 1; + else if (!str_cmp(s, "session-connection")) + out->replace_sess_conn = 1; + else + ilog(LOG_WARN, "Unknown 'replace' flag encountered: '" STR_FORMAT "'", + STR_FMT(s)); +} +static void call_ng_flags_codec_list(struct sdp_ng_flags *out, str *s, void *qp) { + str *s_copy; + s_copy = g_slice_alloc(sizeof(*s_copy)); + *s_copy = *s; + g_queue_push_tail((GQueue *) qp, s_copy); +} +static void call_ng_flags_codec_ht(struct sdp_ng_flags *out, str *s, void *htp) { + str *s_copy; + s_copy = g_slice_alloc(sizeof(*s_copy)); + *s_copy = *s; + g_hash_table_replace((GHashTable *) htp, s_copy, s_copy); +} +// helper to alias values from other dictionaries into the "flags" dictionary +INLINE int call_ng_flags_prefix(struct sdp_ng_flags *out, str *s_ori, const char *prefix, + void (*cb)(struct sdp_ng_flags *, str *, void *), void *ptr) +{ + size_t len = strlen(prefix); + str s = *s_ori; + if (len > 0) + if (str_shift_cmp(&s, prefix)) + return 0; + cb(out, &s, ptr); + return 1; +} +static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { + str_hyphenate(s); + + // XXX use internal hash tables for these + if (!str_cmp(s, "trust-address")) + out->trust_address = 1; + else if (!str_cmp(s, "SIP-source-address")) + out->trust_address = 0; + else if (!str_cmp(s, "asymmetric")) + out->asymmetric = 1; + else if (!str_cmp(s, "no-redis-update")) + out->no_redis_update = 1; + else if (!str_cmp(s, "unidirectional")) + out->unidirectional = 1; + else if (!str_cmp(s, "strict-source")) + out->strict_source = 1; + else if (!str_cmp(s, "media-handover")) + out->media_handover = 1; + else if (!str_cmp(s, "reset")) + out->reset = 1; + else if (!str_cmp(s, "port-latching")) + out->port_latching = 1; + else if (!str_cmp(s, "record-call")) + out->record_call = 1; + else if (!str_cmp(s, "no-rtcp-attribute")) + out->no_rtcp_attr = 1; + else { + // handle values aliases from other dictionaries + if (call_ng_flags_prefix(out, s, "SDES-", ng_sdes_option, NULL)) + return; + if (call_ng_flags_prefix(out, s, "codec-strip-", call_ng_flags_codec_ht, out->codec_strip)) + return; + if (call_ng_flags_prefix(out, s, "codec-offer-", call_ng_flags_codec_list, &out->codec_offer)) + return; + + ilog(LOG_WARN, "Unknown flag encountered: '" STR_FORMAT "'", + STR_FMT(s)); + } +} +static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) { + bencode_item_t *list, *it, *dict; int diridx; str s; ZERO(*out); + out->codec_strip = g_hash_table_new_full(str_hash, str_equal, str_slice_free, NULL); out->trust_address = trust_address_def; out->dtls_passive = dtls_passive_def; - if ((list = bencode_dictionary_get_expect(input, "flags", BENCODE_LIST))) { - for (it = list->child; it; it = it->sibling) { - if (it->type != BENCODE_STRING) - continue; - - str_hyphenate(it); - - if (!bencode_strcmp(it, "trust-address")) - out->trust_address = 1; - else if (!bencode_strcmp(it, "SIP-source-address")) - out->trust_address = 0; - else if (!bencode_strcmp(it, "asymmetric")) - out->asymmetric = 1; - else if (!bencode_strcmp(it, "no-redis-update")) - out->no_redis_update = 1; - else if (!bencode_strcmp(it, "unidirectional")) - out->unidirectional = 1; - else if (!bencode_strcmp(it, "strict-source")) - out->strict_source = 1; - else if (!bencode_strcmp(it, "media-handover")) - out->media_handover = 1; - else if (!bencode_strcmp(it, "reset")) - out->reset = 1; - else if (it->iov[1].iov_len >= 5 && !memcmp(it->iov[1].iov_base, "SDES-", 5)) - ng_sdes_option(out, it, 5); - else if (!bencode_strcmp(it, "port-latching")) - out->port_latching = 1; - else if (!bencode_strcmp(it, "record-call")) - out->record_call = 1; - else - ilog(LOG_WARN, "Unknown flag encountered: '"BENCODE_FORMAT"'", - BENCODE_FMT(it)); - } - } - - if ((list = bencode_dictionary_get_expect(input, "replace", BENCODE_LIST))) { - for (it = list->child; it; it = it->sibling) { - str_hyphenate(it); - if (!bencode_strcmp(it, "origin")) - out->replace_origin = 1; - else if (!bencode_strcmp(it, "session-connection")) - out->replace_sess_conn = 1; - else - ilog(LOG_WARN, "Unknown 'replace' flag encountered: '"BENCODE_FORMAT"'", - BENCODE_FMT(it)); - } - } + call_ng_flags_list(out, input, "flags", call_ng_flags_flags, NULL); + call_ng_flags_list(out, input, "replace", call_ng_flags_replace, NULL); diridx = 0; if ((list = bencode_dictionary_get_expect(input, "direction", BENCODE_LIST))) { @@ -618,30 +673,10 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu STR_FMT(&s)); } - if ((list = bencode_dictionary_get_expect(input, "rtcp-mux", BENCODE_LIST))) { - for (it = list->child; it; it = it->sibling) { - if (!bencode_strcmp(it, "offer")) - out->rtcp_mux_offer = 1; - else if (!bencode_strcmp(it, "require")) - out->rtcp_mux_require = 1; - else if (!bencode_strcmp(it, "demux")) - out->rtcp_mux_demux = 1; - else if (!bencode_strcmp(it, "accept")) - out->rtcp_mux_accept = 1; - else if (!bencode_strcmp(it, "reject")) - out->rtcp_mux_reject = 1; - else - ilog(LOG_WARN, "Unknown 'rtcp-mux' flag encountered: '"BENCODE_FORMAT"'", - BENCODE_FMT(it)); - } - } + call_ng_flags_list(out, input, "rtcp-mux", call_ng_flags_rtcp_mux, NULL); - /* XXX abstractize the other list walking functions using callbacks */ /* XXX module still needs to support this list */ - if ((list = bencode_dictionary_get_expect(input, "SDES", BENCODE_LIST))) { - for (it = list->child; it; it = it->sibling) - ng_sdes_option(out, it, 0); - } + call_ng_flags_list(out, input, "SDES", ng_sdes_option, NULL); bencode_get_alt(input, "transport-protocol", "transport protocol", &out->transport_protocol_str); out->transport_protocol = transport_protocol(&out->transport_protocol_str); @@ -651,9 +686,19 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu out->tos = bencode_dictionary_get_integer(input, "TOS", 256); bencode_get_alt(input, "record-call", "record call", &out->record_call_str); bencode_dictionary_get_str(input, "metadata", &out->metadata); + + if ((dict = bencode_dictionary_get_expect(input, "codec", BENCODE_DICTIONARY))) { + /* XXX module still needs to support these */ + call_ng_flags_list(out, dict, "strip", call_ng_flags_codec_ht, out->codec_strip); + call_ng_flags_list(out, dict, "offer", call_ng_flags_codec_list, &out->codec_offer); + } +} +static void call_ng_free_flags(struct sdp_ng_flags *flags) { + g_hash_table_destroy(flags->codec_strip); + g_queue_clear_full(&flags->codec_offer, str_slice_free); } -static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster *m, +static const char *call_offer_answer_ng(bencode_item_t *input, bencode_item_t *output, enum call_opmode opmode, const char* addr, const endpoint_t *sin) { @@ -694,7 +739,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster goto out; /* OP_ANSWER; OP_OFFER && !IS_FOREIGN_CALL */ - call = call_get(&callid, m); + call = call_get(&callid); /* Failover scenario because of timeout on offer response: siprouter tries * to establish session with another rtpengine2 even though rtpengine1 @@ -707,12 +752,12 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster rwlock_unlock_w(&call->master_lock); call_destroy(call); obj_put(call); - call = call_get_or_create(&callid, m, CT_OWN_CALL); + call = call_get_or_create(&callid, CT_OWN_CALL); } } else { /* call == NULL, should create call */ - call = call_get_or_create(&callid, m, CT_OWN_CALL); + call = call_get_or_create(&callid, CT_OWN_CALL); } } @@ -775,7 +820,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster rwlock_unlock_w(&call->master_lock); if (!flags.no_redis_update) { - redis_update_onekey(call,m->conf.redis_write); + redis_update_onekey(call, rtpe_redis_write); } else { ilog(LOG_DEBUG, "Not updating Redis due to present no-redis-update flag"); } @@ -787,6 +832,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster if (ret == ERROR_NO_FREE_PORTS || ret == ERROR_NO_FREE_LOGS) { ilog(LOG_ERR, "Destroying call"); + errstr = "Ran out of ports"; call_destroy(call); } @@ -800,40 +846,41 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster out: sdp_free(&parsed); streams_free(&streams); + call_ng_free_flags(&flags); return errstr; } -const char *call_offer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output, const char* addr, +const char *call_offer_ng(bencode_item_t *input, bencode_item_t *output, const char* addr, const endpoint_t *sin) { - rwlock_lock_r(&m->conf.config_lock); - if (m->conf.max_sessions>=0) { - rwlock_lock_r(&m->hashlock); - if (g_hash_table_size(m->callhash) - - atomic64_get(&m->stats.foreign_sessions) >= m->conf.max_sessions) { - rwlock_unlock_r(&m->hashlock); + rwlock_lock_r(&rtpe_config.config_lock); + if (rtpe_config.max_sessions>=0) { + rwlock_lock_r(&rtpe_callhash_lock); + if (g_hash_table_size(rtpe_callhash) - + atomic64_get(&rtpe_stats.foreign_sessions) >= rtpe_config.max_sessions) { + rwlock_unlock_r(&rtpe_callhash_lock); /* foreign calls can't get rejected * total_rejected_sess applies only to "own" sessions */ - atomic64_inc(&m->totalstats.total_rejected_sess); - atomic64_inc(&m->totalstats_interval.total_rejected_sess); - ilog(LOG_ERROR, "Parallel session limit reached (%i)",m->conf.max_sessions); + atomic64_inc(&rtpe_totalstats.total_rejected_sess); + atomic64_inc(&rtpe_totalstats_interval.total_rejected_sess); + ilog(LOG_ERROR, "Parallel session limit reached (%i)",rtpe_config.max_sessions); - rwlock_unlock_r(&m->conf.config_lock); + rwlock_unlock_r(&rtpe_config.config_lock); return "Parallel session limit reached"; } - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); } - rwlock_unlock_r(&m->conf.config_lock); - return call_offer_answer_ng(input, m, output, OP_OFFER, addr, sin); + rwlock_unlock_r(&rtpe_config.config_lock); + return call_offer_answer_ng(input, output, OP_OFFER, addr, sin); } -const char *call_answer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { - return call_offer_answer_ng(input, m, output, OP_ANSWER, NULL, NULL); +const char *call_answer_ng(bencode_item_t *input, bencode_item_t *output) { + return call_offer_answer_ng(input, output, OP_ANSWER, NULL, NULL); } -const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { +const char *call_delete_ng(bencode_item_t *input, bencode_item_t *output) { str fromtag, totag, viabranch, callid; bencode_item_t *flags, *it; int fatal = 0, delete_delay; @@ -863,7 +910,7 @@ const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_ } } - if (call_delete_branch(m, &callid, &viabranch, &fromtag, &totag, output, delete_delay)) { + if (call_delete_branch(&callid, &viabranch, &fromtag, &totag, output, delete_delay)) { if (fatal) return "Call-ID not found or tags didn't match"; bencode_dictionary_add_string(output, "warning", "Call-ID not found or tags didn't match"); @@ -1137,29 +1184,29 @@ stats: ng_stats(bencode_dictionary_add_dictionary(dict, "RTCP"), &totals->totals[1], NULL); } -static void ng_list_calls( struct callmaster *m, bencode_item_t *output, long long int limit) { +static void ng_list_calls(bencode_item_t *output, long long int limit) { GHashTableIter iter; gpointer key, value; - rwlock_lock_r(&m->hashlock); + rwlock_lock_r(&rtpe_callhash_lock); - g_hash_table_iter_init (&iter, m->callhash); + g_hash_table_iter_init (&iter, rtpe_callhash); while (limit-- && g_hash_table_iter_next (&iter, &key, &value)) { bencode_list_add_str_dup(output, key); } - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); } -const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { +const char *call_query_ng(bencode_item_t *input, bencode_item_t *output) { str callid, fromtag, totag; struct call *call; if (!bencode_dictionary_get_str(input, "call-id", &callid)) return "No call-id in message"; - call = call_get_opmode(&callid, m, OP_OTHER); + call = call_get_opmode(&callid, OP_OTHER); if (!call) return "Unknown call-id"; bencode_dictionary_get_str(input, "from-tag", &fromtag); @@ -1173,7 +1220,7 @@ const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_i } -const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { +const char *call_list_ng(bencode_item_t *input, bencode_item_t *output) { bencode_item_t *calls = NULL; long long int limit; @@ -1184,13 +1231,13 @@ const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_it } calls = bencode_dictionary_add_list(output, "calls"); - ng_list_calls(m, calls, limit); + ng_list_calls(calls, limit); return NULL; } -const char *call_start_recording_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { +const char *call_start_recording_ng(bencode_item_t *input, bencode_item_t *output) { str callid; struct call *call; str metadata; @@ -1198,7 +1245,7 @@ const char *call_start_recording_ng(bencode_item_t *input, struct callmaster *m, if (!bencode_dictionary_get_str(input, "call-id", &callid)) return "No call-id in message"; bencode_dictionary_get_str(input, "metadata", &metadata); - call = call_get_opmode(&callid, m, OP_OTHER); + call = call_get_opmode(&callid, OP_OTHER); if (!call) return "Unknown call-id"; @@ -1210,13 +1257,13 @@ const char *call_start_recording_ng(bencode_item_t *input, struct callmaster *m, return NULL; } -const char *call_stop_recording_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { +const char *call_stop_recording_ng(bencode_item_t *input, bencode_item_t *output) { str callid; struct call *call; if (!bencode_dictionary_get_str(input, "call-id", &callid)) return "No call-id in message"; - call = call_get_opmode(&callid, m, OP_OTHER); + call = call_get_opmode(&callid, OP_OTHER); if (!call) return "Unknown call-id"; @@ -1227,3 +1274,20 @@ const char *call_stop_recording_ng(bencode_item_t *input, struct callmaster *m, return NULL; } + +int call_interfaces_init() { + const char *errptr; + int erroff; + + info_re = pcre_compile("^([^:,]+)(?::(.*?))?(?:$|,)", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL); + if (!info_re) + return -1; + info_ree = pcre_study(info_re, 0, &errptr); + + streams_re = pcre_compile("^([\\d.]+):(\\d+)(?::(.*?))?(?:$|,)", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL); + if (!streams_re) + return -1; + streams_ree = pcre_study(streams_re, 0, &errptr); + + return 0; +} diff --git a/daemon/call_interfaces.h b/daemon/call_interfaces.h index 4887dc971..7114bd9eb 100644 --- a/daemon/call_interfaces.h +++ b/daemon/call_interfaces.h @@ -13,8 +13,7 @@ struct call; struct call_stats; -struct callmaster; -struct control_stream; +struct streambuf_stream; struct sockaddr_in6; struct sdp_ng_flags { @@ -32,6 +31,8 @@ struct sdp_ng_flags { int tos; str record_call_str; str metadata; + GHashTable *codec_strip; + GQueue codec_offer; int asymmetric:1, no_redis_update:1, unidirectional:1, @@ -47,6 +48,7 @@ struct sdp_ng_flags { rtcp_mux_demux:1, rtcp_mux_accept:1, rtcp_mux_reject:1, + no_rtcp_attr:1, strict_source:1, media_handover:1, dtls_passive:1, @@ -66,26 +68,28 @@ extern int trust_address_def; extern int dtls_passive_def; -str *call_request_tcp(char **, struct callmaster *); -str *call_lookup_tcp(char **, struct callmaster *); -void call_delete_tcp(char **, struct callmaster *); -void calls_status_tcp(struct callmaster *, struct control_stream *); +str *call_request_tcp(char **); +str *call_lookup_tcp(char **); +void call_delete_tcp(char **); +void calls_status_tcp(struct streambuf_stream *); -str *call_update_udp(char **, struct callmaster *, const char*, const endpoint_t *); -str *call_lookup_udp(char **, struct callmaster *); -str *call_delete_udp(char **, struct callmaster *); -str *call_query_udp(char **, struct callmaster *); +str *call_update_udp(char **, const char*, const endpoint_t *); +str *call_lookup_udp(char **); +str *call_delete_udp(char **); +str *call_query_udp(char **); -const char *call_offer_ng(bencode_item_t *, struct callmaster *, bencode_item_t *, const char*, +const char *call_offer_ng(bencode_item_t *, bencode_item_t *, const char*, const endpoint_t *); -const char *call_answer_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); -const char *call_delete_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); -const char *call_query_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); -const char *call_list_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); -const char *call_start_recording_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); -const char *call_stop_recording_ng(bencode_item_t *, struct callmaster *, bencode_item_t *); +const char *call_answer_ng(bencode_item_t *, bencode_item_t *); +const char *call_delete_ng(bencode_item_t *, bencode_item_t *); +const char *call_query_ng(bencode_item_t *, bencode_item_t *); +const char *call_list_ng(bencode_item_t *, bencode_item_t *); +const char *call_start_recording_ng(bencode_item_t *, bencode_item_t *); +const char *call_stop_recording_ng(bencode_item_t *, bencode_item_t *); void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output, struct call_stats *totals); +int call_interfaces_init(void); + #endif diff --git a/daemon/cli.c b/daemon/cli.c index 8ea48eaa5..1af68c888 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -19,11 +19,89 @@ #include "control_ng.h" #include "media_socket.h" #include "cdr.h" +#include "streambuf.h" +#include "tcp_listener.h" +#include "str.h" +#include "statistics.h" +#include "main.h" #include "rtpengine_config.h" -static void destroy_own_foreign_calls(struct callmaster *m, unsigned int foreign_call, unsigned int uint_keyspace_db) { +typedef void (*cli_handler_func)(str *, struct streambuf *); +typedef struct { + const char *cmd; + cli_handler_func handler; +} cli_handler_t; + +static void cli_incoming_list(str *instr, struct streambuf *replybuffer); +static void cli_incoming_set(str *instr, struct streambuf *replybuffer); +static void cli_incoming_terminate(str *instr, struct streambuf *replybuffer); +static void cli_incoming_ksadd(str *instr, struct streambuf *replybuffer); +static void cli_incoming_ksrm(str *instr, struct streambuf *replybuffer); +static void cli_incoming_kslist(str *instr, struct streambuf *replybuffer); + +static void cli_incoming_set_maxopenfiles(str *instr, struct streambuf *replybuffer); +static void cli_incoming_set_maxsessions(str *instr, struct streambuf *replybuffer); +static void cli_incoming_set_timeout(str *instr, struct streambuf *replybuffer); +static void cli_incoming_set_silenttimeout(str *instr, struct streambuf *replybuffer); +static void cli_incoming_set_finaltimeout(str *instr, struct streambuf *replybuffer); +static void cli_incoming_set_loglevel(str *instr, struct streambuf *replybuffer); + +static void cli_incoming_list_numsessions(str *instr, struct streambuf *replybuffer); +static void cli_incoming_list_maxsessions(str *instr, struct streambuf *replybuffer); +static void cli_incoming_list_maxopenfiles(str *instr, struct streambuf *replybuffer); +static void cli_incoming_list_totals(str *instr, struct streambuf *replybuffer); +static void cli_incoming_list_sessions(str *instr, struct streambuf *replybuffer); +static void cli_incoming_list_timeout(str *instr, struct streambuf *replybuffer); +static void cli_incoming_list_loglevel(str *instr, struct streambuf *replybuffer); + +static const cli_handler_t cli_top_handlers[] = { + { "list", cli_incoming_list }, + { "terminate", cli_incoming_terminate }, + { "set", cli_incoming_set }, + { "ksadd", cli_incoming_ksadd }, + { "ksrm", cli_incoming_ksrm }, + { "kslist", cli_incoming_kslist }, + { NULL, }, +}; +static const cli_handler_t cli_set_handlers[] = { + { "maxopenfiles", cli_incoming_set_maxopenfiles }, + { "maxsessions", cli_incoming_set_maxsessions }, + { "timeout", cli_incoming_set_timeout }, + { "silenttimeout", cli_incoming_set_silenttimeout }, + { "finaltimeout", cli_incoming_set_finaltimeout }, + { "loglevel", cli_incoming_set_loglevel }, + { NULL, }, +}; +static const cli_handler_t cli_list_handlers[] = { + { "numsessions", cli_incoming_list_numsessions }, + { "sessions", cli_incoming_list_sessions }, + { "totals", cli_incoming_list_totals }, + { "maxopenfiles", cli_incoming_list_maxopenfiles }, + { "maxsessions", cli_incoming_list_maxsessions }, + { "timeout", cli_incoming_list_timeout }, + { "loglevel", cli_incoming_list_loglevel }, + { NULL, }, +}; + + +static void cli_handler_do(const cli_handler_t *handlers, str *instr, + struct streambuf *replybuffer) +{ + const cli_handler_t *h; + + for (h = handlers; h->cmd; h++) { + if (str_shift_cmp(instr, h->cmd)) + continue; + h->handler(instr, replybuffer); + return; + } + + streambuf_printf(replybuffer, "%s:%s\n", "Unknown or incomplete command:", instr->s); +} + +static void destroy_own_foreign_calls(unsigned int foreign_call, unsigned int uint_keyspace_db) { struct call *c = NULL; struct call_monologue *ml = NULL; GQueue call_list = G_QUEUE_INIT; @@ -32,9 +110,9 @@ static void destroy_own_foreign_calls(struct callmaster *m, unsigned int foreign GList *i; // lock read - rwlock_lock_r(&m->hashlock); + rwlock_lock_r(&rtpe_callhash_lock); - g_hash_table_iter_init(&iter, m->callhash); + g_hash_table_iter_init(&iter, rtpe_callhash); while (g_hash_table_iter_next(&iter, &key, &value)) { c = (struct call*)value; if (!c) { @@ -59,7 +137,7 @@ static void destroy_own_foreign_calls(struct callmaster *m, unsigned int foreign } // unlock read - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); // destroy calls while ((c = g_queue_pop_head(&call_list))) { @@ -77,115 +155,89 @@ static void destroy_own_foreign_calls(struct callmaster *m, unsigned int foreign } } -static void destroy_all_foreign_calls(struct callmaster *m) { - destroy_own_foreign_calls(m, CT_FOREIGN_CALL, UNDEFINED); +static void destroy_all_foreign_calls(void) { + destroy_own_foreign_calls(CT_FOREIGN_CALL, UNDEFINED); } -static void destroy_all_own_calls(struct callmaster *m) { - destroy_own_foreign_calls(m, CT_OWN_CALL, UNDEFINED); +static void destroy_all_own_calls(void) { + destroy_own_foreign_calls(CT_OWN_CALL, UNDEFINED); } -static void destroy_keyspace_foreign_calls(struct callmaster *m, unsigned int uint_keyspace_db) { - destroy_own_foreign_calls(m, CT_FOREIGN_CALL, uint_keyspace_db); +static void destroy_keyspace_foreign_calls(unsigned int uint_keyspace_db) { + destroy_own_foreign_calls(CT_FOREIGN_CALL, uint_keyspace_db); } -static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; +static void cli_incoming_list_totals(str *instr, struct streambuf *replybuffer) { struct timeval avg, calls_dur_iv; u_int64_t num_sessions, min_sess_iv, max_sess_iv; struct request_time offer_iv, answer_iv, delete_iv; - mutex_lock(&m->totalstats.total_average_lock); - avg = m->totalstats.total_average_call_dur; - num_sessions = m->totalstats.total_managed_sess; - mutex_unlock(&m->totalstats.total_average_lock); - - printlen = snprintf(replybuffer,(outbufend-replybuffer), "\nTotal statistics (does not include current running sessions):\n\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Uptime of rtpengine :%llu seconds\n", (unsigned long long)time(NULL)-m->totalstats.started); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total managed sessions :"UINT64F"\n", num_sessions); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total rejected sessions :"UINT64F"\n", atomic64_get(&m->totalstats.total_rejected_sess)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via TIMEOUT :"UINT64F"\n",atomic64_get(&m->totalstats.total_timeout_sess)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via SILENT_TIMEOUT :"UINT64F"\n",atomic64_get(&m->totalstats.total_silent_timeout_sess)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total timed-out sessions via FINAL_TIMEOUT :"UINT64F"\n",atomic64_get(&m->totalstats.total_final_timeout_sess)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total regular terminated sessions :"UINT64F"\n",atomic64_get(&m->totalstats.total_regular_term_sess)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total forced terminated sessions :"UINT64F"\n",atomic64_get(&m->totalstats.total_forced_term_sess)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packets :"UINT64F"\n",atomic64_get(&m->totalstats.total_relayed_packets)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total relayed packet errors :"UINT64F"\n",atomic64_get(&m->totalstats.total_relayed_errors)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of streams with no relayed packets :"UINT64F"\n", atomic64_get(&m->totalstats.total_nopacket_relayed_sess)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total number of 1-way streams :"UINT64F"\n",atomic64_get(&m->totalstats.total_oneway_stream_sess)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Average call duration :%ld.%06ld\n\n",avg.tv_sec,avg.tv_usec); - ADJUSTLEN(printlen,outbufend,replybuffer); - - mutex_lock(&m->totalstats_lastinterval_lock); - calls_dur_iv = m->totalstats_lastinterval.total_calls_duration_interval; - min_sess_iv = m->totalstats_lastinterval.managed_sess_min; - max_sess_iv = m->totalstats_lastinterval.managed_sess_max; - offer_iv = m->totalstats_lastinterval.offer; - answer_iv = m->totalstats_lastinterval.answer; - delete_iv = m->totalstats_lastinterval.delete; - mutex_unlock(&m->totalstats_lastinterval_lock); + mutex_lock(&rtpe_totalstats.total_average_lock); + avg = rtpe_totalstats.total_average_call_dur; + num_sessions = rtpe_totalstats.total_managed_sess; + mutex_unlock(&rtpe_totalstats.total_average_lock); + + streambuf_printf(replybuffer, "\nTotal statistics (does not include current running sessions):\n\n"); + streambuf_printf(replybuffer, " Uptime of rtpengine :%llu seconds\n", (unsigned long long)time(NULL)-rtpe_totalstats.started); + streambuf_printf(replybuffer, " Total managed sessions :"UINT64F"\n", num_sessions); + streambuf_printf(replybuffer, " Total rejected sessions :"UINT64F"\n", atomic64_get(&rtpe_totalstats.total_rejected_sess)); + streambuf_printf(replybuffer, " Total timed-out sessions via TIMEOUT :"UINT64F"\n",atomic64_get(&rtpe_totalstats.total_timeout_sess)); + streambuf_printf(replybuffer, " Total timed-out sessions via SILENT_TIMEOUT :"UINT64F"\n",atomic64_get(&rtpe_totalstats.total_silent_timeout_sess)); + streambuf_printf(replybuffer, " Total timed-out sessions via FINAL_TIMEOUT :"UINT64F"\n",atomic64_get(&rtpe_totalstats.total_final_timeout_sess)); + streambuf_printf(replybuffer, " Total regular terminated sessions :"UINT64F"\n",atomic64_get(&rtpe_totalstats.total_regular_term_sess)); + streambuf_printf(replybuffer, " Total forced terminated sessions :"UINT64F"\n",atomic64_get(&rtpe_totalstats.total_forced_term_sess)); + streambuf_printf(replybuffer, " Total relayed packets :"UINT64F"\n",atomic64_get(&rtpe_totalstats.total_relayed_packets)); + streambuf_printf(replybuffer, " Total relayed packet errors :"UINT64F"\n",atomic64_get(&rtpe_totalstats.total_relayed_errors)); + streambuf_printf(replybuffer, " Total number of streams with no relayed packets :"UINT64F"\n", atomic64_get(&rtpe_totalstats.total_nopacket_relayed_sess)); + streambuf_printf(replybuffer, " Total number of 1-way streams :"UINT64F"\n",atomic64_get(&rtpe_totalstats.total_oneway_stream_sess)); + streambuf_printf(replybuffer, " Average call duration :%ld.%06ld\n\n",avg.tv_sec,avg.tv_usec); + + mutex_lock(&rtpe_totalstats_lastinterval_lock); + calls_dur_iv = rtpe_totalstats_lastinterval.total_calls_duration_interval; + min_sess_iv = rtpe_totalstats_lastinterval.managed_sess_min; + max_sess_iv = rtpe_totalstats_lastinterval.managed_sess_max; + offer_iv = rtpe_totalstats_lastinterval.offer; + answer_iv = rtpe_totalstats_lastinterval.answer; + delete_iv = rtpe_totalstats_lastinterval.delete; + mutex_unlock(&rtpe_totalstats_lastinterval_lock); // compute average offer/answer/delete time timeval_divide(&offer_iv.time_avg, &offer_iv.time_avg, offer_iv.count); timeval_divide(&answer_iv.time_avg, &answer_iv.time_avg, answer_iv.count); timeval_divide(&delete_iv.time_avg, &delete_iv.time_avg, delete_iv.count); - printlen = snprintf(replybuffer,(outbufend-replybuffer), "\nGraphite interval statistics (last reported values to graphite):\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Total calls duration :%ld.%06ld\n\n",calls_dur_iv.tv_sec,calls_dur_iv.tv_usec); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Min managed sessions :"UINT64F"\n", min_sess_iv); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Max managed sessions :"UINT64F"\n", max_sess_iv); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Min/Max/Avg offer processing delay :%llu.%06llu/%llu.%06llu/%llu.%06llu sec\n", + streambuf_printf(replybuffer, "\nGraphite interval statistics (last reported values to graphite):\n"); + streambuf_printf(replybuffer, " Total calls duration :%ld.%06ld\n\n",calls_dur_iv.tv_sec,calls_dur_iv.tv_usec); + streambuf_printf(replybuffer, " Min managed sessions :"UINT64F"\n", min_sess_iv); + streambuf_printf(replybuffer, " Max managed sessions :"UINT64F"\n", max_sess_iv); + streambuf_printf(replybuffer, " Min/Max/Avg offer processing delay :%llu.%06llu/%llu.%06llu/%llu.%06llu sec\n", (unsigned long long)offer_iv.time_min.tv_sec,(unsigned long long)offer_iv.time_min.tv_usec, (unsigned long long)offer_iv.time_max.tv_sec,(unsigned long long)offer_iv.time_max.tv_usec, (unsigned long long)offer_iv.time_avg.tv_sec,(unsigned long long)offer_iv.time_avg.tv_usec); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Min/Max/Avg answer processing delay :%llu.%06llu/%llu.%06llu/%llu.%06llu sec\n", + streambuf_printf(replybuffer, " Min/Max/Avg answer processing delay :%llu.%06llu/%llu.%06llu/%llu.%06llu sec\n", (unsigned long long)answer_iv.time_min.tv_sec,(unsigned long long)answer_iv.time_min.tv_usec, (unsigned long long)answer_iv.time_max.tv_sec,(unsigned long long)answer_iv.time_max.tv_usec, (unsigned long long)answer_iv.time_avg.tv_sec,(unsigned long long)answer_iv.time_avg.tv_usec); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " Min/Max/Avg delete processing delay :%llu.%06llu/%llu.%06llu/%llu.%06llu sec\n", + streambuf_printf(replybuffer, " Min/Max/Avg delete processing delay :%llu.%06llu/%llu.%06llu/%llu.%06llu sec\n", (unsigned long long)delete_iv.time_min.tv_sec,(unsigned long long)delete_iv.time_min.tv_usec, (unsigned long long)delete_iv.time_max.tv_sec,(unsigned long long)delete_iv.time_max.tv_usec, (unsigned long long)delete_iv.time_avg.tv_sec,(unsigned long long)delete_iv.time_avg.tv_usec); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), "\n\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "\n\n"); - printlen = snprintf(replybuffer,(outbufend-replybuffer), "Control statistics:\n\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), " %20s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s \n", + streambuf_printf(replybuffer, "Control statistics:\n\n"); + streambuf_printf(replybuffer, " %20s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s \n", "Proxy", "Offer", "Answer", "Delete", "Ping", "List", "Query", "StartRec", "StopRec", "Errors"); - ADJUSTLEN(printlen,outbufend,replybuffer); - mutex_lock(&m->cngs_lock); - GList *list = g_hash_table_get_values(m->cngs_hash); + mutex_lock(&rtpe_cngs_lock); + GList *list = g_hash_table_get_values(rtpe_cngs_hash); if (!list) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "\n No proxies have yet tried to send data."); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "\n No proxies have yet tried to send data."); } for (GList *l = list; l; l = l->next) { struct control_ng_stats* cur = l->data; - printlen = snprintf(replybuffer,(outbufend-replybuffer), " %20s | %10u | %10u | %10u | %10u | %10u | %10u | %10u | %10u | %10u \n", + streambuf_printf(replybuffer, " %20s | %10u | %10u | %10u | %10u | %10u | %10u | %10u | %10u | %10u \n", sockaddr_print_buf(&cur->proxy), cur->offer, cur->answer, @@ -196,96 +248,83 @@ static void cli_incoming_list_totals(char* buffer, int len, struct callmaster* m cur->start_recording, cur->stop_recording, cur->errors); - ADJUSTLEN(printlen,outbufend,replybuffer); } - printlen = snprintf(replybuffer,(outbufend-replybuffer), "\n\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); - mutex_unlock(&m->cngs_lock); + streambuf_printf(replybuffer, "\n\n"); + mutex_unlock(&rtpe_cngs_lock); g_list_free(list); } -static void cli_incoming_list_maxsessions(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; +static void cli_incoming_list_numsessions(str *instr, struct streambuf *replybuffer) { + rwlock_lock_r(&rtpe_callhash_lock); + streambuf_printf(replybuffer, "Current sessions own: "UINT64F"\n", g_hash_table_size(rtpe_callhash) - atomic64_get(&rtpe_stats.foreign_sessions)); + streambuf_printf(replybuffer, "Current sessions foreign: "UINT64F"\n", atomic64_get(&rtpe_stats.foreign_sessions)); + streambuf_printf(replybuffer, "Current sessions total: %i\n", g_hash_table_size(rtpe_callhash)); + rwlock_unlock_r(&rtpe_callhash_lock); +} +static void cli_incoming_list_maxsessions(str *instr, struct streambuf *replybuffer) { /* don't lock anything while reading the value */ - printlen = snprintf(replybuffer,(outbufend-replybuffer), "Maximum sessions configured on rtpengine: %d\n", m->conf.max_sessions); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Maximum sessions configured on rtpengine: %d\n", rtpe_config.max_sessions); return ; } -static void cli_incoming_list_maxopenfiles(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; +static void cli_incoming_list_maxopenfiles(str *instr, struct streambuf *replybuffer) { struct rlimit rlim; pid_t pid = getpid(); if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "Fail getting rtpengine configured limits; cat /proc/%u/limits\n", pid); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Fail getting rtpengine configured limits; cat /proc/%u/limits\n", pid); return ; } if (rlim.rlim_cur == RLIM_INFINITY) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "Maximum open-files configured on rtpengine: infinite; cat /proc/%u/limits\n", pid); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Maximum open-files configured on rtpengine: infinite; cat /proc/%u/limits\n", pid); } else { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "Maximum open-files configured on rtpengine: %lld; cat /proc/%u/limits\n", (long long) rlim.rlim_cur, pid); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Maximum open-files configured on rtpengine: %lld; cat /proc/%u/limits\n", (long long) rlim.rlim_cur, pid); } return ; } -static void cli_incoming_list_timeout(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; - - rwlock_lock_r(&m->conf.config_lock); +static void cli_incoming_list_timeout(str *instr, struct streambuf *replybuffer) { + rwlock_lock_r(&rtpe_config.config_lock); /* don't lock anything while reading the value */ - printlen = snprintf(replybuffer,(outbufend-replybuffer), "TIMEOUT=%u\n", m->conf.timeout); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), "SILENT_TIMEOUT=%u\n", m->conf.silent_timeout); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer,(outbufend-replybuffer), "FINAL_TIMEOUT=%u\n", m->conf.final_timeout); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "TIMEOUT=%u\n", rtpe_config.timeout); + streambuf_printf(replybuffer, "SILENT_TIMEOUT=%u\n", rtpe_config.silent_timeout); + streambuf_printf(replybuffer, "FINAL_TIMEOUT=%u\n", rtpe_config.final_timeout); - rwlock_unlock_r(&m->conf.config_lock); + rwlock_unlock_r(&rtpe_config.config_lock); return ; } -static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - str callid; +static void cli_incoming_list_callid(str *instr, struct streambuf *replybuffer) { struct call* c=0; struct call_monologue *ml; struct call_media *md; struct packet_stream *ps; GList *l; GList *k, *o; - int printlen=0; struct timeval tim_result_duration; struct timeval now; char * local_addr; - if (len<=1) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } -// ++buffer; --len; // one space - str_init_len(&callid,buffer,len); - c = call_get(&callid, m); + c = call_get(instr); if (!c) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "\nCall Id not found (%s).\n\n",callid.s); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "\nCall Id not found (%s).\n\n",instr->s); return; } - printlen = snprintf (replybuffer,(outbufend-replybuffer), "\ncallid: %60s | deletionmark:%4s | created:%12i | proxy:%s | tos:%u | last_signal:%llu | redis_keyspace:%i | foreign:%s\n\n", + streambuf_printf(replybuffer, "\ncallid: %60s | deletionmark:%4s | created:%12i | proxy:%s | tos:%u | last_signal:%llu | redis_keyspace:%i | foreign:%s\n\n", c->callid.s , c->ml_deleted?"yes":"no", (int)c->created.tv_sec, c->created_from, (unsigned int)c->tos, (unsigned long long)c->last_signal, c->redis_hosted_db, IS_FOREIGN_CALL(c)?"yes":"no"); - ADJUSTLEN(printlen,outbufend,replybuffer); for (l = c->monologues.head; l; l = l->next) { ml = l->data; @@ -295,14 +334,13 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m now = ml->terminated; } timeval_subtract(&tim_result_duration,&now,&ml->started); - printlen = snprintf(replybuffer,(outbufend-replybuffer), "--- Tag '"STR_FORMAT"' type: %s, callduration " + streambuf_printf(replybuffer, "--- Tag '"STR_FORMAT"' type: %s, callduration " "%ld.%06ld , in dialogue with '"STR_FORMAT"'\n", STR_FMT(&ml->tag), get_tag_type_text(ml->tagtype), tim_result_duration.tv_sec, tim_result_duration.tv_usec, ml->active_dialogue ? ml->active_dialogue->tag.len : 6, ml->active_dialogue ? ml->active_dialogue->tag.s : "(none)"); - ADJUSTLEN(printlen,outbufend,replybuffer); for (k = ml->medias.head; k; k = k->next) { md = k->data; @@ -316,7 +354,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m local_addr = ps->selected_sfd ? sockaddr_print_buf(&ps->selected_sfd->socket.local.address) : "0.0.0.0"; #if (RE_HAS_MEASUREDELAY) if (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, %15s:%-5hu <> %15s:%-5hu%s, " + streambuf_printf(replybuffer, "------ Media #%u, %15s:%-5hu <> %15s:%-5hu%s, " ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet\n", md->index, local_addr, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), @@ -327,7 +365,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m atomic64_get(&ps->stats.errors), atomic64_get(&ps->last_packet)); } else { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, %15s:%-5hu <> %15s:%-5hu%s, " + streambuf_printf(replybuffer, "------ Media #%u, %15s:%-5hu <> %15s:%-5hu%s, " ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet, %.9f delay_min, %.9f delay_avg, %.9f delay_max\n", md->index, local_addr, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), @@ -342,7 +380,7 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m (double) ps->stats.delay_max / 1000000); } #else - printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, %15s:%-5u <> %15s:%-5u%s, " + streambuf_printf(replybuffer, "------ Media #%u, %15s:%-5u <> %15s:%-5u%s, " ""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet\n", md->index, local_addr, (unsigned int) (ps->selected_sfd ? ps->selected_sfd->socket.local.port : 0), @@ -353,19 +391,16 @@ static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m atomic64_get(&ps->stats.errors), atomic64_get(&ps->last_packet)); #endif - ADJUSTLEN(printlen,outbufend,replybuffer); } } } - printlen = snprintf(replybuffer,(outbufend-replybuffer), "\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "\n"); rwlock_unlock_w(&c->master_lock); // because of call_get(..) obj_put(c); } -static void cli_incoming_list_sessions(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; +static void cli_incoming_list_sessions(str *instr, struct streambuf *replybuffer) { GHashTableIter iter; gpointer key, value; str *ptrkey; @@ -376,38 +411,35 @@ static void cli_incoming_list_sessions(char* buffer, int len, struct callmaster* static const char* LIST_OWN = "own"; static const char* LIST_FOREIGN = "foreign"; - if (len<=1) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - rwlock_lock_r(&m->hashlock); + rwlock_lock_r(&rtpe_callhash_lock); - if (g_hash_table_size(m->callhash)==0) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "No sessions on this media relay.\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); - rwlock_unlock_r(&m->hashlock); + if (g_hash_table_size(rtpe_callhash)==0) { + streambuf_printf(replybuffer, "No sessions on this media relay.\n"); + rwlock_unlock_r(&rtpe_callhash_lock); return; } - g_hash_table_iter_init (&iter, m->callhash); + g_hash_table_iter_init (&iter, rtpe_callhash); while (g_hash_table_iter_next(&iter, &key, &value)) { ptrkey = (str*)key; call = (struct call*)value; - if (len>=strlen(LIST_ALL) && strncmp(buffer,LIST_ALL,strlen(LIST_ALL)) == 0) { + if (str_cmp(instr, LIST_ALL) == 0) { if (!call) { continue; } - } else if (len>=strlen(LIST_OWN) && strncmp(buffer,LIST_OWN,strlen(LIST_OWN)) == 0) { + } else if (str_cmp(instr, LIST_OWN) == 0) { if (!call || IS_FOREIGN_CALL(call)) { continue; } else { found_own = 1; } - } else if (len>=strlen(LIST_FOREIGN) && strncmp(buffer,LIST_FOREIGN,strlen(LIST_FOREIGN)) == 0) { + } else if (str_cmp(instr, LIST_FOREIGN) == 0) { if (!call || !IS_FOREIGN_CALL(call)) { continue; } else { @@ -418,291 +450,201 @@ static void cli_incoming_list_sessions(char* buffer, int len, struct callmaster* break; } - printlen = snprintf(replybuffer, outbufend-replybuffer, "callid: %60s | deletionmark:%4s | created:%12i | proxy:%s | redis_keyspace:%i | foreign:%s\n", ptrkey->s, call->ml_deleted?"yes":"no", (int)call->created.tv_sec, call->created_from, call->redis_hosted_db, IS_FOREIGN_CALL(call)?"yes":"no"); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "callid: %60s | deletionmark:%4s | created:%12i | proxy:%s | redis_keyspace:%i | foreign:%s\n", ptrkey->s, call->ml_deleted?"yes":"no", (int)call->created.tv_sec, call->created_from, call->redis_hosted_db, IS_FOREIGN_CALL(call)?"yes":"no"); } - rwlock_unlock_r(&m->hashlock); + rwlock_unlock_r(&rtpe_callhash_lock); - if (len>=strlen(LIST_ALL) && strncmp(buffer,LIST_ALL,strlen(LIST_ALL)) == 0) { + if (str_cmp(instr, LIST_ALL) == 0) { ; - } else if (len>=strlen(LIST_OWN) && strncmp(buffer,LIST_OWN,strlen(LIST_OWN)) == 0) { + } else if (str_cmp(instr, LIST_OWN) == 0) { if (!found_own) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "No own sessions on this media relay.\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "No own sessions on this media relay.\n"); } - } else if (len>=strlen(LIST_FOREIGN) && strncmp(buffer,LIST_FOREIGN,strlen(LIST_FOREIGN)) == 0) { + } else if (str_cmp(instr, LIST_FOREIGN) == 0) { if (!found_foreign) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "No foreign sessions on this media relay.\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "No foreign sessions on this media relay.\n"); } } else { // list session for callid - cli_incoming_list_callid(buffer, len, m, replybuffer, outbufend); + cli_incoming_list_callid(instr, replybuffer); } return; } -static void cli_incoming_set_maxopenfiles(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen = 0; +static void cli_incoming_set_maxopenfiles(str *instr, struct streambuf *replybuffer) { unsigned long open_files_num; - str open_files; pid_t pid; char *endptr; // limit the minimum number of open files to avoid rtpengine freeze for low open_files_num values unsigned long min_open_files_num = (1 << 16); - if (len <= 1) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - open_files.s = buffer; - open_files.len = len; - open_files_num = strtoul(open_files.s, &endptr, 10); + open_files_num = strtoul(instr->s, &endptr, 10); if ((errno == ERANGE && (open_files_num == ULONG_MAX)) || (errno != 0 && open_files_num == 0)) { - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting open_files to %.*s; errno=%d\n", open_files.len, open_files.s, errno); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Fail setting open_files to %s; errno=%d\n", instr->s, errno); return; - } else if (endptr == open_files.s) { - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting open_files to %.*s; no digists found\n", open_files.len, open_files.s); - ADJUSTLEN(printlen,outbufend,replybuffer); + } else if (endptr == instr->s) { + streambuf_printf(replybuffer, "Fail setting open_files to %s; no digists found\n", instr->s); return; } else if (open_files_num < min_open_files_num) { - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting open_files to %lu; can't set it under %lu\n", open_files_num, min_open_files_num); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Fail setting open_files to %lu; can't set it under %lu\n", open_files_num, min_open_files_num); return; } else if (rlim(RLIMIT_NOFILE, open_files_num) == -1){ - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting open_files to %lu; errno = %d\n", open_files_num, errno); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Fail setting open_files to %lu; errno = %d\n", open_files_num, errno); return; } else { pid = getpid(); - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Success setting open_files to %lu; cat /proc/%u/limits\n", open_files_num, pid); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Success setting open_files to %lu; cat /proc/%u/limits\n", open_files_num, pid); } } -static void cli_incoming_set_maxsessions(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen = 0; +static void cli_incoming_set_maxsessions(str *instr, struct streambuf *replybuffer) { long maxsessions_num; int disabled = -1; - str maxsessions; char *endptr; - if (len <= 1) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - maxsessions.s = buffer; - maxsessions.len = len; - maxsessions_num = strtol(maxsessions.s, &endptr, 10); + maxsessions_num = strtol(instr->s, &endptr, 10); if ((errno == ERANGE && (maxsessions_num == LONG_MAX || maxsessions_num == LONG_MIN)) || (errno != 0 && maxsessions_num == 0)) { - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting maxsessions to %.*s; errno=%d\n", maxsessions.len, maxsessions.s, errno); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Fail setting maxsessions to %s; errno=%d\n", instr->s, errno); return; - } else if (endptr == maxsessions.s) { - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting maxsessions to %.*s; no digists found\n", maxsessions.len, maxsessions.s); - ADJUSTLEN(printlen,outbufend,replybuffer); + } else if (endptr == instr->s) { + streambuf_printf(replybuffer, "Fail setting maxsessions to %s; no digists found\n", instr->s); return; } else if (maxsessions_num < disabled) { - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting maxsessions to %ld; either positive or -1 values allowed\n", maxsessions_num); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "Fail setting maxsessions to %ld; either positive or -1 values allowed\n", maxsessions_num); } else if (maxsessions_num == disabled) { - rwlock_lock_w(&m->conf.config_lock); - m->conf.max_sessions = maxsessions_num; - rwlock_unlock_w(&m->conf.config_lock); - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Success setting maxsessions to %ld; disable feature\n", maxsessions_num); - ADJUSTLEN(printlen,outbufend,replybuffer); + rwlock_lock_w(&rtpe_config.config_lock); + rtpe_config.max_sessions = maxsessions_num; + rwlock_unlock_w(&rtpe_config.config_lock); + streambuf_printf(replybuffer, "Success setting maxsessions to %ld; disable feature\n", maxsessions_num); } else { - rwlock_lock_w(&m->conf.config_lock); - m->conf.max_sessions = maxsessions_num; - rwlock_unlock_w(&m->conf.config_lock); - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Success setting maxsessions to %ld\n", maxsessions_num); - ADJUSTLEN(printlen,outbufend,replybuffer); + rwlock_lock_w(&rtpe_config.config_lock); + rtpe_config.max_sessions = maxsessions_num; + rwlock_unlock_w(&rtpe_config.config_lock); + streambuf_printf(replybuffer, "Success setting maxsessions to %ld\n", maxsessions_num); } return; } -static void cli_incoming_set_timeout(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend, unsigned int *conf_timeout) { - int printlen = 0; - unsigned long timeout_num; - str timeout; +static void cli_incoming_set_gentimeout(str *instr, struct streambuf *replybuffer, int *conf_timeout) { + long timeout_num; char *endptr; - if (len <= 1) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - timeout.s = buffer; - timeout.len = len; - timeout_num = strtoul(timeout.s, &endptr, 10); + timeout_num = strtol(instr->s, &endptr, 10); - if ((errno == ERANGE && (timeout_num == ULONG_MAX)) || (errno != 0 && timeout_num == 0)) { - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting timeout to %.*s; errno=%d\n", timeout.len, timeout.s, errno); - ADJUSTLEN(printlen,outbufend,replybuffer); + if ((errno == ERANGE && (timeout_num == ULONG_MAX)) || (errno != 0 && timeout_num == 0) || timeout_num < 0 || timeout_num >= INT_MAX) { + streambuf_printf(replybuffer, "Fail setting timeout to %s; errno=%d\n", instr->s, errno); return; - } else if (endptr == timeout.s) { - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Fail setting timeout to %.*s; no digists found\n", timeout.len, timeout.s); - ADJUSTLEN(printlen,outbufend,replybuffer); + } else if (endptr == instr->s) { + streambuf_printf(replybuffer, "Fail setting timeout to %s; no digists found\n", instr->s); return; } else { - /* don't lock anything while writing the value - only this command modifies its value */ - rwlock_lock_w(&m->conf.config_lock); - *conf_timeout = timeout_num; - rwlock_unlock_w(&m->conf.config_lock); - printlen = snprintf (replybuffer,(outbufend-replybuffer), "Success setting timeout to %lu\n", timeout_num); - ADJUSTLEN(printlen,outbufend,replybuffer); + rwlock_lock_w(&rtpe_config.config_lock); + *conf_timeout = (int) timeout_num; + rwlock_unlock_w(&rtpe_config.config_lock); + streambuf_printf(replybuffer, "Success setting timeout to %lu\n", timeout_num); } } -static void cli_incoming_list(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; - - static const char* LIST_NUMSESSIONS = "numsessions"; - static const char* LIST_SESSIONS = "sessions"; - static const char* LIST_TOTALS = "totals"; - static const char* LIST_MAX_OPEN_FILES = "maxopenfiles"; - static const char* LIST_MAX_SESSIONS = "maxsessions"; - static const char* LIST_TIMEOUT = "timeout"; +static void cli_incoming_set_timeout(str *instr, struct streambuf *replybuffer) { + cli_incoming_set_gentimeout(instr, replybuffer, &rtpe_config.timeout); +} +static void cli_incoming_set_silenttimeout(str *instr, struct streambuf *replybuffer) { + cli_incoming_set_gentimeout(instr, replybuffer, &rtpe_config.silent_timeout); +} +static void cli_incoming_set_finaltimeout(str *instr, struct streambuf *replybuffer) { + cli_incoming_set_gentimeout(instr, replybuffer, &rtpe_config.final_timeout); +} - if (len<=1) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); +static void cli_incoming_list(str *instr, struct streambuf *replybuffer) { + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - - if (len>=strlen(LIST_NUMSESSIONS) && strncmp(buffer,LIST_NUMSESSIONS,strlen(LIST_NUMSESSIONS)) == 0) { - rwlock_lock_r(&m->hashlock); - printlen = snprintf(replybuffer, outbufend-replybuffer, "Current sessions own: "UINT64F"\n", g_hash_table_size(m->callhash) - atomic64_get(&m->stats.foreign_sessions)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer, outbufend-replybuffer, "Current sessions foreign: "UINT64F"\n", atomic64_get(&m->stats.foreign_sessions)); - ADJUSTLEN(printlen,outbufend,replybuffer); - printlen = snprintf(replybuffer, outbufend-replybuffer, "Current sessions total: %i\n", g_hash_table_size(m->callhash)); - ADJUSTLEN(printlen,outbufend,replybuffer); - rwlock_unlock_r(&m->hashlock); - } else if (len>=strlen(LIST_SESSIONS) && strncmp(buffer,LIST_SESSIONS,strlen(LIST_SESSIONS)) == 0) { - cli_incoming_list_sessions(buffer+strlen(LIST_SESSIONS), len-strlen(LIST_SESSIONS), m, replybuffer, outbufend); - } else if (len>=strlen(LIST_TOTALS) && strncmp(buffer,LIST_TOTALS,strlen(LIST_TOTALS)) == 0) { - cli_incoming_list_totals(buffer+strlen(LIST_TOTALS), len-strlen(LIST_TOTALS), m, replybuffer, outbufend); - } else if (len>=strlen(LIST_MAX_SESSIONS) && strncmp(buffer,LIST_MAX_SESSIONS,strlen(LIST_MAX_SESSIONS)) == 0) { - cli_incoming_list_maxsessions(buffer+strlen(LIST_MAX_SESSIONS), len-strlen(LIST_MAX_SESSIONS), m, replybuffer, outbufend); - } else if (len>=strlen(LIST_MAX_OPEN_FILES) && strncmp(buffer,LIST_MAX_OPEN_FILES,strlen(LIST_MAX_OPEN_FILES)) == 0) { - cli_incoming_list_maxopenfiles(buffer+strlen(LIST_MAX_OPEN_FILES), len-strlen(LIST_MAX_OPEN_FILES), m, replybuffer, outbufend); - } else if (len>=strlen(LIST_TIMEOUT) && strncmp(buffer,LIST_TIMEOUT,strlen(LIST_TIMEOUT)) == 0) { - cli_incoming_list_timeout(buffer+strlen(LIST_TIMEOUT), len-strlen(LIST_TIMEOUT), m, replybuffer, outbufend); - } else { - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s:%s\n", "Unknown 'list' command", buffer); - ADJUSTLEN(printlen,outbufend,replybuffer); - } -} - -static void cli_incoming_set(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; - static const char* SET_MAX_OPEN_FILES = "maxopenfiles"; - static const char* SET_MAX_SESSIONS = "maxsessions"; - static const char* SET_TIMEOUT = "timeout"; - static const char* SET_SILENT_TIMEOUT = "silenttimeout"; - static const char* SET_FINAL_TIMEOUT = "finaltimeout"; + cli_handler_do(cli_list_handlers, instr, replybuffer); +} - if (len<=1) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); +static void cli_incoming_set(str *instr, struct streambuf *replybuffer) { + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - - if (len>=strlen(SET_MAX_OPEN_FILES) && strncmp(buffer,SET_MAX_OPEN_FILES,strlen(SET_MAX_OPEN_FILES)) == 0) { - cli_incoming_set_maxopenfiles(buffer+strlen(SET_MAX_OPEN_FILES), len-strlen(SET_MAX_OPEN_FILES), m, replybuffer, outbufend); - } else if (len>=strlen(SET_MAX_SESSIONS) && strncmp(buffer,SET_MAX_SESSIONS,strlen(SET_MAX_SESSIONS)) == 0) { - cli_incoming_set_maxsessions(buffer+strlen(SET_MAX_SESSIONS), len-strlen(SET_MAX_SESSIONS), m, replybuffer, outbufend); - } else if (len>=strlen(SET_TIMEOUT) && strncmp(buffer,SET_TIMEOUT,strlen(SET_TIMEOUT)) == 0) { - cli_incoming_set_timeout(buffer+strlen(SET_TIMEOUT), len-strlen(SET_TIMEOUT), m, replybuffer, outbufend, &m->conf.timeout); - } else if (len>=strlen(SET_SILENT_TIMEOUT) && strncmp(buffer,SET_SILENT_TIMEOUT,strlen(SET_SILENT_TIMEOUT)) == 0) { - cli_incoming_set_timeout(buffer+strlen(SET_SILENT_TIMEOUT), len-strlen(SET_SILENT_TIMEOUT), m, replybuffer, outbufend, &m->conf.silent_timeout); - } else if (len>=strlen(SET_FINAL_TIMEOUT) && strncmp(buffer,SET_FINAL_TIMEOUT,strlen(SET_FINAL_TIMEOUT)) == 0) { - cli_incoming_set_timeout(buffer+strlen(SET_FINAL_TIMEOUT), len-strlen(SET_FINAL_TIMEOUT), m, replybuffer, outbufend, &m->conf.final_timeout); - } else { - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s:%s\n", "Unknown 'set' command", buffer); - ADJUSTLEN(printlen,outbufend,replybuffer); - } + + cli_handler_do(cli_set_handlers, instr, replybuffer); } -static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - str termparam; +static void cli_incoming_terminate(str *instr, struct streambuf *replybuffer) { struct call* c=0; - int printlen=0; struct call_monologue *ml; GList *i; - if (len<=1) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - str_init_len(&termparam,buffer,len); // --- terminate all calls - if (!str_memcmp(&termparam,"all")) { + if (!str_memcmp(instr,"all")) { // destroy own calls - destroy_all_own_calls(m); + destroy_all_own_calls(); // destroy foreign calls - destroy_all_foreign_calls(m); + destroy_all_foreign_calls(); // update cli ilog(LOG_INFO,"All calls terminated by operator."); - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "All calls terminated by operator."); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "%s\n", "All calls terminated by operator."); return; // --- terminate own calls - } else if (!str_memcmp(&termparam,"own")) { + } else if (!str_memcmp(instr,"own")) { // destroy own calls - destroy_all_own_calls(m); + destroy_all_own_calls(); // update cli ilog(LOG_INFO,"All own calls terminated by operator."); - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "All own calls terminated by operator."); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "%s\n", "All own calls terminated by operator."); return; // --- terminate foreign calls - } else if (!str_memcmp(&termparam,"foreign")) { + } else if (!str_memcmp(instr,"foreign")) { // destroy foreign calls - destroy_all_foreign_calls(m); + destroy_all_foreign_calls(); // update cli ilog(LOG_INFO,"All foreign calls terminated by operator."); - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "All foreign calls terminated by operator."); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "%s\n", "All foreign calls terminated by operator."); return; } // --- terminate a dedicated call id - c = call_get(&termparam, m); + c = call_get(instr); if (!c) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "\nCall Id not found (%s).\n\n",termparam.s); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "\nCall Id not found (%s).\n\n",instr->s); return; } @@ -714,9 +656,8 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, } } - printlen = snprintf(replybuffer, outbufend-replybuffer, "\nCall Id (%s) successfully terminated by operator.\n\n",termparam.s); - ADJUSTLEN(printlen,outbufend,replybuffer); - ilog(LOG_WARN, "Call Id (%s) successfully terminated by operator.",termparam.s); + streambuf_printf(replybuffer, "\nCall Id (%s) successfully terminated by operator.\n\n",instr->s); + ilog(LOG_WARN, "Call Id (%s) successfully terminated by operator.",instr->s); rwlock_unlock_w(&c->master_lock); @@ -724,220 +665,169 @@ static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, obj_put(c); } -static void cli_incoming_ksadd(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; +static void cli_incoming_ksadd(str *instr, struct streambuf *replybuffer) { unsigned long uint_keyspace_db; - str str_keyspace_db; char *endptr; - if (len<=1) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - str_keyspace_db.s = buffer; - str_keyspace_db.len = len; - uint_keyspace_db = strtoul(str_keyspace_db.s, &endptr, 10); + uint_keyspace_db = strtoul(instr->s, &endptr, 10); if ((errno == ERANGE && (uint_keyspace_db == ULONG_MAX)) || (errno != 0 && uint_keyspace_db == 0)) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "Fail adding keyspace %.*s to redis notifications; errono=%d\n", str_keyspace_db.len, str_keyspace_db.s, errno); - } else if (endptr == str_keyspace_db.s) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "Fail adding keyspace %.*s to redis notifications; no digists found\n", str_keyspace_db.len, str_keyspace_db.s); + streambuf_printf(replybuffer, "Fail adding keyspace %s to redis notifications; errono=%d\n", instr->s, errno); + } else if (endptr == instr->s) { + streambuf_printf(replybuffer, "Fail adding keyspace %s to redis notifications; no digists found\n", instr->s); } else { - rwlock_lock_w(&m->conf.config_lock); - if (!g_queue_find(m->conf.redis_subscribed_keyspaces, GUINT_TO_POINTER(uint_keyspace_db))) { - g_queue_push_tail(m->conf.redis_subscribed_keyspaces, GUINT_TO_POINTER(uint_keyspace_db)); - redis_notify_subscribe_action(m, SUBSCRIBE_KEYSPACE, uint_keyspace_db); - printlen = snprintf(replybuffer, outbufend-replybuffer, "Success adding keyspace %lu to redis notifications.\n", uint_keyspace_db); + rwlock_lock_w(&rtpe_config.config_lock); + if (!g_queue_find(&rtpe_config.redis_subscribed_keyspaces, GUINT_TO_POINTER(uint_keyspace_db))) { + g_queue_push_tail(&rtpe_config.redis_subscribed_keyspaces, GUINT_TO_POINTER(uint_keyspace_db)); + redis_notify_subscribe_action(SUBSCRIBE_KEYSPACE, uint_keyspace_db); + streambuf_printf(replybuffer, "Success adding keyspace %lu to redis notifications.\n", uint_keyspace_db); } else { - printlen = snprintf(replybuffer, outbufend-replybuffer, "Keyspace %lu is already among redis notifications.\n", uint_keyspace_db); + streambuf_printf(replybuffer, "Keyspace %lu is already among redis notifications.\n", uint_keyspace_db); } - rwlock_unlock_w(&m->conf.config_lock); + rwlock_unlock_w(&rtpe_config.config_lock); } - ADJUSTLEN(printlen,outbufend,replybuffer); } -static void cli_incoming_ksrm(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen = 0; +static void cli_incoming_ksrm(str *instr, struct streambuf *replybuffer) { GList *l; unsigned long uint_keyspace_db; - str str_keyspace_db; char *endptr; - if (len <= 1) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required."); - ADJUSTLEN(printlen,outbufend,replybuffer); + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); return; } - ++buffer; --len; // one space - str_keyspace_db.s = buffer; - str_keyspace_db.len = len; - uint_keyspace_db = strtoul(str_keyspace_db.s, &endptr, 10); + uint_keyspace_db = strtoul(instr->s, &endptr, 10); - rwlock_lock_w(&m->conf.config_lock); + rwlock_lock_w(&rtpe_config.config_lock); if ((errno == ERANGE && (uint_keyspace_db == ULONG_MAX)) || (errno != 0 && uint_keyspace_db == 0)) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "Fail removing keyspace %.*s to redis notifications; errono=%d\n", str_keyspace_db.len, str_keyspace_db.s, errno); - } else if (endptr == str_keyspace_db.s) { - printlen = snprintf(replybuffer, outbufend-replybuffer, "Fail removing keyspace %.*s to redis notifications; no digists found\n", str_keyspace_db.len, str_keyspace_db.s); - } else if ((l = g_queue_find(m->conf.redis_subscribed_keyspaces, GUINT_TO_POINTER(uint_keyspace_db)))) { + streambuf_printf(replybuffer, "Fail removing keyspace %s to redis notifications; errono=%d\n", instr->s, errno); + } else if (endptr == instr->s) { + streambuf_printf(replybuffer, "Fail removing keyspace %s to redis notifications; no digists found\n", instr->s); + } else if ((l = g_queue_find(&rtpe_config.redis_subscribed_keyspaces, GUINT_TO_POINTER(uint_keyspace_db)))) { // remove this keyspace - redis_notify_subscribe_action(m, UNSUBSCRIBE_KEYSPACE, uint_keyspace_db); - g_queue_remove(m->conf.redis_subscribed_keyspaces, l->data); - printlen = snprintf(replybuffer, outbufend-replybuffer, "Successfully unsubscribed from keyspace %lu.\n", uint_keyspace_db); + redis_notify_subscribe_action(UNSUBSCRIBE_KEYSPACE, uint_keyspace_db); + g_queue_remove(&rtpe_config.redis_subscribed_keyspaces, l->data); + streambuf_printf(replybuffer, "Successfully unsubscribed from keyspace %lu.\n", uint_keyspace_db); // destroy foreign calls for this keyspace - destroy_keyspace_foreign_calls(m, uint_keyspace_db); + destroy_keyspace_foreign_calls(uint_keyspace_db); // update cli - printlen = snprintf(replybuffer, outbufend-replybuffer, "Successfully removed all foreign calls for keyspace %lu.\n", uint_keyspace_db); + streambuf_printf(replybuffer, "Successfully removed all foreign calls for keyspace %lu.\n", uint_keyspace_db); } else { - printlen = snprintf(replybuffer, outbufend-replybuffer, "Keyspace %lu is not among redis notifications.\n", uint_keyspace_db); + streambuf_printf(replybuffer, "Keyspace %lu is not among redis notifications.\n", uint_keyspace_db); } - rwlock_unlock_w(&m->conf.config_lock); + rwlock_unlock_w(&rtpe_config.config_lock); - ADJUSTLEN(printlen,outbufend,replybuffer); } -static void cli_incoming_kslist(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) { - int printlen=0; +static void cli_incoming_kslist(str *instr, struct streambuf *replybuffer) { GList *l; - printlen = snprintf(replybuffer,(outbufend-replybuffer), "\nSubscribed-on keyspaces:\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); + streambuf_printf(replybuffer, "\nSubscribed-on keyspaces:\n"); - rwlock_lock_r(&m->conf.config_lock); - for (l = m->conf.redis_subscribed_keyspaces->head; l; l = l->next) { - printlen = snprintf(replybuffer,(outbufend-replybuffer), "%u ", GPOINTER_TO_UINT(l->data)); - ADJUSTLEN(printlen,outbufend,replybuffer); + rwlock_lock_r(&rtpe_config.config_lock); + for (l = rtpe_config.redis_subscribed_keyspaces.head; l; l = l->next) { + streambuf_printf(replybuffer, "%u ", GPOINTER_TO_UINT(l->data)); } - rwlock_unlock_r(&m->conf.config_lock); + rwlock_unlock_r(&rtpe_config.config_lock); + + streambuf_printf(replybuffer, "\n"); +} - printlen = snprintf(replybuffer, outbufend-replybuffer, "\n"); - ADJUSTLEN(printlen,outbufend,replybuffer); +static void cli_incoming(struct streambuf_stream *s) { + ilog(LOG_INFO, "New cli connection from %s", s->addr); } -static void cli_incoming(int fd, void *p, uintptr_t u) { - int nfd; - struct sockaddr_in sin; - struct cli *cli = (void *) p; - socklen_t sinl; - static const int BUFLENGTH = 4096*1024; - char replybuffer[BUFLENGTH]; - char* outbuf = replybuffer; - const char* outbufend = replybuffer+BUFLENGTH; +static void cli_stream_readable(struct streambuf_stream *s) { static const int MAXINPUT = 1024; - char inbuf[MAXINPUT+1]; - int inlen = 0, readbytes = 0; - int rc=0; - - memset(replybuffer, 0, BUFLENGTH); - - mutex_lock(&cli->lock); -next: - sinl = sizeof(sin); - nfd = accept(fd, (struct sockaddr *) &sin, &sinl); - if (nfd == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - goto cleanup2; + char *inbuf; + str instr; + + inbuf = streambuf_getline(s->inbuf); + if (!inbuf) { + if (streambuf_bufsize(s->inbuf) > MAXINPUT) { + ilog(LOG_INFO, "Buffer length exceeded in CLI connection from %s", s->addr); + streambuf_stream_close(s); } - ilog(LOG_INFO, "Accept error:%s", strerror(errno)); - goto next; + return; } - ilog(LOG_INFO, "New cli connection from " DF, DP(sin)); - - do { - readbytes = read(nfd, inbuf+inlen, MAXINPUT-inlen); - if (readbytes == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s", strerror(errno)); - goto cleanup; - } - ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s", strerror(errno)); - } - inlen += readbytes; - } while (readbytes > 0 && inlen < sizeof(inbuf)-1); - - inbuf[inlen] = 0; ilog(LOG_INFO, "Got CLI command:%s",inbuf); + str_init(&instr, inbuf); - static const char* LIST = "list"; - static const char* TERMINATE = "terminate"; - static const char* SET = "set"; - static const char* KSADD = "ksadd"; - static const char* KSRM = "ksrm"; - static const char* KSLIST = "kslist"; - - if (strncmp(inbuf,LIST,strlen(LIST)) == 0) { - cli_incoming_list(inbuf+strlen(LIST), inlen-strlen(LIST), cli->callmaster, outbuf, outbufend); - } else if (strncmp(inbuf,TERMINATE,strlen(TERMINATE)) == 0) { - cli_incoming_terminate(inbuf+strlen(TERMINATE), inlen-strlen(TERMINATE), cli->callmaster, outbuf, outbufend); - } else if (strncmp(inbuf,SET,strlen(SET)) == 0) { - cli_incoming_set(inbuf+strlen(SET), inlen-strlen(SET), cli->callmaster, outbuf, outbufend); - } else if (strncmp(inbuf,KSADD,strlen(KSADD)) == 0) { - cli_incoming_ksadd(inbuf+strlen(KSADD), inlen-strlen(KSADD), cli->callmaster, outbuf, outbufend); - } else if (strncmp(inbuf,KSRM,strlen(KSRM)) == 0) { - cli_incoming_ksrm(inbuf+strlen(KSRM), inlen-strlen(KSRM), cli->callmaster, outbuf, outbufend); - } else if (strncmp(inbuf,KSLIST,strlen(KSLIST)) == 0) { - cli_incoming_kslist(inbuf+strlen(KSLIST), inlen-strlen(KSLIST), cli->callmaster, outbuf, outbufend); - } else { - sprintf(replybuffer, "%s:%s\n", "Unknown or incomplete command:", inbuf); - } - - do { - rc += write( nfd, (char *)&replybuffer, strlen(replybuffer) ); - } while (rc < strlen(replybuffer)); + cli_handler_do(cli_top_handlers, &instr, s->outbuf); -cleanup: - close(nfd); - /* in case multiple incoming connections exist, read all of them */ - goto next; -cleanup2: - mutex_unlock(&cli->lock); + free(inbuf); + streambuf_stream_shutdown(s); log_info_clear(); } -static void control_closed(int fd, void *p, uintptr_t u) { - abort(); -} - -struct cli *cli_new(struct poller *p, const endpoint_t *ep, struct callmaster *m) { +struct cli *cli_new(struct poller *p, endpoint_t *ep) { struct cli *c; - socket_t sock; - struct poller_item i; - if (!p || !m) + if (!p) return NULL; - if (open_socket(&sock, SOCK_STREAM, ep->port, &ep->address)) - return NULL; + c = obj_alloc0("cli", sizeof(*c), NULL); - if (listen(sock.fd, 5)) - goto fail; + if (streambuf_listener_init(&c->listeners[0], p, ep, + cli_incoming, cli_stream_readable, + NULL, + NULL, + &c->obj)) + { + ilog(LOG_ERR, "Failed to open TCP control port: %s", strerror(errno)); + goto fail; + } + if (ipv46_any_convert(ep)) { + if (streambuf_listener_init(&c->listeners[1], p, ep, + cli_incoming, cli_stream_readable, + NULL, + NULL, + &c->obj)) + { + ilog(LOG_ERR, "Failed to open TCP control port: %s", strerror(errno)); + goto fail; + } + } - c = obj_alloc0("cli_udp", sizeof(*c), NULL); - c->sock = sock; c->poller = p; - c->callmaster = m; - mutex_init(&c->lock); - - ZERO(i); - i.fd = sock.fd; - i.closed = control_closed; - i.readable = cli_incoming; - i.obj = &c->obj; - if (poller_add_item(p, &i)) - goto fail2; obj_put(c); return c; -fail2: - obj_put(c); fail: - close_socket(&sock); + // XXX streambuf_listener_close ... + obj_put(c); return NULL; } + +static void cli_incoming_list_loglevel(str *instr, struct streambuf *replybuffer) { + streambuf_printf(replybuffer, "%i\n", get_log_level()); +} +static void cli_incoming_set_loglevel(str *instr, struct streambuf *replybuffer) { + int nl; + + if (str_shift(instr, 1)) { + streambuf_printf(replybuffer, "%s\n", "More parameters required."); + return; + } + + nl = atoi(instr->s); + if (nl < 1 || nl > 7) { + streambuf_printf(replybuffer, "Invalid log level '%s', must be number between 1 and 7\n", + instr->s); + return; + } + + g_atomic_int_set(&rtpe_config.common.log_level, nl); + streambuf_printf(replybuffer, "Success setting loglevel to %i\n", nl); +} diff --git a/daemon/cli.h b/daemon/cli.h index e5065b4ca..7f6264d55 100644 --- a/daemon/cli.h +++ b/daemon/cli.h @@ -3,17 +3,16 @@ #include "socket.h" #include "obj.h" +#include "tcp_listener.h" struct cli { struct obj obj; - struct callmaster *callmaster; - socket_t sock; struct poller *poller; - mutex_t lock; + struct streambuf_listener listeners[2]; }; -struct cli *cli_new(struct poller *p, const endpoint_t *, struct callmaster *m); +struct cli *cli_new(struct poller *p, endpoint_t *); #endif /* CLI_UDP_H_ */ diff --git a/daemon/control_ng.c b/daemon/control_ng.c index c956be322..fc10944b2 100644 --- a/daemon/control_ng.c +++ b/daemon/control_ng.c @@ -16,6 +16,10 @@ #include "log_funcs.h" +mutex_t rtpe_cngs_lock; +GHashTable *rtpe_cngs_hash; + + static void timeval_update_request_time(struct request_time *request, const struct timeval *offer_diff) { // lock offers @@ -88,23 +92,22 @@ static void pretty_print(bencode_item_t *el, GString *s) { } struct control_ng_stats* get_control_ng_stats(struct control_ng* c, const sockaddr_t *addr) { - struct callmaster *m = c->callmaster; struct control_ng_stats* cur; - mutex_lock(&m->cngs_lock); - cur = g_hash_table_lookup(m->cngs_hash, addr); + mutex_lock(&rtpe_cngs_lock); + cur = g_hash_table_lookup(rtpe_cngs_hash, addr); if (!cur) { cur = g_slice_alloc0(sizeof(struct control_ng_stats)); cur->proxy = *addr; ilog(LOG_DEBUG,"Adding a proxy for control ng stats:%s", sockaddr_print_buf(addr)); - g_hash_table_insert(m->cngs_hash, &cur->proxy, cur); + g_hash_table_insert(rtpe_cngs_hash, &cur->proxy, cur); } - mutex_unlock(&m->cngs_lock); + mutex_unlock(&rtpe_cngs_lock); return cur; } static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin, char *addr, - struct udp_listener *ul) + socket_t *ul) { struct control_ng *c = (void *) obj; bencode_buffer_t bencbuf; @@ -126,7 +129,8 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin return; } - assert( bencode_buffer_init(&bencbuf) == 0 ); + int ret = bencode_buffer_init(&bencbuf); + assert(ret == 0); resp = bencode_dictionary(&bencbuf); assert(resp != NULL); @@ -169,6 +173,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin g_string_free(log_str, TRUE); } + // XXX do the strcmp's only once errstr = NULL; resultstr = "ok"; if (!str_cmp(&cmd, "ping")) { @@ -179,7 +184,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin // start offer timer gettimeofday(&offer_start, NULL); - errstr = call_offer_ng(dict, c->callmaster, resp, addr, sin); + errstr = call_offer_ng(dict, resp, addr, sin); g_atomic_int_inc(&cur->offer); // stop offer timer @@ -193,7 +198,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin // start answer timer gettimeofday(&answer_start, NULL); - errstr = call_answer_ng(dict, c->callmaster, resp); + errstr = call_answer_ng(dict, resp); g_atomic_int_inc(&cur->answer); // stop answer timer @@ -207,7 +212,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin // start delete timer gettimeofday(&delete_start, NULL); - errstr = call_delete_ng(dict, c->callmaster, resp); + errstr = call_delete_ng(dict, resp); g_atomic_int_inc(&cur->delete); // stop delete timer @@ -218,19 +223,19 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin ilog(LOG_INFO, "delete time = %llu.%06llu sec", (unsigned long long)delete_stop.tv_sec, (unsigned long long)delete_stop.tv_usec); } else if (!str_cmp(&cmd, "query")) { - errstr = call_query_ng(dict, c->callmaster, resp); + errstr = call_query_ng(dict, resp); g_atomic_int_inc(&cur->query); } else if (!str_cmp(&cmd, "list")) { - errstr = call_list_ng(dict, c->callmaster, resp); + errstr = call_list_ng(dict, resp); g_atomic_int_inc(&cur->list); } else if (!str_cmp(&cmd, "start recording")) { - errstr = call_start_recording_ng(dict, c->callmaster, resp); + errstr = call_start_recording_ng(dict, resp); g_atomic_int_inc(&cur->start_recording); } else if (!str_cmp(&cmd, "stop recording")) { - errstr = call_stop_recording_ng(dict, c->callmaster, resp); + errstr = call_stop_recording_ng(dict, resp); g_atomic_int_inc(&cur->stop_recording); } else @@ -243,11 +248,11 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin // update interval statistics if (!str_cmp(&cmd, "offer")) { - timeval_update_request_time(&c->callmaster->totalstats_interval.offer, &offer_stop); + timeval_update_request_time(&rtpe_totalstats_interval.offer, &offer_stop); } else if (!str_cmp(&cmd, "answer")) { - timeval_update_request_time(&c->callmaster->totalstats_interval.answer, &answer_stop); + timeval_update_request_time(&rtpe_totalstats_interval.answer, &answer_stop); } else if (!str_cmp(&cmd, "delete")) { - timeval_update_request_time(&c->callmaster->totalstats_interval.delete, &delete_stop); + timeval_update_request_time(&rtpe_totalstats_interval.delete, &delete_stop); } goto send_resp; @@ -289,7 +294,7 @@ send_only: iov[2].iov_base = to_send->s; iov[2].iov_len = to_send->len; - socket_sendiov(&ul->sock, iov, iovlen, sin); + socket_sendiov(ul, iov, iovlen, sin); if (resp) cookie_cache_insert(&c->cookie_cache, &cookie, &reply); @@ -305,22 +310,26 @@ out: -struct control_ng *control_ng_new(struct poller *p, endpoint_t *ep, struct callmaster *m) { +struct control_ng *control_ng_new(struct poller *p, endpoint_t *ep, unsigned char tos) { struct control_ng *c; - if (!p || !m) + if (!p) return NULL; c = obj_alloc0("control_ng", sizeof(*c), NULL); - c->callmaster = m; cookie_cache_init(&c->cookie_cache); if (udp_listener_init(&c->udp_listeners[0], p, ep, control_ng_incoming, &c->obj)) goto fail2; - if (ipv46_any_convert(ep) && udp_listener_init(&c->udp_listeners[1], p, ep, control_ng_incoming, &c->obj)) - goto fail2; - + if (tos) + set_tos(&c->udp_listeners[0],tos); + if (ipv46_any_convert(ep)) { + if (udp_listener_init(&c->udp_listeners[1], p, ep, control_ng_incoming, &c->obj)) + goto fail2; + if (tos) + set_tos(&c->udp_listeners[1],tos); + } return c; fail2: @@ -328,3 +337,9 @@ fail2: return NULL; } + + +void control_ng_init() { + mutex_init(&rtpe_cngs_lock); + rtpe_cngs_hash = g_hash_table_new(g_sockaddr_hash, g_sockaddr_eq); +} diff --git a/daemon/control_ng.h b/daemon/control_ng.h index fe4ee9a6c..e5f2d0429 100644 --- a/daemon/control_ng.h +++ b/daemon/control_ng.h @@ -8,7 +8,6 @@ struct poller; -struct callmaster; struct control_ng_stats { sockaddr_t proxy; @@ -25,11 +24,14 @@ struct control_ng_stats { struct control_ng { struct obj obj; - struct callmaster *callmaster; struct cookie_cache cookie_cache; - struct udp_listener udp_listeners[2]; + socket_t udp_listeners[2]; }; -struct control_ng *control_ng_new(struct poller *, endpoint_t *, struct callmaster *); +struct control_ng *control_ng_new(struct poller *, endpoint_t *, unsigned char); +void control_ng_init(void); + +extern mutex_t rtpe_cngs_lock; +extern GHashTable *rtpe_cngs_hash; #endif diff --git a/daemon/control_tcp.c b/daemon/control_tcp.c index f65a048cb..35af4ce45 100644 --- a/daemon/control_tcp.c +++ b/daemon/control_tcp.c @@ -18,101 +18,67 @@ #include "call_interfaces.h" #include "socket.h" #include "log_funcs.h" +#include "tcp_listener.h" -struct control_stream { - struct obj obj; - - int fd; - mutex_t lock; - struct streambuf *inbuf; - struct streambuf *outbuf; - struct sockaddr_in inaddr; - - struct control_tcp *control; - struct poller *poller; - int linked:1; -}; - - struct control_tcp { struct obj obj; - int fd; + struct streambuf_listener listeners[2]; + pcre *parse_re; pcre_extra *parse_ree; - mutex_t lock; - GList *streams; - struct poller *poller; - struct callmaster *callmaster; }; -static void control_stream_closed(int fd, void *p, uintptr_t u) { - struct control_stream *s = p; - struct control_tcp *c; - GList *l = NULL; +//static void control_stream_closed(int fd, void *p, uintptr_t u) { +static void control_stream_closed(struct streambuf_stream *s) { + ilog(LOG_INFO, "Control connection from %s closed", s->addr); +} - ilog(LOG_INFO, "Control connection from " DF " closed", DP(s->inaddr)); - c = s->control; +static void control_list(struct control_tcp *c, struct streambuf_stream *s) { + for (int i = 0; i < G_N_ELEMENTS(c->listeners); i++) { + if (!c->listeners[i].listener.family || !c->listeners[i].poller) + continue; // not used -restart: - mutex_lock(&c->lock); - if (s->linked) { - /* we might get called when it's not quite linked yet */ - l = g_list_find(c->streams, s); - if (!l) { - mutex_unlock(&c->lock); - goto restart; - } - c->streams = g_list_delete_link(c->streams, l); - s->linked = 0; - } - mutex_unlock(&c->lock); - if (l) - obj_put(s); - poller_del_item(s->poller, fd); -} + mutex_lock(&c->listeners[i].lock); + GList *streams = g_hash_table_get_values(c->listeners[i].streams); + for (GList *l = streams; l; l = l->next) { + struct streambuf_stream *cl = l->data; + streambuf_printf(s->outbuf, "%s\n", cl->addr); + } -static void control_list(struct control_tcp *c, struct control_stream *s) { - struct control_stream *i; - GList *l; + mutex_unlock(&c->listeners[i].lock); - mutex_lock(&c->lock); - for (l = c->streams; l; l = l->next) { - i = l->data; - mutex_lock(&s->lock); - streambuf_printf(s->outbuf, DF "\n", DP(i->inaddr)); - mutex_unlock(&s->lock); + g_list_free(streams); } - mutex_unlock(&c->lock); streambuf_printf(s->outbuf, "End.\n"); } -static int control_stream_parse(struct control_stream *s, char *line) { +static int control_stream_parse(struct streambuf_stream *s, char *line) { int ovec[60]; int ret; char **out; - struct control_tcp *c = s->control; + struct control_tcp *c = (void *) s->parent; str *output = NULL; ret = pcre_exec(c->parse_re, c->parse_ree, line, strlen(line), 0, 0, ovec, G_N_ELEMENTS(ovec)); if (ret <= 0) { - ilog(LOG_WARNING, "Unable to parse command line from " DF ": %s", DP(s->inaddr), line); + ilog(LOG_WARNING, "Unable to parse command line from %s: %s", s->addr, line); return -1; } - ilog(LOG_INFO, "Got valid command from " DF ": %s", DP(s->inaddr), line); + ilog(LOG_INFO, "Got valid command from %s: %s", s->addr, line); pcre_get_substring_list(line, ovec, ret, (const char ***) &out); @@ -124,175 +90,100 @@ static int control_stream_parse(struct control_stream *s, char *line) { if (!strcmp(out[RE_TCP_RL_CMD], "request")) - output = call_request_tcp(out, c->callmaster); + output = call_request_tcp(out); else if (!strcmp(out[RE_TCP_RL_CMD], "lookup")) - output = call_lookup_tcp(out, c->callmaster); + output = call_lookup_tcp(out); else if (!strcmp(out[RE_TCP_D_CMD], "delete")) - call_delete_tcp(out, c->callmaster); + call_delete_tcp(out); else if (!strcmp(out[RE_TCP_DIV_CMD], "status")) - calls_status_tcp(c->callmaster, s); + calls_status_tcp(s); else if (!strcmp(out[RE_TCP_DIV_CMD], "build") || !strcmp(out[RE_TCP_DIV_CMD], "version")) - control_stream_printf(s, "Version: %s\n", RTPENGINE_VERSION); + streambuf_printf(s->outbuf, "Version: %s\n", RTPENGINE_VERSION); else if (!strcmp(out[RE_TCP_DIV_CMD], "controls")) control_list(c, s); else if (!strcmp(out[RE_TCP_DIV_CMD], "quit") || !strcmp(out[RE_TCP_DIV_CMD], "exit")) ; if (output) { - mutex_lock(&s->lock); streambuf_write_str(s->outbuf, output); - mutex_unlock(&s->lock); free(output); } pcre_free(out); log_info_clear(); - return -1; + return 1; } -static void control_stream_timer(int fd, void *p, uintptr_t u) { - struct control_stream *s = p; - int i; - - mutex_lock(&s->lock); - i = (poller_now - s->inbuf->active) >= 60 || (poller_now - s->outbuf->active) >= 60; - mutex_unlock(&s->lock); - - if (i) - control_stream_closed(s->fd, s, 0); +static void control_stream_timer(struct streambuf_stream *s) { + if ((rtpe_now.tv_sec - s->inbuf->active) >= 60 || (rtpe_now.tv_sec - s->outbuf->active) >= 60) + control_stream_closed(s); } -static void control_stream_readable(int fd, void *p, uintptr_t u) { - struct control_stream *s = p; +//static void control_stream_readable(int fd, void *p, uintptr_t u) { +static void control_stream_readable(struct streambuf_stream *s) { char *line; int ret; - mutex_lock(&s->lock); - - if (streambuf_readable(s->inbuf)) - goto close; - while ((line = streambuf_getline(s->inbuf))) { - mutex_unlock(&s->lock); - ilog(LOG_DEBUG, "Got control line from " DF ": %s", DP(s->inaddr), line); + ilog(LOG_DEBUG, "Got control line from %s: %s", s->addr, line); ret = control_stream_parse(s, line); free(line); + if (ret == 1) { + streambuf_stream_shutdown(s); + break; + } if (ret) - goto close_nolock; - mutex_lock(&s->lock); + goto close; } if (streambuf_bufsize(s->inbuf) > 1024) { - ilog(LOG_WARNING, "Buffer length exceeded in control connection from " DF, DP(s->inaddr)); + ilog(LOG_WARNING, "Buffer length exceeded in control connection from %s", s->addr); goto close; } - mutex_unlock(&s->lock); return; close: - mutex_unlock(&s->lock); -close_nolock: - control_stream_closed(fd, s, 0); -} - -static void control_stream_writeable(int fd, void *p, uintptr_t u) { - struct control_stream *s = p; - - if (streambuf_writeable(s->outbuf)) - control_stream_closed(fd, s, 0); + streambuf_stream_close(s); } -static void control_closed(int fd, void *p, uintptr_t u) { - abort(); +static void control_incoming(struct streambuf_stream *s) { + ilog(LOG_INFO, "New TCP control connection from %s", s->addr); } -static void control_stream_free(void *p) { - struct control_stream *s = p; - - close(s->fd); - streambuf_destroy(s->inbuf); - streambuf_destroy(s->outbuf); - mutex_destroy(&s->lock); -} -static void control_incoming(int fd, void *p, uintptr_t u) { - int nfd; - struct control_tcp *c = p; - struct control_stream *s; - struct poller_item i; - struct sockaddr_in sin; - socklen_t sinl; - -next: - sinl = sizeof(sin); - nfd = accept(fd, (struct sockaddr *) &sin, &sinl); - if (nfd == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) - return; - goto next; - } - nonblock(nfd); - - ilog(LOG_INFO, "New control connection from " DF, DP(sin)); - - s = obj_alloc0("control_stream", sizeof(*s), control_stream_free); - - s->fd = nfd; - s->control = c; - s->poller = c->poller; - s->inbuf = streambuf_new(c->poller, nfd); - s->outbuf = streambuf_new(c->poller, nfd); - memcpy(&s->inaddr, &sin, sizeof(s->inaddr)); - mutex_init(&s->lock); - s->linked = 1; - - ZERO(i); - i.fd = nfd; - i.closed = control_stream_closed; - i.readable = control_stream_readable; - i.writeable = control_stream_writeable; - i.timer = control_stream_timer; - i.obj = &s->obj; - - if (poller_add_item(c->poller, &i)) - goto fail; - - mutex_lock(&c->lock); - /* let the list steal our own ref */ - c->streams = g_list_prepend(c->streams, s); - mutex_unlock(&c->lock); - - goto next; - -fail: - obj_put(s); - goto next; -} - - -struct control_tcp *control_tcp_new(struct poller *p, const endpoint_t *ep, struct callmaster *m) { - socket_t sock; +struct control_tcp *control_tcp_new(struct poller *p, endpoint_t *ep) { struct control_tcp *c; - struct poller_item i; const char *errptr; int erroff; if (!p) return NULL; - if (!m) - return NULL; - if (open_socket(&sock, SOCK_STREAM, ep->port, &ep->address)) - return NULL; + c = obj_alloc0("control", sizeof(*c), NULL); - if (listen(sock.fd, 5)) + if (streambuf_listener_init(&c->listeners[0], p, ep, + control_incoming, control_stream_readable, + control_stream_closed, + control_stream_timer, + &c->obj)) + { + ilog(LOG_ERR, "Failed to open TCP control port: %s", strerror(errno)); goto fail; - - - c = obj_alloc0("control", sizeof(*c), NULL); + } + if (ipv46_any_convert(ep)) { + if (streambuf_listener_init(&c->listeners[1], p, ep, + control_incoming, control_stream_readable, + control_stream_closed, + control_stream_timer, + &c->obj)) + { + ilog(LOG_ERR, "Failed to open TCP control port: %s", strerror(errno)); + goto fail; + } + } c->parse_re = pcre_compile( /* reqtype callid streams ip fromdom fromtype todom totype agent info |reqtype callid info | reqtype */ @@ -300,36 +191,13 @@ struct control_tcp *control_tcp_new(struct poller *p, const endpoint_t *ep, stru PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL); c->parse_ree = pcre_study(c->parse_re, 0, &errptr); - c->fd = sock.fd; c->poller = p; - c->callmaster = m; - mutex_init(&c->lock); - - ZERO(i); - i.fd = sock.fd; - i.closed = control_closed; - i.readable = control_incoming; - i.obj = &c->obj; - if (poller_add_item(p, &i)) - goto fail2; obj_put(c); return c; -fail2: - obj_put(c); fail: - close_socket(&sock); + // XXX streambuf_listener_close ... + obj_put(c); return NULL; } - - -void control_stream_printf(struct control_stream *s, const char *f, ...) { - va_list va; - - va_start(va, f); - mutex_lock(&s->lock); - streambuf_vprintf(s->outbuf, f, va); - mutex_unlock(&s->lock); - va_end(va); -} diff --git a/daemon/control_tcp.h b/daemon/control_tcp.h index 06b3c298f..0b5c36268 100644 --- a/daemon/control_tcp.h +++ b/daemon/control_tcp.h @@ -30,14 +30,12 @@ #define RE_TCP_DIV_CMD 14 struct poller; -struct callmaster; struct control_tcp; -struct control_stream; +struct streambuf_stream; -struct control_tcp *control_tcp_new(struct poller *, const endpoint_t *, struct callmaster *); -void control_stream_printf(struct control_stream *, const char *, ...) __attribute__ ((format (printf, 2, 3))); +struct control_tcp *control_tcp_new(struct poller *, endpoint_t *); diff --git a/daemon/control_udp.c b/daemon/control_udp.c index 0cf4eeee4..ee34c69f8 100644 --- a/daemon/control_udp.c +++ b/daemon/control_udp.c @@ -22,7 +22,7 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *sin, char *addr, - struct udp_listener *ul) { + socket_t *ul) { struct control_udp *u = (void *) obj; int ret; int ovec[100]; @@ -60,7 +60,7 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *si iovlen = 2; } - socket_sendiov(&ul->sock, iov, iovlen, sin); + socket_sendiov(ul, iov, iovlen, sin); pcre_free(out); @@ -75,7 +75,7 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *si reply = cookie_cache_lookup(&u->cookie_cache, &cookie); if (reply) { ilog(LOG_INFO, "Detected command from udp:%s as a duplicate", addr); - socket_sendto(&ul->sock, reply->s, reply->len, sin); + socket_sendto(ul, reply->s, reply->len, sin); free(reply); goto out; } @@ -86,13 +86,13 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *si log_info_c_string(out[RE_UDP_DQ_CALLID]); if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U') - reply = call_update_udp(out, u->callmaster, addr, sin); + reply = call_update_udp(out, addr, sin); else if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L') - reply = call_lookup_udp(out, u->callmaster); + reply = call_lookup_udp(out); else if (chrtoupper(out[RE_UDP_DQ_CMD][0]) == 'D') - reply = call_delete_udp(out, u->callmaster); + reply = call_delete_udp(out); else if (chrtoupper(out[RE_UDP_DQ_CMD][0]) == 'Q') - reply = call_query_udp(out, u->callmaster); + reply = call_query_udp(out); else if (chrtoupper(out[RE_UDP_V_CMD][0]) == 'V') { iovlen = 2; @@ -118,11 +118,11 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *si iov[2].iov_len = 9; iovlen++; } - socket_sendiov(&ul->sock, iov, iovlen, sin); + socket_sendiov(ul, iov, iovlen, sin); } if (reply) { - socket_sendto(&ul->sock, reply->s, reply->len, sin); + socket_sendto(ul, reply->s, reply->len, sin); cookie_cache_insert(&u->cookie_cache, &cookie, reply); free(reply); } @@ -134,17 +134,16 @@ out: log_info_clear(); } -struct control_udp *control_udp_new(struct poller *p, endpoint_t *ep, struct callmaster *m) { +struct control_udp *control_udp_new(struct poller *p, endpoint_t *ep) { struct control_udp *c; const char *errptr; int erroff; - if (!p || !m) + if (!p) return NULL; c = obj_alloc0("control_udp", sizeof(*c), NULL); - c->callmaster = m; c->parse_re = pcre_compile( /* cookie cmd flags callid viabranch:5 */ "^(\\S+)\\s+(?:([ul])(\\S*)\\s+([^;]+)(?:;(\\S+))?\\s+" \ diff --git a/daemon/control_udp.h b/daemon/control_udp.h index 0381c39e0..420b9fb62 100644 --- a/daemon/control_udp.h +++ b/daemon/control_udp.h @@ -39,7 +39,6 @@ #define RE_UDP_V_PARMS 20 struct poller; -struct callmaster; @@ -48,9 +47,8 @@ struct callmaster; struct control_udp { struct obj obj; - struct callmaster *callmaster; struct cookie_cache cookie_cache; - struct udp_listener udp_listeners[2]; + socket_t udp_listeners[2]; pcre *parse_re; pcre_extra *parse_ree; @@ -61,7 +59,7 @@ struct control_udp { -struct control_udp *control_udp_new(struct poller *, endpoint_t *, struct callmaster *); +struct control_udp *control_udp_new(struct poller *, endpoint_t *); diff --git a/daemon/cookie_cache.c b/daemon/cookie_cache.c index 8f5b2052d..bbcf7c552 100644 --- a/daemon/cookie_cache.c +++ b/daemon/cookie_cache.c @@ -18,19 +18,19 @@ INLINE void cookie_cache_state_init(struct cookie_cache_state *s) { void cookie_cache_init(struct cookie_cache *c) { cookie_cache_state_init(&c->current); cookie_cache_state_init(&c->old); - c->swap_time = poller_now; + c->swap_time = rtpe_now.tv_sec; mutex_init(&c->lock); cond_init(&c->cond); } /* lock must be held */ static void __cookie_cache_check_swap(struct cookie_cache *c) { - if (poller_now - c->swap_time >= 30) { + if (rtpe_now.tv_sec - c->swap_time >= 30) { g_hash_table_remove_all(c->old.cookies); g_string_chunk_clear(c->old.chunks); swap_ptrs(&c->old.chunks, &c->current.chunks); swap_ptrs(&c->old.cookies, &c->current.cookies); - c->swap_time = poller_now; + c->swap_time = rtpe_now.tv_sec; } } diff --git a/daemon/dtls.c b/daemon/dtls.c index 9943901e7..3915b39fe 100644 --- a/daemon/dtls.c +++ b/daemon/dtls.c @@ -317,7 +317,7 @@ static void __dtls_timer(void *p) { long int left; c = dtls_cert(); - left = c->expires - poller_now; + left = c->expires - rtpe_now.tv_sec; if (left > CERT_EXPIRY_TIME/2) goto out; diff --git a/daemon/graphite.c b/daemon/graphite.c index 4003d5eb0..39e2343d3 100644 --- a/daemon/graphite.c +++ b/daemon/graphite.c @@ -20,12 +20,16 @@ #include "call.h" #include "graphite.h" #include "socket.h" +#include "statistics.h" +#include "main.h" + +struct timeval rtpe_latest_graphite_interval_start; static socket_t graphite_sock; static int connection_state = STATE_DISCONNECTED; //struct totalstats totalstats_prev; static time_t next_run; -// HEAD: static time_t g_now, next_run; +// HEAD: static time_t rtpe_now, next_run; static char* graphite_prefix = NULL; static struct timeval graphite_interval_tv; static struct totalstats graphite_stats; @@ -81,16 +85,10 @@ int connect_to_graphite_server(const endpoint_t *graphite_ep) { return 0; } -int send_graphite_data(struct callmaster *cm, struct totalstats *sent_data) { +int send_graphite_data(struct totalstats *sent_data) { int rc=0; - // sanity checks - if (!cm) { - ilog(LOG_ERROR, "NULL callmaster when trying to send data"); - return -1; - } - if (graphite_sock.fd < 0) { ilog(LOG_ERROR,"Graphite socket is not connected."); return -1; @@ -102,46 +100,46 @@ int send_graphite_data(struct callmaster *cm, struct totalstats *sent_data) { struct totalstats *ts = sent_data; /* atomically copy values to stack and reset to zero */ - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_timeout_sess); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_rejected_sess); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_silent_timeout_sess); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_final_timeout_sess); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_regular_term_sess); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_forced_term_sess); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_relayed_packets); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_relayed_errors); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_nopacket_relayed_sess); - atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_oneway_stream_sess); - - mutex_lock(&cm->totalstats_interval.total_average_lock); - ts->total_average_call_dur = cm->totalstats_interval.total_average_call_dur; - ts->total_managed_sess = cm->totalstats_interval.total_managed_sess; - ZERO(cm->totalstats_interval.total_average_call_dur); - ZERO(cm->totalstats_interval.total_managed_sess); - mutex_unlock(&cm->totalstats_interval.total_average_lock); - - mutex_lock(&cm->totalstats_interval.total_calls_duration_lock); - ts->total_calls_duration_interval = cm->totalstats_interval.total_calls_duration_interval; - cm->totalstats_interval.total_calls_duration_interval.tv_sec = 0; - cm->totalstats_interval.total_calls_duration_interval.tv_usec = 0; - //ZERO(cm->totalstats_interval.total_calls_duration_interval); - mutex_unlock(&cm->totalstats_interval.total_calls_duration_lock); - - ts->offer = timeval_clear_request_time(&cm->totalstats_interval.offer); - ts->answer = timeval_clear_request_time(&cm->totalstats_interval.answer); - ts->delete = timeval_clear_request_time(&cm->totalstats_interval.delete); - - rwlock_lock_r(&cm->hashlock); - mutex_lock(&cm->totalstats_interval.managed_sess_lock); - ts->managed_sess_max = cm->totalstats_interval.managed_sess_max; - ts->managed_sess_min = cm->totalstats_interval.managed_sess_min; - ts->total_sessions = g_hash_table_size(cm->callhash); - ts->foreign_sessions = atomic64_get(&cm->stats.foreign_sessions); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_timeout_sess); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_rejected_sess); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_silent_timeout_sess); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_final_timeout_sess); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_regular_term_sess); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_forced_term_sess); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_relayed_packets); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_relayed_errors); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_nopacket_relayed_sess); + atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_oneway_stream_sess); + + mutex_lock(&rtpe_totalstats_interval.total_average_lock); + ts->total_average_call_dur = rtpe_totalstats_interval.total_average_call_dur; + ts->total_managed_sess = rtpe_totalstats_interval.total_managed_sess; + ZERO(rtpe_totalstats_interval.total_average_call_dur); + ZERO(rtpe_totalstats_interval.total_managed_sess); + mutex_unlock(&rtpe_totalstats_interval.total_average_lock); + + mutex_lock(&rtpe_totalstats_interval.total_calls_duration_lock); + ts->total_calls_duration_interval = rtpe_totalstats_interval.total_calls_duration_interval; + rtpe_totalstats_interval.total_calls_duration_interval.tv_sec = 0; + rtpe_totalstats_interval.total_calls_duration_interval.tv_usec = 0; + //ZERO(rtpe_totalstats_interval.total_calls_duration_interval); + mutex_unlock(&rtpe_totalstats_interval.total_calls_duration_lock); + + ts->offer = timeval_clear_request_time(&rtpe_totalstats_interval.offer); + ts->answer = timeval_clear_request_time(&rtpe_totalstats_interval.answer); + ts->delete = timeval_clear_request_time(&rtpe_totalstats_interval.delete); + + rwlock_lock_r(&rtpe_callhash_lock); + mutex_lock(&rtpe_totalstats_interval.managed_sess_lock); + ts->managed_sess_max = rtpe_totalstats_interval.managed_sess_max; + ts->managed_sess_min = rtpe_totalstats_interval.managed_sess_min; + ts->total_sessions = g_hash_table_size(rtpe_callhash); + ts->foreign_sessions = atomic64_get(&rtpe_stats.foreign_sessions); ts->own_sessions = ts->total_sessions - ts->foreign_sessions; - cm->totalstats_interval.managed_sess_max = ts->own_sessions;; - cm->totalstats_interval.managed_sess_min = ts->own_sessions; - mutex_unlock(&cm->totalstats_interval.managed_sess_lock); - rwlock_unlock_r(&cm->hashlock); + rtpe_totalstats_interval.managed_sess_max = ts->own_sessions;; + rtpe_totalstats_interval.managed_sess_min = ts->own_sessions; + mutex_unlock(&rtpe_totalstats_interval.managed_sess_lock); + rwlock_unlock_r(&rtpe_callhash_lock); // compute average offer/answer/delete time timeval_divide(&ts->offer.time_avg, &ts->offer.time_avg, ts->offer.count); @@ -149,69 +147,69 @@ int send_graphite_data(struct callmaster *cm, struct totalstats *sent_data) { timeval_divide(&ts->delete.time_avg, &ts->delete.time_avg, ts->delete.count); if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"offer_time_min %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_min.tv_sec,(unsigned long long)ts->offer.time_min.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"offer_time_min %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_min.tv_sec,(unsigned long long)ts->offer.time_min.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"offer_time_max %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_max.tv_sec,(unsigned long long)ts->offer.time_max.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"offer_time_max %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_max.tv_sec,(unsigned long long)ts->offer.time_max.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"offer_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_avg.tv_sec,(unsigned long long)ts->offer.time_avg.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"offer_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_avg.tv_sec,(unsigned long long)ts->offer.time_avg.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"answer_time_min %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_min.tv_sec,(unsigned long long)ts->answer.time_min.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"answer_time_min %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_min.tv_sec,(unsigned long long)ts->answer.time_min.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"answer_time_max %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_max.tv_sec,(unsigned long long)ts->answer.time_max.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"answer_time_max %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_max.tv_sec,(unsigned long long)ts->answer.time_max.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"answer_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_avg.tv_sec,(unsigned long long)ts->answer.time_avg.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"answer_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_avg.tv_sec,(unsigned long long)ts->answer.time_avg.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"delete_time_min %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_min.tv_sec,(unsigned long long)ts->delete.time_min.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"delete_time_min %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_min.tv_sec,(unsigned long long)ts->delete.time_min.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"delete_time_max %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_max.tv_sec,(unsigned long long)ts->delete.time_max.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"delete_time_max %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_max.tv_sec,(unsigned long long)ts->delete.time_max.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"delete_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_avg.tv_sec,(unsigned long long)ts->delete.time_avg.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"delete_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_avg.tv_sec,(unsigned long long)ts->delete.time_avg.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr, "call_dur %llu.%06llu %llu\n",(unsigned long long)ts->total_calls_duration_interval.tv_sec,(unsigned long long)ts->total_calls_duration_interval.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr, "call_dur %llu.%06llu %llu\n",(unsigned long long)ts->total_calls_duration_interval.tv_sec,(unsigned long long)ts->total_calls_duration_interval.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"average_call_dur %llu.%06llu %llu\n",(unsigned long long)ts->total_average_call_dur.tv_sec,(unsigned long long)ts->total_average_call_dur.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"average_call_dur %llu.%06llu %llu\n",(unsigned long long)ts->total_average_call_dur.tv_sec,(unsigned long long)ts->total_average_call_dur.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"forced_term_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_forced_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"forced_term_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_forced_term_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"managed_sess "UINT64F" %llu\n", ts->total_managed_sess,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"managed_sess "UINT64F" %llu\n", ts->total_managed_sess,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"managed_sess_min "UINT64F" %llu\n", ts->managed_sess_min,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"managed_sess_min "UINT64F" %llu\n", ts->managed_sess_min,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"managed_sess_max "UINT64F" %llu\n", ts->managed_sess_max,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"managed_sess_max "UINT64F" %llu\n", ts->managed_sess_max,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"current_sessions_total "UINT64F" %llu\n", ts->total_sessions,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"current_sessions_total "UINT64F" %llu\n", ts->total_sessions,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"current_sessions_own "UINT64F" %llu\n", ts->own_sessions,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"current_sessions_own "UINT64F" %llu\n", ts->own_sessions,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"current_sessions_foreign "UINT64F" %llu\n", ts->foreign_sessions,(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"current_sessions_foreign "UINT64F" %llu\n", ts->foreign_sessions,(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"nopacket_relayed_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_nopacket_relayed_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"nopacket_relayed_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_nopacket_relayed_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"oneway_stream_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_oneway_stream_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"oneway_stream_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_oneway_stream_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"regular_term_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_regular_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"regular_term_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_regular_term_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"relayed_errors "UINT64F" %llu\n", atomic64_get_na(&ts->total_relayed_errors),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"relayed_errors "UINT64F" %llu\n", atomic64_get_na(&ts->total_relayed_errors),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"relayed_packets "UINT64F" %llu\n", atomic64_get_na(&ts->total_relayed_packets),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"relayed_packets "UINT64F" %llu\n", atomic64_get_na(&ts->total_relayed_packets),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"silent_timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_silent_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"silent_timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_silent_timeout_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"final_timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_final_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"final_timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_final_timeout_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_timeout_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc; if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; } - rc = sprintf(ptr,"reject_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_rejected_sess),(unsigned long long)g_now.tv_sec); ptr += rc; + rc = sprintf(ptr,"reject_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_rejected_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc; ilog(LOG_DEBUG, "min_sessions:%llu max_sessions:%llu, call_dur_per_interval:%llu.%06llu at time %llu\n", (unsigned long long) ts->managed_sess_min, (unsigned long long) ts->managed_sess_max, (unsigned long long ) ts->total_calls_duration_interval.tv_sec, (unsigned long long ) ts->total_calls_duration_interval.tv_usec, - (unsigned long long ) g_now.tv_sec); + (unsigned long long ) rtpe_now.tv_sec); ilog(LOG_DEBUG, "Min/Max/Avg offer processing delay: %llu.%06llu/%llu.%06llu/%llu.%06llu sec", (unsigned long long)ts->offer.time_min.tv_sec,(unsigned long long)ts->offer.time_min.tv_usec, @@ -244,17 +242,11 @@ static inline void copy_with_lock(struct totalstats *ts_dst, struct totalstats * mutex_unlock(ts_lock); } -void graphite_loop_run(struct callmaster *cm, endpoint_t *graphite_ep, int seconds) { +void graphite_loop_run(endpoint_t *graphite_ep, int seconds) { int rc=0; struct pollfd wfds[1]; - // sanity checks - if (!cm) { - ilog(LOG_ERROR, "NULL callmaster"); - return ; - } - if (!graphite_ep) { ilog(LOG_ERROR, "NULL graphite_ep"); return ; @@ -294,50 +286,42 @@ void graphite_loop_run(struct callmaster *cm, endpoint_t *graphite_ep, int secon } } - gettimeofday(&g_now, NULL); - if (g_now.tv_sec < next_run) { + gettimeofday(&rtpe_now, NULL); + if (rtpe_now.tv_sec < next_run) { usleep(100000); return; } - next_run = g_now.tv_sec + seconds; + next_run = rtpe_now.tv_sec + seconds; if (graphite_sock.fd < 0 && connection_state == STATE_DISCONNECTED) { connect_to_graphite_server(graphite_ep); } if (graphite_sock.fd >= 0 && connection_state == STATE_CONNECTED) { - add_total_calls_duration_in_interval(cm, &graphite_interval_tv); + add_total_calls_duration_in_interval(&graphite_interval_tv); - rc = send_graphite_data(cm, &graphite_stats); - gettimeofday(&cm->latest_graphite_interval_start, NULL); + rc = send_graphite_data(&graphite_stats); + gettimeofday(&rtpe_latest_graphite_interval_start, NULL); if (rc < 0) { ilog(LOG_ERROR,"Sending graphite data failed."); close_socket(&graphite_sock); connection_state = STATE_DISCONNECTED; } - copy_with_lock(&cm->totalstats_lastinterval, &graphite_stats, &cm->totalstats_lastinterval.total_average_lock); + copy_with_lock(&rtpe_totalstats_lastinterval, &graphite_stats, &rtpe_totalstats_lastinterval.total_average_lock); } } void graphite_loop(void *d) { - struct callmaster *cm = d; - - // sanity checks - if (!cm) { - ilog(LOG_ERROR, "NULL callmaster"); - return ; - } - - if (cm->conf.graphite_interval <= 0) { + if (rtpe_config.graphite_interval <= 0) { ilog(LOG_WARNING,"Graphite send interval was not set. Setting it to 1 second."); - cm->conf.graphite_interval=1; + rtpe_config.graphite_interval=1; } - connect_to_graphite_server(&cm->conf.graphite_ep); + connect_to_graphite_server(&rtpe_config.graphite_ep); - while (!g_shutdown) - graphite_loop_run(cm, &cm->conf.graphite_ep, cm->conf.graphite_interval); // time in seconds + while (!rtpe_shutdown) + graphite_loop_run(&rtpe_config.graphite_ep, rtpe_config.graphite_interval); // time in seconds } diff --git a/daemon/graphite.h b/daemon/graphite.h index 9ffd34c70..84a6647d9 100644 --- a/daemon/graphite.h +++ b/daemon/graphite.h @@ -16,9 +16,11 @@ enum connection_state { STATE_CONNECTED, }; +extern struct timeval rtpe_latest_graphite_interval_start; + int connect_to_graphite_server(const endpoint_t *ep); -int send_graphite_data(struct callmaster *cm, struct totalstats *sent_data); -void graphite_loop_run(struct callmaster *cm, endpoint_t *graphite_ep, int seconds); +int send_graphite_data(struct totalstats *sent_data); +void graphite_loop_run(endpoint_t *graphite_ep, int seconds); void set_prefix(char* prefix); void graphite_loop(void *d); void set_latest_graphite_interval_start(struct timeval *tv); diff --git a/daemon/ice.c b/daemon/ice.c index 7b9171138..7238f562e 100644 --- a/daemon/ice.c +++ b/daemon/ice.c @@ -240,7 +240,7 @@ static void __ice_agent_initialize(struct ice_agent *ag) { create_random_ice_string(call, &ag->ufrag[1], 8); create_random_ice_string(call, &ag->pwd[1], 26); - atomic64_set(&ag->last_activity, poller_now); + atomic64_set(&ag->last_activity, rtpe_now.tv_sec); } static struct ice_agent *__ice_agent_new(struct call_media *media) { @@ -315,7 +315,7 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) { if (!ag) return; - atomic64_set(&ag->last_activity, poller_now); + atomic64_set(&ag->last_activity, rtpe_now.tv_sec); media = ag->media; call = media->call; @@ -519,7 +519,7 @@ static void __ice_agent_free(void *p) { static void __agent_schedule(struct ice_agent *ag, unsigned long usec) { struct timeval nxt; - nxt = g_now; + nxt = rtpe_now; timeval_add_usec(&nxt, usec); __agent_schedule_abs(ag, &nxt); } @@ -622,7 +622,7 @@ static void __do_ice_check(struct ice_candidate_pair *pair) { mutex_lock(&ag->lock); - pair->retransmit = g_now; + pair->retransmit = rtpe_now; if (!PAIR_SET(pair, IN_PROGRESS)) { PAIR_CLEAR2(pair, FROZEN, FAILED); pair->retransmit_ms = STUN_RETRANSMIT_INTERVAL; @@ -721,7 +721,7 @@ static void __do_ice_checks(struct ice_agent *ag) { if (!ag->pwd[0].s) return; - atomic64_set(&ag->last_activity, poller_now); + atomic64_set(&ag->last_activity, rtpe_now.tv_sec); __DBG("running checks, call "STR_FORMAT" tag "STR_FORMAT"", STR_FMT(&ag->call->callid), STR_FMT(&ag->media->monologue->tag)); @@ -730,7 +730,7 @@ static void __do_ice_checks(struct ice_agent *ag) { /* check if we're done and should start nominating pairs */ if (AGENT_ISSET(ag, CONTROLLING) && !AGENT_ISSET(ag, NOMINATING) && ag->start_nominating.tv_sec) { - if (timeval_cmp(&g_now, &ag->start_nominating) >= 0) + if (timeval_cmp(&rtpe_now, &ag->start_nominating) >= 0) __nominate_pairs(ag); timeval_lowest(&next_run, &ag->start_nominating); } @@ -739,7 +739,7 @@ static void __do_ice_checks(struct ice_agent *ag) { pair = g_queue_pop_head(&ag->triggered); if (pair) { PAIR_CLEAR(pair, TRIGGERED); - next_run = g_now; + next_run = rtpe_now; goto check; } @@ -764,7 +764,7 @@ static void __do_ice_checks(struct ice_agent *ag) { if (valid && valid->pair_priority > pair->pair_priority) continue; - if (timeval_cmp(&pair->retransmit, &g_now) <= 0) + if (timeval_cmp(&pair->retransmit, &rtpe_now) <= 0) g_queue_push_tail(&retransmits, pair); /* can't run check directly due to locks */ else @@ -1090,7 +1090,7 @@ int ice_request(struct stream_fd *sfd, const endpoint_t *src, if (!ag) return -1; - atomic64_set(&ag->last_activity, poller_now); + atomic64_set(&ag->last_activity, rtpe_now.tv_sec); /* determine candidate pair */ mutex_lock(&ag->lock); @@ -1198,7 +1198,7 @@ int ice_response(struct stream_fd *sfd, const endpoint_t *src, if (!ag) return -1; - atomic64_set(&ag->last_activity, poller_now); + atomic64_set(&ag->last_activity, rtpe_now.tv_sec); mutex_lock(&ag->lock); @@ -1261,7 +1261,7 @@ int ice_response(struct stream_fd *sfd, const endpoint_t *src, if (!ag->start_nominating.tv_sec) { if (__check_succeeded_complete(ag)) { - ag->start_nominating = g_now; + ag->start_nominating = rtpe_now; timeval_add_usec(&ag->start_nominating, 100000); __agent_schedule_abs(ag, &ag->start_nominating); } @@ -1323,8 +1323,8 @@ void ice_thread_run(void *p) { mutex_lock(&ice_agents_timers_lock); - while (!g_shutdown) { - gettimeofday(&g_now, NULL); + while (!rtpe_shutdown) { + gettimeofday(&rtpe_now, NULL); /* lock our list and get the first element */ ag = g_tree_find_first(ice_agents_timers, NULL, NULL); @@ -1332,12 +1332,12 @@ void ice_thread_run(void *p) { * steal the reference and run it */ if (!ag) goto sleep; - if (timeval_cmp(&g_now, &ag->next_check) < 0) + if (timeval_cmp(&rtpe_now, &ag->next_check) < 0) goto sleep; g_tree_remove(ice_agents_timers, ag); ZERO(ag->next_check); - ag->last_run = g_now; + ag->last_run = rtpe_now; mutex_unlock(&ice_agents_timers_lock); /* this agent is scheduled to run right now */ @@ -1359,9 +1359,9 @@ void ice_thread_run(void *p) { sleep: /* figure out how long we should sleep */ - sleeptime = ag ? timeval_diff(&ag->next_check, &g_now) : 100000; + sleeptime = ag ? timeval_diff(&ag->next_check, &rtpe_now) : 100000; sleeptime = MIN(100000, sleeptime); /* 100 ms at the most */ - tv = g_now; + tv = rtpe_now; timeval_add_usec(&tv, sleeptime); cond_timedwait(&ice_agents_timers_cond, &ice_agents_timers_lock, &tv); continue; diff --git a/daemon/iptables.c b/daemon/iptables.c index 84981113f..b12efc1ec 100644 --- a/daemon/iptables.c +++ b/daemon/iptables.c @@ -1,6 +1,5 @@ #include "iptables.h" -char *g_iptables_chain; int (*iptables_add_rule)(const socket_t *local_sock, const str *comment); int (*iptables_del_rule)(const socket_t *local_sock); @@ -23,6 +22,7 @@ int (*iptables_del_rule)(const socket_t *local_sock); #include "log.h" #include "socket.h" #include "str.h" +#include "main.h" #undef __ALIGN_KERNEL #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof(x))(a) - 1) @@ -139,7 +139,7 @@ static const char *ip4tables_add_rule(const socket_t *local_sock, const str *com ip4_fill_entry(&entry, local_sock, comment); err = "failed to append iptables entry"; - if (!iptc_append_entry(g_iptables_chain, &entry.entry, h)) + if (!iptc_append_entry(rtpe_config.iptables_chain, &entry.entry, h)) goto err; err = "failed to commit iptables changes"; if (!iptc_commit(h)) @@ -169,7 +169,7 @@ static const char *ip6tables_add_rule(const socket_t *local_sock, const str *com ip6_fill_entry(&entry, local_sock, comment); err = "failed to append ip6tables entry"; - if (!ip6tc_append_entry(g_iptables_chain, &entry.entry, h)) + if (!ip6tc_append_entry(rtpe_config.iptables_chain, &entry.entry, h)) goto err; err = "failed to commit ip6tables changes"; if (!ip6tc_commit(h)) @@ -207,7 +207,7 @@ static const char *ip4tables_del_rule(const socket_t *local_sock) { memset(&mask.matches.target, 0xff, sizeof(mask.matches.target)); err = "failed to delete iptables entry"; - if (!iptc_delete_entry(g_iptables_chain, &entry.entry, (unsigned char *) &mask, h)) + if (!iptc_delete_entry(rtpe_config.iptables_chain, &entry.entry, (unsigned char *) &mask, h)) goto err; err = "failed to commit iptables changes"; if (!iptc_commit(h)) @@ -245,7 +245,7 @@ static const char *ip6tables_del_rule(const socket_t *local_sock) { memset(&mask.matches.target, 0xff, sizeof(mask.matches.target)); err = "failed to delete ip6tables entry"; - if (!ip6tc_delete_entry(g_iptables_chain, &entry.entry, (unsigned char *) &mask, h)) + if (!ip6tc_delete_entry(rtpe_config.iptables_chain, &entry.entry, (unsigned char *) &mask, h)) goto err; err = "failed to commit ip6tables changes"; if (!ip6tc_commit(h)) @@ -313,10 +313,10 @@ static int __iptables_stub(void) { void iptables_init(void) { - if (g_iptables_chain && !g_iptables_chain[0]) - g_iptables_chain = NULL; + if (rtpe_config.iptables_chain && !rtpe_config.iptables_chain[0]) + rtpe_config.iptables_chain = NULL; - if (!g_iptables_chain) { + if (!rtpe_config.iptables_chain) { iptables_add_rule = (void *) __iptables_stub; iptables_del_rule = (void *) __iptables_stub; return; @@ -340,7 +340,7 @@ void iptables_init(void) { if (!h) goto out; err = "could not flush iptables chain"; - if (!iptc_flush_entries(g_iptables_chain, h)) + if (!iptc_flush_entries(rtpe_config.iptables_chain, h)) goto err2; err = "could not commit iptables changes"; if (!iptc_commit(h)) @@ -352,7 +352,7 @@ void iptables_init(void) { if (!h) goto out; err = "could not flush ip6tables chain"; - if (!ip6tc_flush_entries(g_iptables_chain, h)) + if (!ip6tc_flush_entries(rtpe_config.iptables_chain, h)) goto err1; err = "could not commit iptables changes"; if (!ip6tc_commit(h)) diff --git a/daemon/iptables.h b/daemon/iptables.h index 77f988550..e92e178e5 100644 --- a/daemon/iptables.h +++ b/daemon/iptables.h @@ -6,8 +6,6 @@ #include "str.h" -extern char *g_iptables_chain; - void iptables_init(void); extern int (*iptables_add_rule)(const socket_t *local_sock, const str *comment); extern int (*iptables_del_rule)(const socket_t *local_sock); diff --git a/daemon/kernel.c b/daemon/kernel.c index 0b17700ee..7ce3fefca 100644 --- a/daemon/kernel.c +++ b/daemon/kernel.c @@ -28,15 +28,18 @@ struct kernel_interface kernel; -static int kernel_create_table(unsigned int id) { +static int kernel_action_table(const char *action, unsigned int id) { char str[64]; + int saved_errno; int fd; int i; fd = open(PREFIX "/control", O_WRONLY | O_TRUNC); if (fd == -1) return -1; - sprintf(str, "add %u\n", id); + i = snprintf(str, sizeof(str), "%s %u\n", action, id); + if (i >= sizeof(str)) + goto fail; i = write(fd, str, strlen(str)); if (i == -1) goto fail; @@ -45,12 +48,23 @@ static int kernel_create_table(unsigned int id) { return 0; fail: + saved_errno = errno; close(fd); + errno = saved_errno; return -1; } +static int kernel_create_table(unsigned int id) { + return kernel_action_table("add", id); +} + +static int kernel_delete_table(unsigned int id) { + return kernel_action_table("del", id); +} + static int kernel_open_table(unsigned int id) { char str[64]; + int saved_errno; int fd; struct rtpengine_message msg; int i; @@ -69,7 +83,9 @@ static int kernel_open_table(unsigned int id) { return fd; fail: + saved_errno = errno; close(fd); + errno = saved_errno; return -1; } @@ -79,6 +95,11 @@ int kernel_setup_table(unsigned int id) { kernel.is_wanted = 1; + if (kernel_delete_table(id) && errno != ENOENT) { + ilog(LOG_ERR, "FAILED TO DELETE KERNEL TABLE %i (%s), KERNEL FORWARDING DISABLED", + id, strerror(errno)); + return -1; + } if (kernel_create_table(id)) { ilog(LOG_ERR, "FAILED TO CREATE KERNEL TABLE %i (%s), KERNEL FORWARDING DISABLED", id, strerror(errno)); diff --git a/daemon/log.c b/daemon/log.c index e096284ba..6f2038c73 100644 --- a/daemon/log.c +++ b/daemon/log.c @@ -8,6 +8,7 @@ #include "poller.h" #include "ice.h" #include "loglib.h" +#include "main.h" @@ -17,46 +18,101 @@ struct log_info __thread log_info; int _log_facility_cdr = 0; int _log_facility_rtcp = 0; +typedef void (ilog_prefix_func)(char *prefix, size_t prefix_len); +static ilog_prefix_func ilog_prefix_default; +static ilog_prefix_func ilog_prefix_parsable; + +static ilog_prefix_func *ilog_prefix = ilog_prefix_default; + +static ilog_prefix_func * const ilog_prefix_funcs[__LF_LAST] = { + [LF_DEFAULT] = ilog_prefix_default, + [LF_PARSABLE] = ilog_prefix_parsable, +}; -void __ilog(int prio, const char *fmt, ...) { - char prefix[300]; - va_list ap; + +static void ilog_prefix_default(char *prefix, size_t prefix_len) { switch (log_info.e) { case LOG_INFO_NONE: prefix[0] = 0; break; case LOG_INFO_CALL: - snprintf(prefix, sizeof(prefix), "["STR_FORMAT"]: ", + snprintf(prefix, prefix_len, "["STR_FORMAT"]: ", STR_FMT(&log_info.u.call->callid)); break; case LOG_INFO_STREAM_FD: if (log_info.u.stream_fd->call) - snprintf(prefix, sizeof(prefix), "["STR_FORMAT" port %5u]: ", + snprintf(prefix, prefix_len, "["STR_FORMAT" port %5u]: ", STR_FMT(&log_info.u.stream_fd->call->callid), log_info.u.stream_fd->socket.local.port); break; case LOG_INFO_STR: - snprintf(prefix, sizeof(prefix), "["STR_FORMAT"]: ", + snprintf(prefix, prefix_len, "["STR_FORMAT"]: ", STR_FMT(log_info.u.str)); break; case LOG_INFO_C_STRING: - snprintf(prefix, sizeof(prefix), "[%s]: ", log_info.u.cstr); + snprintf(prefix, prefix_len, "[%s]: ", log_info.u.cstr); break; case LOG_INFO_ICE_AGENT: - snprintf(prefix, sizeof(prefix), "["STR_FORMAT"/"STR_FORMAT"/%u]: ", + snprintf(prefix, prefix_len, "["STR_FORMAT"/"STR_FORMAT"/%u]: ", STR_FMT(&log_info.u.ice_agent->call->callid), STR_FMT(&log_info.u.ice_agent->media->monologue->tag), log_info.u.ice_agent->media->index); break; } +} + +static void ilog_prefix_parsable(char *prefix, size_t prefix_len) { + switch (log_info.e) { + case LOG_INFO_NONE: + prefix[0] = 0; + break; + case LOG_INFO_CALL: + snprintf(prefix, prefix_len, "[ID=\""STR_FORMAT"\"]: ", + STR_FMT(&log_info.u.call->callid)); + break; + case LOG_INFO_STREAM_FD: + if (log_info.u.stream_fd->call) + snprintf(prefix, prefix_len, "[ID=\""STR_FORMAT"\" port=\"%5u\"]: ", + STR_FMT(&log_info.u.stream_fd->call->callid), + log_info.u.stream_fd->socket.local.port); + break; + case LOG_INFO_STR: + snprintf(prefix, prefix_len, "[ID=\""STR_FORMAT"\"]: ", + STR_FMT(log_info.u.str)); + break; + case LOG_INFO_C_STRING: + snprintf(prefix, prefix_len, "[ID=\"%s\"]: ", log_info.u.cstr); + break; + case LOG_INFO_ICE_AGENT: + snprintf(prefix, prefix_len, "[ID=\""STR_FORMAT"\" tag=\""STR_FORMAT"\" index=\"%u\"]: ", + STR_FMT(&log_info.u.ice_agent->call->callid), + STR_FMT(&log_info.u.ice_agent->media->monologue->tag), + log_info.u.ice_agent->media->index); + break; + } +} + +void __ilog(int prio, const char *fmt, ...) { + char prefix[300]; + va_list ap; + + ilog_prefix(prefix, sizeof(prefix)); va_start(ap, fmt); __vpilog(prio, prefix, fmt, ap); va_end(ap); } +void log_format(enum log_format f) { + if (f < 0 || f >= __LF_LAST) + die("Invalid log format enum"); + ilog_prefix = ilog_prefix_funcs[f]; + if (!ilog_prefix) + die("Invalid log format enum"); +} + void cdrlog(const char* cdrbuffer) { if (_log_facility_cdr) { syslog(LOG_INFO | _log_facility_cdr, "%s", cdrbuffer); diff --git a/daemon/log.h b/daemon/log.h index 881cb3e8c..626f41373 100644 --- a/daemon/log.h +++ b/daemon/log.h @@ -10,6 +10,7 @@ struct call; struct stream_fd; struct ice_agent; +enum log_format; struct log_info { union { @@ -42,6 +43,7 @@ void cdrlog(const char* cdrbuffer); void rtcplog(const char* cdrbuffer); +void log_format(enum log_format); void __ilog(int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); diff --git a/daemon/main.c b/daemon/main.c index 5c7eef25e..9209292c4 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -1,3 +1,5 @@ +#include "main.h" + #include #include #include @@ -34,57 +36,38 @@ #include "auxlib.h" #include "rtcp.h" #include "iptables.h" +#include "statistics.h" +#include "graphite.h" -struct main_context { - struct poller *p; - struct callmaster *m; -}; +struct poller *rtpe_poller; + +struct rtpengine_config rtpe_config = { + // non-zero defaults + .kernel_table = -1, + .max_sessions = -1, + .delete_delay = 30, + .redis_subscribed_keyspaces = G_QUEUE_INIT, + .redis_expires_secs = 86400, + .interfaces = G_QUEUE_INIT, + .homer_protocol = SOCK_DGRAM, + .homer_id = 2001, + .port_min = 30000, + .port_max = 40000, + .redis_db = -1, + .redis_write_db = -1, + .redis_allowed_errors = -1, + .redis_disable_time = 10, + .redis_connect_timeout = 1000, + .rec_method = "pcap", + .rec_format = "raw", +}; + -static GQueue interfaces = G_QUEUE_INIT; -static GQueue keyspaces = G_QUEUE_INIT; -static endpoint_t tcp_listen_ep; -static endpoint_t udp_listen_ep; -static endpoint_t ng_listen_ep; -static endpoint_t cli_listen_ep; -static endpoint_t graphite_ep; -static endpoint_t redis_ep; -static endpoint_t redis_write_ep; -static endpoint_t homer_ep; -static int homer_protocol = SOCK_DGRAM; -static int homer_id = 2001; -static int tos; -static int table = -1; -static int no_fallback; -static unsigned int timeout; -static unsigned int silent_timeout; -static unsigned int final_timeout; -static unsigned int redis_expires = 86400; -static int port_min = 30000; -static int port_max = 40000; -static int max_sessions = -1; -static int redis_db = -1; -static int redis_write_db = -1; -static int redis_num_threads; -static int no_redis_required; -static int redis_allowed_errors = -1; -static int redis_disable_time = 10; -static int redis_cmd_timeout = 0; -static int redis_connect_timeout = 1000; -static char *redis_auth; -static char *redis_write_auth; -static char *b2b_url; -static enum xmlrpc_format xmlrpc_fmt = XF_SEMS; -static int num_threads; -static int delete_delay = 30; -static int graphite_interval = 0; -static char *spooldir; -static char *rec_method = "pcap"; -static char *rec_format = "raw"; static void sighandler(gpointer x) { sigset_t ss; @@ -100,7 +83,7 @@ static void sighandler(gpointer x) { ts.tv_sec = 0; ts.tv_nsec = 100000000; /* 0.1 sec */ - while (!g_shutdown) { + while (!rtpe_shutdown) { ret = sigtimedwait(&ss, NULL, &ts); if (ret == -1) { if (errno == EAGAIN || errno == EINTR) @@ -109,17 +92,17 @@ static void sighandler(gpointer x) { } if (ret == SIGINT || ret == SIGTERM) - g_shutdown = 1; + rtpe_shutdown = 1; else if (ret == SIGUSR1) { if (get_log_level() > 0) { - g_atomic_int_add(&log_level, -1); + g_atomic_int_add(&rtpe_config.common.log_level, -1); ilog(get_log_level(), "Set log level to %d\n", get_log_level()); } } else if (ret == SIGUSR2) { if (get_log_level() < 7) { - g_atomic_int_add(&log_level, 1); + g_atomic_int_add(&rtpe_config.common.log_level, 1); ilog(get_log_level(), "Set log level to %d\n", get_log_level()); } @@ -196,8 +179,12 @@ static struct intf_config *if_addr_parse(char *s) { ifa->local_address.type = socktype_udp; ifa->advertised_address.addr = adv; ifa->advertised_address.type = ifa->local_address.type; - ifa->port_min = port_min; - ifa->port_max = port_max; + ifa->port_min = rtpe_config.port_min; + ifa->port_max = rtpe_config.port_max; + + // handle "base:suffix" separation for round-robin selection + ifa->name_rr_spec = ifa->name; + str_token(&ifa->name_base, &ifa->name_rr_spec, ':'); // sets name_rr_spec to null string if no ':' found return ifa; } @@ -254,14 +241,15 @@ static void options(int *argc, char ***argv) { char *redisps_write = NULL; char *log_facility_cdr_s = NULL; char *log_facility_rtcp_s = NULL; + char *log_format = NULL; int sip_source = 0; char *homerp = NULL; char *homerproto = NULL; char *endptr; GOptionEntry e[] = { - { "table", 't', 0, G_OPTION_ARG_INT, &table, "Kernel table to use", "INT" }, - { "no-fallback",'F', 0, G_OPTION_ARG_NONE, &no_fallback, "Only start when kernel module is available", NULL }, + { "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" }, + { "no-fallback",'F', 0, G_OPTION_ARG_NONE, &rtpe_config.no_fallback, "Only start when kernel module is available", NULL }, { "interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY,&if_a, "Local interface for RTP", "[NAME/]IP[!IP]"}, { "subscribe-keyspace", 'k', 0, G_OPTION_ARG_STRING_ARRAY,&ks_a, "Subscription keyspace list", "INT INT ..."}, { "listen-tcp", 'l', 0, G_OPTION_ARG_STRING, &listenps, "TCP port to listen on", "[IP:]PORT" }, @@ -269,46 +257,48 @@ static void options(int *argc, char ***argv) { { "listen-ng", 'n', 0, G_OPTION_ARG_STRING, &listenngs, "UDP port to listen on, NG protocol", "[IP46|HOSTNAME:]PORT" }, { "listen-cli", 'c', 0, G_OPTION_ARG_STRING, &listencli, "UDP port to listen on, CLI", "[IP46|HOSTNAME:]PORT" }, { "graphite", 'g', 0, G_OPTION_ARG_STRING, &graphitep, "Address of the graphite server", "IP46|HOSTNAME:PORT" }, - { "graphite-interval", 'G', 0, G_OPTION_ARG_INT, &graphite_interval, "Graphite send interval in seconds", "INT" }, + { "graphite-interval", 'G', 0, G_OPTION_ARG_INT, &rtpe_config.graphite_interval, "Graphite send interval in seconds", "INT" }, { "graphite-prefix",0, 0, G_OPTION_ARG_STRING, &graphite_prefix_s, "Prefix for graphite line", "STRING"}, - { "tos", 'T', 0, G_OPTION_ARG_INT, &tos, "Default TOS value to set on streams", "INT" }, - { "timeout", 'o', 0, G_OPTION_ARG_INT, &timeout, "RTP timeout", "SECS" }, - { "silent-timeout",'s',0,G_OPTION_ARG_INT, &silent_timeout,"RTP timeout for muted", "SECS" }, - { "final-timeout",'a',0,G_OPTION_ARG_INT, &final_timeout, "Call timeout", "SECS" }, - { "port-min", 'm', 0, G_OPTION_ARG_INT, &port_min, "Lowest port to use for RTP", "INT" }, - { "port-max", 'M', 0, G_OPTION_ARG_INT, &port_max, "Highest port to use for RTP", "INT" }, + { "tos", 'T', 0, G_OPTION_ARG_INT, &rtpe_config.default_tos, "Default TOS value to set on streams", "INT" }, + { "control-tos",0 , 0, G_OPTION_ARG_INT, &rtpe_config.control_tos, "Default TOS value to set on control-ng", "INT" }, + { "timeout", 'o', 0, G_OPTION_ARG_INT, &rtpe_config.timeout, "RTP timeout", "SECS" }, + { "silent-timeout",'s',0,G_OPTION_ARG_INT, &rtpe_config.silent_timeout,"RTP timeout for muted", "SECS" }, + { "final-timeout",'a',0,G_OPTION_ARG_INT, &rtpe_config.final_timeout, "Call timeout", "SECS" }, + { "port-min", 'm', 0, G_OPTION_ARG_INT, &rtpe_config.port_min, "Lowest port to use for RTP", "INT" }, + { "port-max", 'M', 0, G_OPTION_ARG_INT, &rtpe_config.port_max, "Highest port to use for RTP", "INT" }, { "redis", 'r', 0, G_OPTION_ARG_STRING, &redisps, "Connect to Redis database", "[PW@]IP:PORT/INT" }, { "redis-write",'w', 0, G_OPTION_ARG_STRING, &redisps_write, "Connect to Redis write database", "[PW@]IP:PORT/INT" }, - { "redis-num-threads", 0, 0, G_OPTION_ARG_INT, &redis_num_threads, "Number of Redis restore threads", "INT" }, - { "redis-expires", 0, 0, G_OPTION_ARG_INT, &redis_expires, "Expire time in seconds for redis keys", "INT" }, - { "no-redis-required", 'q', 0, G_OPTION_ARG_NONE, &no_redis_required, "Start no matter of redis connection state", NULL }, - { "redis-allowed-errors", 0, 0, G_OPTION_ARG_INT, &redis_allowed_errors, "Number of allowed errors before redis is temporarily disabled", "INT" }, - { "redis-disable-time", 0, 0, G_OPTION_ARG_INT, &redis_disable_time, "Number of seconds redis communication is disabled because of errors", "INT" }, - { "redis-cmd-timeout", 0, 0, G_OPTION_ARG_INT, &redis_cmd_timeout, "Sets a timeout in milliseconds for redis commands", "INT" }, - { "redis-connect-timeout", 0, 0, G_OPTION_ARG_INT, &redis_connect_timeout, "Sets a timeout in milliseconds for redis connections", "INT" }, - { "b2b-url", 'b', 0, G_OPTION_ARG_STRING, &b2b_url, "XMLRPC URL of B2B UA" , "STRING" }, + { "redis-num-threads", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_num_threads, "Number of Redis restore threads", "INT" }, + { "redis-expires", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_expires_secs, "Expire time in seconds for redis keys", "INT" }, + { "no-redis-required", 'q', 0, G_OPTION_ARG_NONE, &rtpe_config.no_redis_required, "Start no matter of redis connection state", NULL }, + { "redis-allowed-errors", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_allowed_errors, "Number of allowed errors before redis is temporarily disabled", "INT" }, + { "redis-disable-time", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_disable_time, "Number of seconds redis communication is disabled because of errors", "INT" }, + { "redis-cmd-timeout", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_cmd_timeout, "Sets a timeout in milliseconds for redis commands", "INT" }, + { "redis-connect-timeout", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_connect_timeout, "Sets a timeout in milliseconds for redis connections", "INT" }, + { "b2b-url", 'b', 0, G_OPTION_ARG_STRING, &rtpe_config.b2b_url, "XMLRPC URL of B2B UA" , "STRING" }, { "log-facility-cdr",0, 0, G_OPTION_ARG_STRING, &log_facility_cdr_s, "Syslog facility to use for logging CDRs", "daemon|local0|...|local7"}, { "log-facility-rtcp",0, 0, G_OPTION_ARG_STRING, &log_facility_rtcp_s, "Syslog facility to use for logging RTCP", "daemon|local0|...|local7"}, - { "xmlrpc-format",'x', 0, G_OPTION_ARG_INT, &xmlrpc_fmt, "XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only", "INT" }, - { "num-threads", 0, 0, G_OPTION_ARG_INT, &num_threads, "Number of worker threads to create", "INT" }, - { "delete-delay", 'd', 0, G_OPTION_ARG_INT, &delete_delay, "Delay for deleting a session from memory.", "INT" }, + { "log-format", 0, 0, G_OPTION_ARG_STRING, &log_format, "Log prefix format", "default|parsable"}, + { "xmlrpc-format",'x', 0, G_OPTION_ARG_INT, &rtpe_config.fmt, "XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only", "INT" }, + { "num-threads", 0, 0, G_OPTION_ARG_INT, &rtpe_config.num_threads, "Number of worker threads to create", "INT" }, + { "delete-delay", 'd', 0, G_OPTION_ARG_INT, &rtpe_config.delete_delay, "Delay for deleting a session from memory.", "INT" }, { "sip-source", 0, 0, G_OPTION_ARG_NONE, &sip_source, "Use SIP source address by default", NULL }, { "dtls-passive", 0, 0, G_OPTION_ARG_NONE, &dtls_passive_def,"Always prefer DTLS passive role", NULL }, - { "max-sessions", 0, 0, G_OPTION_ARG_INT, &max_sessions, "Limit of maximum number of sessions", "INT" }, + { "max-sessions", 0, 0, G_OPTION_ARG_INT, &rtpe_config.max_sessions, "Limit of maximum number of sessions", "INT" }, { "homer", 0, 0, G_OPTION_ARG_STRING, &homerp, "Address of Homer server for RTCP stats","IP46|HOSTNAME:PORT"}, { "homer-protocol",0,0,G_OPTION_ARG_STRING, &homerproto, "Transport protocol for Homer (default udp)", "udp|tcp" }, - { "homer-id", 0, 0, G_OPTION_ARG_STRING, &homer_id, "'Capture ID' to use within the HEP protocol", "INT" }, - { "recording-dir", 0, 0, G_OPTION_ARG_STRING, &spooldir, "Directory for storing pcap and metadata files", "FILE" }, - { "recording-method",0, 0, G_OPTION_ARG_STRING, &rec_method, "Strategy for call recording", "pcap|proc" }, - { "recording-format",0, 0, G_OPTION_ARG_STRING, &rec_format, "File format for stored pcap files", "raw|eth" }, + { "homer-id", 0, 0, G_OPTION_ARG_STRING, &rtpe_config.homer_id, "'Capture ID' to use within the HEP protocol", "INT" }, + { "recording-dir", 0, 0, G_OPTION_ARG_STRING, &rtpe_config.spooldir, "Directory for storing pcap and metadata files", "FILE" }, + { "recording-method",0, 0, G_OPTION_ARG_STRING, &rtpe_config.rec_method, "Strategy for call recording", "pcap|proc" }, + { "recording-format",0, 0, G_OPTION_ARG_STRING, &rtpe_config.rec_format, "File format for stored pcap files", "raw|eth" }, #ifdef WITH_IPTABLES_OPTION - { "iptables-chain",0,0, G_OPTION_ARG_STRING, &g_iptables_chain,"Add explicit firewall rules to this iptables chain","STRING" }, + { "iptables-chain",0,0, G_OPTION_ARG_STRING, &rtpe_config.iptables_chain,"Add explicit firewall rules to this iptables chain","STRING" }, #endif { NULL, } }; config_load(argc, argv, e, " - next-generation media proxy", - "/etc/rtpengine/rtpengine.conf", "rtpengine"); + "/etc/rtpengine/rtpengine.conf", "rtpengine", &rtpe_config.common); if (!if_a) die("Missing option --interface"); @@ -319,7 +309,7 @@ static void options(int *argc, char ***argv) { ifa = if_addr_parse(*iter); if (!ifa) die("Invalid interface specification: %s", *iter); - g_queue_push_tail(&interfaces, ifa); + g_queue_push_tail(&rtpe_config.interfaces, ifa); } if (ks_a) { @@ -334,29 +324,29 @@ static void options(int *argc, char ***argv) { } else if (endptr == str_keyspace_db.s) { ilog(LOG_ERR, "Fail adding keyspace %.*s to redis notifications; no digits found\n", str_keyspace_db.len, str_keyspace_db.s); } else { - g_queue_push_tail(&keyspaces, GUINT_TO_POINTER(uint_keyspace_db)); + g_queue_push_tail(&rtpe_config.redis_subscribed_keyspaces, GUINT_TO_POINTER(uint_keyspace_db)); } } } if (listenps) { - if (endpoint_parse_any_getaddrinfo(&tcp_listen_ep, listenps)) + if (endpoint_parse_any_getaddrinfo(&rtpe_config.tcp_listen_ep, listenps)) die("Invalid IP or port (--listen-tcp)"); } if (listenudps) { - if (endpoint_parse_any_getaddrinfo(&udp_listen_ep, listenudps)) + if (endpoint_parse_any_getaddrinfo(&rtpe_config.udp_listen_ep, listenudps)) die("Invalid IP or port (--listen-udp)"); } if (listenngs) { - if (endpoint_parse_any_getaddrinfo(&ng_listen_ep, listenngs)) + if (endpoint_parse_any_getaddrinfo(&rtpe_config.ng_listen_ep, listenngs)) die("Invalid IP or port (--listen-ng)"); } - if (listencli) {if (endpoint_parse_any_getaddrinfo(&cli_listen_ep, listencli)) + if (listencli) {if (endpoint_parse_any_getaddrinfo(&rtpe_config.cli_listen_ep, listencli)) die("Invalid IP or port (--listen-cli)"); } - if (graphitep) {if (endpoint_parse_any_getaddrinfo_full(&graphite_ep, graphitep)) + if (graphitep) {if (endpoint_parse_any_getaddrinfo_full(&rtpe_config.graphite_ep, graphitep)) die("Invalid IP or port (--graphite)"); } @@ -364,45 +354,45 @@ static void options(int *argc, char ***argv) { set_prefix(graphite_prefix_s); if (homerp) { - if (endpoint_parse_any_getaddrinfo_full(&homer_ep, homerp)) + if (endpoint_parse_any_getaddrinfo_full(&rtpe_config.homer_ep, homerp)) die("Invalid IP or port (--homer)"); } if (homerproto) { if (!strcmp(homerproto, "tcp")) - homer_protocol = SOCK_STREAM; + rtpe_config.homer_protocol = SOCK_STREAM; else if (!strcmp(homerproto, "udp")) - homer_protocol = SOCK_DGRAM; + rtpe_config.homer_protocol = SOCK_DGRAM; else die("Invalid protocol (--homer-protocol)"); } - if (tos < 0 || tos > 255) + if (rtpe_config.default_tos < 0 || rtpe_config.default_tos > 255) die("Invalid TOS value"); - if (timeout <= 0) - timeout = 60; + if (rtpe_config.control_tos < 0 || rtpe_config.control_tos > 255) + die("Invalid control-ng TOS value"); + + if (rtpe_config.timeout <= 0) + rtpe_config.timeout = 60; - if (silent_timeout <= 0) - silent_timeout = 3600; + if (rtpe_config.silent_timeout <= 0) + rtpe_config.silent_timeout = 3600; - if (final_timeout <= 0) - final_timeout = 0; + if (rtpe_config.final_timeout <= 0) + rtpe_config.final_timeout = 0; if (redisps) - if (redis_ep_parse(&redis_ep, &redis_db, &redis_auth, "RTPENGINE_REDIS_AUTH_PW", redisps)) + if (redis_ep_parse(&rtpe_config.redis_ep, &rtpe_config.redis_db, &rtpe_config.redis_auth, "RTPENGINE_REDIS_AUTH_PW", redisps)) die("Invalid Redis endpoint [IP:PORT/INT] (--redis)"); if (redisps_write) - if (redis_ep_parse(&redis_write_ep, &redis_write_db, &redis_write_auth, + if (redis_ep_parse(&rtpe_config.redis_write_ep, &rtpe_config.redis_write_db, &rtpe_config.redis_write_auth, "RTPENGINE_REDIS_WRITE_AUTH_PW", redisps_write)) die("Invalid Redis endpoint [IP:PORT/INT] (--redis-write)"); - if (xmlrpc_fmt > 1) + if (rtpe_config.fmt > 1) die("Invalid XMLRPC format"); - if ((log_level < LOG_EMERG) || (log_level > LOG_DEBUG)) - die("Invalid log level (--log_level)"); - if (log_facility_cdr_s) { if (!parse_log_facility(log_facility_cdr_s, &_log_facility_cdr)) { print_available_log_facilities(); @@ -417,6 +407,15 @@ static void options(int *argc, char ***argv) { } } + if (log_format) { + if (!strcmp(log_format, "default")) + rtpe_config.log_format = LF_DEFAULT; + else if (!strcmp(log_format, "parsable")) + rtpe_config.log_format = LF_PARSABLE; + else + die("Invalid --log-format option"); + } + if (!sip_source) trust_address_def = 1; } @@ -468,7 +467,8 @@ static void init_everything() { struct timespec ts; log_init("rtpengine"); - recording_fs_init(spooldir, rec_method, rec_format); + log_format(rtpe_config.log_format); + recording_fs_init(rtpe_config.spooldir, rtpe_config.rec_method, rtpe_config.rec_format); clock_gettime(CLOCK_REALTIME, &ts); srandom(ts.tv_sec ^ ts.tv_nsec); SSL_library_init(); @@ -489,13 +489,16 @@ static void init_everything() { dtls_init(); ice_init(); crypto_init_main(); - interfaces_init(&interfaces); + interfaces_init(&rtpe_config.interfaces); iptables_init(); + control_ng_init(); + if (call_interfaces_init()) + abort(); + statistics_init(); } -static void create_everything(struct main_context *ctx) { - struct callmaster_config mc; +static void create_everything(void) { struct control_tcp *ct; struct control_udp *cu; struct control_ng *cn; @@ -504,10 +507,10 @@ static void create_everything(struct main_context *ctx) { struct timeval redis_start, redis_stop; double redis_diff = 0; - if (table < 0) + if (rtpe_config.kernel_table < 0) goto no_kernel; - if (kernel_setup_table(table)) { - if (no_fallback) { + if (kernel_setup_table(rtpe_config.kernel_table)) { + if (rtpe_config.no_fallback) { ilog(LOG_CRIT, "Userspace fallback disallowed - exiting"); exit(-1); } @@ -515,117 +518,102 @@ static void create_everything(struct main_context *ctx) { } no_kernel: - ctx->p = poller_new(); - if (!ctx->p) + rtpe_poller = poller_new(); + if (!rtpe_poller) die("poller creation failed"); - ctx->m = callmaster_new(ctx->p); - if (!ctx->m) - die("callmaster creation failed"); + dtls_timer(rtpe_poller); - dtls_timer(ctx->p); + if (call_init()) + abort(); - ZERO(mc); - rwlock_init(&mc.config_lock); - if (max_sessions < -1) { - max_sessions = -1; + rwlock_init(&rtpe_config.config_lock); + if (rtpe_config.max_sessions < -1) { + rtpe_config.max_sessions = -1; } - mc.max_sessions = max_sessions; - mc.timeout = timeout; - mc.silent_timeout = silent_timeout; - mc.final_timeout = final_timeout; - mc.delete_delay = delete_delay; - mc.default_tos = tos; - mc.b2b_url = b2b_url; - mc.fmt = xmlrpc_fmt; - mc.graphite_ep = graphite_ep; - mc.graphite_interval = graphite_interval; - mc.redis_subscribed_keyspaces = g_queue_copy(&keyspaces); - - if (redis_num_threads < 1) { + + if (rtpe_config.redis_num_threads < 1) { #ifdef _SC_NPROCESSORS_ONLN - redis_num_threads = sysconf( _SC_NPROCESSORS_ONLN ); + rtpe_config.redis_num_threads = sysconf( _SC_NPROCESSORS_ONLN ); #endif - if (redis_num_threads < 1) { - redis_num_threads = REDIS_RESTORE_NUM_THREADS; + if (rtpe_config.redis_num_threads < 1) { + rtpe_config.redis_num_threads = REDIS_RESTORE_NUM_THREADS; } } - mc.redis_num_threads = redis_num_threads; ct = NULL; - if (tcp_listen_ep.port) { - ct = control_tcp_new(ctx->p, &tcp_listen_ep, ctx->m); + if (rtpe_config.tcp_listen_ep.port) { + ct = control_tcp_new(rtpe_poller, &rtpe_config.tcp_listen_ep); if (!ct) die("Failed to open TCP control connection port"); } cu = NULL; - if (udp_listen_ep.port) { - interfaces_exclude_port(udp_listen_ep.port); - cu = control_udp_new(ctx->p, &udp_listen_ep, ctx->m); + if (rtpe_config.udp_listen_ep.port) { + interfaces_exclude_port(rtpe_config.udp_listen_ep.port); + cu = control_udp_new(rtpe_poller, &rtpe_config.udp_listen_ep); if (!cu) die("Failed to open UDP control connection port"); } cn = NULL; - if (ng_listen_ep.port) { - interfaces_exclude_port(ng_listen_ep.port); - cn = control_ng_new(ctx->p, &ng_listen_ep, ctx->m); + if (rtpe_config.ng_listen_ep.port) { + interfaces_exclude_port(rtpe_config.ng_listen_ep.port); + cn = control_ng_new(rtpe_poller, &rtpe_config.ng_listen_ep, rtpe_config.control_tos); if (!cn) die("Failed to open UDP control connection port"); } cl = NULL; - if (cli_listen_ep.port) { - interfaces_exclude_port(cli_listen_ep.port); - cl = cli_new(ctx->p, &cli_listen_ep, ctx->m); + if (rtpe_config.cli_listen_ep.port) { + interfaces_exclude_port(rtpe_config.cli_listen_ep.port); + cl = cli_new(rtpe_poller, &rtpe_config.cli_listen_ep); if (!cl) die("Failed to open UDP CLI connection port"); } - if (!is_addr_unspecified(&redis_write_ep.address)) { - mc.redis_write = redis_new(&redis_write_ep, redis_write_db, - redis_write_auth, ANY_REDIS_ROLE, no_redis_required, - redis_allowed_errors, redis_disable_time, redis_cmd_timeout, redis_connect_timeout); - if (!mc.redis_write) + if (!is_addr_unspecified(&rtpe_config.redis_write_ep.address)) { + rtpe_redis_write = redis_new(&rtpe_config.redis_write_ep, + rtpe_config.redis_write_db, rtpe_config.redis_write_auth, + ANY_REDIS_ROLE, rtpe_config.no_redis_required, + rtpe_config.redis_allowed_errors, + rtpe_config.redis_disable_time, rtpe_config.redis_cmd_timeout, + rtpe_config.redis_connect_timeout); + if (!rtpe_redis_write) die("Cannot start up without running Redis %s write database! See also NO_REDIS_REQUIRED parameter.", - endpoint_print_buf(&redis_write_ep)); + endpoint_print_buf(&rtpe_config.redis_write_ep)); } - if (!is_addr_unspecified(&redis_ep.address)) { - mc.redis = redis_new(&redis_ep, redis_db, redis_auth, - mc.redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, - no_redis_required, redis_allowed_errors, redis_disable_time, - redis_cmd_timeout, redis_connect_timeout); - mc.redis_notify = redis_new(&redis_ep, redis_db, redis_auth, - mc.redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, - no_redis_required, redis_allowed_errors, redis_disable_time, - redis_cmd_timeout, redis_connect_timeout); - if (!mc.redis || !mc.redis_notify) + if (!is_addr_unspecified(&rtpe_config.redis_ep.address)) { + rtpe_redis = redis_new(&rtpe_config.redis_ep, rtpe_config.redis_db, rtpe_config.redis_auth, rtpe_redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, rtpe_config.no_redis_required, + rtpe_config.redis_allowed_errors, + rtpe_config.redis_disable_time, rtpe_config.redis_cmd_timeout, + rtpe_config.redis_connect_timeout); + rtpe_redis_notify = redis_new(&rtpe_config.redis_ep, rtpe_config.redis_db, rtpe_config.redis_auth, rtpe_redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, rtpe_config.no_redis_required, + rtpe_config.redis_allowed_errors, + rtpe_config.redis_disable_time, rtpe_config.redis_cmd_timeout, + rtpe_config.redis_connect_timeout); + if (!rtpe_redis || !rtpe_redis_notify) die("Cannot start up without running Redis %s database! See also NO_REDIS_REQUIRED parameter.", - endpoint_print_buf(&redis_ep)); + endpoint_print_buf(&rtpe_config.redis_ep)); - if (!mc.redis_write) - mc.redis_write = mc.redis; + if (!rtpe_redis_write) + rtpe_redis_write = rtpe_redis; } - mc.redis_expires_secs = redis_expires; - - ctx->m->conf = mc; - daemonize(); wpidfile(); - homer_sender_init(&homer_ep, homer_protocol, homer_id); + homer_sender_init(&rtpe_config.homer_ep, rtpe_config.homer_protocol, rtpe_config.homer_id); rtcp_init(); // must come after Homer init - if (mc.redis) { + if (rtpe_redis) { // start redis restore timer gettimeofday(&redis_start, NULL); // restore - if (redis_restore(ctx->m, mc.redis)) + if (redis_restore(rtpe_redis)) die("Refusing to continue without working Redis database"); // stop redis restore timer @@ -636,54 +624,53 @@ no_kernel: ilog(LOG_INFO, "Redis restore time = %.0lf ms", redis_diff); } - gettimeofday(&ctx->m->latest_graphite_interval_start, NULL); + gettimeofday(&rtpe_latest_graphite_interval_start, NULL); - timeval_from_us(&tmp_tv, (long long) graphite_interval*1000000); + timeval_from_us(&tmp_tv, (long long) rtpe_config.graphite_interval*1000000); set_graphite_interval_tv(&tmp_tv); } int main(int argc, char **argv) { - struct main_context ctx; int idx=0; early_init(); options(&argc, &argv); init_everything(); - create_everything(&ctx); + create_everything(); ilog(LOG_INFO, "Startup complete, version %s", RTPENGINE_VERSION); thread_create_detach(sighandler, NULL); - thread_create_detach(poller_timer_loop, ctx.p); + thread_create_detach(poller_timer_loop, rtpe_poller); - if (!is_addr_unspecified(&redis_ep.address)) - thread_create_detach(redis_notify_loop, ctx.m); + if (!is_addr_unspecified(&rtpe_config.redis_ep.address)) + thread_create_detach(redis_notify_loop, NULL); - if (!is_addr_unspecified(&graphite_ep.address)) - thread_create_detach(graphite_loop, ctx.m); + if (!is_addr_unspecified(&rtpe_config.graphite_ep.address)) + thread_create_detach(graphite_loop, NULL); thread_create_detach(ice_thread_run, NULL); - if (num_threads < 1) { + if (rtpe_config.num_threads < 1) { #ifdef _SC_NPROCESSORS_ONLN - num_threads = sysconf( _SC_NPROCESSORS_ONLN ) + 3; + rtpe_config.num_threads = sysconf( _SC_NPROCESSORS_ONLN ) + 3; #endif - if (num_threads <= 1) - num_threads = 4; + if (rtpe_config.num_threads <= 1) + rtpe_config.num_threads = 4; } - for (;idx +#include "socket.h" +#include "auxlib.h" + +enum xmlrpc_format { + XF_SEMS = 0, + XF_CALLID, +}; +enum log_format { + LF_DEFAULT = 0, + LF_PARSABLE, + + __LF_LAST +}; + +struct rtpengine_config { + /* everything below protected by config_lock */ + rwlock_t config_lock; + + struct rtpengine_common_config common; + + int kernel_table; + int max_sessions; + int timeout; + int silent_timeout; + int final_timeout; + int delete_delay; + GQueue redis_subscribed_keyspaces; + int redis_expires_secs; + char *b2b_url; + int default_tos; + int control_tos; + enum xmlrpc_format fmt; + enum log_format log_format; + endpoint_t graphite_ep; + int graphite_interval; + int redis_num_threads; + GQueue interfaces; + endpoint_t tcp_listen_ep; + endpoint_t udp_listen_ep; + endpoint_t ng_listen_ep; + endpoint_t cli_listen_ep; + endpoint_t redis_ep; + endpoint_t redis_write_ep; + endpoint_t homer_ep; + int homer_protocol; + int homer_id; + int no_fallback; + int port_min; + int port_max; + int redis_db; + int redis_write_db; + int no_redis_required; + int redis_allowed_errors; + int redis_disable_time; + int redis_cmd_timeout; + int redis_connect_timeout; + char *redis_auth; + char *redis_write_auth; + int num_threads; + char *spooldir; + char *rec_method; + char *rec_format; + char *iptables_chain; +}; + + +struct poller; +extern struct poller *rtpe_poller; // main global poller instance XXX convert to struct instead of pointer? + + +extern struct rtpengine_config rtpe_config; + + + +#endif diff --git a/daemon/media_socket.c b/daemon/media_socket.c index f4434dd1f..60b5042bf 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -23,6 +23,7 @@ #include "rtcplib.h" #include "ssrc.h" #include "iptables.h" +#include "main.h" #ifndef PORT_RANDOM_MIN @@ -48,6 +49,12 @@ struct streamhandler { const struct streamhandler_io *in; const struct streamhandler_io *out; }; +struct intf_rr { + struct logical_intf hash_key; + mutex_t lock; + GQueue logical_intfs; + struct logical_intf *singular; // set iff only one is present in the list - no lock needed +}; static void determine_handler(struct packet_stream *in, const struct packet_stream *out); @@ -74,6 +81,9 @@ static int call_savpf2avp_rtcp(str *s, struct packet_stream *, struct stream_fd //static int call_savpf2savp_rtcp(str *s, struct packet_stream *); +static struct logical_intf *__get_logical_interface(const str *name, sockfamily_t *fam); + + @@ -256,13 +266,12 @@ static const struct rtpengine_srtp __res_null = { static GQueue *__interface_list_for_family(sockfamily_t *fam); -static GHashTable *__logical_intf_name_family_hash; -static GHashTable *__intf_spec_addr_type_hash; -static GHashTable *__local_intf_addr_type_hash; // hash of lists +static GHashTable *__logical_intf_name_family_hash; // name + family -> struct logical_intf +static GHashTable *__logical_intf_name_family_rr_hash; // name + family -> struct intf_rr +static GHashTable *__intf_spec_addr_type_hash; // addr + type -> struct intf_spec +static GHashTable *__local_intf_addr_type_hash; // addr + type -> GList of struct local_intf static GQueue __preferred_lists_for_family[__SF_LAST]; -static __thread unsigned int selection_index = 0; -static __thread unsigned int selection_count = 0; /* checks for free no_ports on a local interface */ @@ -272,7 +281,7 @@ static int has_free_ports_loc(struct local_intf *loc, unsigned int num_ports) { return 0; } - if (num_ports > loc->spec->port_pool.free_ports) { + if (num_ports > g_atomic_int_get(&loc->spec->port_pool.free_ports)) { ilog(LOG_ERR, "Didn't found %d ports available for %.*s/%s", num_ports, loc->logical->name.len, loc->logical->name.s, sockaddr_print_buf(&loc->spec->local_address.addr)); @@ -332,64 +341,49 @@ static int has_free_ports_log_all(struct logical_intf *log, unsigned int num_por } /* run round-robin-calls algorithm */ -static struct logical_intf* run_round_robin_calls(GQueue *q, unsigned int num_ports) { +static struct logical_intf* run_round_robin_calls(struct intf_rr *rr, unsigned int num_ports) { struct logical_intf *log = NULL; - volatile unsigned int nr_tries = 0; - unsigned int nr_logs = 0; - nr_logs = g_queue_get_length(q); + mutex_lock(&rr->lock); -select_log: - // choose the next logical interface - log = g_queue_peek_nth(q, selection_index); - if (!log) { - if (selection_index == 0) - return NULL; - selection_index = 0; - goto select_log; - } - __C_DBG("Trying %d ports on logical interface %.*s", num_ports, log->name.len, log->name.s); + unsigned int max_tries = rr->logical_intfs.length; + unsigned int num_tries = 0; - // test for free ports for the logical interface - if(!has_free_ports_log_all(log, num_ports)) { - // count the logical interfaces tried - nr_tries++; + while (num_tries++ < max_tries) { + log = g_queue_pop_head(&rr->logical_intfs); + g_queue_push_tail(&rr->logical_intfs, log); - // the logical interface selected has no ports available, try another one - selection_index ++; - selection_index = selection_index % nr_logs; + mutex_unlock(&rr->lock); - // all the logical interfaces have no ports available - if (nr_tries == nr_logs) { - ilog(LOG_ERR, "No logical interface with free ports found; fallback to default behaviour"); - return NULL; - } + __C_DBG("Trying %d ports on logical interface " STR_FORMAT, num_ports, STR_FMT(&log->name)); + + if (has_free_ports_log_all(log, num_ports)) + goto done; + log = NULL; - goto select_log; + mutex_lock(&rr->lock); } - __C_DBG("Round Robin Calls algorithm found logical %.*s; count=%u index=%u", log->name.len, log->name.s, selection_count, selection_index); + mutex_unlock(&rr->lock); - // 1 stream => 2 x get_logical_interface calls at offer - // 2 streams => 4 x get_logical_interface calls at offer - selection_count ++; - if (selection_count % (num_ports / 2) == 0) { - selection_count = 0; - selection_index ++; - selection_index = selection_index % nr_logs; +done: + if (!log) { + ilog(LOG_ERR, "No logical interface with free ports found; fallback to default behaviour"); + return NULL; } - + __C_DBG("Round Robin Calls algorithm found logical " STR_FORMAT, STR_FMT(&log->name)); return log; } +// 'fam' may only be NULL if 'name' is also NULL struct logical_intf *get_logical_interface(const str *name, sockfamily_t *fam, int num_ports) { - struct logical_intf d, *log = NULL; + struct logical_intf *log = NULL; __C_DBG("Get logical interface for %d ports", num_ports); - if (G_UNLIKELY(!name || !name->s || - str_cmp(name, ALGORITHM_ROUND_ROBIN_CALLS) == 0)) { - + if (G_UNLIKELY(!name || !name->s)) { + // trivial case: no interface given. just pick one suitable for the address family. + // always used for legacy TCP and UDP protocols. GQueue *q; if (fam) q = __interface_list_for_family(fam); @@ -404,35 +398,46 @@ got_some: ; } - // round-robin-calls behaviour - return next interface with free ports - if (name && name->s && str_cmp(name, ALGORITHM_ROUND_ROBIN_CALLS) == 0 && num_ports > 0) { - log = run_round_robin_calls(q, num_ports); - if (log) { - __C_DBG("Choose logical interface %.*s because of direction %.*s", - log->name.len, log->name.s, - name->len, name->s); - } else { - __C_DBG("Choose logical interface NULL because of direction %.*s", - name->len, name->s); - } - return log; - } - - // default behaviour - return first logical interface return q->head ? q->head->data : NULL; } + // check if round-robin is desired + struct logical_intf key; + key.name = *name; + key.preferred_family = fam; + struct intf_rr *rr = g_hash_table_lookup(__logical_intf_name_family_rr_hash, &key); + if (!rr) + return __get_logical_interface(name, fam); + if (rr->singular) { + __C_DBG("Returning non-RR logical interface '" STR_FORMAT "' based on direction '" \ + STR_FORMAT "'", + STR_FMT(&rr->singular->name), + STR_FMT(name)); + return rr->singular; + } + + __C_DBG("Running RR interface selection for direction '" STR_FORMAT "'", + STR_FMT(name)); + + log = run_round_robin_calls(rr, num_ports); + if (log) + return log; + return __get_logical_interface(name, fam); +} +static struct logical_intf *__get_logical_interface(const str *name, sockfamily_t *fam) { + struct logical_intf d, *log = NULL; + d.name = *name; d.preferred_family = fam; log = g_hash_table_lookup(__logical_intf_name_family_hash, &d); if (log) { - __C_DBG("Choose logical interface %.*s because of direction %.*s", - log->name.len, log->name.s, - name->len, name->s); + __C_DBG("Choose logical interface " STR_FORMAT " because of direction " STR_FORMAT, + STR_FMT(&log->name), + STR_FMT(name)); } else { - __C_DBG("Choose logical interface NULL because of direction %.*s", - name->len, name->s); + __C_DBG("Choose logical interface NULL because of direction " STR_FORMAT, + STR_FMT(name)); } return log; @@ -482,26 +487,50 @@ int is_local_endpoint(const struct intf_address *addr, unsigned int port) { } +// called during single-threaded startup only +static void __add_intf_rr_1(struct logical_intf *lif, str *name_base, sockfamily_t *fam) { + struct logical_intf key; + key.name = *name_base; + key.preferred_family = fam; + struct intf_rr *rr = g_hash_table_lookup(__logical_intf_name_family_rr_hash, &key); + if (!rr) { + rr = g_slice_alloc0(sizeof(*rr)); + rr->hash_key = key; + mutex_init(&rr->lock); + g_hash_table_insert(__logical_intf_name_family_rr_hash, &rr->hash_key, rr); + } + g_queue_push_tail(&rr->logical_intfs, lif); + rr->singular = (rr->logical_intfs.length == 1) ? lif : NULL; + g_hash_table_insert(lif->rr_specs, &rr->hash_key.name, lif); +} +static void __add_intf_rr(struct logical_intf *lif, str *name_base, sockfamily_t *fam) { + __add_intf_rr_1(lif, name_base, fam); + static str legacy_rr_str = STR_CONST_INIT("round-robin-calls"); + __add_intf_rr_1(lif, &legacy_rr_str, fam); +} static GQueue *__interface_list_for_family(sockfamily_t *fam) { return &__preferred_lists_for_family[fam->idx]; } +// called during single-threaded startup only static void __interface_append(struct intf_config *ifa, sockfamily_t *fam) { struct logical_intf *lif; GQueue *q; struct local_intf *ifc; struct intf_spec *spec; - lif = get_logical_interface(&ifa->name, fam, 0); + lif = __get_logical_interface(&ifa->name, fam); if (!lif) { lif = g_slice_alloc0(sizeof(*lif)); lif->name = ifa->name; lif->preferred_family = fam; lif->addr_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq); + lif->rr_specs = g_hash_table_new(str_hash, str_equal); g_hash_table_insert(__logical_intf_name_family_hash, lif, lif); if (ifa->local_address.addr.family == fam) { q = __interface_list_for_family(fam); g_queue_push_tail(q, lif); + __add_intf_rr(lif, &ifa->name_base, fam); } } @@ -527,6 +556,7 @@ static void __interface_append(struct intf_config *ifa, sockfamily_t *fam) { __insert_local_intf_addr_type(&ifc->advertised_address, ifc); } +// called during single-threaded startup only void interfaces_init(GQueue *interfaces) { int i; GList *l; @@ -535,6 +565,7 @@ void interfaces_init(GQueue *interfaces) { /* init everything */ __logical_intf_name_family_hash = g_hash_table_new(__name_family_hash, __name_family_eq); + __logical_intf_name_family_rr_hash = g_hash_table_new(__name_family_hash, __name_family_eq); __intf_spec_addr_type_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq); __local_intf_addr_type_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq); @@ -1167,7 +1198,6 @@ static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, unk = 0; int i; struct call *call; - struct callmaster *cm; /*unsigned char cc;*/ struct endpoint endpoint; rewrite_func rwf_in, rwf_out; @@ -1178,7 +1208,6 @@ static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, struct ssrc_ctx *ssrc_in = NULL, *ssrc_out = NULL; call = sfd->call; - cm = call->callmaster; rwlock_lock_r(&call->master_lock); @@ -1274,7 +1303,8 @@ loop_ok: if (G_LIKELY(media->protocol && media->protocol->rtp)) { if (G_LIKELY(!rtcp && !rtp_payload(&rtp_h, NULL, s))) { - __stream_ssrc(in_srtp, out_srtp, rtp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash); + if (G_LIKELY(out_srtp != NULL)) + __stream_ssrc(in_srtp, out_srtp, rtp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash); // check the payload type i = (rtp_h->m_pt & 0x7f); @@ -1287,7 +1317,7 @@ loop_ok: ilog(LOG_WARNING | LOG_FLAG_LIMIT, "RTP packet with unknown payload type %u received", i); atomic64_inc(&stream->stats.errors); - atomic64_inc(&cm->statsps.errors); + atomic64_inc(&rtpe_statsps.errors); } else { @@ -1296,16 +1326,17 @@ loop_ok: } } else if (rtcp && !rtcp_payload(&rtcp_h, NULL, s)) { - __stream_ssrc(in_srtp, out_srtp, rtcp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash); + if (G_LIKELY(out_srtp != NULL)) + __stream_ssrc(in_srtp, out_srtp, rtcp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash); } } /* do we have somewhere to forward it to? */ - if (G_UNLIKELY(!sink || !sink->selected_sfd || !out_srtp->selected_sfd || !in_srtp->selected_sfd)) { + if (G_UNLIKELY(!sink || !sink->selected_sfd || !out_srtp || !out_srtp->selected_sfd || !in_srtp->selected_sfd)) { ilog(LOG_WARNING, "RTP packet from %s discarded", endpoint_print_buf(fsin)); atomic64_inc(&stream->stats.errors); - atomic64_inc(&cm->statsps.errors); + atomic64_inc(&rtpe_statsps.errors); goto unlock_out; } @@ -1403,7 +1434,7 @@ loop_ok: /* wait at least 3 seconds after last signal before committing to a particular * endpoint address */ - if (!call->last_signal || poller_now <= call->last_signal + 3) + if (!call->last_signal || rtpe_now.tv_sec <= call->last_signal + 3) goto update_peerinfo; ilog(LOG_INFO, "Confirmed peer address as %s", endpoint_print_buf(fsin)); @@ -1478,7 +1509,7 @@ forward: ret = -errno; ilog(LOG_DEBUG,"Error when sending message. Error: %s",strerror(errno)); atomic64_inc(&stream->stats.errors); - atomic64_inc(&cm->statsps.errors); + atomic64_inc(&rtpe_statsps.errors); goto out; } @@ -1490,9 +1521,9 @@ drop: ret = 0; atomic64_inc(&stream->stats.packets); atomic64_add(&stream->stats.bytes, s->len); - atomic64_set(&stream->last_packet, poller_now); - atomic64_inc(&cm->statsps.packets); - atomic64_add(&cm->statsps.bytes, s->len); + atomic64_set(&stream->last_packet, rtpe_now.tv_sec); + atomic64_inc(&rtpe_statsps.packets); + atomic64_add(&rtpe_statsps.bytes, s->len); out: if (ret == 0 && update) @@ -1565,7 +1596,7 @@ out: ca = sfd->call ? : NULL; if (ca && update) { - redis_update_onekey(ca, ca->callmaster->conf.redis_write); + redis_update_onekey(ca, rtpe_redis_write); } done: log_info_clear(); @@ -1587,7 +1618,6 @@ static void stream_fd_free(void *p) { struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct local_intf *lif) { struct stream_fd *sfd; struct poller_item pi; - struct poller *po = call->callmaster->poller; sfd = obj_alloc0("stream_fd", sizeof(*sfd), stream_fd_free); sfd->unique_id = g_queue_get_length(&call->stream_fds); @@ -1605,7 +1635,7 @@ struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct lo pi.readable = stream_fd_readable; pi.closed = stream_fd_closed; - if (poller_add_item(po, &pi)) + if (poller_add_item(rtpe_poller, &pi)) ilog(LOG_ERR, "Failed to add stream_fd to poller"); return sfd; diff --git a/daemon/media_socket.h b/daemon/media_socket.h index 6e27679b5..173f8e9f6 100644 --- a/daemon/media_socket.h +++ b/daemon/media_socket.h @@ -21,7 +21,8 @@ struct logical_intf { str name; sockfamily_t *preferred_family; GQueue list; /* struct local_intf */ - GHashTable *addr_hash; + GHashTable *addr_hash; // addr + type -> struct local_intf XXX obsolete? + GHashTable *rr_specs; }; struct port_pool { BIT_ARRAY_DECLARE(ports_used, 0x10000); @@ -35,7 +36,9 @@ struct intf_address { sockaddr_t addr; }; struct intf_config { - str name; + str name; // full name (before the '/' separator in config) + str name_base; // if name is "foo:bar", this is "foo" + str name_rr_spec; // if name is "foo:bar", this is "bar" struct intf_address local_address; struct intf_address advertised_address; unsigned int port_min, port_max; @@ -78,9 +81,7 @@ int is_local_endpoint(const struct intf_address *addr, unsigned int port); //int get_port(socket_t *r, unsigned int port, const struct local_intf *lif, const struct call *c); //void release_port(socket_t *r, const struct local_intf *); -INLINE void set_tos(socket_t *s, unsigned int tos) { - s->family->tos(s, tos); -} + int __get_consecutive_ports(GQueue *out, unsigned int num_ports, unsigned int wanted_start_port, struct intf_spec *spec, const str *); int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log, const str *); diff --git a/daemon/poller.c b/daemon/poller.c index 9382acf91..ccdbf7a9f 100644 --- a/daemon/poller.c +++ b/daemon/poller.c @@ -55,7 +55,7 @@ struct poller *poller_new(void) { p = malloc(sizeof(*p)); memset(p, 0, sizeof(*p)); - gettimeofday(&g_now, NULL); + gettimeofday(&rtpe_now, NULL); p->fd = epoll_create1(0); if (p->fd == -1) abort(); @@ -311,7 +311,7 @@ int poller_poll(struct poller *p, int timeout) { if (ret <= 0) goto out; - gettimeofday(&g_now, NULL); + gettimeofday(&rtpe_now, NULL); for (i = 0; i < ret; i++) { ev = &evs[i]; @@ -482,9 +482,9 @@ void poller_timer_loop(void *d) { struct timeval tv; int wt; - while (!g_shutdown) { + while (!rtpe_shutdown) { gettimeofday(&tv, NULL); - if (tv.tv_sec != poller_now) + if (tv.tv_sec != rtpe_now.tv_sec) goto now; wt = 1000000 - tv.tv_usec; @@ -493,7 +493,7 @@ void poller_timer_loop(void *d) { continue; now: - gettimeofday(&g_now, NULL); + gettimeofday(&rtpe_now, NULL); poller_timers_run(p); } } @@ -501,6 +501,6 @@ now: void poller_loop(void *d) { struct poller *p = d; - while (!g_shutdown) + while (!rtpe_shutdown) poller_poll(p, 100); } diff --git a/daemon/poller.h b/daemon/poller.h index 41a29e751..8ede2f343 100644 --- a/daemon/poller.h +++ b/daemon/poller.h @@ -30,10 +30,6 @@ struct poller_item { struct poller; -/* XXX replace all occurrences with g_now */ -#define poller_now g_now.tv_sec - - struct poller *poller_new(void); int poller_add_item(struct poller *, struct poller_item *); int poller_update_item(struct poller *, struct poller_item *); diff --git a/daemon/recording.c b/daemon/recording.c index 464f4a241..f1047c7af 100644 --- a/daemon/recording.c +++ b/daemon/recording.c @@ -373,7 +373,7 @@ static int pcap_meta_finish_file(struct call *call) { // Print start timestamp and end timestamp // YYYY-MM-DDThh:mm:ss time_t start = call->created.tv_sec; - time_t end = g_now.tv_sec; + time_t end = rtpe_now.tv_sec; char timebuffer[20]; struct tm *timeinfo; timeinfo = localtime(&start); @@ -503,7 +503,7 @@ static void stream_pcap_dump(pcap_dumper_t *pdumper, struct packet_stream *strea // Set up PCAP packet header struct pcap_pkthdr header; ZERO(header); - header.ts = g_now; + header.ts = rtpe_now; header.caplen = pkt_len; header.len = pkt_len; @@ -730,7 +730,7 @@ static void setup_media_proc(struct call_media *media) { if (!recording) return; - GList *pltypes = g_hash_table_get_values(media->rtp_payload_types); + GList *pltypes = g_hash_table_get_values(media->codecs); for (GList *l = pltypes; l; l = l->next) { struct rtp_payload_type *pt = l->data; diff --git a/daemon/redis.c b/daemon/redis.c index 4c9519efd..69fb72dc7 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -30,6 +30,15 @@ #include "rtplib.h" #include "str.h" #include "ssrc.h" +#include "main.h" + +struct redis *rtpe_redis; +struct redis *rtpe_redis_write; +struct redis *rtpe_redis_notify; + +struct event_base *rtpe_redis_notify_event_base; +struct redisAsyncContext *rtpe_redis_notify_async_context; + INLINE redisReply *redis_expect(int type, redisReply *r) { @@ -70,7 +79,7 @@ static int redisCommandNR(redisContext *r, const char *fmt, ...) #define REDIS_FMT(x) (x)->len, (x)->str static int redis_check_conn(struct redis *r); -static void json_restore_call(struct redis *r, struct callmaster *m, const str *id, enum call_type type); +static void json_restore_call(struct redis *r, const str *id, enum call_type type); static void redis_pipe(struct redis *r, const char *fmt, ...) { va_list ap; @@ -268,25 +277,17 @@ err: void on_redis_notification(redisAsyncContext *actx, void *reply, void *privdata) { - - struct callmaster *cm = privdata; struct redis *r = 0; struct call *c = NULL; str callid; str keyspace_id; - // sanity checks - if (!cm) { - rlog(LOG_ERROR, "Struct callmaster is NULL on on_redis_notification"); - return; - } - - if (!cm->conf.redis_notify) { + if (!rtpe_redis_notify) { rlog(LOG_ERROR, "A redis notification has been received but no redis_notify database found"); return; } - r = cm->conf.redis_notify; + r = rtpe_redis_notify; mutex_lock(&r->lock); @@ -331,7 +332,7 @@ void on_redis_notification(redisAsyncContext *actx, void *reply, void *privdata) } if (strncmp(rr->element[3]->str,"set",3)==0) { - c = call_get(&callid, cm); + c = call_get(&callid); if (c) { rwlock_unlock_w(&c->master_lock); if (IS_FOREIGN_CALL(c)) @@ -341,11 +342,11 @@ void on_redis_notification(redisAsyncContext *actx, void *reply, void *privdata) goto err; } } - json_restore_call(r, cm, &callid, CT_FOREIGN_CALL); + json_restore_call(r, &callid, CT_FOREIGN_CALL); } if (strncmp(rr->element[3]->str,"del",3)==0) { - c = call_get(&callid, cm); + c = call_get(&callid); if (!c) { rlog(LOG_NOTICE, "Redis-Notifier: DEL did not find call with callid: %s\n", rr->element[2]->str); goto err; @@ -384,36 +385,30 @@ void redis_async_context_disconnect(const redisAsyncContext *redis_notify_async_ } } -int redis_async_context_alloc(struct callmaster *cm) { +int redis_async_context_alloc() { struct redis *r = 0; - // sanity checks - if (!cm) { - rlog(LOG_ERROR, "Struct callmaster is NULL on context free"); - return -1; - } - - if (!cm->conf.redis_notify) { + if (!rtpe_redis_notify) { rlog(LOG_INFO, "redis_notify database is NULL."); return -1; } // get redis_notify database - r = cm->conf.redis_notify; + r = rtpe_redis_notify; rlog(LOG_INFO, "Use Redis %s for notifications", endpoint_print_buf(&r->endpoint)); // alloc async context - cm->conf.redis_notify_async_context = redisAsyncConnect(r->host, r->endpoint.port); - if (!cm->conf.redis_notify_async_context) { + rtpe_redis_notify_async_context = redisAsyncConnect(r->host, r->endpoint.port); + if (!rtpe_redis_notify_async_context) { rlog(LOG_ERROR, "redis_notify_async_context can't create new"); return -1; } - if (cm->conf.redis_notify_async_context->err) { - rlog(LOG_ERROR, "redis_notify_async_context can't create new error: %s", cm->conf.redis_notify_async_context->errstr); + if (rtpe_redis_notify_async_context->err) { + rlog(LOG_ERROR, "redis_notify_async_context can't create new error: %s", rtpe_redis_notify_async_context->errstr); return -1; } - if (redisAsyncSetDisconnectCallback(cm->conf.redis_notify_async_context, redis_async_context_disconnect) != REDIS_OK) { + if (redisAsyncSetDisconnectCallback(rtpe_redis_notify_async_context, redis_async_context_disconnect) != REDIS_OK) { rlog(LOG_ERROR, "redis_notify_async_context can't set disconnect callback"); return -1; } @@ -421,14 +416,8 @@ int redis_async_context_alloc(struct callmaster *cm) { return 0; } -int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action action) { - // sanity checks - if (!cm) { - rlog(LOG_ERROR, "Struct callmaster is NULL on event base action %d", action); - return -1; - } - - if (!cm->conf.redis_notify_event_base && action!=EVENT_BASE_ALLOC) { +int redis_notify_event_base_action(enum event_base_action action) { + if (!rtpe_redis_notify_event_base && action!=EVENT_BASE_ALLOC) { rlog(LOG_ERROR, "redis_notify_event_base is NULL on event base action %d", action); return -1; } @@ -436,8 +425,8 @@ int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action // exec event base action switch (action) { case EVENT_BASE_ALLOC: - cm->conf.redis_notify_event_base = event_base_new(); - if (!cm->conf.redis_notify_event_base) { + rtpe_redis_notify_event_base = event_base_new(); + if (!rtpe_redis_notify_event_base) { rlog(LOG_ERROR, "Fail alloc redis_notify_event_base"); return -1; } else { @@ -446,12 +435,12 @@ int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action break; case EVENT_BASE_FREE: - event_base_free(cm->conf.redis_notify_event_base); + event_base_free(rtpe_redis_notify_event_base); rlog(LOG_DEBUG, "Success free redis_notify_event_base"); break; case EVENT_BASE_LOOPBREAK: - if (event_base_loopbreak(cm->conf.redis_notify_event_base)) { + if (event_base_loopbreak(rtpe_redis_notify_event_base)) { rlog(LOG_ERROR, "Fail loopbreak redis_notify_event_base"); return -1; } else { @@ -467,38 +456,32 @@ int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action return 0; } -int redis_notify_subscribe_action(struct callmaster *cm, enum subscribe_action action, int keyspace) { - // sanity checks - if (!cm) { - rlog(LOG_ERROR, "Struct callmaster is NULL on subscribe action"); - return -1; - } - - if (!cm->conf.redis_notify_async_context) { +int redis_notify_subscribe_action(enum subscribe_action action, int keyspace) { + if (!rtpe_redis_notify_async_context) { rlog(LOG_ERROR, "redis_notify_async_context is NULL on subscribe action"); return -1; } - if (cm->conf.redis_notify_async_context->err) { - rlog(LOG_ERROR, "redis_notify_async_context error on subscribe action: %s", cm->conf.redis_notify_async_context->errstr); + if (rtpe_redis_notify_async_context->err) { + rlog(LOG_ERROR, "redis_notify_async_context error on subscribe action: %s", rtpe_redis_notify_async_context->errstr); return -1; } switch (action) { case SUBSCRIBE_KEYSPACE: - if (redisAsyncCommand(cm->conf.redis_notify_async_context, on_redis_notification, (void*)cm, "psubscribe __keyspace@%i__:*", keyspace) != REDIS_OK) { + if (redisAsyncCommand(rtpe_redis_notify_async_context, on_redis_notification, NULL, "psubscribe __keyspace@%i__:*", keyspace) != REDIS_OK) { rlog(LOG_ERROR, "Fail redisAsyncCommand on JSON SUBSCRIBE_KEYSPACE"); return -1; } break; case UNSUBSCRIBE_KEYSPACE: - if (redisAsyncCommand(cm->conf.redis_notify_async_context, on_redis_notification, (void*)cm, "punsubscribe __keyspace@%i__:*", keyspace) != REDIS_OK) { + if (redisAsyncCommand(rtpe_redis_notify_async_context, on_redis_notification, NULL, "punsubscribe __keyspace@%i__:*", keyspace) != REDIS_OK) { rlog(LOG_ERROR, "Fail redisAsyncCommand on JSON UNSUBSCRIBE_KEYSPACE"); return -1; } break; case UNSUBSCRIBE_ALL: - if (redisAsyncCommand(cm->conf.redis_notify_async_context, on_redis_notification, (void *) cm, "punsubscribe") != REDIS_OK) { + if (redisAsyncCommand(rtpe_redis_notify_async_context, on_redis_notification, NULL, "punsubscribe") != REDIS_OK) { rlog(LOG_ERROR, "Fail redisAsyncCommand on JSON UNSUBSCRIBE_ALL"); return -1; } @@ -511,39 +494,33 @@ int redis_notify_subscribe_action(struct callmaster *cm, enum subscribe_action a return 0; } -static int redis_notify(struct callmaster *cm) { +static int redis_notify() { struct redis *r = 0; GList *l; - // sanity checks - if (!cm) { - rlog(LOG_ERROR, "Struct callmaster is NULL on redis_notify()"); - return -1; - } - - if (!cm->conf.redis_notify) { + if (!rtpe_redis_notify) { rlog(LOG_ERROR, "redis_notify database is NULL on redis_notify()"); return -1; } - if (!cm->conf.redis_notify_async_context) { + if (!rtpe_redis_notify_async_context) { rlog(LOG_ERROR, "redis_notify_async_context is NULL on redis_notify()"); return -1; } - if (!cm->conf.redis_notify_event_base) { + if (!rtpe_redis_notify_event_base) { rlog(LOG_ERROR, "redis_notify_event_base is NULL on redis_notify()"); return -1; } // get redis_notify database - r = cm->conf.redis_notify; + r = rtpe_redis_notify; rlog(LOG_INFO, "Use Redis %s to subscribe to notifications", endpoint_print_buf(&r->endpoint)); // attach event base - if (redisLibeventAttach(cm->conf.redis_notify_async_context, cm->conf.redis_notify_event_base) == REDIS_ERR) { - if (cm->conf.redis_notify_async_context->err) { - rlog(LOG_ERROR, "redis_notify_async_context can't attach event base error: %s", cm->conf.redis_notify_async_context->errstr); + if (redisLibeventAttach(rtpe_redis_notify_async_context, rtpe_redis_notify_event_base) == REDIS_ERR) { + if (rtpe_redis_notify_async_context->err) { + rlog(LOG_ERROR, "redis_notify_async_context can't attach event base error: %s", rtpe_redis_notify_async_context->errstr); } else { rlog(LOG_ERROR, "redis_notify_async_context can't attach event base"); @@ -552,14 +529,14 @@ static int redis_notify(struct callmaster *cm) { } // subscribe to the values in the configured keyspaces - rwlock_lock_r(&cm->conf.config_lock); - for (l = cm->conf.redis_subscribed_keyspaces->head; l; l = l->next) { - redis_notify_subscribe_action(cm, SUBSCRIBE_KEYSPACE, GPOINTER_TO_UINT(l->data)); + rwlock_lock_r(&rtpe_config.config_lock); + for (l = rtpe_config.redis_subscribed_keyspaces.head; l; l = l->next) { + redis_notify_subscribe_action(SUBSCRIBE_KEYSPACE, GPOINTER_TO_UINT(l->data)); } - rwlock_unlock_r(&cm->conf.config_lock); + rwlock_unlock_r(&rtpe_config.config_lock); // dispatch event base => thread blocks here - if (event_base_dispatch(cm->conf.redis_notify_event_base) < 0) { + if (event_base_dispatch(rtpe_redis_notify_event_base) < 0) { rlog(LOG_ERROR, "Fail event_base_dispatch()"); return -1; } @@ -569,17 +546,10 @@ static int redis_notify(struct callmaster *cm) { void redis_notify_loop(void *d) { int seconds = 1, redis_notify_return = 0; - time_t next_run = g_now.tv_sec; - struct callmaster *cm = (struct callmaster *)d; + time_t next_run = rtpe_now.tv_sec; struct redis *r; - // sanity checks - if (!cm) { - ilog(LOG_ERROR, "NULL callmaster"); - return ; - } - - r = cm->conf.redis_notify; + r = rtpe_redis_notify; if (!r) { rlog(LOG_ERROR, "Don't use Redis notifications. See --redis-notifications parameter."); return ; @@ -592,49 +562,49 @@ void redis_notify_loop(void *d) { } // alloc redis async context - if (redis_async_context_alloc(cm) < 0) { + if (redis_async_context_alloc() < 0) { return ; } // alloc event base - if (redis_notify_event_base_action(cm, EVENT_BASE_ALLOC) < 0) { + if (redis_notify_event_base_action(EVENT_BASE_ALLOC) < 0) { return ; } // initial redis_notify if (redis_check_conn(r) == REDIS_STATE_CONNECTED) { - redis_notify_return = redis_notify(cm); + redis_notify_return = redis_notify(); } // loop redis_notify => in case of lost connection - while (!g_shutdown) { - gettimeofday(&g_now, NULL); - if (g_now.tv_sec < next_run) { + while (!rtpe_shutdown) { + gettimeofday(&rtpe_now, NULL); + if (rtpe_now.tv_sec < next_run) { usleep(100000); continue; } - next_run = g_now.tv_sec + seconds; + next_run = rtpe_now.tv_sec + seconds; if (redis_check_conn(r) == REDIS_STATE_RECONNECTED || redis_notify_return < 0) { // alloc new redis async context upon redis breakdown - if (redis_async_context_alloc(cm) < 0) { + if (redis_async_context_alloc() < 0) { continue; } // prepare notifications - redis_notify_return = redis_notify(cm); + redis_notify_return = redis_notify(); } } // unsubscribe notifications - redis_notify_subscribe_action(cm, UNSUBSCRIBE_ALL, 0); + redis_notify_subscribe_action(UNSUBSCRIBE_ALL, 0); // free async context - redisAsyncDisconnect(cm->conf.redis_notify_async_context); + redisAsyncDisconnect(rtpe_redis_notify_async_context); // free event base - redis_notify_event_base_action(cm, EVENT_BASE_FREE); + redis_notify_event_base_action(EVENT_BASE_FREE); } struct redis *redis_new(const endpoint_t *ep, int db, const char *auth, @@ -696,7 +666,7 @@ static void redis_count_err_and_disable(struct redis *r) r->consecutive_errors++; if (r->consecutive_errors > r->allowed_errors) { - r->restore_tick = g_now.tv_sec + r->disable_time; + r->restore_tick = rtpe_now.tv_sec + r->disable_time; ilog(LOG_WARNING, "Redis server %s disabled for %d seconds", endpoint_print_buf(&r->endpoint), r->disable_time); @@ -706,7 +676,7 @@ static void redis_count_err_and_disable(struct redis *r) /* must be called with r->lock held */ static int redis_check_conn(struct redis *r) { - if ((r->state == REDIS_STATE_DISCONNECTED) && (r->restore_tick > g_now.tv_sec)) { + if ((r->state == REDIS_STATE_DISCONNECTED) && (r->restore_tick > rtpe_now.tv_sec)) { ilog(LOG_WARNING, "Redis server %s is disabled. Don't try RE-Establishing for %d seconds", endpoint_print_buf(&r->endpoint),r->disable_time); return REDIS_STATE_DISCONNECTED; @@ -1207,7 +1177,7 @@ static int redis_tags(struct call *c, struct redis_list *tags) { static int rbl_cb_plts(str *s, GQueue *q, struct redis_list *list, void *ptr) { struct rtp_payload_type *pt; - str ptype, enc, clock, parms; + str ptype, enc, clock, enc_parms, fmt_parms; struct call_media *med = ptr; struct call *call = med->call; @@ -1217,7 +1187,12 @@ static int rbl_cb_plts(str *s, GQueue *q, struct redis_list *list, void *ptr) { return -1; if (str_token(&clock, s, '/')) return -1; - parms = *s; + if (str_token(&enc_parms, s, '/')) { + enc_parms = *s; + fmt_parms = STR_EMPTY; + } + else + fmt_parms = *s; // from call.c // XXX remove all the duplicate code @@ -1225,8 +1200,9 @@ static int rbl_cb_plts(str *s, GQueue *q, struct redis_list *list, void *ptr) { pt->payload_type = str_to_ui(&ptype, 0); call_str_cpy(call, &pt->encoding, &enc); pt->clock_rate = str_to_ui(&clock, 0); - call_str_cpy(call, &pt->encoding_parameters, &parms); - g_hash_table_replace(med->rtp_payload_types, &pt->payload_type, pt); + call_str_cpy(call, &pt->encoding_parameters, &enc_parms); + call_str_cpy(call, &pt->format_parameters, &fmt_parms); + g_hash_table_replace(med->codecs, &pt->payload_type, pt); return 0; } static int json_medias(struct call *c, struct redis_list *medias, JsonReader *root_reader) { @@ -1241,7 +1217,7 @@ static int json_medias(struct call *c, struct redis_list *medias, JsonReader *ro /* from call.c:__get_media() */ med = uid_slice_alloc0(med, &c->medias); med->call = c; - med->rtp_payload_types = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, + med->codecs = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __payload_type_free); if (redis_hash_get_unsigned(&med->index, rh, "index")) @@ -1386,7 +1362,7 @@ static int json_link_streams(struct call *c, struct redis_list *streams, return -1; if (ps->media) - __rtp_stats_update(ps->rtp_stats, ps->media->rtp_payload_types); + __rtp_stats_update(ps->rtp_stats, ps->media->codecs); } return 0; @@ -1479,7 +1455,7 @@ static int json_build_ssrc(struct call *c, JsonReader *root_reader) { return 0; } -static void json_restore_call(struct redis *r, struct callmaster *m, const str *callid, enum call_type type) { +static void json_restore_call(struct redis *r, const str *callid, enum call_type type) { redisReply* rr_jsonStr; struct redis_hash call; struct redis_list tags, sfds, streams, medias, maps; @@ -1505,7 +1481,7 @@ static void json_restore_call(struct redis *r, struct callmaster *m, const str * if (!root_reader) goto err1; - c = call_get_or_create(callid, m, type); + c = call_get_or_create(callid, type); err = "failed to create call struct"; if (!c) goto err1; @@ -1625,9 +1601,9 @@ err1: if (c) call_destroy(c); else { - mutex_lock(&m->conf.redis_write->lock); - redisCommandNR(m->conf.redis_write->ctx, "DEL " PB, STR(callid)); - mutex_unlock(&m->conf.redis_write->lock); + mutex_lock(&rtpe_redis_write->lock); + redisCommandNR(rtpe_redis_write->ctx, "DEL " PB, STR(callid)); + mutex_unlock(&rtpe_redis_write->lock); } } if (c) @@ -1635,7 +1611,6 @@ err1: } struct thread_ctx { - struct callmaster *m; GQueue r_q; mutex_t r_m; }; @@ -1653,14 +1628,14 @@ static void restore_thread(void *call_p, void *ctx_p) { r = g_queue_pop_head(&ctx->r_q); mutex_unlock(&ctx->r_m); - json_restore_call(r, ctx->m, &callid, CT_OWN_CALL); + json_restore_call(r, &callid, CT_OWN_CALL); mutex_lock(&ctx->r_m); g_queue_push_tail(&ctx->r_q, r); mutex_unlock(&ctx->r_m); } -int redis_restore(struct callmaster *m, struct redis *r) { +int redis_restore(struct redis *r) { redisReply *calls = NULL, *call; int i, ret = -1; GThreadPool *gtp; @@ -1669,7 +1644,7 @@ int redis_restore(struct callmaster *m, struct redis *r) { if (!r) return 0; - log_level |= LOG_FLAG_RESTORE; + rtpe_config.common.log_level |= LOG_FLAG_RESTORE; rlog(LOG_DEBUG, "Restoring calls from Redis..."); @@ -1690,16 +1665,14 @@ int redis_restore(struct callmaster *m, struct redis *r) { goto err; } - ctx.m = m; mutex_init(&ctx.r_m); g_queue_init(&ctx.r_q); - for (i = 0; i < m->conf.redis_num_threads; i++) + for (i = 0; i < rtpe_config.redis_num_threads; i++) g_queue_push_tail(&ctx.r_q, redis_new(&r->endpoint, r->db, r->auth, r->role, r->no_redis_required, r->allowed_errors, - r->disable_time,r->cmd_timeout, - r->connect_timeout)); - gtp = g_thread_pool_new(restore_thread, &ctx, m->conf.redis_num_threads, TRUE, NULL); + r->disable_time, r->cmd_timeout, r->connect_timeout)); + gtp = g_thread_pool_new(restore_thread, &ctx, rtpe_config.redis_num_threads, TRUE, NULL); for (i = 0; i < calls->elements; i++) { call = calls->element[i]; @@ -1717,7 +1690,7 @@ int redis_restore(struct callmaster *m, struct redis *r) { freeReplyObject(calls); err: - log_level &= ~LOG_FLAG_RESTORE; + rtpe_config.common.log_level &= ~LOG_FLAG_RESTORE; return ret; } @@ -2013,15 +1986,16 @@ char* redis_encode_json(struct call *c) { } json_builder_end_array (builder); - k = g_hash_table_get_values(media->rtp_payload_types); + k = g_hash_table_get_values(media->codecs); snprintf(tmp, sizeof(tmp), "payload_types-%u", media->unique_id); json_builder_set_member_name(builder, tmp); json_builder_begin_array (builder); for (m = k; m; m = m->next) { pt = m->data; - JSON_ADD_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT, + JSON_ADD_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT "/" STR_FORMAT, pt->payload_type, STR_FMT(&pt->encoding), - pt->clock_rate, STR_FMT(&pt->encoding_parameters)); + pt->clock_rate, STR_FMT(&pt->encoding_parameters), + STR_FMT(&pt->format_parameters)); } json_builder_end_array (builder); @@ -2121,7 +2095,7 @@ void redis_update_onekey(struct call *c, struct redis *r) { rwlock_lock_r(&c->master_lock); - redis_expires_s = c->callmaster->conf.redis_expires_secs; + redis_expires_s = rtpe_config.redis_expires_secs; c->redis_hosted_db = r->db; if (redisCommandNR(r->ctx, "SELECT %i", c->redis_hosted_db)) { diff --git a/daemon/redis.h b/daemon/redis.h index 54883cf4b..c9c635a09 100644 --- a/daemon/redis.h +++ b/daemon/redis.h @@ -43,7 +43,6 @@ enum subscribe_action { UNSUBSCRIBE_ALL, }; -struct callmaster; struct call; @@ -80,6 +79,15 @@ struct redis_list { }; +extern struct redis *rtpe_redis; +extern struct redis *rtpe_redis_write; +extern struct redis *rtpe_redis_notify; + +extern struct event_base *rtpe_redis_notify_event_base; +extern struct redisAsyncContext *rtpe_redis_notify_async_context; + + + #if !GLIB_CHECK_VERSION(2,40,0) INLINE gboolean g_hash_table_insert_check(GHashTable *h, gpointer k, gpointer v) { gboolean ret = TRUE; @@ -99,13 +107,13 @@ void redis_notify_loop(void *d); struct redis *redis_new(const endpoint_t *, int, const char *, enum redis_role, int, int, int, int, int); -int redis_restore(struct callmaster *, struct redis *); +int redis_restore(struct redis *); void redis_update(struct call *, struct redis *); void redis_update_onekey(struct call *c, struct redis *r); void redis_delete(struct call *, struct redis *); void redis_wipe(struct redis *); -int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action); -int redis_notify_subscribe_action(struct callmaster *cm, enum subscribe_action action, int keyspace); +int redis_notify_event_base_action(enum event_base_action); +int redis_notify_subscribe_action(enum subscribe_action action, int keyspace); diff --git a/daemon/sdp.c b/daemon/sdp.c index ef8fa61ff..8702d6475 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -162,6 +162,13 @@ struct attribute_rtpmap { struct rtp_payload_type rtp_pt; }; +struct attribute_fmtp { + str payload_type_str; + str format_parms_str; + + unsigned int payload_type; +}; + struct sdp_attribute { str full_line, /* including a= and \r\n */ line_value, /* without a= and without \r\n */ @@ -192,6 +199,7 @@ struct sdp_attribute { ATTR_FINGERPRINT, ATTR_SETUP, ATTR_RTPMAP, + ATTR_FMTP, ATTR_IGNORE, ATTR_END_OF_CANDIDATES, } attr; @@ -205,6 +213,7 @@ struct sdp_attribute { struct attribute_fingerprint fingerprint; struct attribute_setup setup; struct attribute_rtpmap rtpmap; + struct attribute_fmtp fmtp; } u; }; @@ -391,8 +400,7 @@ static int parse_attribute_ssrc(struct sdp_attribute *output) { return -1; s->attr = s->attr_str; - str_chr_str(&s->value, &s->attr, ':'); - if (s->value.s) { + if (str_chr_str(&s->value, &s->attr, ':')) { s->attr.len = s->value.s - s->attr.s; str_shift(&s->value, 1); } @@ -471,8 +479,7 @@ static int parse_attribute_crypto(struct sdp_attribute *output) { if (c->lifetime_str.s[0] != '|') goto error; str_shift(&c->lifetime_str, 1); - str_chr_str(&c->mki_str, &c->lifetime_str, '|'); - if (!c->mki_str.s) { + if (!str_chr_str(&c->mki_str, &c->lifetime_str, '|')) { if (str_chr(&c->lifetime_str, ':')) { c->mki_str = c->lifetime_str; c->lifetime_str = STR_NULL; @@ -507,9 +514,8 @@ static int parse_attribute_crypto(struct sdp_attribute *output) { } if (c->mki_str.s) { - str_chr_str(&s, &c->mki_str, ':'); err = "invalid MKI specification"; - if (!s.s) + if (!str_chr_str(&s, &c->mki_str, ':')) goto error; u32 = htonl(strtoul(c->mki_str.s, NULL, 10)); c->mki_len = strtoul(s.s + 1, NULL, 10); @@ -723,16 +729,14 @@ static int parse_attribute_rtpmap(struct sdp_attribute *output) { if (ep == a->payload_type_str.s) return -1; - str_chr_str(&a->clock_rate_str, &a->encoding_str, '/'); - if (!a->clock_rate_str.s) + if (!str_chr_str(&a->clock_rate_str, &a->encoding_str, '/')) return -1; pt->encoding = a->encoding_str; pt->encoding.len -= a->clock_rate_str.len; str_shift(&a->clock_rate_str, 1); - str_chr_str(&pt->encoding_parameters, &a->clock_rate_str, '/'); - if (pt->encoding_parameters.s) { + if (str_chr_str(&pt->encoding_parameters, &a->clock_rate_str, '/')) { a->clock_rate_str.len -= pt->encoding_parameters.len; str_shift(&pt->encoding_parameters, 1); } @@ -747,19 +751,37 @@ static int parse_attribute_rtpmap(struct sdp_attribute *output) { return 0; } +static int parse_attribute_fmtp(struct sdp_attribute *output) { + PARSE_DECL; + char *ep; + struct attribute_fmtp *a; + + output->attr = ATTR_FMTP; + + PARSE_INIT; + EXTRACT_TOKEN(u.fmtp.payload_type_str); + EXTRACT_TOKEN(u.fmtp.format_parms_str); + + a = &output->u.fmtp; + + a->payload_type = strtoul(a->payload_type_str.s, &ep, 10); + if (ep == a->payload_type_str.s) + return -1; + + return 0; +} + static int parse_attribute(struct sdp_attribute *a) { int ret; a->name = a->line_value; - str_chr_str(&a->value, &a->name, ':'); - if (a->value.s) { + if (str_chr_str(&a->value, &a->name, ':')) { a->name.len -= a->value.len; a->value.s++; a->value.len--; a->key = a->name; - str_chr_str(&a->param, &a->value, ' '); - if (a->param.s) { + if (str_chr_str(&a->param, &a->value, ' ')) { a->key.len += 1 + (a->value.len - a->param.len); @@ -784,6 +806,8 @@ static int parse_attribute(struct sdp_attribute *a) { ret = parse_attribute_rtcp(a); else if (!str_cmp(&a->name, "ssrc")) ret = parse_attribute_ssrc(a); + else if (!str_cmp(&a->name, "fmtp")) + ret = parse_attribute_fmtp(a); break; case 5: if (!str_cmp(&a->name, "group")) @@ -1080,7 +1104,7 @@ static int fill_endpoint(struct endpoint *ep, const struct sdp_media *media, str static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media) { - GHashTable *ht; + GHashTable *ht_rtpmap, *ht_fmtp; GQueue *q; GList *ql; struct sdp_attribute *attr; @@ -1090,15 +1114,21 @@ static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media return 0; /* first go through a=rtpmap and build a hash table of attrs */ - ht = g_hash_table_new(g_int_hash, g_int_equal); + ht_rtpmap = g_hash_table_new(g_int_hash, g_int_equal); q = attr_list_get_by_id(&media->attributes, ATTR_RTPMAP); for (ql = q ? q->head : NULL; ql; ql = ql->next) { struct rtp_payload_type *pt; attr = ql->data; pt = &attr->u.rtpmap.rtp_pt; - g_hash_table_insert(ht, &pt->payload_type, pt); + g_hash_table_insert(ht_rtpmap, &pt->payload_type, pt); + } + // do the same for a=fmtp + ht_fmtp = g_hash_table_new(g_int_hash, g_int_equal); + q = attr_list_get_by_id(&media->attributes, ATTR_FMTP); + for (ql = q ? q->head : NULL; ql; ql = ql->next) { + attr = ql->data; + g_hash_table_insert(ht_fmtp, &attr->u.fmtp.payload_type, &attr->u.fmtp.format_parms_str); } - /* a=fmtp processing would go here */ /* then go through the format list and associate */ for (ql = media->format_list.head; ql; ql = ql->next) { @@ -1115,13 +1145,18 @@ static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media /* first look in rtpmap for a match, then check RFC types, * else fall back to an "unknown" type */ - ptl = rtp_payload_type(i, ht); + ptl = rtp_payload_type(i, ht_rtpmap); pt = g_slice_alloc0(sizeof(*pt)); if (ptl) *pt = *ptl; else pt->payload_type = i; + + s = g_hash_table_lookup(ht_fmtp, &i); + if (s) + pt->format_parameters = *s; + g_queue_push_tail(&sp->rtp_payload_types, pt); } @@ -1131,7 +1166,8 @@ error: ret = -1; goto out; out: - g_hash_table_destroy(ht); + g_hash_table_destroy(ht_rtpmap); + g_hash_table_destroy(ht_fmtp); return ret; } @@ -1312,6 +1348,7 @@ error: return -1; } +// XXX iovec can probably be eliminated now and this moved to a regular string builder struct sdp_chopper *sdp_chopper_new(str *input) { struct sdp_chopper *c = g_slice_alloc0(sizeof(*c)); c->input = input; @@ -1415,6 +1452,21 @@ static int replace_transport_protocol(struct sdp_chopper *chop, return 0; } +static int replace_codec_list(struct sdp_chopper *chop, + struct sdp_media *media, struct call_media *cm) +{ + if (cm->codecs_prefs.length == 0) + return 0; // legacy protocol or usage error + + for (GList *l = cm->codecs_prefs.head; l; l = l->next) { + struct rtp_payload_type *pt = l->data; + chopper_append_printf(chop, " %u", pt->payload_type); + } + if (skip_over(chop, &media->formats)) + return -1; + return 0; +} + static int replace_media_port(struct sdp_chopper *chop, struct sdp_media *media, struct packet_stream *ps) { str *port = &media->port; unsigned int p; @@ -1650,6 +1702,22 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media * goto strip; // hack/workaround: always remove a=mid break; + case ATTR_RTPMAP: + if (media->codecs_prefs.length == 0) + break; // legacy protocol or usage error + if (!g_hash_table_lookup(media->codecs, + &attr->u.rtpmap.rtp_pt.payload_type)) + goto strip; + break; + + case ATTR_FMTP: + if (media->codecs_prefs.length == 0) + break; // legacy protocol or usage error + if (!g_hash_table_lookup(media->codecs, + &attr->u.fmtp.payload_type)) + goto strip; + break; + default: break; } @@ -1958,6 +2026,8 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu goto error; if (replace_transport_protocol(chop, sdp_media, call_media)) goto error; + if (replace_codec_list(chop, sdp_media, call_media)) + goto error; if (sdp_media->connection.parsed) { if (replace_network_address(chop, &sdp_media->connection.address, ps, @@ -1998,18 +2068,28 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu || (flags->opmode == OP_OFFER && flags->rtcp_mux_require))) { - chopper_append_c(chop, "a=rtcp:"); - chopper_append_printf(chop, "%u", ps->selected_sfd->socket.local.port); - chopper_append_c(chop, "\r\na=rtcp-mux\r\n"); + if (!flags->no_rtcp_attr) { + chopper_append_c(chop, "a=rtcp:"); + chopper_append_printf(chop, "%u", ps->selected_sfd->socket.local.port); + chopper_append_c(chop, "\r\na=rtcp-mux\r\n"); + } + else + chopper_append_c(chop, "a=rtcp-mux\r\n"); ps_rtcp = NULL; } else if (ps_rtcp && !flags->ice_force_relay) { - chopper_append_c(chop, "a=rtcp:"); - chopper_append_printf(chop, "%u", ps_rtcp->selected_sfd->socket.local.port); - if (!MEDIA_ISSET(call_media, RTCP_MUX)) - chopper_append_c(chop, "\r\n"); - else - chopper_append_c(chop, "\r\na=rtcp-mux\r\n"); + if (!flags->no_rtcp_attr) { + chopper_append_c(chop, "a=rtcp:"); + chopper_append_printf(chop, "%u", ps_rtcp->selected_sfd->socket.local.port); + if (!MEDIA_ISSET(call_media, RTCP_MUX)) + chopper_append_c(chop, "\r\n"); + else + chopper_append_c(chop, "\r\na=rtcp-mux\r\n"); + } + else { + if (MEDIA_ISSET(call_media, RTCP_MUX)) + chopper_append_c(chop, "a=rtcp-mux\r\n"); + } } } else diff --git a/daemon/socket.c b/daemon/socket.c index 00f731c49..b1fe71f94 100644 --- a/daemon/socket.c +++ b/daemon/socket.c @@ -25,6 +25,8 @@ static int __ip4_is_specified(const sockaddr_t *a); static int __ip6_is_specified(const sockaddr_t *a); static int __ip_bind(socket_t *s, unsigned int, const sockaddr_t *); static int __ip_connect(socket_t *s, const endpoint_t *); +static int __ip_listen(socket_t *s, int backlog); +static int __ip_accept(socket_t *s, socket_t *new_sock); static int __ip_timestamping(socket_t *s); static int __ip4_sockaddr2endpoint(endpoint_t *, const void *); static int __ip6_sockaddr2endpoint(endpoint_t *, const void *); @@ -76,6 +78,8 @@ static struct socket_family __socket_families[__SF_LAST] = { .addrport2sockaddr = __ip4_addrport2sockaddr, .bind = __ip_bind, .connect = __ip_connect, + .listen = __ip_listen, + .accept = __ip_accept, .timestamping = __ip_timestamping, .recvfrom = __ip_recvfrom, .recvfrom_ts = __ip_recvfrom_ts, @@ -105,6 +109,8 @@ static struct socket_family __socket_families[__SF_LAST] = { .addrport2sockaddr = __ip6_addrport2sockaddr, .bind = __ip_bind, .connect = __ip_connect, + .listen = __ip_listen, + .accept = __ip_accept, .timestamping = __ip_timestamping, .recvfrom = __ip_recvfrom, .recvfrom_ts = __ip_recvfrom_ts, @@ -261,6 +267,30 @@ static int __ip_connect(socket_t *s, const endpoint_t *ep) { } return 0; } +static int __ip_listen(socket_t *s, int backlog) { + return listen(s->fd, backlog); +} +static int __ip_accept(socket_t *s, socket_t *newsock) { + int nfd; + struct sockaddr_storage sin; + socklen_t sinlen; + + ZERO(*newsock); + + sinlen = sizeof(sin); + nfd = accept(s->fd, (struct sockaddr *) &sin, &sinlen); + if (nfd == -1) { + __C_DBG("accept fail, fd=%d, port=%d", s->fd, s->local.port); + return -1; + } + + newsock->fd = nfd; + newsock->family = s->family; + newsock->local = s->local; + s->family->sockaddr2endpoint(&newsock->remote, &sin); + + return 0; +} static ssize_t __ip_recvfrom_ts(socket_t *s, void *buf, size_t len, endpoint_t *ep, struct timeval *tv) { ssize_t ret; struct sockaddr_storage sin; @@ -619,6 +649,8 @@ int open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *sa) nonblock(r->fd); reuseaddr(r->fd); + if (r->family->af == AF_INET6) + ipv6only(r->fd, 1); if (port > 0xffff) { __C_DBG("open socket fail, port=%d > 0xfffffd", port); diff --git a/daemon/socket.h b/daemon/socket.h index 424e529c5..7a010d849 100644 --- a/daemon/socket.h +++ b/daemon/socket.h @@ -64,6 +64,8 @@ struct socket_family { int (*addrport2sockaddr)(void *, const sockaddr_t *, unsigned int); int (*bind)(socket_t *, unsigned int, const sockaddr_t *); int (*connect)(socket_t *, const endpoint_t *); + int (*listen)(socket_t *, int); + int (*accept)(socket_t *, socket_t *); int (*timestamping)(socket_t *); ssize_t (*recvfrom)(socket_t *, void *, size_t, endpoint_t *); ssize_t (*recvfrom_ts)(socket_t *, void *, size_t, endpoint_t *, struct timeval *); @@ -261,7 +263,9 @@ INLINE int ipv46_any_convert(endpoint_t *ep) { #define endpoint_packet_header(o, src, dst, len) (dst)->address.family->packet_header(o, src, dst, len) - +INLINE void set_tos(socket_t *s, unsigned int tos) { + s->family->tos(s, tos); +} socktype_t *get_socket_type(const str *s); socktype_t *get_socket_type_c(const char *s); diff --git a/daemon/ssrc.c b/daemon/ssrc.c index 74c734008..ba856350a 100644 --- a/daemon/ssrc.c +++ b/daemon/ssrc.c @@ -21,6 +21,7 @@ static struct ssrc_entry *create_ssrc_entry(u_int32_t ssrc) { } static void add_ssrc_entry(struct ssrc_entry *ent, struct ssrc_hash *ht) { g_hash_table_replace(ht->ht, &ent->ssrc, ent); + g_queue_push_tail(&ht->q, ent); } static void free_sender_report(struct ssrc_sender_report_item *i) { g_slice_free1(sizeof(*i), i); @@ -78,11 +79,11 @@ restart: rwlock_lock_w(&ht->lock); - if (G_UNLIKELY(g_hash_table_size(ht->ht) > 20)) { // arbitrary limit - rwlock_unlock_w(&ht->lock); - free_ssrc_entry(ent); - ilog(LOG_INFO, "SSRC hash table exceeded size limit (trying to add %u)", ssrc); - return NULL; + while (G_UNLIKELY(ht->q.length > 20)) { // arbitrary limit + struct ssrc_entry *old_ent = g_queue_pop_head(&ht->q); + ilog(LOG_DEBUG, "SSRC hash table exceeded size limit (trying to add %u) - deleting SSRC %u", + ssrc, old_ent->ssrc); + g_hash_table_remove(ht->ht, &old_ent->ssrc); } if (g_hash_table_lookup(ht->ht, &ssrc)) { @@ -101,6 +102,7 @@ void free_ssrc_hash(struct ssrc_hash **ht) { if (!*ht) return; g_hash_table_destroy((*ht)->ht); + g_queue_clear(&(*ht)->q); g_slice_free1(sizeof(**ht), *ht); *ht = NULL; } @@ -249,7 +251,7 @@ void ssrc_receiver_report(struct call_media *m, const struct ssrc_receiver_repor } } - const struct rtp_payload_type *rpt = rtp_payload_type(pt, m->rtp_payload_types); + const struct rtp_payload_type *rpt = rtp_payload_type(pt, m->codecs); if (!rpt) { ilog(LOG_INFO, "Invalid RTP payload type %i, discarding RTCP RR", pt); goto out_nl; diff --git a/daemon/ssrc.h b/daemon/ssrc.h index 93755edf3..0d261542f 100644 --- a/daemon/ssrc.h +++ b/daemon/ssrc.h @@ -21,6 +21,7 @@ enum ssrc_dir; struct ssrc_hash { GHashTable *ht; + GQueue q; rwlock_t lock; }; struct ssrc_ctx { diff --git a/daemon/statistics.c b/daemon/statistics.c index a785fd06b..74bfd04de 100644 --- a/daemon/statistics.c +++ b/daemon/statistics.c @@ -1,5 +1,14 @@ #include "call.h" #include "statistics.h" +#include "graphite.h" +#include "main.h" + + +struct totalstats rtpe_totalstats; +struct totalstats rtpe_totalstats_interval; +mutex_t rtpe_totalstats_lastinterval_lock; +struct totalstats rtpe_totalstats_lastinterval; + static void timeval_totalstats_average_add(struct totalstats *s, const struct timeval *add) { struct timeval dp, oa; @@ -59,53 +68,48 @@ static void timeval_totalstats_interval_call_duration_add(struct totalstats *s, } -void statistics_update_totals(struct callmaster* m, struct packet_stream *ps) { - atomic64_add(&m->totalstats.total_relayed_packets, +void statistics_update_totals(struct packet_stream *ps) { + atomic64_add(&rtpe_totalstats.total_relayed_packets, atomic64_get(&ps->stats.packets)); - atomic64_add(&m->totalstats_interval.total_relayed_packets, + atomic64_add(&rtpe_totalstats_interval.total_relayed_packets, atomic64_get(&ps->stats.packets)); - atomic64_add(&m->totalstats.total_relayed_errors, + atomic64_add(&rtpe_totalstats.total_relayed_errors, atomic64_get(&ps->stats.errors)); - atomic64_add(&m->totalstats_interval.total_relayed_errors, + atomic64_add(&rtpe_totalstats_interval.total_relayed_errors, atomic64_get(&ps->stats.errors)); } void statistics_update_foreignown_dec(struct call* c) { - struct callmaster *m; - - m = c->callmaster; - if (IS_FOREIGN_CALL(c)) { - atomic64_dec(&m->stats.foreign_sessions); + atomic64_dec(&rtpe_stats.foreign_sessions); } if(IS_OWN_CALL(c)) { - mutex_lock(&m->totalstats_interval.managed_sess_lock); - m->totalstats_interval.managed_sess_min = MIN(m->totalstats_interval.managed_sess_min, - g_hash_table_size(m->callhash) - atomic64_get(&m->stats.foreign_sessions)); - mutex_unlock(&m->totalstats_interval.managed_sess_lock); + mutex_lock(&rtpe_totalstats_interval.managed_sess_lock); + rtpe_totalstats_interval.managed_sess_min = MIN(rtpe_totalstats_interval.managed_sess_min, + g_hash_table_size(rtpe_callhash) - atomic64_get(&rtpe_stats.foreign_sessions)); + mutex_unlock(&rtpe_totalstats_interval.managed_sess_lock); } } -void statistics_update_foreignown_inc(struct callmaster *m, struct call* c) { +void statistics_update_foreignown_inc(struct call* c) { if (IS_OWN_CALL(c)) { - mutex_lock(&m->totalstats_interval.managed_sess_lock); - m->totalstats_interval.managed_sess_max = MAX( - m->totalstats_interval.managed_sess_max, - g_hash_table_size(m->callhash) - - atomic64_get(&m->stats.foreign_sessions)); - mutex_unlock(&m->totalstats_interval.managed_sess_lock); + mutex_lock(&rtpe_totalstats_interval.managed_sess_lock); + rtpe_totalstats_interval.managed_sess_max = MAX( + rtpe_totalstats_interval.managed_sess_max, + g_hash_table_size(rtpe_callhash) + - atomic64_get(&rtpe_stats.foreign_sessions)); + mutex_unlock(&rtpe_totalstats_interval.managed_sess_lock); } else if (IS_FOREIGN_CALL(c)) { /* foreign call*/ - atomic64_inc(&m->stats.foreign_sessions); - atomic64_inc(&m->totalstats.total_foreign_sessions); + atomic64_inc(&rtpe_stats.foreign_sessions); + atomic64_inc(&rtpe_totalstats.total_foreign_sessions); } } void statistics_update_oneway(struct call* c) { - struct callmaster *m; struct packet_stream *ps = NULL, *ps2 = NULL; struct call_monologue *ml; struct call_media *md; @@ -114,8 +118,6 @@ void statistics_update_oneway(struct call* c) { GList *l; struct timeval tim_result_duration; - m = c->callmaster; - // --- for statistics getting one way stream or no relay at all int total_nopacket_relayed_sess = 0; @@ -162,8 +164,8 @@ void statistics_update_oneway(struct call* c) { if (ps && ps2 && atomic64_get(&ps2->stats.packets)==0) { if (atomic64_get(&ps->stats.packets)!=0 && IS_OWN_CALL(c)){ if (atomic64_get(&ps->stats.packets)!=0) { - atomic64_inc(&m->totalstats.total_oneway_stream_sess); - atomic64_inc(&m->totalstats_interval.total_oneway_stream_sess); + atomic64_inc(&rtpe_totalstats.total_oneway_stream_sess); + atomic64_inc(&rtpe_totalstats_interval.total_oneway_stream_sess); } } else { @@ -173,42 +175,55 @@ void statistics_update_oneway(struct call* c) { } if (IS_OWN_CALL(c)) { - atomic64_add(&m->totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); - atomic64_add(&m->totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); + atomic64_add(&rtpe_totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); + atomic64_add(&rtpe_totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2); } if (c->monologues.head) { ml = c->monologues.head->data; - timeval_subtract(&tim_result_duration, &g_now, &ml->started); + timeval_subtract(&tim_result_duration, &rtpe_now, &ml->started); if (IS_OWN_CALL(c)) { if (ml->term_reason==TIMEOUT) { - atomic64_inc(&m->totalstats.total_timeout_sess); - atomic64_inc(&m->totalstats_interval.total_timeout_sess); + atomic64_inc(&rtpe_totalstats.total_timeout_sess); + atomic64_inc(&rtpe_totalstats_interval.total_timeout_sess); } else if (ml->term_reason==SILENT_TIMEOUT) { - atomic64_inc(&m->totalstats.total_silent_timeout_sess); - atomic64_inc(&m->totalstats_interval.total_silent_timeout_sess); + atomic64_inc(&rtpe_totalstats.total_silent_timeout_sess); + atomic64_inc(&rtpe_totalstats_interval.total_silent_timeout_sess); } else if (ml->term_reason==REGULAR) { - atomic64_inc(&m->totalstats.total_regular_term_sess); - atomic64_inc(&m->totalstats_interval.total_regular_term_sess); + atomic64_inc(&rtpe_totalstats.total_regular_term_sess); + atomic64_inc(&rtpe_totalstats_interval.total_regular_term_sess); } else if (ml->term_reason==FORCED) { - atomic64_inc(&m->totalstats.total_forced_term_sess); - atomic64_inc(&m->totalstats_interval.total_forced_term_sess); + atomic64_inc(&rtpe_totalstats.total_forced_term_sess); + atomic64_inc(&rtpe_totalstats_interval.total_forced_term_sess); } - timeval_totalstats_average_add(&m->totalstats, &tim_result_duration); - timeval_totalstats_average_add(&m->totalstats_interval, &tim_result_duration); + timeval_totalstats_average_add(&rtpe_totalstats, &tim_result_duration); + timeval_totalstats_average_add(&rtpe_totalstats_interval, &tim_result_duration); timeval_totalstats_interval_call_duration_add( - &m->totalstats_interval, &ml->started, &ml->terminated, - &m->latest_graphite_interval_start, - m->conf.graphite_interval); + &rtpe_totalstats_interval, &ml->started, &ml->terminated, + &rtpe_latest_graphite_interval_start, + rtpe_config.graphite_interval); } if (ml->term_reason==FINAL_TIMEOUT) { - atomic64_inc(&m->totalstats.total_final_timeout_sess); - atomic64_inc(&m->totalstats_interval.total_final_timeout_sess); + atomic64_inc(&rtpe_totalstats.total_final_timeout_sess); + atomic64_inc(&rtpe_totalstats_interval.total_final_timeout_sess); } } } + +void statistics_init() { + mutex_init(&rtpe_totalstats.total_average_lock); + mutex_init(&rtpe_totalstats_interval.total_average_lock); + mutex_init(&rtpe_totalstats_interval.managed_sess_lock); + mutex_init(&rtpe_totalstats_interval.total_calls_duration_lock); + + rtpe_totalstats.started = rtpe_now.tv_sec; + //rtpe_totalstats_interval.managed_sess_min = 0; // already zeroed + //rtpe_totalstats_interval.managed_sess_max = 0; + + mutex_init(&rtpe_totalstats_lastinterval_lock); +} diff --git a/daemon/statistics.h b/daemon/statistics.h index 333a1e7ba..0f3bf4150 100644 --- a/daemon/statistics.h +++ b/daemon/statistics.h @@ -69,12 +69,16 @@ struct call_stats { struct stats totals[4]; /* rtp in, rtcp in, rtp out, rtcp out */ }; - -struct callmaster; +extern struct totalstats rtpe_totalstats; +extern struct totalstats rtpe_totalstats_interval; +extern mutex_t rtpe_totalstats_lastinterval_lock; +extern struct totalstats rtpe_totalstats_lastinterval; void statistics_update_oneway(struct call *); void statistics_update_foreignown_dec(struct call *); -void statistics_update_foreignown_inc(struct callmaster *m, struct call* c); -void statistics_update_totals(struct callmaster *, struct packet_stream *) ; +void statistics_update_foreignown_inc(struct call* c); +void statistics_update_totals(struct packet_stream *) ; + +void statistics_init(void); #endif /* STATISTICS_H_ */ diff --git a/daemon/streambuf.c b/daemon/streambuf.c index 24a7c241d..073609fbe 100644 --- a/daemon/streambuf.c +++ b/daemon/streambuf.c @@ -13,16 +13,19 @@ + + struct streambuf *streambuf_new(struct poller *p, int fd) { struct streambuf *b; b = malloc(sizeof(*b)); ZERO(*b); + mutex_init(&b->lock); b->buf = g_string_new(""); b->fd = fd; b->poller = p; - b->active = poller_now; + b->active = rtpe_now.tv_sec; return b; } @@ -38,6 +41,8 @@ int streambuf_writeable(struct streambuf *b) { int ret; unsigned int out; + mutex_lock(&b->lock); + for (;;) { if (!b->buf->len) break; @@ -48,14 +53,16 @@ int streambuf_writeable(struct streambuf *b) { if (ret < 0) { if (errno == EINTR) continue; - if (errno != EAGAIN && errno != EWOULDBLOCK) + if (errno != EAGAIN && errno != EWOULDBLOCK) { + mutex_unlock(&b->lock); return -1; + } ret = 0; } if (ret > 0) { g_string_erase(b->buf, 0, ret); - b->active = poller_now; + b->active = rtpe_now.tv_sec; } if (ret != out) { @@ -64,6 +71,7 @@ int streambuf_writeable(struct streambuf *b) { } } + mutex_unlock(&b->lock); return 0; } @@ -71,32 +79,43 @@ int streambuf_readable(struct streambuf *b) { int ret; char buf[1024]; + mutex_lock(&b->lock); + for (;;) { ret = read(b->fd, buf, 1024); - if (ret == 0) - return -1; + if (ret == 0) { + // don't discard already read data in the buffer + b->eof = 1; + ret = b->buf->len ? -2 : -1; + mutex_unlock(&b->lock); + return ret; + } if (ret < 0) { if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) break; + mutex_unlock(&b->lock); return -1; } g_string_append_len(b->buf, buf, ret); - b->active = poller_now; + b->active = rtpe_now.tv_sec; } + mutex_unlock(&b->lock); return 0; } char *streambuf_getline(struct streambuf *b) { char *p; - int len; + int len, to_del; char *s = NULL; + mutex_lock(&b->lock); + for (;;) { if (s) { free(s); @@ -104,19 +123,29 @@ char *streambuf_getline(struct streambuf *b) { } p = memchr(b->buf->str, '\n', b->buf->len); - if (!p) - return NULL; - - len = p - b->buf->str; - if (len == 0) { - g_string_erase(b->buf, 0, 1); - continue; + if (!p) { + if (b->eof) { + // use entire string + len = b->buf->len; + to_del = len; + } + else + break; + } + else { + len = p - b->buf->str; + to_del = len + 1; + if (len == 0) { + // blank line, skip it + g_string_erase(b->buf, 0, 1); + continue; + } } s = malloc(len + 1); memcpy(s, b->buf->str, len); s[len] = '\0'; - g_string_erase(b->buf, 0, len + 1); + g_string_erase(b->buf, 0, to_del); if (s[--len] == '\r') { if (len == 0) @@ -127,6 +156,7 @@ char *streambuf_getline(struct streambuf *b) { break; } + mutex_unlock(&b->lock); return s; } @@ -157,6 +187,8 @@ void streambuf_write(struct streambuf *b, const char *s, unsigned int len) { unsigned int out; int ret; + mutex_lock(&b->lock); + while (len && !poller_isblocked(b->poller, b->fd)) { out = (len > 1024) ? 1024 : len; ret = write(b->fd, s, out); @@ -176,11 +208,13 @@ void streambuf_write(struct streambuf *b, const char *s, unsigned int len) { s += ret; len -= ret; - b->active = poller_now; + b->active = rtpe_now.tv_sec; } if (b->buf->len > 5242880) poller_error(b->poller, b->fd); else if (len) g_string_append_len(b->buf, s, len); + + mutex_unlock(&b->lock); } diff --git a/daemon/streambuf.h b/daemon/streambuf.h index a7f50ef26..0b79d95e2 100644 --- a/daemon/streambuf.h +++ b/daemon/streambuf.h @@ -10,6 +10,7 @@ #include "compat.h" #include "str.h" +#include "aux.h" @@ -18,10 +19,12 @@ struct poller; struct streambuf { + mutex_t lock; GString *buf; int fd; struct poller *poller; time_t active; + int eof; }; diff --git a/daemon/stun.c b/daemon/stun.c index a236f0e98..dfecde7cd 100644 --- a/daemon/stun.c +++ b/daemon/stun.c @@ -222,9 +222,12 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns, break; default: - ilog(LOG_NOTICE, "Unknown STUN attribute: 0x%04x", type); - if ((type & 0x8000)) + if ((type & 0x8000)) { + // comprehension optional + ilog(LOG_DEBUG, "Unknown STUN attribute: 0x%04x", type); break; + } + ilog(LOG_NOTICE, "Unknown STUN attribute: 0x%04x", type); unknowns[uc] = tlv->type; unknowns[++uc] = 0xffff; if (uc >= UNKNOWNS_COUNT - 1) @@ -437,8 +440,7 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *med if (attrs->username.s) { /* request */ ufrag[dst] = attrs->username; - str_chr_str(&ufrag[src], &ufrag[dst], ':'); - if (!ufrag[src].s) + if (!str_chr_str(&ufrag[src], &ufrag[dst], ':')) return -1; ufrag[dst].len -= ufrag[src].len; str_shift(&ufrag[src], 1); diff --git a/daemon/tcp_listener.c b/daemon/tcp_listener.c new file mode 100644 index 000000000..e232f2bb8 --- /dev/null +++ b/daemon/tcp_listener.c @@ -0,0 +1,229 @@ +#include "tcp_listener.h" + +#include + +#include "poller.h" +#include "obj.h" +#include "socket.h" +#include "aux.h" +#include "log.h" +#include "streambuf.h" + +struct tcp_listener_callback { + struct obj obj; + tcp_listener_callback_t func; + socket_t *ul; + struct obj *p; +}; +struct streambuf_callback { + struct obj obj; + streambuf_callback_t newconn_func; + streambuf_callback_t newdata_func; + streambuf_callback_t closed_func; + streambuf_callback_t timer_func; + struct streambuf_listener *listener; + struct obj *parent; +}; + +static void tcp_listener_incoming(int fd, void *p, uintptr_t x) { + struct tcp_listener_callback *cb = p; + int ret; + char addr[64]; + socket_t *listener; + socket_t newsock; + + listener = cb->ul; + + for (;;) { + ret = listener->family->accept(listener, &newsock); + if (ret == -1) { + if (errno == EINTR) + continue; + if (errno != EWOULDBLOCK && errno != EAGAIN) + ilog(LOG_WARNING, "Error accepting TCP connection: %s", strerror(errno)); + return; + } + nonblock(newsock.fd); + + endpoint_print(&newsock.remote, addr, sizeof(addr)); + + cb->func(cb->p, &newsock, addr, listener); + } +} + +static void tcp_listener_closed(int fd, void *p, uintptr_t u) { + abort(); +} + +int tcp_listener_init(socket_t *sock, struct poller *p, const endpoint_t *ep, + tcp_listener_callback_t func, struct obj *obj) +{ + struct poller_item i; + struct tcp_listener_callback *cb; + + cb = obj_alloc("tcp_listener_callback", sizeof(*cb), NULL); + cb->func = func; + cb->p = obj_get_o(obj); + cb->ul = sock; + + if (open_socket(sock, SOCK_STREAM, ep->port, &ep->address)) + goto fail; + if (sock->family->listen(sock, 5)) + goto fail; + + ZERO(i); + i.fd = sock->fd; + i.closed = tcp_listener_closed; + i.readable = tcp_listener_incoming; + i.obj = &cb->obj; + if (poller_add_item(p, &i)) + goto fail; + + return 0; + +fail: + close_socket(sock); + obj_put_o(obj); + obj_put(cb); + return -1; +} + +static void streambuf_stream_free(void *p) { + struct streambuf_stream *s = p; + close_socket(&s->sock); + streambuf_destroy(s->inbuf); + streambuf_destroy(s->outbuf); + obj_put(s->cb); + obj_put_o(s->parent); + free(s->addr); +} + +static void streambuf_stream_closed(int fd, void *p, uintptr_t u) { + struct streambuf_stream *s = p; + + if (s->sock.fd == -1) + return; + + if (s->cb->closed_func) + s->cb->closed_func(s); + + struct streambuf_listener *l = s->listener; + mutex_lock(&l->lock); + int ret = g_hash_table_remove(l->streams, s); + mutex_unlock(&l->lock); + poller_del_item(s->listener->poller, s->sock.fd); + if (ret) + obj_put(s); +} + +static void streambuf_stream_timer(int fd, void *p, uintptr_t u) { + struct streambuf_stream *s = p; + if (s->cb->timer_func) + s->cb->timer_func(s); +} + + +static void streambuf_stream_readable(int fd, void *p, uintptr_t u) { + struct streambuf_stream *s = p; + + int ret = streambuf_readable(s->inbuf); + if (ret == -1) + goto close; + s->cb->newdata_func(s); + if (ret == -2) + goto close; + + return; + +close: + streambuf_stream_closed(fd, s, 0); +} + +static void streambuf_stream_writeable(int fd, void *p, uintptr_t u) { + struct streambuf_stream *s = p; + + if (streambuf_writeable(s->outbuf)) + streambuf_stream_closed(fd, s, 0); +} + + +static void streambuf_listener_newconn(struct obj *p, socket_t *newsock, char *addr, socket_t *listener_sock) { + struct streambuf_callback *cb = (void *) p; + struct streambuf_stream *s; + struct streambuf_listener *listener; + struct poller_item i; + + listener = cb->listener; + + s = obj_alloc0("streambuf_stream", sizeof(*s), streambuf_stream_free); + s->sock = *newsock; + s->inbuf = streambuf_new(listener->poller, newsock->fd); + s->outbuf = streambuf_new(listener->poller, newsock->fd); + s->listener = listener; + s->cb = obj_get(cb); + s->parent = obj_get_o(cb->parent); + s->addr = strdup(addr); + + ZERO(i); + i.fd = newsock->fd; + i.closed = streambuf_stream_closed; + i.readable = streambuf_stream_readable; + i.writeable = streambuf_stream_writeable; + i.timer = streambuf_stream_timer; + i.obj = &s->obj; + + if (poller_add_item(listener->poller, &i)) + goto fail; + + if (cb->newconn_func) + cb->newconn_func(s); + + mutex_lock(&listener->lock); + g_hash_table_insert(listener->streams, s, s); // hand over ref + mutex_unlock(&listener->lock); + + return; + +fail: + obj_put(s); +} + +int streambuf_listener_init(struct streambuf_listener *listener, struct poller *p, const endpoint_t *ep, + streambuf_callback_t newconn_func, + streambuf_callback_t newdata_func, + streambuf_callback_t closed_func, + streambuf_callback_t timer_func, + struct obj *obj) +{ + struct streambuf_callback *cb; + + ZERO(*listener); + + listener->poller = p; + mutex_init(&listener->lock); + listener->streams = g_hash_table_new(g_direct_hash, g_direct_equal); + + cb = obj_alloc("streambuf_callback", sizeof(*cb), NULL); + cb->newconn_func = newconn_func; + cb->newdata_func = newdata_func; + cb->closed_func = closed_func; + cb->timer_func = timer_func; + cb->parent = obj_get_o(obj); + cb->listener = listener; + + if (tcp_listener_init(&listener->listener, p, ep, streambuf_listener_newconn, &cb->obj)) + goto fail; + + return 0; + +fail: + obj_put(cb); + return -1; +} + +void streambuf_stream_close(struct streambuf_stream *s) { + streambuf_stream_closed(s->sock.fd, s, 0); +} +void streambuf_stream_shutdown(struct streambuf_stream *s) { + shutdown(s->sock.fd, SHUT_WR); +} diff --git a/daemon/tcp_listener.h b/daemon/tcp_listener.h new file mode 100644 index 000000000..0bb297a79 --- /dev/null +++ b/daemon/tcp_listener.h @@ -0,0 +1,47 @@ +#ifndef _TCP_LISTENER_H_ +#define _TCP_LISTENER_H_ + +#include "socket.h" +#include "obj.h" + + +struct poller; +struct obj; +struct streambuf_callback; +struct streambuf_stream; + +typedef void (*tcp_listener_callback_t)(struct obj *p, socket_t *sock, char *addr, socket_t *); +typedef void (*streambuf_callback_t)(struct streambuf_stream *); + +struct streambuf_listener { + socket_t listener; + struct poller *poller; + mutex_t lock; + GHashTable *streams; +}; +struct streambuf_stream { + struct obj obj; + socket_t sock; + struct streambuf_listener *listener; + struct streambuf_callback *cb; + struct obj *parent; + char *addr; + struct streambuf *inbuf, + *outbuf; + +}; + +int tcp_listener_init(socket_t *, struct poller *p, const endpoint_t *, tcp_listener_callback_t, struct obj *); + +int streambuf_listener_init(struct streambuf_listener *listener, struct poller *p, const endpoint_t *ep, + streambuf_callback_t newconn_func, + streambuf_callback_t newdata_func, + streambuf_callback_t closed_func, + streambuf_callback_t timer_func, + struct obj *obj); + +void streambuf_stream_close(struct streambuf_stream *); +void streambuf_stream_shutdown(struct streambuf_stream *); + + +#endif diff --git a/daemon/udp_listener.c b/daemon/udp_listener.c index 209038dc4..656df6e70 100644 --- a/daemon/udp_listener.c +++ b/daemon/udp_listener.c @@ -17,7 +17,7 @@ struct udp_listener_callback { struct obj obj; udp_listener_callback_t func; - struct udp_listener *ul; + socket_t *ul; struct obj *p; }; @@ -31,13 +31,11 @@ static void udp_listener_incoming(int fd, void *p, uintptr_t x) { char buf[0x10000]; char addr[64]; str str; - struct udp_listener *ul; socket_t *listener; endpoint_t sin; str.s = buf; - ul = cb->ul; - listener = &ul->sock; + listener = cb->ul; for (;;) { len = socket_recvfrom(listener, buf, sizeof(buf)-1, &sin); @@ -53,11 +51,11 @@ static void udp_listener_incoming(int fd, void *p, uintptr_t x) { endpoint_print(&sin, addr, sizeof(addr)); str.len = len; - cb->func(cb->p, &str, &sin, addr, ul); + cb->func(cb->p, &str, &sin, addr, listener); } } -int udp_listener_init(struct udp_listener *u, struct poller *p, const endpoint_t *ep, +int udp_listener_init(socket_t *sock, struct poller *p, const endpoint_t *ep, udp_listener_callback_t func, struct obj *obj) { struct poller_item i; @@ -66,15 +64,13 @@ int udp_listener_init(struct udp_listener *u, struct poller *p, const endpoint_t cb = obj_alloc("udp_listener_callback", sizeof(*cb), NULL); cb->func = func; cb->p = obj_get_o(obj); - cb->ul = u; + cb->ul = sock; - if (open_socket(&u->sock, SOCK_DGRAM, ep->port, &ep->address)) + if (open_socket(sock, SOCK_DGRAM, ep->port, &ep->address)) goto fail; - ipv6only(u->sock.fd, 1); - ZERO(i); - i.fd = u->sock.fd; + i.fd = sock->fd; i.closed = udp_listener_closed; i.readable = udp_listener_incoming; i.obj = &cb->obj; @@ -84,7 +80,7 @@ int udp_listener_init(struct udp_listener *u, struct poller *p, const endpoint_t return 0; fail: - close_socket(&u->sock); + close_socket(sock); obj_put_o(obj); obj_put(cb); return -1; diff --git a/daemon/udp_listener.h b/daemon/udp_listener.h index d69ef6d82..5aa44c107 100644 --- a/daemon/udp_listener.h +++ b/daemon/udp_listener.h @@ -9,16 +9,9 @@ struct poller; struct obj; -struct udp_listener; -typedef void (*udp_listener_callback_t)(struct obj *p, str *buf, const endpoint_t *ep, char *addr, - struct udp_listener *); +typedef void (*udp_listener_callback_t)(struct obj *p, str *buf, const endpoint_t *ep, char *addr, socket_t *); -struct udp_listener { - socket_t sock; - struct poller *poller; -}; - -int udp_listener_init(struct udp_listener *, struct poller *p, const endpoint_t *, udp_listener_callback_t, struct obj *); +int udp_listener_init(socket_t *, struct poller *p, const endpoint_t *, udp_listener_callback_t, struct obj *); #endif diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 000000000..6fad2ea3c --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1,13 @@ +*.debhelper +*.log +*.substvars +/.debhelper/ +/debhelper-build-stamp +/files +/ngcp-rtpengine-daemon/ +/ngcp-rtpengine-iptables/ +/ngcp-rtpengine-kernel-dkms/ +/ngcp-rtpengine-kernel-source/ +/ngcp-rtpengine-recording-daemon/ +/ngcp-rtpengine-utils/ +/ngcp-rtpengine/ diff --git a/debian/changelog b/debian/changelog index 91ee0f295..c1c9e470b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,55 @@ +ngcp-rtpengine (6.1.0.0+0~mr6.1.0.0) unstable; urgency=medium + + [ Richard Fuchs ] + * [5bcbf27] fix metadata DB insert without trailing pipe character + * [13d51f0] add extra thread allowance for blocking CLI code + * [cc056c1] add rtcp-mux-require option to force rtcp-mux usage for WebRTC + * [23eebfc] Fix frame PTS when decoder returns multiple frames in a row + * [ba46768] small fixes and improvements for packet forwarding feature #411 + * [031921c] purge old entries from SSRC hash table if it gets too full + + [ Claudiu Boriga ] + * [ca622b4] set TOS for redis streams + * [d5fea12] check call.recording structure before using it + * [3dcddf3] recording-daemon: add option to forward calls + * [2250ab1] make METADATA section appear in the metafile at intialization when recording using the proc method + * [ab0cb0c] don't use g_hash_table_foreach for call_timer_iterator + + [ Pawel Kuzak ] + * [f81fdf4] Deleted outdated graphics + + [ Guillem Jover ] + * [d5f7624] TT#24097 Fix perl filehandle usage + * [4f006a5] TT#24097 Use block form for map and grep + * [aa2eaee] TT#24097 Use a regex for split + * [bf514fb] TT#24097 Do not directly return result from sort + * [d5ad4a9] TT#24097 Do not use magic variable names + * [301af4a] TT#24097 Declare variable as my + * [0c728b2] TT#24097 Localize %ENV variable assignments + * [b4d0ff7] TT#24097 Use upper-case HERE-doc markers + * [0012037] TT#24097 Allow perl builtin homonyms for method names + * [acf84a7] TT#24097 Use semicolon instead of colons for end of statement + * [cff9d29] TT#24097 Reorder hash assignment to make perlcritic life easier + * [0bdc116] TT#24097 Rework perl code flow + * [b2636dc] TT#24097 Enable strict and warnings everywhere + * [19b4df7] TT#24097 Do not use unportable test operators + * [3351821] TT#24097 Remove unused shell variables + * [747661f] TT#24097 Do not use bash builtin names in sh scripts + * [04f5790] TT#24097 Switch for loop to use the i variable + * [33bafcf] TT#24097 Use $() instead of deprecated `` + * [376df64] TT#24097 Use «grep -E» instead of deprecated egrep + * [53b3c36] TT#24097 Fix shell quoting + * [465f3fe] TT#24097 Remove $ from variables inside arithmetic evaluation + * [677c9a5] TT#22072 Update packaging + * [6721f3c] TT#22072 Update debian/.gitignore + + [ Dmitry Poroh ] + * [cc09f4d] Crash stream_packet in case of out_srtp is NULL is fixed + + [ Sipwise Jenkins Builder ] + + -- Sipwise Jenkins Builder Wed, 06 Dec 2017 00:14:35 +0100 + ngcp-rtpengine (6.0.0.0+0~mr6.0.0.0) unstable; urgency=medium [ Richard Fuchs ] diff --git a/debian/compat b/debian/compat index ec635144f..f599e28b8 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -9 +10 diff --git a/debian/control b/debian/control index feeadfd7f..f4fcaf556 100644 --- a/debian/control +++ b/debian/control @@ -1,9 +1,11 @@ Source: ngcp-rtpengine Section: net -Priority: extra +Priority: optional Maintainer: Sipwise Development Team +Homepage: https://www.sipwise.com/ +Standards-Version: 3.9.8 Build-Depends: - debhelper (>= 9~), + debhelper (>= 10~), default-libmysqlclient-dev | libmysqlclient-dev, iptables-dev (>= 1.4), libavcodec-dev (>= 6:10), @@ -23,8 +25,6 @@ Build-Depends: libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07), markdown, zlib1g-dev, -Standards-Version: 3.9.7 -Homepage: http://sipwise.com/ Package: ngcp-rtpengine-daemon Architecture: any @@ -122,27 +122,6 @@ Description: IPtables kernel module for the NGCP media proxy - DKMS. performance packet forwarding. This package contains the source to be built with dkms. -Package: ngcp-rtpengine-dbg -Architecture: any -Section: debug -Depends: - ngcp-rtpengine-daemon (= ${binary:Version}), - ngcp-rtpengine-iptables (= ${binary:Version}), - ${misc:Depends}, -Conflicts: - ngcp-mediaproxy-ng-dbg, -Replaces: - ngcp-mediaproxy-ng-dbg, -Description: debugging symbols for ngcp-rtpengine - The ngcp-rtpengine daemon handles the first stages of proxying media streams and talks to - the kernel part of the proxy for eventual high-performance packet forwarding. - . - ngcp-rtpengine-iptables provides the IPtables extension needed - to configure the mediaproxy rule. - . - This package contains the debugging symbols for ngcp-rtpengine-daemon - and ngcp-rtpengine-iptables - Package: ngcp-rtpengine-utils Architecture: all Depends: diff --git a/debian/ngcp-rtpengine-daemon.init b/debian/ngcp-rtpengine-daemon.init index 72e23715e..875aaa31f 100755 --- a/debian/ngcp-rtpengine-daemon.init +++ b/debian/ngcp-rtpengine-daemon.init @@ -10,6 +10,7 @@ # Description: Proxy for RTP and other media streams ### END INIT INFO +set -e PATH=/sbin:/bin:/usr/sbin:/usr/bin NAME=ngcp-rtpengine-daemon @@ -22,21 +23,19 @@ DEFAULTS=/etc/default/${NAME} test -f "$DAEMON" || exit 0 +. /lib/lsb/init-functions + # Load startup options if available if [ -f "$DEFAULTS" ]; then . "$DEFAULTS" || true fi if [ "$RUN_RTPENGINE" != "yes" ]; then - echo "rtpengine not yet configured. Edit $DEFAULTS first." + log_action_msg "rtpengine not yet configured. Edit $DEFAULTS first." exit 0 fi [ -z "$PIDFILE" ] && PIDFILE="/var/run/rtpengine.pid" -set -e - -. /lib/lsb/init-functions - OPTIONS="" START_OPTIONS="" MODPROBE_OPTIONS="" @@ -71,16 +70,16 @@ fi [ -z "$TOS" ] || OPTIONS="$OPTIONS --tos=$TOS" [ -z "$PORT_MIN" ] || OPTIONS="$OPTIONS --port-min=$PORT_MIN" [ -z "$PORT_MAX" ] || OPTIONS="$OPTIONS --port-max=$PORT_MAX" -[ -z "$REDIS" -o -z "$REDIS_DB" ] || OPTIONS="$OPTIONS --redis=$REDIS/$REDIS_DB" +[ -z "$REDIS" ] || [ -z "$REDIS_DB" ] || OPTIONS="$OPTIONS --redis=$REDIS/$REDIS_DB" [ -z "$REDIS_AUTH_PW" ] || export RTPENGINE_REDIS_AUTH_PW="$REDIS_AUTH_PW" -[ -z "$REDIS_WRITE" -o -z "$REDIS_WRITE_DB" ] || OPTIONS="$OPTIONS --redis-write=$REDIS_WRITE/$REDIS_WRITE_DB" +[ -z "$REDIS_WRITE" ] || [ -z "$REDIS_WRITE_DB" ] || OPTIONS="$OPTIONS --redis-write=$REDIS_WRITE/$REDIS_WRITE_DB" [ -z "$REDIS_WRITE_AUTH_PW" ] || export RTPENGINE_REDIS_WRITE_AUTH_PW="$REDIS_WRITE_AUTH_PW" [ -z "$REDIS_NUM_THREADS" ] || OPTIONS="$OPTIONS --redis-num-threads=$REDIS_NUM_THREADS" [ -z "$REDIS_EXPIRES" ] || OPTIONS="$OPTIONS --redis-expires=$REDIS_EXPIRES" [ -z "$REDIS_MULTIKEY" ] || OPTIONS="$OPTIONS --redis-multikey=$REDIS_MULTIKEY" -[ -z "$NO_REDIS_REQUIRED" -o \( "$NO_REDIS_REQUIRED" != "1" -a "$NO_REDIS_REQUIRED" != "yes" \) ] || OPTIONS="$OPTIONS --no-redis-required" +[ -z "$NO_REDIS_REQUIRED" ] || ( [ "$NO_REDIS_REQUIRED" != "1" ] && [ "$NO_REDIS_REQUIRED" != "yes" ] ) || OPTIONS="$OPTIONS --no-redis-required" [ -z "$B2B_URL" ] || OPTIONS="$OPTIONS --b2b-url=$B2B_URL" -[ -z "$NO_FALLBACK" -o \( "$NO_FALLBACK" != "1" -a "$NO_FALLBACK" != "yes" \) ] || OPTIONS="$OPTIONS --no-fallback" +[ -z "$NO_FALLBACK" ] || ( [ "$NO_FALLBACK" != "1" ] && [ "$NO_FALLBACK" != "yes" ] ) || OPTIONS="$OPTIONS --no-fallback" OPTIONS="$OPTIONS --table=$TABLE" [ -z "$LOG_LEVEL" ] || OPTIONS="$OPTIONS --log-level=$LOG_LEVEL" [ -z "$LOG_FACILITY" ] || OPTIONS="$OPTIONS --log-facility=$LOG_FACILITY" @@ -104,7 +103,7 @@ if [ ! -z "$RECORDING_DIR" ]; then fi [ -z "$RECORDING_METHOD" ] || OPTIONS="$OPTIONS --recording-method=$RECORDING_METHOD" [ -z "$RECORDING_FORMAT" ] || OPTIONS="$OPTIONS --recording-format=$RECORDING_FORMAT" -[ -z "$DTLS_PASSIVE" -o \( "$DTLS_PASSIVE" != "yes" -a "$DTLS_PASSIVE" != "1" \) ] || OPTIONS="$OPTIONS --dtls-passive" +[ -z "$DTLS_PASSIVE" ] || ( [ "$DTLS_PASSIVE" != "yes" ] && [ "$DTLS_PASSIVE" != "1" ] ) || OPTIONS="$OPTIONS --dtls-passive" if test "$FORK" = "no" ; then OPTIONS="$OPTIONS --foreground" @@ -127,19 +126,19 @@ fi if ! test -z "$SET_USER"; then START_OPTIONS="$START_OPTIONS --chuid $SET_USER" - UID=$(id -u "$SET_USER" 2> /dev/null) - test -z "$UID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_uid=$UID" + PUID=$(id -u "$SET_USER" 2> /dev/null) + test -z "$PUID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_uid=$PUID" if test -z "$SET_GROUP"; then - GID=$(id -g "$SET_USER" 2> /dev/null) - test -z "$GID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$GID" + PGID=$(id -g "$SET_USER" 2> /dev/null) + test -z "$PGID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$PGID" fi test "$DO_DIR_CHOWN" = 1 && chown "$SET_USER": "$PIDDIR" fi if ! test -z "$SET_GROUP"; then START_OPTIONS="$START_OPTIONS --group $SET_GROUP" - GID=$(grep "^$SET_GROUP:" /etc/group | cut -d: -f3 2> /dev/null) - test -z "$GID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$GID" + PGID=$(grep "^$SET_GROUP:" /etc/group | cut -d: -f3 2> /dev/null) + test -z "$PGID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$PGID" test "$DO_DIR_CHOWN" = 1 && chgrp "$SET_GROUP" "$PIDDIR" fi @@ -159,17 +158,17 @@ case "$1" in status=$? case "${status}" in 0|3) - echo "Active node or transition." + log_action_msg "Active node or transition." ;; *) - echo "Ignored start action in inactive node ($status)" + log_action_msg "Ignored start action in inactive node ($status)" exit 0 ;; esac fi - log_daemon_msg "Starting $DESC: $NAME" if [ "$TABLE" -ge 0 ] && [ "$VIRT" != "yes" ]; then if [ "$MANAGE_IPTABLES" = "yes" ]; then + # shellcheck disable=SC2086 modprobe xt_RTPENGINE $MODPROBE_OPTIONS iptables -N rtpengine 2> /dev/null @@ -183,19 +182,18 @@ case "$1" in ip6tables -D rtpengine -p udp -j RTPENGINE --id "$TABLE" 2>/dev/null ip6tables -I rtpengine -p udp -j RTPENGINE --id "$TABLE" fi - if [ -e /proc/rtpengine/control ]; then - echo "del $TABLE" > /proc/rtpengine/control 2>/dev/null - fi fi set -e + log_daemon_msg "Starting $DESC" "$NAME" + # shellcheck disable=SC2086 start-stop-daemon --start --quiet --pidfile "$PIDFILE" \ --exec "$DAEMON" $START_OPTIONS -- $OPTIONS || log_progress_msg " already running" log_end_msg $? ;; stop) - log_daemon_msg "Stopping $DESC: $NAME" + log_daemon_msg "Stopping $DESC" "$NAME" start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \ - --exec "$DAEMON" + --retry 5 --exec "$DAEMON" if [ "$?" -ne 0 ]; then return $? fi @@ -217,7 +215,6 @@ case "$1" in ;; restart|force-reload) $0 stop - sleep 1 $0 start ;; status) diff --git a/debian/ngcp-rtpengine-recording-daemon.init b/debian/ngcp-rtpengine-recording-daemon.init index 32457eb1c..77d0da233 100755 --- a/debian/ngcp-rtpengine-recording-daemon.init +++ b/debian/ngcp-rtpengine-recording-daemon.init @@ -10,33 +10,31 @@ # Description: Recording daemon for RTP and other media streams ### END INIT INFO +set -e PATH=/sbin:/bin:/usr/sbin:/usr/bin NAME=ngcp-rtpengine-recording-daemon DESC="RTP/media recording daemon" -TABLE=0 DAEMON=$(which rtpengine-recording) DEFAULTS=/etc/default/${NAME} test -f "$DAEMON" || exit 0 +. /lib/lsb/init-functions + # Load startup options if available if [ -f "$DEFAULTS" ]; then . "$DEFAULTS" || true fi if [ "$RUN_RTPENGINE_RECORDING" != "yes" ]; then - echo "rtpengine-recording not yet configured. Edit $DEFAULTS first." + log_action_msg "rtpengine-recording not yet configured. Edit $DEFAULTS first." exit 0 fi [ -z "$PIDFILE" ] && PIDFILE="/var/run/rtpengine-recording.pid" [ -z "$NFS_OPTIONS" ] && NFS_OPTIONS="hard,tcp,intr" -set -e - -. /lib/lsb/init-functions - OPTIONS="" START_OPTIONS="" @@ -58,30 +56,16 @@ fi if ! test -z "$SET_USER"; then START_OPTIONS="$START_OPTIONS --chuid $SET_USER" - UID=$(id -u "$SET_USER" 2> /dev/null) - test -z "$UID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_uid=$UID" - if test -z "$SET_GROUP"; then - GID=$(id -g "$SET_USER" 2> /dev/null) - test -z "$GID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$GID" - fi test "$DO_DIR_CHOWN" = 1 && chown "$SET_USER": "$PIDDIR" fi if ! test -z "$SET_GROUP"; then START_OPTIONS="$START_OPTIONS --group $SET_GROUP" - GID=$(grep "^$SET_GROUP:" /etc/group | cut -d: -f3 2> /dev/null) - test -z "$GID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$GID" test "$DO_DIR_CHOWN" = 1 && chgrp "$SET_GROUP" "$PIDDIR" fi ### -if [ -x /usr/sbin/ngcp-virt-identify ]; then - if /usr/sbin/ngcp-virt-identify --type container; then - VIRT="yes" - fi -fi - case "$1" in start) set +e @@ -90,34 +74,35 @@ case "$1" in status=$? case "${status}" in 0|3) - echo "Active node or transition." + log_action_msg "Active node or transition." ;; *) - echo "Ignored start action in inactive node ($status)" + log_action_msg "Ignored start action in inactive node ($status)" exit 0 ;; esac fi set -e - log_daemon_msg "Starting $DESC: $NAME" + log_daemon_msg "Starting $DESC" "$NAME" if [ "$MUST_NFS" = yes ]; then - if ! egrep -q '^[^ :]+:[^ :]+ '"$NFS_LOCAL_MOUNT"' nfs.? ' /proc/mounts; then + if ! grep -E -q "^[^ :]+:[^ :]+ $NFS_LOCAL_MOUNT nfs.? " /proc/mounts; then log_progress_msg "Mounting NFS share" test -d "$NFS_LOCAL_MOUNT" || mkdir -p "$NFS_LOCAL_MOUNT" - mount -t nfs -o "$NFS_OPTIONS" "$NFS_HOST":"$NFS_REMOTE_PATH" "$NFS_LOCAL_MOUNT" + mount -t nfs -o "$NFS_OPTIONS" "$NFS_HOST:$NFS_REMOTE_PATH" "$NFS_LOCAL_MOUNT" fi fi + # shellcheck disable=SC2086 start-stop-daemon --start --quiet --pidfile "$PIDFILE" \ --exec "$DAEMON" $START_OPTIONS -- $OPTIONS || log_progress_msg " already running" log_end_msg $? ;; stop) - log_daemon_msg "Stopping $DESC: $NAME" + log_daemon_msg "Stopping $DESC" "$NAME" start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \ - --exec "$DAEMON" + --retry 5 --exec "$DAEMON" if [ "$?" -ne 0 ]; then return $? fi diff --git a/debian/rules b/debian/rules index cbcf64520..6747ecbe0 100755 --- a/debian/rules +++ b/debian/rules @@ -12,7 +12,7 @@ pdkms:=ngcp-rtpengine-kernel-dkms # short upstream name, used for module source directory sname:=ngcp-rtpengine # Source version -sversion:=$(shell dpkg-parsechangelog|grep "^Version:"|cut -d" " -f2|rev|cut -d- -f2-|rev|cut -d':' -f2) +sversion:=$(shell dpkg-parsechangelog -SVersion | sed -e 's/^[^:]\+://;s/-[^-]\+$$//') PACKAGE=ngcp-rtpengine-kernel ## end of kernel package specific stuff @@ -68,6 +68,6 @@ override_dh_install: .PHONY: override_dh_strip override_dh_strip: - dh_strip --dbg-package=ngcp-rtpengine-dbg + dh_strip --dbgsym-migration='ngcp-rtpengine-dbg (<= 6.0.0.0+0~mr6.0.0.0)' override_dh_auto_test: diff --git a/el/rtpengine.init b/el/rtpengine.init index 5804b79f8..f9c95867b 100644 --- a/el/rtpengine.init +++ b/el/rtpengine.init @@ -39,7 +39,7 @@ MODULE=0 build_opts() { shopt -s nocasematch - RPMS=`rpm -qa | grep rtpengine-kernel` + RPMS=$(rpm -qa | grep rtpengine-kernel) if [[ "$KERNEL" == "yes" && -n "$TABLE" && -n "$RPMS" ]] then MODULE=1 @@ -179,15 +179,15 @@ start() { then if [[ -n "$RE_GROUP" ]] then - proc_gid=$(grep ^$RE_GROUP: /etc/group | cut -f3 -d:) + proc_gid="$(grep "^$RE_GROUP:" /etc/group | cut -f3 -d:)" else - proc_gid=$(id $RE_USER -g) + proc_gid="$(id "$RE_USER" -g)" fi - modprobe xt_RTPENGINE proc_uid=$(id $RE_USER -u) proc_gid=$proc_gid + modprobe xt_RTPENGINE proc_uid="$(id "$RE_USER" -u)" proc_gid="$proc_gid" else modprobe xt_RTPENGINE fi - temp=`firewall-cmd --state 2>/dev/null` + firewall-cmd --state 2>/dev/null if [[ $? == 0 ]] then # Using firewalld @@ -202,15 +202,15 @@ start() { firewall-cmd --direct --add-chain ipv4 filter rtpengine firewall-cmd --direct --add-rule ipv4 filter INPUT_prefilter 0 -j rtpengine - firewall-cmd --direct --add-rule ipv4 filter rtpengine 0 -p udp -j RTPENGINE --id $TABLE - firewall-cmd --direct --add-rule ipv6 filter rtpengine 0 -p udp -j RTPENGINE --id $TABLE + firewall-cmd --direct --add-rule ipv4 filter rtpengine 0 -p udp -j RTPENGINE --id "$TABLE" + firewall-cmd --direct --add-rule ipv6 filter rtpengine 0 -p udp -j RTPENGINE --id "$TABLE" firewall-cmd --reload else iptables -N rtpengine # We insert the rtpengine rule at the top of the input chain iptables -t filter -I INPUT -j rtpengine - iptables -I rtpengine -p udp -j RTPENGINE --id $TABLE - ip6tables -I rtpengine -p udp -j RTPENGINE --id $TABLE + iptables -I rtpengine -p udp -j RTPENGINE --id "$TABLE" + ip6tables -I rtpengine -p udp -j RTPENGINE --id "$TABLE" fi cat < "$cachefile" @@ -220,19 +220,21 @@ EOF echo -n $"Starting $prog: " if [[ -n "$RE_USER" ]] then - daemon --user $RE_USER --pidfile=${pidfile} $rtpengine $OPTS + # shellcheck disable=SC2086 + daemon --user "$RE_USER" --pidfile="${pidfile}" "$rtpengine" $OPTS else - daemon --pidfile=${pidfile} $rtpengine $OPTS + # shellcheck disable=SC2086 + daemon --pidfile="${pidfile}" "$rtpengine" $OPTS fi RETVAL=$? echo - [ $RETVAL = 0 ] && touch ${lockfile} + [ $RETVAL = 0 ] && touch "${lockfile}" return $RETVAL } stop() { echo -n $"Stopping $prog: " - killproc -p ${pidfile} $rtpengine + killproc -p "${pidfile}" "$rtpengine" RETVAL=$? echo if [ -f "$cachefile" ] @@ -240,7 +242,7 @@ stop() { . "$cachefile" echo "Unloading module for in-kernel packet forwarding" echo "del $TABLE" > /proc/rtpengine/control - temp=`firewall-cmd --state 2>/dev/null` + firewall-cmd --state 2>/dev/null if [[ $? == 0 ]] then firewall-cmd --direct --remove-rules ipv4 filter rtpengine @@ -249,16 +251,16 @@ stop() { firewall-cmd --direct --remove-chain ipv4 filter rtpengine firewall-cmd --reload else - iptables -D rtpengine -p udp -j RTPENGINE --id $CUR_TABLE - ip6tables -D rtpengine -p udp -j RTPENGINE --id $CUR_TABLE + iptables -D rtpengine -p udp -j RTPENGINE --id "$CUR_TABLE" + ip6tables -D rtpengine -p udp -j RTPENGINE --id "$CUR_TABLE" iptables -t filter -D INPUT -j rtpengine iptables -X rtpengine fi rmmod xt_RTPENGINE - rm -f $cachefile + rm -f "$cachefile" fi - [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile} + [ $RETVAL = 0 ] && rm -f "${lockfile}" "${pidfile}" } # See how we were called. @@ -270,7 +272,7 @@ case "$1" in stop ;; status) - status -p ${pidfile} $rtpengine + status -p "${pidfile}" "$rtpengine" RETVAL=$? ;; restart) @@ -278,7 +280,7 @@ case "$1" in start ;; condrestart|try-restart) - if status -p ${pidfile} $rtpengine >&/dev/null; then + if status -p "${pidfile}" "$rtpengine" >&/dev/null; then stop start fi diff --git a/etc/rtpengine.sample.conf b/etc/rtpengine.sample.conf index f25d56e31..80259fd70 100644 --- a/etc/rtpengine.sample.conf +++ b/etc/rtpengine.sample.conf @@ -21,6 +21,7 @@ listen-ng = 127.0.0.1:2223 timeout = 60 silent-timeout = 3600 tos = 184 +#control-tos = 184 # delete-delay = 30 # final-timeout = 10800 diff --git a/lib/auxlib.c b/lib/auxlib.c index 8f24fb0fb..9c724176b 100644 --- a/lib/auxlib.c +++ b/lib/auxlib.c @@ -7,18 +7,15 @@ #include #include #include "log.h" +#include "loglib.h" -static const char *config_file; -static const char *config_section; -static const char *pid_file; -static const char *log_facility; -static int foreground; static int version; +struct rtpengine_common_config *rtpe_common_config_ptr; void daemonize(void) { - if (foreground) + if (rtpe_common_config_ptr->foreground) return; if (fork()) _exit(0); @@ -32,10 +29,10 @@ void daemonize(void) { void wpidfile() { FILE *fp; - if (!pid_file) + if (!rtpe_common_config_ptr->pidfile) return; - fp = fopen(pid_file, "w"); + fp = fopen(rtpe_common_config_ptr->pidfile, "w"); if (!fp) { ilog(LOG_CRIT, "Failed to create PID file (%s), aborting startup", strerror(errno)); exit(-1); @@ -54,22 +51,10 @@ static unsigned int options_length(const GOptionEntry *arr) { } -static const GOptionEntry shared_options[] = { - { "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Print build time and exit", NULL }, - { "config-file", 0, 0, G_OPTION_ARG_STRING, &config_file, "Load config from this file", "FILE" }, - { "config-section", 0, 0, G_OPTION_ARG_STRING, &config_section,"Config file section to use", "STRING" }, - { "log-facility", 0, 0, G_OPTION_ARG_STRING, &log_facility, "Syslog facility to use for logging", "daemon|local0|...|local7"}, - { "log-level", 'L', 0, G_OPTION_ARG_INT, (void *)&log_level,"Mask log priorities above this level","INT" }, - { "log-stderr", 'E', 0, G_OPTION_ARG_NONE, &ilog_stderr, "Log on stderr instead of syslog", NULL }, - { "pidfile", 'p', 0, G_OPTION_ARG_FILENAME, &pid_file, "Write PID to file", "FILE" }, - { "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Don't fork to background", NULL }, - { NULL, } -}; - #define CONF_OPTION_GLUE(get_func, data_type, ...) \ { \ data_type *varptr = e->arg_data; \ - data_type var = g_key_file_get_ ## get_func(kf, config_section, e->long_name, \ + data_type var = g_key_file_get_ ## get_func(kf, rtpe_common_config_ptr->config_section, e->long_name, \ ##__VA_ARGS__, &er); \ if (er && g_error_matches(er, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) { \ g_error_free(er); \ @@ -83,7 +68,8 @@ static const GOptionEntry shared_options[] = { } void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char *description, - const char *default_config, const char *default_section) + char *default_config, char *default_section, + struct rtpengine_common_config *cconfig) { GOptionContext *c; GError *er = NULL; @@ -92,6 +78,28 @@ void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char int saved_argc = *argc; char **saved_argv = g_strdupv(*argv); + rtpe_common_config_ptr = cconfig; + + // defaults +#ifndef __DEBUG + rtpe_common_config_ptr->log_level = LOG_INFO; +#else + rtpe_common_config_ptr->log_level = LOG_DEBUG; +#endif + + GOptionEntry shared_options[] = { + { "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Print build time and exit", NULL }, + { "config-file", 0, 0, G_OPTION_ARG_STRING, &rtpe_common_config_ptr->config_file, "Load config from this file", "FILE" }, + { "config-section", 0, 0, G_OPTION_ARG_STRING, &rtpe_common_config_ptr->config_section,"Config file section to use", "STRING" }, + { "log-facility", 0, 0, G_OPTION_ARG_STRING, &rtpe_common_config_ptr->log_facility, "Syslog facility to use for logging", "daemon|local0|...|local7"}, + { "log-level", 'L', 0, G_OPTION_ARG_INT, (void *)&rtpe_common_config_ptr->log_level,"Mask log priorities above this level","INT" }, + { "log-stderr", 'E', 0, G_OPTION_ARG_NONE, &rtpe_common_config_ptr->log_stderr, "Log on stderr instead of syslog", NULL }, + { "pidfile", 'p', 0, G_OPTION_ARG_FILENAME, &rtpe_common_config_ptr->pidfile, "Write PID to file", "FILE" }, + { "foreground", 'f', 0, G_OPTION_ARG_NONE, &rtpe_common_config_ptr->foreground, "Don't fork to background", NULL }, + { NULL, } + }; + + // prepend shared CLI options unsigned int shared_len = options_length(shared_options); unsigned int app_len = options_length(app_entries); @@ -99,8 +107,8 @@ void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char memcpy(entries, shared_options, sizeof(*entries) * shared_len); memcpy(&entries[shared_len], app_entries, sizeof(*entries) * (app_len + 1)); - if (!config_section) - config_section = default_section; + if (!rtpe_common_config_ptr->config_section) + rtpe_common_config_ptr->config_section = default_section; c = g_option_context_new(description); g_option_context_add_main_entries(c, entries, NULL); @@ -109,8 +117,8 @@ void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char // is there a config file to load? use_config = default_config; - if (config_file) { - use_config = config_file; + if (rtpe_common_config_ptr->config_file) { + use_config = rtpe_common_config_ptr->config_file; fatal = 1; } @@ -153,14 +161,17 @@ out: } - if (log_facility) { - if (!parse_log_facility(log_facility, &ilog_facility)) { + if (rtpe_common_config_ptr->log_facility) { + if (!parse_log_facility(rtpe_common_config_ptr->log_facility, &ilog_facility)) { print_available_log_facilities(); - die ("Invalid log facility '%s' (--log-facility)", log_facility); + die ("Invalid log facility '%s' (--log-facility)", rtpe_common_config_ptr->log_facility); } } - if (ilog_stderr) { + if ((rtpe_common_config_ptr->log_level < LOG_EMERG) || (rtpe_common_config_ptr->log_level > LOG_DEBUG)) + die("Invalid log level (--log_level)"); + + if (rtpe_common_config_ptr->log_stderr) { write_log = log_to_stderr; max_log_line_length = 0; } diff --git a/lib/auxlib.h b/lib/auxlib.h index 3e29c7223..eae9ef72f 100644 --- a/lib/auxlib.h +++ b/lib/auxlib.h @@ -3,10 +3,23 @@ #include +struct rtpengine_common_config { + char *config_file; + char *config_section; + char *log_facility; + volatile int log_level; + int log_stderr; + char *pidfile; + int foreground; +}; + +extern struct rtpengine_common_config *rtpe_common_config_ptr; + void daemonize(void); void wpidfile(void); void config_load(int *argc, char ***argv, GOptionEntry *entries, const char *description, - const char *default_config, const char *default_section); + char *default_config, char *default_section, + struct rtpengine_common_config *); #endif diff --git a/lib/loglib.c b/lib/loglib.c index 2f80e4f41..2837567e9 100644 --- a/lib/loglib.c +++ b/lib/loglib.c @@ -8,6 +8,7 @@ #include #include #include +#include "auxlib.h" struct log_limiter_entry { @@ -22,14 +23,6 @@ typedef struct _fac_code { -#ifndef __DEBUG -volatile gint log_level = LOG_INFO; -#else -volatile gint log_level = LOG_DEBUG; -#endif - - - static write_log_t log_both; unsigned int max_log_line_length = 500; @@ -73,7 +66,6 @@ static const char* const prio_str[] = { "DEBUG" }; -gboolean ilog_stderr = 0; int ilog_facility = LOG_DAEMON; @@ -230,7 +222,7 @@ void log_init(const char *handle) { __log_limiter = g_hash_table_new(log_limiter_entry_hash, log_limiter_entry_equal); __log_limiter_strings = g_string_chunk_new(1024); - if (!ilog_stderr) + if (!rtpe_common_config_ptr->log_stderr) openlog(handle, LOG_PID | LOG_NDELAY, ilog_facility); } diff --git a/lib/loglib.h b/lib/loglib.h index 72aeffd67..04ebbea7b 100644 --- a/lib/loglib.h +++ b/lib/loglib.h @@ -6,13 +6,11 @@ #include #include #include "compat.h" +#include "auxlib.h" -extern gboolean ilog_stderr; extern int ilog_facility; - -extern volatile gint log_level; extern unsigned int max_log_line_length; @@ -46,7 +44,7 @@ void __ilog_np(int prio, const char *format, ...) __attribute__ ((format (printf INLINE int get_log_level(void) { - return g_atomic_int_get(&log_level); + return g_atomic_int_get(&rtpe_common_config_ptr->log_level); } diff --git a/lib/rtplib.h b/lib/rtplib.h index 3ef82c753..6e525b90e 100644 --- a/lib/rtplib.h +++ b/lib/rtplib.h @@ -21,6 +21,7 @@ struct rtp_payload_type { str encoding; unsigned int clock_rate; str encoding_parameters; + str format_parameters; }; diff --git a/lib/str.h b/lib/str.h index 1a02b26bc..0ebe2cf4b 100644 --- a/lib/str.h +++ b/lib/str.h @@ -35,7 +35,7 @@ INLINE char *str_end(const str *s); /* returns pointer to first occurrence of "c" in s */ INLINE char *str_chr(const str *s, int c); /* sets "out" to point to first occurrence of c in s. adjusts len also */ -INLINE str *str_chr_str(str *out, const str *s, int c); +INLINE char *str_chr_str(str *out, const str *s, int c); /* compares a str to a regular string */ INLINE int str_cmp(const str *a, const char *b); /* compares a str to a non-null-terminated string */ @@ -70,7 +70,7 @@ INLINE void str_swap(str *a, str *b); INLINE int str_to_i(str *s, int def); /* parses a string uinto an int, returns default if conversion fails */ INLINE uint str_to_ui(str *s, int def); -/* extracts the first/next token into "new_token" and modifies "ori_and_remainer" in place */ +/* extracts the first/next token into "new_token" and modifies "ori_and_remaidner" in place */ INLINE int str_token(str *new_token, str *ori_and_remainder, int sep); /* same as str_token but allows for a trailing non-empty token (e.g. "foo,bar" -> "foo", "bar" ) */ INLINE int str_token_sep(str *new_token, str *ori_and_remainder, int sep); @@ -132,16 +132,16 @@ INLINE int str_shift_cmp(str *s, const char *t) { INLINE char *str_chr(const str *s, int c) { return memchr(s->s, c, s->len); } -INLINE str *str_chr_str(str *out, const str *s, int c) { +INLINE char *str_chr_str(str *out, const str *s, int c) { char *p; p = str_chr(s, c); if (!p) { *out = STR_NULL; - return out; + return NULL; } *out = *s; str_shift(out, p - out->s); - return out; + return out->s; } INLINE int str_cmp_len(const str *a, const char *b, int l) { if (a->len < l) diff --git a/perl/NGCP/Rtpclient/DTLS.pm b/perl/NGCP/Rtpclient/DTLS.pm index a438e7ff8..9dcb8d222 100644 --- a/perl/NGCP/Rtpclient/DTLS.pm +++ b/perl/NGCP/Rtpclient/DTLS.pm @@ -67,7 +67,7 @@ sub set_cert { } # XXX unify these two -sub connect { +sub connect { ## no critic: Subroutines::ProhibitBuiltinHomonyms my ($self) = @_; $self->{_connected} and return; @@ -95,7 +95,7 @@ sub connect { $self->{_mux}->add($near); $self->{_mux}->add($openssl_out); } -sub accept { +sub accept { ## no critic: Subroutines::ProhibitBuiltinHomonyms my ($self) = @_; $self->{_connected} and return; @@ -303,13 +303,13 @@ sub encode { my ($self, @rest) = @_; return $self->[0]->encode(@rest); } -sub connect { +sub connect { ## no critic: Subroutines::ProhibitBuiltinHomonyms my ($self, @rest) = @_; for my $cl (@$self) { $cl->accept(@rest); } } -sub accept { +sub accept { ## no critic: Subroutines::ProhibitBuiltinHomonyms my ($self, @rest) = @_; for my $cl (@$self) { $cl->accept(@rest); diff --git a/perl/NGCP/Rtpclient/ICE.pm b/perl/NGCP/Rtpclient/ICE.pm index c3c062c3e..b8e85ba0d 100644 --- a/perl/NGCP/Rtpclient/ICE.pm +++ b/perl/NGCP/Rtpclient/ICE.pm @@ -596,17 +596,17 @@ sub stun_handler_binding_success { sub check_to_nominate { my ($self) = @_; - $self->{controlling} or return; - $self->{start_nominating} && time() < $self->{start_nominating} and return; - $self->{nominate} and return; - @{$self->{triggered_checks}} and return; + return unless $self->{controlling}; + return if $self->{start_nominating} && time() < $self->{start_nominating}; + return if $self->{nominate}; + return if @{$self->{triggered_checks}}; my @succeeded; for my $pair (values(%{$self->{candidate_pairs}})) { my @comps = @{$pair->{components}}; my @succeeded_comps = grep {$_->{state} eq 'succeeded'} @comps; - @succeeded_comps < $self->{components} and next; + next if @succeeded_comps < $self->{components}; $self->debug("got fully succeeded pair $pair->{foundation}\n"); push(@succeeded, $pair); } @@ -753,7 +753,8 @@ sub timer { # run checks - defined($self->{other_ufrag}) && defined($self->{other_pwd}) or return; # not enough info + # not enough info + return if !defined($self->{other_ufrag}) || !defined($self->{other_pwd}); if (my $pair = shift(@{$self->{triggered_checks}})) { $pair->debug("running triggered check\n"); @@ -795,7 +796,10 @@ sub keepalives { sub sort_pairs { my ($pair_list) = @_; - return sort {$a->priority() <=> $b->priority()} @$pair_list; + my @sorted_list = sort { + $a->priority() <=> $b->priority() + } @{$pair_list}; + return @sorted_list; } sub get_send_component { diff --git a/perl/NGCP/Rtpengine.pm b/perl/NGCP/Rtpengine.pm index 3c7f816c4..804c4607e 100644 --- a/perl/NGCP/Rtpengine.pm +++ b/perl/NGCP/Rtpengine.pm @@ -48,11 +48,11 @@ sub req { sub offer { my ($self, $packet) = @_; - return $self->req( { %$packet, command => 'offer' } ); + return $self->req( { command => 'offer', %$packet } ); } sub answer { my ($self, $packet) = @_; - return $self->req( { %$packet, command => 'answer' } ); + return $self->req( { command => 'answer', %$packet } ); } 1; diff --git a/perl/NGCP/Rtpengine/Test.pm b/perl/NGCP/Rtpengine/Test.pm index ab090d04a..4a572abef 100644 --- a/perl/NGCP/Rtpengine/Test.pm +++ b/perl/NGCP/Rtpengine/Test.pm @@ -50,7 +50,7 @@ sub new { $self->{mux} = IO::Multiplex->new(); $self->{mux}->set_callback_object($self); - $self->{media_port} = 2000; + $self->{media_port} = $args{media_port} // 2000; $self->{timers} = []; $self->{clients} = []; @@ -149,11 +149,13 @@ sub _new { ($args{sockdomain} && $args{sockdomain} != $address->{sockdomain}) and next; my $rtp = IO::Socket::IP->new(Type => &SOCK_DGRAM, Proto => 'udp', - LocalHost => $address->{address}, LocalPort => $parent->{media_port}++) - or die($address->{address}); + LocalHost => $address->{address}, LocalPort => $parent->{media_port}) + or die("$address->{address}:$parent->{media_port}"); + $parent->{media_port}++; my $rtcp = IO::Socket::IP->new(Type => &SOCK_DGRAM, Proto => 'udp', - LocalHost => $address->{address}, LocalPort => $parent->{media_port}++) - or die($address->{address}); + LocalHost => $address->{address}, LocalPort => $parent->{media_port}) + or die("$address->{address}:$parent->{media_port}"); + $parent->{media_port}++; push(@sockets, [$rtp, $rtcp]); # component 0 and 1 push(@rtp, $rtp); @@ -271,7 +273,7 @@ sub _default_req_args { my $req = { command => $cmd, 'call-id' => $self->{parent}->{callid} }; - for my $cp (qw(sdp from-tag to-tag ICE transport-protocol address-family label)) { + for my $cp (qw(sdp from-tag to-tag ICE transport-protocol address-family label direction)) { $args{$cp} and $req->{$cp} = $args{$cp}; } for my $cp (@{$args{flags}}) { @@ -298,6 +300,7 @@ sub _offered { my ($self, $req) = @_; my $sdp_body = $req->{sdp} or die; + $self->{remote_sdp_raw} = $sdp_body; $self->{remote_sdp} = NGCP::Rtpclient::SDP->decode($sdp_body); # XXX validate SDP @{$self->{remote_sdp}->{medias}} == 1 or die; @@ -323,6 +326,7 @@ sub _answered { my ($self, $req) = @_; my $sdp_body = $req->{sdp} or die; + $self->{remote_sdp_raw} = $sdp_body; $self->{remote_sdp} = NGCP::Rtpclient::SDP->decode($sdp_body); # XXX validate SDP @{$self->{remote_sdp}->{medias}} == 1 or die; diff --git a/recording-daemon/main.c b/recording-daemon/main.c index 4a18a1f26..55bfd610d 100644 --- a/recording-daemon/main.c +++ b/recording-daemon/main.c @@ -45,6 +45,9 @@ static GQueue threads = G_QUEUE_INIT; // only accessed from main thread volatile int shutdown_flag; +struct rtpengine_common_config rtpe_common_config; + + static void signals(void) { sigset_t ss; @@ -186,7 +189,7 @@ static void options(int *argc, char ***argv) { }; config_load(argc, argv, e, " - rtpengine recording daemon", - "/etc/rtpengine/rtpengine-recording.conf", "rtpengine-recording"); + "/etc/rtpengine/rtpengine-recording.conf", "rtpengine-recording", &rtpe_common_config); if (!strcmp(output_format, "none")) { output_enabled = 0; diff --git a/recording-daemon/main.h b/recording-daemon/main.h index bf33b8309..f11b39105 100644 --- a/recording-daemon/main.h +++ b/recording-daemon/main.h @@ -2,6 +2,9 @@ #define _MAIN_H_ +#include "auxlib.h" + + extern int ktable; extern int num_threads; extern const char *spool_dir; @@ -19,4 +22,7 @@ extern const char *forward_to; extern volatile int shutdown_flag; +extern struct rtpengine_common_config rtpe_common_config; + + #endif diff --git a/t/test-interfaces.pl b/t/test-interfaces.pl new file mode 100755 index 000000000..a4fb31658 --- /dev/null +++ b/t/test-interfaces.pl @@ -0,0 +1,55 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use NGCP::Rtpengine::Test; +use IO::Socket; + +my $iterations = 10; +my @interfaces = qw(int ext); +my @domains = (&Socket::AF_INET); + +my $r = NGCP::Rtpengine::Test->new(media_port => 50000); + +for my $a_domain (@domains) { + for my $b_domain (@domains) { + if (!@interfaces) { + for (1 .. $iterations) { + run_test([], $a_domain, $b_domain); + } + } + else { + for my $a_interface (@interfaces) { + for my $b_interface (@interfaces) { + for (1 .. $iterations) { + run_test([$a_interface, $b_interface], $a_domain, $b_domain); + } + } + } + } + } +} + +sub run_test { + my ($directions, $a_domain, $b_domain) = @_; + print("Testing directions @{$directions} between $a_domain and $b_domain\n"); + + my ($a, $b) = $r->client_pair( + {sockdomain => $a_domain}, + {sockdomain => $b_domain} + ); + + print("Offering with address: " . $a->{sockets}->[0]->[0]->sockhost . "\n"); + my %dir_arg = (); + $dir_arg{direction} = $directions if @{$directions}; + $a->offer($b, ICE => 'remove', label => "caller", %dir_arg); + print("Offer out with address: " . $b->{remote_media}->connection->{address} . "\n"); + + print("Answering with address: " . $b->{sockets}->[0]->[0]->sockhost . "\n"); + $b->answer($a, ICE => 'remove', label => "callee"); + print("Answer out with address: " . $a->{remote_media}->connection->{address} . "\n"); + + $a->teardown(); + + print("\n"); +} diff --git a/tests/3-way-connect-simulator b/tests/3-way-connect-simulator index 24e75d241..948825223 100755 --- a/tests/3-way-connect-simulator +++ b/tests/3-way-connect-simulator @@ -4,7 +4,7 @@ use warnings; use strict; use Socket; -$| = 1; +STDOUT->autoflush(1); @@ -28,11 +28,9 @@ sub mp_msg { my $fd; socket($fd, AF_INET, SOCK_STREAM, 0) or die; connect($fd, sockaddr_in(25060, inet_aton('127.0.0.1'))) or die; - my $old = select($fd); - $| = 1; - print("$cmd\n"); + $fd->autoflush(1); + print { $fd } ("$cmd\n"); my $ret = <$fd>; - select($old); close($fd); chomp($ret); return $ret; @@ -53,7 +51,7 @@ sub udp_sock { sub send_rcv { my ($sendfd, $sendtoip, $sendtoport, $recvfd) = @_; print("sending to $sendtoip:$sendtoport... "); - my $pkt = join('',map(rand,1..10)); + my $pkt = join('', map { rand } 1..10); send($sendfd, $pkt, 0, sockaddr_in($sendtoport, inet_aton($sendtoip))) or die; my $inc; { @@ -64,7 +62,10 @@ sub send_rcv { recv($recvfd, $inc, length($pkt), 0); alarm(0); } - $inc eq $pkt or print("NOT received packed ok\n"), return; + if ($inc ne $pkt) { + print("NOT received packed ok\n"); + return; + } print("received packet ok\n"); } @@ -99,12 +100,12 @@ sub sim_lk { -my $callid1 = join('',map(rand,1..2)); -my $fromtag1 = join('',map(rand,1..4)); -my $totag1 = join('',map(rand,1..4)); -my $callid2 = join('',map(rand,1..2)); -my $fromtag2 = join('',map(rand,1..4)); -my $totag2 = join('',map(rand,1..4)); +my $callid1 = join('', map { rand } 1..2); +my $fromtag1 = join('', map { rand } 1..4); +my $totag1 = join('', map { rand } 1..4); +my $callid2 = join('', map { rand } 1..2); +my $fromtag2 = join('', map { rand } 1..4); +my $totag2 = join('', map { rand } 1..4); my ($client1, $lp1) = udp_sock(); my ($client2, $lp2) = udp_sock(); diff --git a/tests/blist.pl b/tests/blist.pl index 34368f6bd..a5f1ad1ab 100755 --- a/tests/blist.pl +++ b/tests/blist.pl @@ -10,23 +10,24 @@ my $t = $ARGV[0] || "0"; my $format = 'SS ia16SS ia16SS ia16SS CCCC LLLLLL'; my $len = length(pack($format, (0) x 100)); -open(X, "<", "/proc/rtpengine/$t/blist") or die; +open(my $fh, "<", "/proc/rtpengine/$t/blist") or die; my $buf; -while (sysread(X, $buf, $len)) { - my @b = unpack($format, $buf); +while (sysread($fh, $buf, $len)) { + my @buf = unpack($format, $buf); for (2,6,10) { - if ($b[$_] == AF_INET) { - $b[$_ + 1] = inet_ntoa($b[$_ + 1]); + if ($buf[$_] == AF_INET) { + $buf[$_ + 1] = inet_ntoa($buf[$_ + 1]); } - elsif ($b[$_] == AF_INET6) { - $b[$_ + 1] = inet_ntop(AF_INET6, $b[$_ + 1]); + elsif ($buf[$_] == AF_INET6) { + $buf[$_ + 1] = inet_ntop(AF_INET6, $buf[$_ + 1]); } - elsif ($b[$_] == 0) { - $b[$_ + 1] = '---'; + elsif ($buf[$_] == 0) { + $buf[$_ + 1] = '---'; } } for (18, 20, 22) { - $b[$_] += $b[$_ + 1] * 2**32; + $buf[$_] += $buf[$_ + 1] * 2**32; } - printf("%5u %15s:%-5u -> %15s:%-5u (-> %15s:%-5u) [%u] [%llu %llu %llu]\n", @b[0,3,4,7,8,11,12,14,18,20,22]); + printf("%5u %15s:%-5u -> %15s:%-5u (-> %15s:%-5u) [%u] [%llu %llu %llu]\n", @buf[0,3,4,7,8,11,12,14,18,20,22]); } +close($fh); diff --git a/tests/kernel-module-test.pl b/tests/kernel-module-test.pl index d53cf8501..8c38842c7 100755 --- a/tests/kernel-module-test.pl +++ b/tests/kernel-module-test.pl @@ -8,14 +8,10 @@ use Socket6; my %cmds = (noop => 1, add => 2, delete => 3, update => 4, add_call => 5, del_call => 6, add_stream => 7, del_stream => 8, packet => 9); my %ciphers = ('null' => 1, 'aes-cm' => 2, 'aes-f8' => 3); my %hmacs = ('null' => 1, 'hmac-sha1' => 2); -$| = 1; +STDOUT->autoflush(1); -open(F, "+> /proc/rtpengine/0/control") or die; -{ - my $x = select(F); - $| = 1; - select($x); -} +open(my $fh, '+>', '/proc/rtpengine/0/control') or die; +$fh->autoflush(1); sub re_address { my ($fam, $addr, $port) = @_; @@ -38,10 +34,14 @@ sub re_address { } sub re_srtp { my ($h) = @_; - no warnings; - return pack('VV a16 a16 a256 Q VV', $ciphers{$$h{cipher}}, $hmacs{$$h{hmac}}, - @$h{qw(master_key master_salt mki last_index auth_tag_len mki_len)}); - use warnings; + my %opts = %{$h}; + + # Explicitly initialize the hash entries. + $opts{$_} //= q{} foreach (qw(master_key master_salt mki)); + $opts{$_} //= 0 foreach (qw(last_index auth_tag_len mki_len)); + + return pack('VV a16 a16 a256 Q VV', $ciphers{$opts{cipher}}, $hmacs{$opts{hmac}}, + @opts{qw(master_key master_salt mki last_index auth_tag_len mki_len)}); } sub rtpengine_message { my ($cmd, %args) = @_; @@ -140,47 +140,47 @@ my $ret; my $msg; # print("add 9876 -> 1234/6543\n"); -# $ret = syswrite(F, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); # print("add fail\n"); -# $ret = syswrite(F, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); # print("update 9876 -> 1234/6543 & 6789\n"); -# $ret = syswrite(F, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); # print("update 9876 -> 2345/7890 & 4321\n"); -# $ret = syswrite(F, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 2345, dst_addr => \@dst, dst_port => 7890, mirror_addr => \@dst, mirror_port => 4321, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 2345, dst_addr => \@dst, dst_port => 7890, mirror_addr => \@dst, mirror_port => 4321, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); # print("add fail\n"); -# $ret = syswrite(F, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); # print("update 9876 -> 1234/6543\n"); -# $ret = syswrite(F, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); # print("delete\n"); -# $ret = syswrite(F, rtpengine_message('delete', local_addr => \@local, local_port => 9876, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('delete', local_addr => \@local, local_port => 9876, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); # print("delete fail\n"); -# $ret = syswrite(F, rtpengine_message('delete', local_addr => \@local, local_port => 9876, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('delete', local_addr => \@local, local_port => 9876, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); # print("update fail\n"); -# $ret = syswrite(F, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; +# $ret = syswrite($fh, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); @@ -215,7 +215,7 @@ if (0) { print("creating call $name\n"); $msg = rtpengine_message_call('add_call', 0, $name); - $ret = sysread(F, $msg, length($msg)) // '-'; + $ret = sysread($fh, $msg, length($msg)) // '-'; #print("reply: " . unpack("H*", $msg) . "\n"); print("ret = $ret, code = $!\n"); @@ -232,7 +232,7 @@ if (0) { print("creating stream $name under call idx $call\n"); $msg = rtpengine_message_stream('add_stream', $call, 0, $name); - $ret = sysread(F, $msg, length($msg)) // '-'; + $ret = sysread($fh, $msg, length($msg)) // '-'; #print("reply: " . unpack("H*", $msg) . "\n"); print("ret = $ret, code = $!\n"); @@ -249,7 +249,7 @@ if (0) { print("deleting call idx $call\n"); $msg = rtpengine_message_call('del_call', $call); - $ret = syswrite(F, $msg) // '-'; + $ret = syswrite($fh, $msg) // '-'; #print("ret = $ret, code = $!, reply: " . unpack("H*", $msg) . "\n"); print("ret = $ret, code = $!\n"); @@ -278,7 +278,7 @@ if (0) { print("deleting stream idx $stream->[1] (call $stream->[0])\n"); $msg = rtpengine_message_stream('del_stream', $stream->[0], $stream->[1]); - $ret = syswrite(F, $msg) // '-'; + $ret = syswrite($fh, $msg) // '-'; #print("ret = $ret, code = $!, reply: " . unpack("H*", $msg) . "\n"); print("ret = $ret, code = $!\n"); @@ -295,7 +295,7 @@ if (0) { print("delivering a packet to $idx\n"); $msg = rtpengine_message_packet('packet', 0, $idx, 'packet data bla bla ' . rand() . "\n"); - $ret = syswrite(F, $msg) // '-'; + $ret = syswrite($fh, $msg) // '-'; print("ret = $ret, code = $!\n"); sleep($sleep); @@ -316,7 +316,7 @@ if (0) { print("creating call\n"); $msg = rtpengine_message_call('add_call', 0, 'test call'); -$ret = sysread(F, $msg, length($msg)) // '-'; +$ret = sysread($fh, $msg, length($msg)) // '-'; #print("reply: " . unpack("H*", $msg) . "\n"); print("ret = $ret, code = $!\n"); @@ -330,7 +330,7 @@ sleep($sleep); # print("creating identical call\n"); # # $msg = rtpengine_message_call('add_call', 0, 'test call'); -# $ret = sysread(F, $msg, length($msg)) // '-'; +# $ret = sysread($fh, $msg, length($msg)) // '-'; # #print("reply: " . unpack("H*", $msg) . "\n"); # print("ret = $ret, code = $!\n"); # @@ -344,7 +344,7 @@ sleep($sleep); # print("creating other call\n"); # # $msg = rtpengine_message_call('add_call', 0, 'another test call'); -# $ret = sysread(F, $msg, length($msg)) // '-'; +# $ret = sysread($fh, $msg, length($msg)) // '-'; # #print("reply: " . unpack("H*", $msg) . "\n"); # print("ret = $ret, code = $!\n"); # @@ -359,7 +359,7 @@ for my $exp (0 .. 1000) { print("creating a stream\n"); $msg = rtpengine_message_stream('add_stream', $idx1, 0, 'test stream ' . rand()); - $ret = sysread(F, $msg, length($msg)) // '-'; + $ret = sysread($fh, $msg, length($msg)) // '-'; #print("reply: " . unpack("H*", $msg) . "\n"); print("ret = $ret, code = $!\n"); @@ -373,7 +373,7 @@ for my $exp (0 .. 1000) { # print("creating a stream\n"); # # $msg = rtpengine_message_stream('add_stream', $idx1, 0, 'test stream'); -# $ret = sysread(F, $msg, length($msg)) // '-'; +# $ret = sysread($fh, $msg, length($msg)) // '-'; # #print("reply: " . unpack("H*", $msg) . "\n"); # print("ret = $ret, code = $!\n"); # @@ -387,7 +387,7 @@ for my $exp (0 .. 1000) { # print("creating identical stream\n"); # # $msg = rtpengine_message_stream('add_stream', $idx1, 0, 'test stream'); -# $ret = sysread(F, $msg, length($msg)) // '-'; +# $ret = sysread($fh, $msg, length($msg)) // '-'; # #print("reply: " . unpack("H*", $msg) . "\n"); # print("ret = $ret, code = $!\n"); # @@ -401,7 +401,7 @@ for my $exp (0 .. 1000) { # print("creating different stream\n"); # # $msg = rtpengine_message_stream('add_stream', $idx3, 0, 'test stream'); -# $ret = sysread(F, $msg, length($msg)) // '-'; +# $ret = sysread($fh, $msg, length($msg)) // '-'; # #print("reply: " . unpack("H*", $msg) . "\n"); # print("ret = $ret, code = $!\n"); # @@ -413,7 +413,7 @@ for my $exp (0 .. 1000) { # print("add 9876 -> 1234/6543\n"); -# $ret = syswrite(F, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc, stream_idx => $sidx1, flags => 0x20)) // '-'; +# $ret = syswrite($fh, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc, stream_idx => $sidx1, flags => 0x20)) // '-'; # print("ret = $ret, code = $!\n"); # sleep($sleep); @@ -423,7 +423,7 @@ for my $exp (0 .. 1000) { # print("delivering a packet\n"); # # $msg = rtpengine_message_packet('packet', $idx1, $sidx1, 'packet data bla bla ' . rand() . "\n"); -# $ret = syswrite(F, $msg) // '-'; +# $ret = syswrite($fh, $msg) // '-'; # #print("reply: " . unpack("H*", $msg) . "\n"); # print("ret = $ret, code = $!\n"); # @@ -436,7 +436,7 @@ for my $exp (0 .. 1000) { # print("deleting stream\n"); # # $msg = rtpengine_message_stream('del_stream', $idx1, $sidx1, ''); -# $ret = syswrite(F, $msg) // '-'; +# $ret = syswrite($fh, $msg) // '-'; # #print("ret = $ret, code = $!, reply: " . unpack("H*", $msg) . "\n"); # print("ret = $ret, code = $!\n"); # @@ -447,7 +447,7 @@ for my $exp (0 .. 1000) { # print("deleting call\n"); # # $msg = rtpengine_message_call('del_call', $idx1, ''); -# $ret = syswrite(F, $msg) // '-'; +# $ret = syswrite($fh, $msg) // '-'; # #print("ret = $ret, code = $!, reply: " . unpack("H*", $msg) . "\n"); # print("ret = $ret, code = $!\n"); # @@ -456,4 +456,4 @@ for my $exp (0 .. 1000) { -close(F); +close($fh); diff --git a/tests/reinvite-simulator b/tests/reinvite-simulator index aff0c0143..f10dbfabe 100755 --- a/tests/reinvite-simulator +++ b/tests/reinvite-simulator @@ -4,7 +4,7 @@ use warnings; use strict; use Socket; -$| = 1; +STDOUT->autoflush(1); @@ -28,11 +28,9 @@ sub mp_msg { my $fd; socket($fd, AF_INET, SOCK_STREAM, 0) or die; connect($fd, sockaddr_in(25060, inet_aton('127.0.0.1'))) or die; - my $old = select($fd); - $| = 1; - print("$cmd\n"); + $fd->autoflush(1); + print { $fd } ("$cmd\n"); my $ret = <$fd>; - select($old); close($fd); chomp($ret); return $ret; @@ -55,7 +53,7 @@ sub send_rcv { my $laddr = getsockname($sendfd); my ($lport, $lip) = sockaddr_in($laddr); print("local port $lport sending to $sendtoip:$sendtoport... "); - my $pkt = join('',map(rand,1..10)); + my $pkt = join('', map { rand } 1..10); send($sendfd, $pkt, 0, sockaddr_in($sendtoport, inet_aton($sendtoip))) or die; my ($inc, $addr); { @@ -67,7 +65,10 @@ sub send_rcv { $addr = recv($recvfd, $inc, length($pkt), 0); alarm(0); } - $inc eq $pkt or print("did NOT receive packet\n"), return; + if ($inc ne $pkt) { + print("did NOT receive packet\n"); + return; + } my ($port, $ip) = sockaddr_in($addr); $laddr = getsockname($recvfd); ($lport, $lip) = sockaddr_in($laddr); @@ -110,9 +111,9 @@ sub sim_lk { -my $callid = join('',map(rand,1..2)); -my $fromtag = join('',map(rand,1..4)); -my $totag = join('',map(rand,1..4)); +my $callid = join('', map { rand } 1..2); +my $fromtag = join('', map { rand } 1..4); +my $totag = join('', map { rand } 1..4); my ($client1, $lp1) = udp_sock(); my ($client2, $lp2) = udp_sock(); diff --git a/tests/simulator-ng.pl b/tests/simulator-ng.pl index 339bce7fd..9c2aa4188 100755 --- a/tests/simulator-ng.pl +++ b/tests/simulator-ng.pl @@ -46,11 +46,11 @@ GetOptions( ($IP || $IPV6) or die("at least one of --local-ip or --local-ipv6 must be given"); -$SIG{ALRM} = sub { print "alarm!\n"; }; +local $SIG{ALRM} = sub { print "alarm!\n"; }; setrlimit(RLIMIT_NOFILE, 8000, 8000); $PROTOS and $PROTOS = [split(/\s*[,;:]+\s*/, $PROTOS)]; -$PROTOS && @$PROTOS == 1 and $$PROTOS[1] = $$PROTOS[0]; +$$PROTOS[1] = $$PROTOS[0] if $PROTOS && @$PROTOS == 1; $DEST and $DEST = [$DEST =~ /^(?:([a-z.-]+)(?::(\d+))?|([\d.]+)(?::(\d+))?|([\da-f:]+)|\[([\da-f:]+)\]:(\d+))$/si]; my $dest_host = $$DEST[0] || $$DEST[2] || $$DEST[4] || $$DEST[5] || 'localhost'; my $dest_port = $$DEST[1] || $$DEST[3] || $$DEST[6] || 2223; @@ -86,8 +86,14 @@ sub msg { my @dests = getaddrinfo($dest_host, $dest_port, AF_UNSPEC, SOCK_DGRAM); while (@dests >= 5) { my ($fam, $type, $prot, $addr, $canon, @dests) = @dests; - socket($fd, $fam, $type, $prot) or undef($fd), next; - connect($fd, $addr) or undef($fd), next; + if (!socket($fd, $fam, $type, $prot)) { + undef($fd); + next; + } + if (!connect($fd, $addr)) { + undef($fd); + next; + } last; } $fd or die($!); @@ -106,7 +112,7 @@ sub send_receive { alarm(1); recv($receive_fd, $x, 0xffff, 0) or $err = "$!"; alarm(0); - $err && $err !~ /interrupt/i and die $err; + die $err if $err && $err !~ /interrupt/i; return $x; } @@ -334,24 +340,24 @@ sub rtp_savp { sub savp_crypto { my ($sdp, $ctx, $ctx_o) = @_; - my @a = $sdp =~ /[\r\n]a=crypto:(\d+) (\w+) inline:([\w\/+=]{40,})(?:\|(?:2\^(\d+)|(\d+)))?(?:\|(\d+):(\d+))?(?: (.*?))?[\r\n]/sig; - @a or die; + my @aa = $sdp =~ /[\r\n]a=crypto:(\d+) (\w+) inline:([\w\/+=]{40,})(?:\|(?:2\^(\d+)|(\d+)))?(?:\|(\d+):(\d+))?(?: (.*?))?[\r\n]/sig; + @aa or die; my $i = 0; - while (@a >= 8) { - $$ctx[$i]{in}{crypto_suite} = $NGCP::Rtpclient::SRTP::crypto_suites{$a[1]} or die; - $$ctx[$i]{in}{crypto_tag} = $a[0]; + while (@aa >= 8) { + $$ctx[$i]{in}{crypto_suite} = $NGCP::Rtpclient::SRTP::crypto_suites{$aa[1]} or die; + $$ctx[$i]{in}{crypto_tag} = $aa[0]; ($$ctx[$i]{in}{rtp_master_key}, $$ctx[$i]{in}{rtp_master_salt}) - = NGCP::Rtpclient::SRTP::decode_inline_base64($a[2], $$ctx[$i]{in}{crypto_suite}); - $$ctx[$i]{in}{rtp_mki} = $a[5]; - $$ctx[$i]{in}{rtp_mki_len} = $a[6]; + = NGCP::Rtpclient::SRTP::decode_inline_base64($aa[2], $$ctx[$i]{in}{crypto_suite}); + $$ctx[$i]{in}{rtp_mki} = $aa[5]; + $$ctx[$i]{in}{rtp_mki_len} = $aa[6]; undef($$ctx[$i]{in}{rtp_session_key}); undef($$ctx[$i]{in}{rtcp_session_key}); - ($a[7] || '') =~ /UNENCRYPTED_SRTP/ and $$ctx[$i]{in}{unenc_srtp} = 1; - ($a[7] || '') =~ /UNENCRYPTED_SRTCP/ and $$ctx[$i]{in}{unenc_srtcp} = 1; - ($a[7] || '') =~ /UNAUTHENTICATED_SRTP/ and $$ctx[$i]{in}{unauth_srtp} = 1; + ($aa[7] || '') =~ /UNENCRYPTED_SRTP/ and $$ctx[$i]{in}{unenc_srtp} = 1; + ($aa[7] || '') =~ /UNENCRYPTED_SRTCP/ and $$ctx[$i]{in}{unenc_srtcp} = 1; + ($aa[7] || '') =~ /UNAUTHENTICATED_SRTP/ and $$ctx[$i]{in}{unauth_srtp} = 1; $i++; - @a = @a[8 .. $#a]; + @aa = @aa[8 .. $#aa]; } } @@ -403,7 +409,7 @@ sub do_rtp { $KEEPGOING or undef($c); } $NOENC and $repl = $expect; - !$repl && $KEEPGOING and next; + next if !$repl && $KEEPGOING; $repl eq $expect or die hexdump($repl, $expect) . " $$trans{name} > $$trans_o{name}, $$c{callid}, RTP port $$outputs[$j][0]"; $rtcp or next; @@ -421,7 +427,7 @@ sub do_rtp { $dst = $$pr{sockaddr}($dstport, $addr); $repl = send_receive($sendfd, $expfd, $payload, $dst); $NOENC and $repl = $expect; - !$repl && $KEEPGOING and next; + next if !$repl && $KEEPGOING; $repl eq $expect or die hexdump($repl, $expect) . " $$trans{name} > $$trans_o{name}, $$c{callid}, RTCP"; } } @@ -606,20 +612,20 @@ sub offer_answer { my $tcx = $$A{trans_contexts}; my $tcx_o = $$B{trans_contexts}; - my $sdp = <<"!"; + my $sdp = <<"SDP"; v=0 o=blah 123 123 IN $$pr{family_str} $$ips_t[0] s=session c=IN $$pr{family_str} $$ips_t[0] t=0 0 -! +SDP my $ul = $$A{num_streams}; - $op eq 'answer' && $$A{streams_seen} < $$A{num_streams} - and $ul = $$A{streams_seen}; + $ul = $$A{streams_seen} if $op eq 'answer' && $$A{streams_seen} < $$A{num_streams}; - $$A{want_bundle} && $op eq 'offer' and - $$A{bundle} = 1, + if ($$A{want_bundle} && $op eq 'offer') { + $$A{bundle} = 1; $sdp .= "a=group:BUNDLE " . join(' ', (0 .. $ul)) . "\n"; + } for my $i (0 .. $ul) { my $bi = $i; @@ -628,14 +634,13 @@ t=0 0 my $p = $$ports_t[$bi]; my $cp = $p + 1; - $$A{bundle} && $$A{want_rtcpmux} && $op eq 'offer' - and $cp = $p; + $cp = $p if $$A{bundle} && $$A{want_rtcpmux} && $op eq 'offer'; - $sdp .= <<"!"; + $sdp .= <<"SDP"; m=audio $p $$tr{name} 0 8 111 a=rtpmap:8 PCMA/8000 a=rtpmap:111 opus/48000/2 -! +SDP if ($$A{want_rtcpmux} && $op eq 'offer') { $sdp .= "a=rtcp-mux\n"; $sdp .= "a=rtcp:$cp\n"; @@ -676,7 +681,7 @@ a=rtpmap:111 opus/48000/2 rand() > .5 and $$dict{'to-tag'} = $$B{tag}; } elsif ($op eq 'answer') { - $dict->{'from-tag'} = $$B{tag}, + $dict->{'from-tag'} = $$B{tag}; $dict->{'to-tag'} = $$A{tag}; } if (!$LAZY @@ -749,7 +754,10 @@ sub answer { } for my $iter (1 .. $NUM) { - ($iter % 10 == 0) and print("$iter calls established\n"), do_rtp(); + if ($iter % 10 == 0) { + print("$iter calls established\n"); + do_rtp(); + } my $c = {}; offer($c, 0, 1); @@ -792,7 +800,7 @@ while (time() < $end) { do_rtp($rtcp); - @calls = sort {rand() < .5} grep(defined, @calls); + @calls = sort { rand() < .5 } grep { defined } @calls; if ($REINVITES && $now >= $last_reinv + 15) { $last_reinv = $now; diff --git a/tests/simulator-tcp.sh b/tests/simulator-tcp.sh index ed29e1d47..3f1d7f05e 100755 --- a/tests/simulator-tcp.sh +++ b/tests/simulator-tcp.sh @@ -9,25 +9,25 @@ pipe() { pipe_o > /dev/null } ip() { - echo $(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1)) + echo $((RANDOM % 254 + 1)).$((RANDOM % 254 + 1)).$((RANDOM % 254 + 1)).$((RANDOM % 254 + 1)) } port() { - echo $(($RANDOM % 64000 + 1024)) + echo $((RANDOM % 64000 + 1024)) } ids="" ports="" -for i in $(seq 1 1000); do - callid=`uuid` +for (( i = 0 ; i < 1000 ; i++ )); do + callid=$(uuid) test -z "$callid" && exit 1 - src=`ip`:`port` - dst=`ip`:`port` - gw=`ip` - fromtag=`uuid` - totag=`uuid` + src=$(ip):$(port) + dst=$(ip):$(port) + gw=$(ip) + fromtag=$(uuid) + totag=$(uuid) - src_rel=`echo "request $callid $src:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o` - dst_rel=`echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o` + src_rel=$(echo "request $callid $src:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o) + dst_rel=$(echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o) echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe echo version | pipe (echo status | pipe) & @@ -36,7 +36,7 @@ for i in $(seq 1 1000); do dst_path=${dst_rel/ //} ports="$ports $src_path $dst_path" for port in $ports; do - echo foobar > /dev/udp/$port + echo foobar > "/dev/udp/$port" done ids="$ids $callid" diff --git a/tests/simulator-udp.pl b/tests/simulator-udp.pl index 9556832b1..bc201c4a7 100755 --- a/tests/simulator-udp.pl +++ b/tests/simulator-udp.pl @@ -23,7 +23,7 @@ GetOptions( ($IP || $IPV6) or die("at least one of --local-ip or --local-ipv6 must be given"); -$SIG{ALRM} = sub { print "alarm!\n"; }; +local $SIG{ALRM} = sub { print "alarm!\n"; }; setrlimit(RLIMIT_NOFILE, 8000, 8000); my @chrs = ('a' .. 'z', 'A' .. 'Z', '0' .. '9'); @@ -75,7 +75,7 @@ sub do_rtp { alarm(1); recv($$fds[$b], $x, 0xffff, 0) or $err = "$!"; alarm(0); - $err && $err !~ /interrupt/i and die $err; + die $err if $err && $err !~ /interrupt/i; if (($x || '') ne $payload) { warn("no rtp reply received, ports $$outputs[$b][0] and $$outputs[$a][0]"); $KEEPGOING or undef($c); @@ -162,7 +162,10 @@ sub update_lookup { } for my $iter (1 .. $NUM) { - ($iter % 10 == 0) and print("$iter\n"), do_rtp(); + if ($iter % 10 == 0) { + print("$iter\n"); + do_rtp(); + } my $c = []; update_lookup($c, 0); @@ -175,7 +178,7 @@ while (time() < $end) { sleep(1); do_rtp(); - @calls = sort {rand() < .5} grep(defined, @calls); + @calls = sort { rand() < .5 } grep { defined } @calls; if ($REINVITES) { my $c = $calls[rand(@calls)]; @@ -200,7 +203,7 @@ if (!$NODEL) { for my $c (@calls) { $c or next; my ($tags, $callid) = @$c[3,5]; - $BRANCHES && rand() < .3 and $callid =~ s/;.*//; + $callid =~ s/;.*// if $BRANCHES && rand() < .3; msg("D $callid $$tags[0] $$tags[1]"); } } diff --git a/tests/stun-client b/tests/stun-client index 72c2dd881..dfce3db6d 100755 --- a/tests/stun-client +++ b/tests/stun-client @@ -13,8 +13,14 @@ my $fd; my @dests = getaddrinfo($ip, $port, AF_UNSPEC, SOCK_DGRAM); while (@dests >= 5) { my ($fam, $type, $prot, $addr, $canon, @dests) = @dests; - socket($fd, $fam, $type, $prot) or undef($fd), next; - connect($fd, $addr) or undef($fd), next; + if (!socket($fd, $fam, $type, $prot)) { + undef($fd); + next; + } + if (!connect($fd, $addr)) { + undef($fd); + next; + } last; } $fd or die($!); diff --git a/tests/stun-server b/tests/stun-server index b4446774b..15440008f 100755 --- a/tests/stun-server +++ b/tests/stun-server @@ -54,14 +54,29 @@ while (1) { } next; } - $cmd == 1 or print("not stun request\n"), next; - length($attrs) == $len or print("length mismatch\n"), next; + if ($cmd != 1) { + print("not stun request\n") + next; + } + if (length($attrs) != $len) { + print("length mismatch\n") + next; + } my ($list, $hash) = unpack_attrs($attrs); - $$list[$#$list]{name} eq 'fingerprint' or print("last attr not fingerprint\n"), next; - $$list[$#$list-1]{name} eq 'message-integrity' or print("last but one attr not MI\n"), next; - $$hash{username} or print("no username\n"), next; + if ($$list[$#$list]{name} ne 'fingerprint') { + print("last attr not fingerprint\n"); + next; + } + if ($$list[$#$list-1]{name} ne 'message-integrity') { + print("last but one attr not MI\n") + next; + } + unless ($$hash{username}) { + print("no username\n") + next; + } $$hash{controlling} and print("is controlling\n"); $$hash{controlled} and print("is controlled\n"); diff --git a/utils/build_deps.sh b/utils/build_deps.sh index 5b299d2e2..ec4016bea 100755 --- a/utils/build_deps.sh +++ b/utils/build_deps.sh @@ -11,12 +11,12 @@ if ! [ -f "${CONTROL_FILE}" ]; then exit 1 fi -BUILD_DEPS=$(/usr/bin/gdebi --quiet --non-interactive \ +BUILD_DEPS=($(/usr/bin/gdebi --quiet --non-interactive \ --option=APT::Install-Recommends=false \ - --apt-line ${CONTROL_FILE}) -if [ -z "${BUILD_DEPS}" ]; then + --apt-line "${CONTROL_FILE}")) +if [ ${#BUILD_DEPS[@]} -eq 0 ]; then echo "Error: no build deps packages resolved" exit 2 fi -apt-get install -y $BUILD_DEPS +apt-get install -y "${BUILD_DEPS[@]}" diff --git a/utils/kernel-intercept-dump.pl b/utils/kernel-intercept-dump.pl index 5a951a93d..c5142fd06 100755 --- a/utils/kernel-intercept-dump.pl +++ b/utils/kernel-intercept-dump.pl @@ -187,12 +187,12 @@ sub setup { if ($COMBINE == 0) { $callbacks{stream_setup} = \&stream_pcap; $callbacks{stream_close} = \&stream_pcap_close; - $callbacks{packet} = \&stream_packet, + $callbacks{packet} = \&stream_packet; } elsif ($COMBINE == 1) { $callbacks{call_setup} = \&call_pcap; $callbacks{call_close} = \&call_pcap_close; - $callbacks{packet} = \&call_packet, + $callbacks{packet} = \&call_packet; } } sub cb { diff --git a/utils/patch-kernel b/utils/patch-kernel index b9acad660..429a93b6f 100755 --- a/utils/patch-kernel +++ b/utils/patch-kernel @@ -34,7 +34,7 @@ if ! grep -q CONFIG_NETFILTER_XT_TARGET_RTPENGINE "$KERN"/net/netfilter/Makefile ( echo echo "EXTRA_CFLAGS += -DRTPENGINE_VERSION=\"\\\"$4\\\"\"" - echo 'obj-$(CONFIG_NETFILTER_XT_TARGET_RTPENGINE) += xt_RTPENGINE.o' + echo "obj-\$(CONFIG_NETFILTER_XT_TARGET_RTPENGINE) += xt_RTPENGINE.o" ) >> "$KERN"/net/netfilter/Makefile fi diff --git a/utils/rtpengine-ctl b/utils/rtpengine-ctl index ff1b21362..db6d77487 100755 --- a/utils/rtpengine-ctl +++ b/utils/rtpengine-ctl @@ -1,8 +1,11 @@ #!/usr/bin/perl +use strict; +use warnings; + use IO::Socket::INET; -$num_args = $#ARGV + 1; +my $num_args = $#ARGV + 1; if ( ($num_args == 0) or (($num_args == 1) && (($ARGV[0] eq "--help") or ($ARGV[0] eq "-h"))) ) { @@ -10,9 +13,6 @@ if ( ($num_args == 0) or exit; } -# auto-flush on socket -$| = 1; - my $argumentstring = ""; my $ip = "127.0.0.1"; my $port = "9900"; @@ -22,6 +22,9 @@ for (my $argnum=0; $argnum <= $#ARGV; $argnum++) { die "No argument after -ip\n" unless $argnum+1<=$#ARGV; $argnum = $argnum+1; $ip = $ARGV[$argnum]; + if ($ip =~ s/:(\d)$//) { + $port = $1; + } } elsif ($ARGV[$argnum] eq "-port") { die "No argument after -port\n" unless $argnum+1<=$#ARGV; $argnum = $argnum+1; @@ -39,6 +42,8 @@ my $socket = new IO::Socket::INET ( ); die "Cannot connect to the rtpengine $!\n" unless $socket; +$socket->autoflush(1); + #set send/recv timeout so script doesn't hang when rtpengine doesn't interact setsockopt($socket, SOL_SOCKET, SO_SNDTIMEO, pack('L!L!', 3, 0) ) or die $!; setsockopt($socket, SOL_SOCKET, SO_RCVTIMEO, pack('L!L!', 3, 0) ) or die $!; @@ -62,11 +67,12 @@ $socket->close(); sub showusage { print "\n"; - print " rtpengine-ctl [ -ip -port ] \n"; + print " rtpengine-ctl [ -ip [:] -port ] \n"; print "\n"; print " Supported commands are:\n"; print "\n"; - print " list [ numsessions | maxsessions | maxopenfiles | sessions [ | all | own | foreign ] | totals ]\n"; + print " list [ numsessions | maxsessions | maxopenfiles\n"; + print " | sessions [ | all | own | foreign ] | totals | loglevel ]\n"; print " numsessions : print the number of sessions\n"; print " maxsessions : print the number of allowed sessions\n"; print " maxopenfiles : print the number of allowed open files\n"; @@ -76,6 +82,7 @@ sub showusage { print " sessions foreign : print one-liner foreign sessions information\n"; print " totals : print total statistics\n"; print " timeout : print timeout parameters\n"; + print " loglevel : print current log level\n"; print "\n"; print " terminate [ | all | own | foreign ]\n"; print " : session is immediately terminated\n"; @@ -83,12 +90,14 @@ sub showusage { print " own : terminates own current sessions\n"; print " foreign : terminates foreign current sessions\n"; print "\n"; - print " set [ maxsessions | maxopenfiles | timeout | silent_timeout | final_timeout ]\n"; + print " set [ maxsessions | maxopenfiles | timeout \n"; + print " | silent_timeout | final_timeout | loglevel ]\n"; print " maxsessions : set the max nr of allowed sessions\n"; print " maxopenfiles : set the max nr of allowed open files\n"; print " timeout : set the --timeout parameter \n"; print " silenttimeout : set the --silent-timeout parameter \n"; print " finaltimeout : set the --final-timeout parameter \n"; + print " loglevel : set the log level to new value (1-7)\n"; print "\n"; print " ksadd [ keyspace ]\n"; print " keyspace : subscribe to 'keyspace' database\n"; diff --git a/utils/rtpengine-ng-client b/utils/rtpengine-ng-client index c431841d9..6082bf5ec 100755 --- a/utils/rtpengine-ng-client +++ b/utils/rtpengine-ng-client @@ -21,6 +21,7 @@ GetOptions( 'protocol=s' => \$options{'transport protocol'}, 'trust-address' => \$options{'trust address'}, 'sip-source-address' => \$options{'sip source address'}, + 'no-rtcp-attribute' => \$options{'no rtcp attribute'}, 'symmetric' => \$options{'symmetric'}, 'asymmetric' => \$options{'asymmetric'}, 'replace-origin' => \$options{'replace-origin'}, @@ -43,40 +44,52 @@ GetOptions( 'reset' => \$options{'reset'}, 'port-latching' => \$options{'port latching'}, 'media-address=s' => \$options{'media address'}, + 'codec-strip=s@' => \$options{'codec-strip'}, + 'codec-offer=s@' => \$options{'codec-offer'}, + 'flags=s@' => \$options{'flags'}, ) or die; my $cmd = shift(@ARGV) or die; my %packet = (command => $cmd); -for my $x (split(',', 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address')) { +for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address')) { defined($options{$x}) and $packet{$x} = \$options{$x}; } -for my $x (split(',', 'TOS,delete-delay')) { +for my $x (split(/,/, 'TOS,delete-delay')) { defined($options{$x}) and $packet{$x} = $options{$x}; } -for my $x (split(',', 'trust address,symmetric,asymmetric,force,strict source,media handover,sip source address,reset,port latching')) { +for my $x (split(/,/, 'trust address,symmetric,asymmetric,force,strict source,media handover,sip source address,reset,port latching,no rtcp attribute')) { defined($options{$x}) and push(@{$packet{flags}}, $x); } -for my $x (split(',', 'origin,session connection')) { +for my $x (split(/,/, 'origin,session connection')) { defined($options{'replace-' . $x}) and push(@{$packet{replace}}, $x); } -for my $x (split(',', 'rtcp-mux,SDES')) { - defined($options{$x}) && ref($options{$x}) eq 'ARRAY' - and $packet{$x} = $options{$x}; +for my $x (split(/,/, 'rtcp-mux,SDES')) { + $packet{$x} = $options{$x} + if defined($options{$x}) && ref($options{$x}) eq 'ARRAY'; } if (defined($options{direction})) { $options{direction} =~ /(.*),(.*)/ or die; $packet{direction} = [$1,$2]; } +if ($options{'codec-strip'} && @{$options{'codec-strip'}}) { + $packet{codec}{strip} = $options{'codec-strip'}; +} +if ($options{'codec-offer'} && @{$options{'codec-offer'}}) { + $packet{codec}{offer} = $options{'codec-offer'}; +} +if ($options{'flags'} && @{$options{'flags'}}) { + push(@{$packet{flags}}, @{$options{'flags'}}); +} if (defined($options{sdp})) { $packet{sdp} = $options{sdp}; } elsif (defined($options{'sdp-file'})) { - open(F, '<', $options{'sdp-file'}) or die $!; - my @sdp = or die $!; - close(F); + open(my $fh, '<', $options{'sdp-file'}) or die $!; + my @sdp = <$fh> or die $!; + close($fh); $packet{sdp} = join('', @sdp); } #elsif (@ARGV && $ARGV[0] eq 'sdp') {