plugin_stats, first throw

This commit is contained in:
Thomas Ries
2016-09-01 20:58:17 +00:00
parent 19c31266ff
commit 06b27e0f8b
3 changed files with 377 additions and 3 deletions
+7 -2
View File
@@ -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'
#
+365
View File
@@ -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
View File
@@ -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 ---*/