plugin_stats, first throw
This commit is contained in:
+7
-2
@@ -42,7 +42,8 @@ pkglib_LTLIBRARIES = plugin_demo.la \
|
||||
plugin_codecfilter.la \
|
||||
plugin_stripheader.la \
|
||||
plugin_siptrunk.la \
|
||||
plugin_fix_fbox_anoncall.la
|
||||
plugin_fix_fbox_anoncall.la \
|
||||
plugin_stats.la
|
||||
|
||||
DLOPENPLUGINS = -dlopen plugin_demo.la \
|
||||
-dlopen plugin_shortdial.la \
|
||||
@@ -56,7 +57,8 @@ DLOPENPLUGINS = -dlopen plugin_demo.la \
|
||||
-dlopen plugin_codecfilter.la \
|
||||
-dlopen plugin_stripheader.la \
|
||||
-dlopen plugin_siptrunk.la \
|
||||
-dlopen plugin_fix_fbox_anoncall.la
|
||||
-dlopen plugin_fix_fbox_anoncall.la \
|
||||
-dlopen plugin_stats.la
|
||||
#
|
||||
plugin_demo_la_SOURCES = plugin_demo.c
|
||||
plugin_demo_la_LDFLAGS = -module -avoid-version -shrext '.so'
|
||||
@@ -96,6 +98,9 @@ plugin_siptrunk_la_LDFLAGS = -module -avoid-version -shrext '.so'
|
||||
#
|
||||
plugin_fix_fbox_anoncall_la_SOURCES = plugin_fix_fbox_anoncall.c
|
||||
plugin_fix_fbox_anoncall_la_LDFLAGS = -module -avoid-version -shrext '.so'
|
||||
#
|
||||
plugin_stats_la_SOURCES = plugin_stats.c
|
||||
plugin_stats_la_LDFLAGS = -module -avoid-version -shrext '.so'
|
||||
|
||||
|
||||
#
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
Copyright (C) 2016 Thomas Ries <tries@gmx.net>
|
||||
|
||||
This file is part of Siproxd.
|
||||
|
||||
Siproxd is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Siproxd is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warrantry of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Siproxd; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* must be defined before including <plugin.h> */
|
||||
#define PLUGIN_NAME plugin_stats
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <osipparser2/osip_parser.h>
|
||||
|
||||
#include "siproxd.h"
|
||||
#include "rtpproxy.h"
|
||||
#include "plugins.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
static char const ident[]="$Id$";
|
||||
|
||||
/* Plug-in identification */
|
||||
static char name[]="plugin_stats";
|
||||
static char desc[]="Upon receiving SIGUSR1, dump some call statistics";
|
||||
|
||||
/* constants */
|
||||
#define STATS_SYSLOG 0x01
|
||||
#define STATS_FILE 0x02
|
||||
#define STATS_ALL 0xff
|
||||
|
||||
/* global configuration storage - required for config file location */
|
||||
extern struct siproxd_config configuration;
|
||||
/* need access to the proxy table */
|
||||
extern rtp_proxytable_t rtp_proxytable[];
|
||||
extern struct urlmap_s urlmap[];
|
||||
|
||||
/* plugin configuration storage */
|
||||
static struct plugin_config {
|
||||
int to_syslog; // 0: off, -1 only upon SIGUSR1, >0, every n seconds
|
||||
int to_file; // 0: off, -1 only upon SIGUSR1, >0, every n seconds
|
||||
char *filename;
|
||||
} plugin_cfg;
|
||||
|
||||
/* Instructions for config parser */
|
||||
static cfgopts_t plugin_cfg_opts[] = {
|
||||
{ "plugin_stats_to_syslog", TYP_INT4, &plugin_cfg.to_syslog, {0, NULL} },
|
||||
{ "plugin_stats_to_file", TYP_INT4, &plugin_cfg.to_file, {0, NULL} },
|
||||
{ "plugin_stats_filename", TYP_STRING, &plugin_cfg.filename, {0, NULL} },
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/* local storage needed by plugin */
|
||||
static int dump_stats=0;
|
||||
static int idx_to_rtp_proxytable[RTPPROXY_SIZE]; // <0: empty, >=0, index into rtp_proxytable
|
||||
static int stats_num_streams=0;
|
||||
static int stats_num_calls=0;
|
||||
static int stats_num_act_clients=0;
|
||||
static int stats_num_reg_clients=0;
|
||||
|
||||
|
||||
/* local prototypes */
|
||||
static void stats_sighandler(int sig);
|
||||
static void stats_prepare(void);
|
||||
static void stats_to_syslog(void);
|
||||
static void stats_to_file(void);
|
||||
|
||||
/*
|
||||
* Initialization.
|
||||
* Called once suring siproxd startup.
|
||||
*/
|
||||
int PLUGIN_INIT(plugin_def_t *plugin_def) {
|
||||
struct sigaction act;
|
||||
|
||||
/* API version number of siproxd that this plugin is built against.
|
||||
* This constant will change whenever changes to the API are made
|
||||
* that require adaptions in the plugin. */
|
||||
plugin_def->api_version=SIPROXD_API_VERSION;
|
||||
|
||||
/* Name and descriptive text of the plugin */
|
||||
plugin_def->name=name;
|
||||
plugin_def->desc=desc;
|
||||
|
||||
/* Execution mask - during what stages of SIP processing shall
|
||||
* the plugin be called. */
|
||||
plugin_def->exe_mask=PLUGIN_TIMER;
|
||||
|
||||
/* read the config file */
|
||||
if (read_config(configuration.configfile,
|
||||
configuration.config_search,
|
||||
plugin_cfg_opts, name) == STS_FAILURE) {
|
||||
ERROR("Plugin '%s': could not load config file", name);
|
||||
return STS_FAILURE;
|
||||
}
|
||||
|
||||
/* check if statistics dump is actually enabled, if not exit */
|
||||
if ((plugin_cfg.to_syslog == 0) && (plugin_cfg.to_file == 0)) {
|
||||
plugin_def->exe_mask=PLUGIN_NOOP;
|
||||
WARN("Plugin_stats loaded but not enabled in config.");
|
||||
return STS_SUCCESS;
|
||||
}
|
||||
/*&&& require rtp_proxy_enable==1, otherwise no stats in rtp_proxytable */
|
||||
|
||||
/* setup signal handler - SIGUSR1 */
|
||||
act.sa_handler=stats_sighandler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags=SA_RESTART;
|
||||
if (sigaction(SIGUSR1, &act, NULL)) {
|
||||
ERROR("Failed to install SIGUSR1 handler");
|
||||
}
|
||||
|
||||
INFO("plugin_stats is initialized and armed");
|
||||
return STS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processing.
|
||||
*
|
||||
*/
|
||||
int PLUGIN_PROCESS(int stage, sip_ticket_t *ticket){
|
||||
static time_t last_run=0;
|
||||
time_t now=0;
|
||||
/* stage contains the PLUGIN_* value - the stage of SIP processing. */
|
||||
// INFO("plugin_stats: processing - stage %i",stage);
|
||||
|
||||
time(&now);
|
||||
if (last_run==0) { last_run=now; }
|
||||
|
||||
if ((plugin_cfg.to_syslog > 0) &&
|
||||
(now - last_run > plugin_cfg.to_syslog)) { dump_stats |= STATS_SYSLOG; }
|
||||
if ((plugin_cfg.to_file > 0) &&
|
||||
(now - last_run > plugin_cfg.to_file)) { dump_stats |= STATS_FILE; }
|
||||
|
||||
if (dump_stats) {
|
||||
DEBUGC(DBCLASS_PLUGIN, "plugin_stats: triggered, dump_stats=0x%x", dump_stats);
|
||||
stats_prepare();
|
||||
if (dump_stats & STATS_SYSLOG) { stats_to_syslog(); }
|
||||
if (dump_stats & STATS_FILE) { stats_to_file(); }
|
||||
dump_stats=0;
|
||||
last_run=now;
|
||||
}
|
||||
|
||||
|
||||
return STS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* De-Initialization.
|
||||
* Called during shutdown of siproxd. Gives the plugin the chance
|
||||
* to clean up its mess (e.g. dynamic memory allocation, database
|
||||
* connections, whatever the plugin messes around with)
|
||||
*/
|
||||
int PLUGIN_END(plugin_def_t *plugin_def){
|
||||
INFO("plugin_stats ends here");
|
||||
return STS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* module-local functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Signal handler
|
||||
*
|
||||
* this one is called asynchronously whevener a registered
|
||||
* signal is applied. Just set a flag and don't do any funny
|
||||
* things here.
|
||||
*/
|
||||
static void stats_sighandler(int sig) {
|
||||
/* set flag to dump statistics at next Tick */
|
||||
if (sig==SIGUSR1) dump_stats=STATS_ALL;
|
||||
return;
|
||||
}
|
||||
|
||||
static int stats_compare(const void *p1, const void *p2) {
|
||||
int i1;
|
||||
int i2;
|
||||
int sts;
|
||||
|
||||
// safety: don't do anythigng if bad data given.
|
||||
if ((p1 == NULL) || (p2 == NULL)) return 0;
|
||||
|
||||
i1=*(int*)p1;
|
||||
i2=*(int*)p2;
|
||||
|
||||
// sort by (1)client-id, (2)call-id, (3)stream number
|
||||
|
||||
// check client-id
|
||||
sts = memcmp(&rtp_proxytable[i1].client_id,
|
||||
&rtp_proxytable[i2].client_id,
|
||||
sizeof(client_id_t));
|
||||
if (sts != 0) return sts;
|
||||
|
||||
// check call-id host
|
||||
sts = memcmp(&rtp_proxytable[i1].callid_host,
|
||||
&rtp_proxytable[i2].callid_host,
|
||||
sizeof(CALLIDHOST_SIZE));
|
||||
if (sts != 0) return sts;
|
||||
|
||||
// check call-id number
|
||||
sts = memcmp(&rtp_proxytable[i1].callid_number,
|
||||
&rtp_proxytable[i2].callid_number,
|
||||
sizeof(CALLIDNUM_SIZE));
|
||||
if (sts != 0) return sts;
|
||||
|
||||
// check media stream number
|
||||
if (rtp_proxytable[i1].media_stream_no < rtp_proxytable[i2].media_stream_no) return -1;
|
||||
if (rtp_proxytable[i1].media_stream_no > rtp_proxytable[i2].media_stream_no) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stats_prepare(void) {
|
||||
int i;
|
||||
int j=0;
|
||||
int sts;
|
||||
|
||||
// loop through rtp_proxytable and populate idx_to_rtp_proxytable
|
||||
for (i=0; i < RTPPROXY_SIZE; i++) {
|
||||
if (rtp_proxytable[i].rtp_rx_sock) {
|
||||
idx_to_rtp_proxytable[j++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// put the EOT mark
|
||||
idx_to_rtp_proxytable[j]=-1;
|
||||
|
||||
// run Q-Sort
|
||||
qsort(idx_to_rtp_proxytable, j, sizeof(idx_to_rtp_proxytable[0]), stats_compare);
|
||||
|
||||
// run through sorted table and calculate counters
|
||||
stats_num_streams=0;
|
||||
stats_num_calls=0;
|
||||
stats_num_act_clients=0;
|
||||
stats_num_reg_clients=0;
|
||||
|
||||
for (i=0; i < j; i++) {
|
||||
// each entry -> +1 stream
|
||||
stats_num_streams++;
|
||||
|
||||
if (i>0) {
|
||||
// change of call-id? -> +1 call
|
||||
// check call-id host
|
||||
sts = memcmp(&rtp_proxytable[i].callid_host,
|
||||
&rtp_proxytable[i-1].callid_host,
|
||||
sizeof(CALLIDHOST_SIZE));
|
||||
if (sts != 0) {
|
||||
stats_num_calls++;
|
||||
} else {
|
||||
// check call-id number
|
||||
sts = memcmp(&rtp_proxytable[i].callid_number,
|
||||
&rtp_proxytable[i-1].callid_number,
|
||||
sizeof(CALLIDNUM_SIZE));
|
||||
if (sts != 0) {
|
||||
stats_num_calls++;
|
||||
}
|
||||
}
|
||||
// change of client-id -> +1 client
|
||||
sts = memcmp(&rtp_proxytable[i].client_id,
|
||||
&rtp_proxytable[i-1].client_id,
|
||||
sizeof(client_id_t));
|
||||
if (sts != 0) {
|
||||
stats_num_act_clients++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i < URLMAP_SIZE; i++) {
|
||||
if (urlmap[i].active == 1) { stats_num_reg_clients++; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void stats_to_syslog(void) {
|
||||
INFO("RTP-STATS: %i active Streams, %i active Calls, %i active Clients, %i registered Clients",
|
||||
stats_num_streams, stats_num_calls, stats_num_act_clients, stats_num_reg_clients);
|
||||
}
|
||||
|
||||
static void stats_to_file(void) {
|
||||
int i;
|
||||
FILE *stream;
|
||||
char remip[16];
|
||||
char lclip[16];
|
||||
|
||||
if (plugin_cfg.filename) {
|
||||
DEBUGC(DBCLASS_REG,"opening stats file for write");
|
||||
/* write urlmap back to file */
|
||||
stream = fopen(plugin_cfg.filename, "w+");
|
||||
if (!stream) {
|
||||
/* try to unlink it and open again */
|
||||
unlink(plugin_cfg.filename);
|
||||
stream = fopen(plugin_cfg.filename, "w+");
|
||||
|
||||
/* open file for write failed, complain */
|
||||
if (!stream) {
|
||||
ERROR("unable to write statistics file, disabling statistics");
|
||||
plugin_cfg.to_file = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// write header
|
||||
fprintf(stream, "RTP-STATS\n---------\n");
|
||||
// fprintf(stream, "%i active Streams\n", stats_num_streams);
|
||||
// fprintf(stream, "%i active Calls\n", stats_num_calls);
|
||||
// fprintf(stream, "%i active Clients\n", stats_num_act_clients);
|
||||
// fprintf(stream, "%i registered Clients\n\n", stats_num_reg_clients);
|
||||
|
||||
fprintf(stream, "active Streams: %6i\n", stats_num_streams);
|
||||
fprintf(stream, "active Calls: %6i\n", stats_num_calls);
|
||||
fprintf(stream, "active Clients: %6i\n", stats_num_act_clients);
|
||||
fprintf(stream, "registered Clients: %6i\n\n", stats_num_reg_clients);
|
||||
|
||||
fprintf(stream, "Client-Id;Call-Id;Direction;local IP; remote IP\n");
|
||||
|
||||
for (i=0; i < RTPPROXY_SIZE; i++) {
|
||||
if (idx_to_rtp_proxytable[i] < 0) break;
|
||||
|
||||
fprintf(stream, "%s;", rtp_proxytable[i].client_id.idstring);
|
||||
fprintf(stream, "%s@%s;", rtp_proxytable[i].callid_number, rtp_proxytable[i].callid_host);
|
||||
fprintf(stream, "%s;\n", (rtp_proxytable[i].direction==DIR_INCOMING)? "Incoming":"Outgoing");
|
||||
strcpy(lclip, utils_inet_ntoa(rtp_proxytable[i].local_ipaddr));
|
||||
fprintf(stream, "%s;", lclip);
|
||||
strcpy(remip, utils_inet_ntoa(rtp_proxytable[i].remote_ipaddr));
|
||||
fprintf(stream, "%s;", remip);
|
||||
fprintf(stream, "\n");
|
||||
|
||||
// - # of RTP streams
|
||||
// - last activity time
|
||||
|
||||
|
||||
}
|
||||
fclose(stream);
|
||||
DEBUGC(DBCLASS_REG,"closed stats file");
|
||||
} else {
|
||||
ERROR("no statistics file name given, disabling statistics");
|
||||
plugin_cfg.to_file = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
+5
-1
@@ -45,6 +45,10 @@
|
||||
/*
|
||||
* Processing stages for Plugins
|
||||
*/
|
||||
/* NOOP */
|
||||
/* No activation */
|
||||
#define PLUGIN_NOOP 0x00000000
|
||||
|
||||
/* get cyclic trigger */
|
||||
/* NO ticket is present (ticket = NULL pointer) */
|
||||
#define PLUGIN_TIMER 0x00000001
|
||||
@@ -53,7 +57,7 @@
|
||||
/* may end the current SIP processing in siproxd by returning STS_FALSE *
|
||||
* may be used to intercept other traffic on SIP port */
|
||||
/* ticket with NO sipmsg is present (ticket.sipmsg = NULL pointer) */
|
||||
#define PLUGIN_PROCESS_RAW 0x00000005
|
||||
#define PLUGIN_PROCESS_RAW 0x00000008
|
||||
|
||||
/*--------- below here a valid sip message (ticket->sipmsg) is present ---*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user