Ein Commandline interface, wo man von der console and der rtpengine
bestimme Dinge abfragen kann.

Author:    Frederic-Philippe Metz <Frederic.Metz@1und1.de>
pull/60/head
Frederic-Philippe Metz 11 years ago committed by Richard Fuchs
parent ee655bdcc6
commit 131c9e8110

@ -162,6 +162,7 @@ option and which are reproduced below:
-F, --no-fallback Only start when kernel module is available
-i, --interface=[NAME/]IP[!IP] Local interface for RTP
-l, --listen-tcp=[IP:]PORT TCP port to listen on
-c, --listen-cli=[IP46:]PORT TCP port to listen on, CLI (command line interface)
-u, --listen-udp=[IP46:]PORT UDP port to listen on
-n, --listen-ng=[IP46:]PORT UDP port to listen on, NG protocol
-T, --tos=INT TOS value to set on streams
@ -267,6 +268,10 @@ The options are described in more detail below.
It is recommended to specify not only a local port number, but also 127.0.0.1 as interface to bind to.
* -c, --listen-cli
TCP ip and port to listen for the CLI (command line interface).
* -t, --tos
Takes an integer as argument and if given, specifies the TOS value that should be set in outgoing

@ -61,7 +61,7 @@ endif
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
crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c
OBJS= $(SRCS:.c=.o)

