mirror of https://github.com/sipwise/rtpengine.git
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
parent
ee655bdcc6
commit
131c9e8110
@ -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_ */
|
@ -1 +1,2 @@
|
||||
daemon/rtpengine /usr/sbin/
|
||||
utils/rtpengine-ctl /usr/sbin/
|
||||
|
@ -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…
Reference in new issue