@ -2645,7 +2645,7 @@ restart:
}
/* returns call with master_lock held in W, or NULL if not found */
static struct call *call_get(const str *callid, struct callmaster *m) {
struct call *call_get(const str *callid, struct callmaster *m) {
struct call *ret;
rwlock_lock_r(&m->hashlock);

@ -420,6 +420,7 @@ struct packet_stream *__packet_stream_new(struct call *call);
struct call *call_get_or_create(const str *callid, struct callmaster *m);
struct call *call_get_opmode(const str *callid, struct callmaster *m, enum call_opmode opmode);
struct call_monologue *call_get_mono_dialogue(struct call *call, const str *fromtag, const str *totag);
struct call *call_get(const str *callid, struct callmaster *m);
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,
const str *fromtag, const str *totag, bencode_item_t *output);

@ -0,0 +1,308 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <netinet/in.h>
#include <errno.h>
#include <glib.h>
#include "poller.h"
#include "aux.h"
#include "log.h"
#include "call.h"
#include "cli.h"
static const char* TRUNCATED = " ... Output truncated. Increase Output Buffer ...\n";
#define truncate_output(x) do { x -= strlen(TRUNCATED)+1; x += sprintf(x,"%s",TRUNCATED); } while (0);
#define ADJUSTLEN(printlen,outbuflen,replybuffer) do { if (printlen>=(outbufend-replybuffer)) \
truncate_output(replybuffer); \
replybuffer += (printlen>=outbufend-replybuffer)?outbufend-replybuffer:printlen; } while (0);
static void cli_incoming_list_callid(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) {
str callid;
struct call* c=0;
struct call_monologue *ml;
struct call_media *md;
struct packet_stream *ps;
GSList *l;
GList *k, *o;
char buf[64];
int printlen=0;
if (len<=1) {
printlen = snprintf(replybuffer,(outbufend-replybuffer), "%s\n", "More parameters required.");
ADJUSTLEN(printlen,outbufend,replybuffer);
return;
}
++buffer; --len; // one space
str_init_len(&callid,buffer,len);
c = call_get(&callid, m);
if (!c) {
printlen = snprintf(replybuffer,(outbufend-replybuffer), "\nCall Id not found (%s).\n\n",callid.s);
ADJUSTLEN(printlen,outbufend,replybuffer);
return;
}
printlen = snprintf (replybuffer,(outbufend-replybuffer), "\ncallid: %30s | deletionmark:%4s | created:%12i\n\n", c->callid.s , c->ml_deleted?"yes":"no", (int)c->created);
ADJUSTLEN(printlen,outbufend,replybuffer);
for (l = c->monologues; l; l = l->next) {
ml = l->data;
printlen = snprintf(replybuffer,(outbufend-replybuffer), "--- Tag '"STR_FORMAT"', callduration "
"%u:%02u , in dialogue with '"STR_FORMAT"'\n",
STR_FMT(&ml->tag),
(unsigned int) (poller_now - ml->created) / 60,
(unsigned int) (poller_now - ml->created) % 60,
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;
for (o = md->streams.head; o; o = o->next) {
ps = o->data;
if (PS_ISSET(ps, FALLBACK_RTCP))
continue;
smart_ntop_p(buf, &ps->endpoint.ip46, sizeof(buf));
printlen = snprintf(replybuffer,(outbufend-replybuffer), "------ Media #%u, port %5u <> %15s:%-5hu%s, "
"%llu p, %llu b, %llu e\n",
md->index,
(unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0),
buf, ps->endpoint.port,
(!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "",
(unsigned long long) ps->stats.packets,
(unsigned long long) ps->stats.bytes,
(unsigned long long) ps->stats.errors);
ADJUSTLEN(printlen,outbufend,replybuffer);
}
}
}
printlen = snprintf(replybuffer,(outbufend-replybuffer), "\n");
ADJUSTLEN(printlen,outbufend,replybuffer);
rwlock_unlock_w(&c->master_lock); // because of call_get(..)
}
static void cli_incoming_list(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) {
GHashTableIter iter;
gpointer key, value;
str *ptrkey;
struct call *call;
int printlen=0;
static const char* LIST_NUMSESSIONS = "numsessions";
static const char* LIST_SESSIONS = "sessions";
static const char* LIST_SESSION = "session";
if (len<=1) {
printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required.");
ADJUSTLEN(printlen,outbufend,replybuffer);
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 on rtpengine:%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) {
rwlock_lock_r(&m->hashlock);
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);
return;
}
g_hash_table_iter_init (&iter, m->callhash);
while (g_hash_table_iter_next (&iter, &key, &value)) {
ptrkey = (str*)key;
call = (struct call*)value;
printlen = snprintf(replybuffer, outbufend-replybuffer, "callid: %30s | deletionmark:%4s | created:%12i\n", ptrkey->s, call->ml_deleted?"yes":"no", (int)call->created);
ADJUSTLEN(printlen,outbufend,replybuffer);
}
rwlock_unlock_r(&m->hashlock);
} else if (len>=strlen(LIST_SESSION) && strncmp(buffer,LIST_SESSION,strlen(LIST_SESSION)) == 0) {
cli_incoming_list_callid(buffer+strlen(LIST_SESSION), len-strlen(LIST_SESSION), m, replybuffer, outbufend);
} else {
printlen = snprintf(replybuffer, outbufend-replybuffer, "%s:%s\n", "Unknown 'list' command", buffer);
ADJUSTLEN(printlen,outbufend,replybuffer);
}
}
static void cli_incoming_terminate(char* buffer, int len, struct callmaster* m, char* replybuffer, const char* outbufend) {
str termparam;
struct call* c=0;
int printlen=0;
GHashTableIter iter;
gpointer key, value;
if (len<=1) {
printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "More parameters required.");
ADJUSTLEN(printlen,outbufend,replybuffer);
return;
}
++buffer; --len; // one space
str_init_len(&termparam,buffer,len);
// --- terminate all calls
if (!str_memcmp(&termparam,"all")) {
while (g_hash_table_size(m->callhash)) {
g_hash_table_iter_init (&iter, m->callhash);
g_hash_table_iter_next (&iter, &key, &value);
c = (struct call*)value;
if (!c) continue;
call_destroy(c);
}
ilog(LOG_INFO,"All calls terminated by operator.");
printlen = snprintf(replybuffer, outbufend-replybuffer, "%s\n", "All calls terminated by operator.");
ADJUSTLEN(printlen,outbufend,replybuffer);
return;
}
// --- terminate a dedicated call id
c = call_get(&termparam, m);
if (!c) {
printlen = snprintf(replybuffer, outbufend-replybuffer, "\nCall Id not found (%s).\n\n",termparam.s);
ADJUSTLEN(printlen,outbufend,replybuffer);
return;
}
call_destroy(c);
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);
rwlock_unlock_w(&c->master_lock);
}
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]; memset(&replybuffer,0,BUFLENGTH);
char* outbuf = replybuffer;
const char* outbufend = replybuffer+BUFLENGTH;
static const int MAXINPUT = 1024;
char inbuf[MAXINPUT]; memset(&inbuf,0,MAXINPUT);
int inlen = 0, readbytes = 0;
int rc=0;
mutex_lock(&cli->lock);
next:
sinl = sizeof(sin);
nfd = accept(fd, (struct sockaddr *) &sin, &sinl);
if (nfd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
sprintf(replybuffer, "Could currently not accept CLI commands. Reason:%s\n", strerror(errno));
goto cleanup;
}
ilog(LOG_INFO, "Accept error:%s\n", strerror(errno));
goto next;
}
ilog(LOG_INFO, "New cli connection from " DF, DP(sin));
do {
readbytes = read(nfd, inbuf+inlen, MAXINPUT);
if (readbytes == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s\n", strerror(errno));
goto cleanup;
}
ilog(LOG_INFO, "Could currently not read CLI commands. Reason:%s\n", strerror(errno));
}
inlen += readbytes;
} while (readbytes > 0);
ilog(LOG_INFO, "Got CLI command:%s\n",inbuf);
static const char* LIST = "list";
static const char* TERMINATE = "terminate";
if (inlen>=strlen(LIST) && strncmp(inbuf,LIST,strlen(LIST)) == 0) {
cli_incoming_list(inbuf+strlen(LIST), inlen-strlen(LIST), cli->callmaster, outbuf, outbufend);
} else if (inlen>=strlen(TERMINATE) && strncmp(inbuf,TERMINATE,strlen(TERMINATE)) == 0) {
cli_incoming_terminate(inbuf+strlen(TERMINATE), inlen-strlen(TERMINATE), 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));
cleanup:
close(nfd);
mutex_unlock(&cli->lock);
}
static void control_closed(int fd, void *p, uintptr_t u) {
abort();
}
struct cli *cli_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callmaster *m) {
struct cli *c;
int fd;
struct sockaddr_in sin;
struct poller_item i;
if (!p || !m)
return NULL;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
return NULL;
nonblock(fd);
reuseaddr(fd);
ZERO(sin);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ip;
sin.sin_port = htons(port);
if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)))
goto fail;
if (listen(fd, 5))
goto fail;
c = obj_alloc0("cli_udp", sizeof(*c), NULL);
c->fd = fd;
c->poller = p;
c->callmaster = m;
mutex_init(&c->lock);
ZERO(i);
i.fd = 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(fd);
return NULL;
}

@ -0,0 +1,18 @@
#ifndef CLI_UDP_H_
#define CLI_UDP_H_
#include <netinet/in.h>
struct cli {
struct obj obj;
struct callmaster *callmaster;
int fd;
struct poller *poller;
mutex_t lock;
};
struct cli *cli_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callmaster *m);
#endif /* CLI_UDP_H_ */

@ -25,6 +25,7 @@
#include "sdp.h"
#include "dtls.h"
#include "call_interfaces.h"
#include "cli.h"
@ -91,6 +92,8 @@ static struct in6_addr udp_listenp;
static u_int16_t udp_listenport;
static struct in6_addr ng_listenp;
static u_int16_t ng_listenport;
static u_int32_t cli_listenp;
static u_int16_t cli_listenport;
static int tos;
static int table = -1;
static int no_fallback;
@ -309,6 +312,7 @@ static void options(int *argc, char ***argv) {
char *listenps = NULL;
char *listenudps = NULL;
char *listenngs = NULL;
char *listencli = NULL;
char *redisps = NULL;
char *log_facility_s = NULL;
char *log_facility_cdr_s = NULL;
@ -323,6 +327,7 @@ static void options(int *argc, char ***argv) {
{ "listen-tcp", 'l', 0, G_OPTION_ARG_STRING, &listenps, "TCP port to listen on", "[IP:]PORT" },
{ "listen-udp", 'u', 0, G_OPTION_ARG_STRING, &listenudps, "UDP port to listen on", "[IP46:]PORT" },
{ "listen-ng", 'n', 0, G_OPTION_ARG_STRING, &listenngs, "UDP port to listen on, NG protocol", "[IP46:]PORT" },
{ "listen-cli", 'c', 0, G_OPTION_ARG_STRING, &listencli, "UDP port to listen on, CLI", "[IP46:]PORT" },
{ "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" },
@ -380,6 +385,10 @@ static void options(int *argc, char ***argv) {
die("Invalid IP or port (--listen-ng)");
}
if (listencli) {if (parse_ip_port(&cli_listenp, &cli_listenport, listencli))
die("Invalid IP or port (--listen-cli)");
}
if (tos < 0 || tos > 255)
die("Invalid TOS value");
@ -557,6 +566,7 @@ void create_everything(struct main_context *ctx) {
struct control_tcp *ct;
struct control_udp *cu;
struct control_ng *cn;
struct cli *cl;
int kfd = -1;
void *dlh;
const char **strp;
@ -625,6 +635,14 @@ no_kernel:
die("Failed to open UDP control connection port");
}
cl = NULL;
if (cli_listenport) {
callmaster_exclude_port(ctx->m, cli_listenport);
cl = cli_new(ctx->p, cli_listenp, cli_listenport, ctx->m);
if (!cl)
die("Failed to open UDP CLI connection port");
}
if (redis_ip) {
dlh = dlopen(MP_PLUGIN_DIR "/rtpengine-redis.so", RTLD_NOW | RTLD_GLOBAL);
if (!dlh && !g_file_test(MP_PLUGIN_DIR "/rtpengine-redis.so", G_FILE_TEST_IS_REGULAR)

@ -2,6 +2,7 @@ RUN_RTPENGINE=no
LISTEN_TCP=25060
LISTEN_UDP=12222
LISTEN_NG=22222
LISTEN_CLI=9900
# INTERFACES="123.234.345.456"
# INTERFACES="internal/12.23.34.45 external/23.34.45.54"
# INTERFACES="12.23.34.45!23.34.45.56"

@ -55,6 +55,7 @@ fi
[ -z "$LISTEN_TCP" ] || OPTIONS="$OPTIONS --listen-tcp=$LISTEN_TCP"
[ -z "$LISTEN_UDP" ] || OPTIONS="$OPTIONS --listen-udp=$LISTEN_UDP"
[ -z "$LISTEN_NG" ] || OPTIONS="$OPTIONS --listen-ng=$LISTEN_NG"
[ -z "$LISTEN_CLI" ] || OPTIONS="$OPTIONS --listen-cli=$LISTEN_CLI"
[ -z "$TIMEOUT" ] || OPTIONS="$OPTIONS --timeout=$TIMEOUT"
[ -z "$SILENT_TIMEOUT" ] || OPTIONS="$OPTIONS --silent-timeout=$SILENT_TIMEOUT"
[ -z "$PIDFILE" ] || OPTIONS="$OPTIONS --pidfile=$PIDFILE"

@ -1 +1,2 @@
daemon/rtpengine /usr/sbin/
utils/rtpengine-ctl /usr/sbin/

@ -92,6 +92,11 @@ build_opts() {
OPTS+=" --listen-ng=$LISTEN_NG"
fi
if [[ -n "$LISTEN_CLI" ]]
then
OPTS+=" --listen-cli=$LISTEN_CLI"
fi
if [[ -n "$TOS" ]]
then
OPTS+=" --tos=$TOS"

@ -20,6 +20,7 @@ LISTEN_UDP=127.0.0.1:2222 # IP address and port combination for UDP
# control
#LISTEN_NG=127.0.0.1:2223 # IP address and port combination for NG (UDP)
# control
#LISTEN_CLI=127.0.0.1:9900
#
#TOS=184 # (o) TOS value to use in outgoing packets
#TIMEOUT=60 # (o) Number of seconds after which a media stream is

@ -0,0 +1,69 @@
#!/bin/bash
#
host=127.0.0.1
port=9900
error_rc=255
prgname=${0##*/}
prgdir=${0%$prgname}
showusage() {
echo ""
echo " rectl [ -ip <ipaddress> -port <port> ] <command>"
echo ""
echo " Supported commands are:"
echo ""
echo " list [ numsessions | sessions | session <callid> ]"
echo " numsessions : prints the number of sessions"
echo " sessions : print one-liner session information"
echo " session <callid> : print detail about one session"
echo ""
echo " terminate [ all | <callid> ]"
echo " all : terminates all current sessions"
echo " <callid> : session is immediately terminated"
echo ""
echo ""
echo " Return Value:"
echo " 0 on success with ouput from server side, other values for failure."
echo ""
exit 0
}
if [ $# -eq 0 ]; then showusage; fi
command -v nc 2>&1 >/dev/null
if [ $? -ne 0 ]; then
echo "Error: rectl requires netcat to be installed."
exit 0
fi
while [ $# -gt 0 ]; do
case $1 in
"-?"|"-help"|"-h")
showusage
;;
"-ip")
shift
if [ $# -gt 0 ]; then
host=$1
else
echo "Missing parameter for option '-ip'" >&2
fi
;;
"-port")
shift
if [ $# -gt 0 ]; then
port=$1
else
echo "Missing parameter for option '-port'" >&2
fi
;;
*)
varargs="$varargs $1"
esac
shift
done
echo -n ${varargs} | nc ${host} ${port}
Loading…
Cancel
Save