src/modules/snmpstats/snmpstats.c
2709a6b8
 /*
0e86b73a
  * SNMPStats Module
2709a6b8
  * Copyright (C) 2006 SOMA Networks, INC.
  * Written by: Jeffrey Magder (jmagder@somanetworks.com)
7d272f89
  * Copyright (C) 2013 Edvina AB, Sollentuna, Sweden
  * Updated and extended by: Olle E. Johansson (oej@edvina.net)
2709a6b8
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
2709a6b8
  *
27642a08
  * Kamailio is free software; you can redistribute it and/or modify it
2709a6b8
  * 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
  *
27642a08
  * Kamailio is distributed in the hope that it will be useful, but
2709a6b8
  * WITHOUT ANY WARRANTY; without even the implied warranty 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 this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
2709a6b8
  * USA
  *
  * There are some important points to understanding the SNMPStat modules
  * architecture.
  *
  * 1) The SNMPStats module will fork off a new process in mod_child_init when
  *    the rank is equal to PROC_MAIN_PROCESS.  The sub-process will be
  *    responsible for registering with a master agent (the source of snmp
0e86b73a
  *    requests), and handling all received requests.
2709a6b8
  *
  * 2) The Module will register a periodic alarm checking function with a sip
  *    timer using register_timer().  This function checks for alarm conditions,
  *    and will send out traps to the master agent when it detects their
  *    presence.
  *
  * 3) The SNMPStats module is required to run an external application upon
  *    startup, to collect sysUpTime data from the master agent.  This involves
  *    spawning a short-lived process.  For this reason, the module temporarily
  *    installs a new SIGCHLD handler to deal specifically with this process.  It
  *    does not change the normal SIGCHLD behaviour for any process except for
0e86b73a
  *    this short lived sysUpTime process.
2709a6b8
  *
  * 4) mod_init() will initialize some interprocess communication buffers, as
  *    well as callback mechanisms for the usrloc module.  To understand what the
  *    interprocess buffer and callbacks are and are for, please see the comments
7952711e
  *    at the beginning of snmpSIPRegUserTable.c
94dd7c5f
  */
 
 /*!
  * \file
  * \brief SNMP statistic module
  * \ingroup snmpstats
ca00a464
  * - Module: \ref snmpstats
9f97c5e5
  * \author Jeffrey Magder (jmagder@somanetworks.com)
  * \author Olle E. Johansson (oej@edvina.net)
ca00a464
  */
 
 /*!
  * \defgroup snmpstats SNMPSTATS :: The Kamailio snmpstats Module
  *
68d7468c
  * The SNMPStats module provides an SNMP management interface to Kamailio.
ca00a464
  * Specifically, it provides general SNMP queryable scalar statistics, table
  * representations of more complicated data such as user and contact information,
  * and alarm monitoring capabilities.
2709a6b8
  */
 
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
 #include <signal.h>
 #include <sys/wait.h>
 #include "snmpstats.h"
 #include "snmpstats_globals.h"
cf83221d
 #include "../../core/timer.h"
 #include "../../core/cfg/cfg_select.h"
 #include "../../core/cfg/cfg_ctx.h"
2709a6b8
 
 #include <net-snmp/net-snmp-config.h>
 #include <net-snmp/net-snmp-includes.h>
 #include <net-snmp/agent/net-snmp-agent-includes.h>
 
0e86b73a
 #include "snmp_statistics.h"
cf83221d
 #include "../../core/sr_module.h"
 #include "../../core/dprint.h"
 #include "../../core/error.h"
 #include "../../core/ut.h"
 #include "../../core/script_cb.h"
 #include "../../core/mem/mem.h"
 #include "../../core/mem/shm_mem.h"
8c26d722
 
2a25dc59
 #include "snmpSIPRegUserTable.h"
 #include "snmpSIPContactTable.h"
2709a6b8
 
 #include "interprocess_buffer.h"
 
 #include "hashTable.h"
 #include "alarm_checks.h"
 #include "utilities.h"
 #include "sub_agent.h"
 
ded7d208
 /* Required in every Kamailio Module. */
2709a6b8
 MODULE_VERSION
 
0696af54
 /* module parameter to register for usrloc callbacks or not,
  * in order to export registrar records (0 - don't export, 1 - export) */
 static int snmp_export_registrar = 0;
 
8c26d722
 /*! This is the first function to be called by Kamailio, to initialize the module.
  * This call must always return a value as soon as possible.  If it were not to
  * return, then Kamailio would not be able to initialize any of the other
  * modules. */
db0b73b0
 static int mod_init(void);
8c26d722
 
 /*! This function is called when Kamailio has finished creating all instances of
  * itself.  It is at this point that we want to create our AgentX sub-agent
  * process, and register a handler for any state changes of our child. */
db0b73b0
 static int mod_child_init(int rank);
8c26d722
 
 
 /*! This function is called when Kamailio is shutting down.  When this happens, we
  * log a useful message and kill the AgentX Sub-Agent child process */
 static void mod_destroy(void);
 
 /*!
  * This structure defines the SNMPStats parameters that can be configured
09004101
  * through the kamailio.cfg configuration file.
8c26d722
  */
db0b73b0
 static param_export_t mod_params[] = {
 	{"sipEntityType", PARAM_STRING | USE_FUNC_PARAM,
 			(void *)handleSipEntityType},
 	{"MsgQueueMinorThreshold", INT_PARAM | USE_FUNC_PARAM,
 			(void *)set_queue_minor_threshold},
 	{"MsgQueueMajorThreshold", INT_PARAM | USE_FUNC_PARAM,
 			(void *)set_queue_major_threshold},
 	{"dlg_minor_threshold", INT_PARAM | USE_FUNC_PARAM,
 			(void *)set_dlg_minor_threshold},
 	{"dlg_major_threshold", INT_PARAM | USE_FUNC_PARAM,
 			(void *)set_dlg_major_threshold},
 	{"snmpgetPath", PARAM_STRING | USE_FUNC_PARAM,
 			(void *)set_snmpget_path},
 	{"snmpCommunity", PARAM_STRING | USE_FUNC_PARAM,
 			(void *)set_snmp_community},
9f3b344a
 	{"snmpVersion", PARAM_STRING | USE_FUNC_PARAM,
 			(void *)set_snmp_version},
db0b73b0
 	{"export_registrar", INT_PARAM, &snmp_export_registrar},
 	{0, 0, 0}
8c26d722
 };
 
 
db0b73b0
 struct module_exports exports = {
09004101
 	SNMPSTATS_MODULE_NAME,	/* module name */
 	DEFAULT_DLFLAGS,		/* dlopen flags */
 	0,						/* exported functions */
 	mod_params,				/* exported parameters */
 	0,						/* exported rpc functions */
 	0,						/* exported pseudo-variables */
 	0,						/* reply processing function */
 	mod_init,				/* module init function */
 	mod_child_init,			/* per-child init function */
 	mod_destroy				/* module destroy function */
8c26d722
 };
 
94dd7c5f
 /*!
2709a6b8
  * The module will fork off a child process to run an snmp command via execve().
  * We need a customized handler to ignore the SIGCHLD when the execve()
  * finishes.  We keep around the child process's pid for the customized
94dd7c5f
  * handler.
2709a6b8
  *
  * Specifically, If the process that generated the SIGCHLD doesn't match this
ded7d208
  * pid, we call Kamailio's default handlers.  Otherwise, we just ignore SIGCHLD.
2709a6b8
  */
 volatile pid_t sysUpTime_pid;
 
94dd7c5f
 /*! The functions spawns a sysUpTime child.  See the function definition below
2709a6b8
  * for a full description. */
 static int spawn_sysUpTime_child();
 
9f3b344a
 /*! Storage for the "snmpgetPath", "snmpCommunity" and "snmpVersion" kamailio.cfg parameters.
2709a6b8
  * The parameters are used to define what happens with the sysUpTime child.  */
db0b73b0
 char *snmpget_path = NULL;
2709a6b8
 char *snmp_community = NULL;
9f3b344a
 char *snmp_version = NULL;
2709a6b8
 
94dd7c5f
 /*!
2709a6b8
  * This module replaces the default SIGCHLD handler with our own, as explained
  * in the documentation for sysUpTime_pid above.  This structure holds the old
ded7d208
  * handler so we can call and restore Kamailio's usual handler when appropriate
2709a6b8
  */
 static struct sigaction old_sigchld_handler;
 
94dd7c5f
 /*! The following message codes are from Wikipedia at:
  *
09004101
  *     http://en.wikipedia.org/wiki/SIP_Responses
2709a6b8
  *
4547a28b
  * Updated by oej to use the IETF reference page
  *     http://www.iana.org/assignments/sip-parameters/sip-parameters.xml#sip-parameters-7
  *
2709a6b8
  * If there are more message codes added at a later time, they should be added
09004101
  * here, and to out_message_code_names below.
2709a6b8
  *
  * The array is used to register the statistics keeping track of the number of
  * messages received with the response code X.
4547a28b
  *
2709a6b8
  */
db0b73b0
 char *in_message_code_names[] = {
4547a28b
 	"100_in", "180_in", "181_in", "182_in", "183_in", "199_in",
db0b73b0
 
4547a28b
 	"200_in", "202_in", "204_in",
db0b73b0
 
2709a6b8
 	"300_in", "301_in", "302_in", "305_in", "380_in",
db0b73b0
 
 	"400_in", "401_in", "402_in", "403_in", "404_in", "405_in", "406_in",
 	"407_in", "408_in", "410_in", "412_in", "413_in", "414_in", "415_in",
4547a28b
 	"416_in", "417_in", "420_in", "421_in", "422_in", "423_in", "424_in",
 	"428_in", "429_in", "430_in", "433_in", "436_in", "437_in", "438_in",
 	"439_in", "440_in", "469_in", "470_in", "480_in", "481_in", "482_in",
 	"483_in", "484_in", "485_in", "486_in", "487_in", "488_in", "489_in",
db0b73b0
 	"491_in", "492_in", "493_in", "494_in",
 
2709a6b8
 	"500_in", "501_in", "502_in", "503_in", "504_in", "505_in", "513_in",
4547a28b
 	"580_in",
 
2709a6b8
 	"600_in", "603_in", "604_in", "606_in"
 };
 
94dd7c5f
 /*! The following message codes are from Wikipedia at:
  *
09004101
  *     http://en.wikipedia.org/wiki/SIP_Responses
2709a6b8
  *
4547a28b
  * Updated by oej to use the IETF reference page
  *     http://www.iana.org/assignments/sip-parameters/sip-parameters.xml#sip-parameters-7
  *
2709a6b8
  * If there are more message codes added at a later time, they should be added
  * here, and to in_message_code_names above.
  *
  * The array is used to register the statistics keeping track of the number of
  * messages send out with the response code X.
  */
db0b73b0
 char *out_message_code_names[] = {
4547a28b
 	"100_out", "180_out", "181_out", "182_out", "183_out", "199_out",
db0b73b0
 
4547a28b
 	"200_out", "202_out", "204_out",
db0b73b0
 
2709a6b8
 	"300_out", "301_out", "302_out", "305_out", "380_out",
db0b73b0
 
 	"400_out", "401_out", "402_out", "403_out", "404_out", "405_out",
 	"406_out", "407_out", "408_out", "410_out", "412_out", "413_out",
 	"414_out", "415_out", "416_out", "417_out", "420_out", "421_out",
 	"422_out", "423_out", "424_out", "428_out", "429_out", "430_out",
 	"433_out", "436_out", "437_out", "438_out", "439_out", "440_out",
 	"469_out", "470_out", "480_out", "481_out", "482_out", "483_out",
 	"484_out", "485_out", "486_out", "487_out", "488_out", "489_out",
 	"491_out", "492_out", "493_out", "494_out",
 
 	"500_out", "501_out", "502_out", "503_out", "504_out", "505_out",
 	"513_out", "580_out",
4547a28b
 
2709a6b8
 	"600_out", "603_out", "604_out", "606_out"
 };
 
94dd7c5f
 /*! message_code_stat_array[0] will be the data source for message_code_array[0]
2709a6b8
  * message_code_stat_array[3] will be the data source for message_code_array[3]
  * and so on. */
db0b73b0
 stat_var **in_message_code_stats = NULL;
2709a6b8
 stat_var **out_message_code_stats = NULL;
 
94dd7c5f
 /*! Adds the message code statistics to the statistics framework */
db0b73b0
 static int register_message_code_statistics(void)
2709a6b8
 {
 	int i;
 
db0b73b0
 	int number_of_message_codes =
 			sizeof(in_message_code_names) / sizeof(char *);
2709a6b8
 
db0b73b0
 	in_message_code_stats =
 			shm_malloc(sizeof(stat_var *) * number_of_message_codes);
2709a6b8
 
db0b73b0
 	out_message_code_stats =
 			shm_malloc(sizeof(stat_var *) * number_of_message_codes);
2709a6b8
 
 	/* We can only proceed if we had enough memory to allocate the
 	 * statistics.  Note that we don't free the memory, but we don't care
 	 * because the system is going to shut down */
db0b73b0
 	if(in_message_code_stats == NULL || out_message_code_stats == NULL) {
2709a6b8
 		return -1;
 	}
 
 	/* Make sure everything is zeroed out */
db0b73b0
 	memset(in_message_code_stats, 0,
 			sizeof(stat_var *) * number_of_message_codes);
 	memset(out_message_code_stats, 0,
 			sizeof(stat_var *) * number_of_message_codes);
2709a6b8
 
db0b73b0
 	for(i = 0; i < number_of_message_codes; i++) {
5b00ef8b
 		if(register_stat(SNMPSTATS_MODULE_NAME, in_message_code_names[i],
 				&in_message_code_stats[i], 0)!=0) {
 			LM_ERR("failed to register in_message_code_names[%d]\n", i);
 		}
 		if(register_stat(SNMPSTATS_MODULE_NAME, out_message_code_names[i],
 				&out_message_code_stats[i], 0)!=0) {
 			LM_ERR("failed to register out_message_code_names[%d]\n", i);
 		}
2709a6b8
 	}
 
 	return 0;
 }
 
94dd7c5f
 /*! This is the first function to be called by Kamailio, to initialize the module.
2709a6b8
  * This call must always return a value as soon as possible.  If it were not to
ded7d208
  * return, then Kamailio would not be able to initialize any of the other
2709a6b8
  * modules. */
db0b73b0
 static int mod_init(void)
2709a6b8
 {
db0b73b0
 	if(register_message_code_statistics() < 0) {
2709a6b8
 		return -1;
 	}
 
 	/* Initialize shared memory used to buffer communication between the
 	 * usrloc module and the snmpstats module.  */
 	initInterprocessBuffers();
db0b73b0
 
2709a6b8
 	/* We need to register for callbacks with usrloc module, for whenever a
 	 * contact is added or removed from the system.  We need to do it now
ded7d208
 	 * before Kamailio's functions get a chance to load up old user data from
2709a6b8
 	 * the database.  That load will happen if a lookup() function is come
ded7d208
 	 * across in kamailio.cfg. */
2709a6b8
 
db0b73b0
 	if(snmp_export_registrar != 0) {
 		if(!registerForUSRLOCCallbacks()) {
0696af54
 			/* Originally there were descriptive error messages here to help
 			 * the operator debug problems.  Turns out this may instead
 			 * alarm them about problems they don't need to worry about.  So
 			 * the messages are commented out for now */
db0b73b0
 
0696af54
 			/*
 			LM_ERR("snmpstats module was unable to register callbacks"
 					" with the usrloc module\n");
 			LM_ERR("Are you sure that the usrloc module was loaded"
 					" before the snmpstats module in ");
68d7468c
 			LM_ERR("kamailio.cfg?  kamailioSIPRegUserTable will not be "
0696af54
 				   "updated.");
 			*/
 		}
 	}
2709a6b8
 
 	/* Register the alarm checking function to run periodically */
 	register_timer(run_alarm_check, 0, ALARM_AGENT_FREQUENCY_IN_SECONDS);
 
41e163cc
 	/* add space for one extra process */
 	register_procs(1);
17e94fd7
 	/* add child to update local config framework structures */
 	cfg_register_child(1);
244bfd50
 	/* Initialize config framework in utilities.c */
 	config_context_init();
41e163cc
 
2709a6b8
 	return 0;
 }
 
 
94dd7c5f
 /*! This function is called when Kamailio has finished creating all instances of
2709a6b8
  * itself.  It is at this point that we want to create our AgentX sub-agent
  * process, and register a handler for any state changes of our child. */
db0b73b0
 static int mod_child_init(int rank)
2709a6b8
 {
41e163cc
 	int pid;
 
2709a6b8
 	/* We only want to setup a single process, under the main attendant. */
db0b73b0
 	if(rank != PROC_MAIN) {
2709a6b8
 		return 0;
 	}
 
97a616f1
 	/* Spawn SNMP AgentX process */
db0b73b0
 	pid = fork_process(PROC_NOCHLDINIT, "SNMP AgentX", 1);
 	if(pid < 0)
41e163cc
 		return -1; /* error */
db0b73b0
 	if(pid == 0) {
41e163cc
 		/* child */
17e94fd7
 		/* initialize the config framework */
db0b73b0
 		if(cfg_child_init())
17e94fd7
 			return -1;
 
41e163cc
 		agentx_child(1);
97a616f1
 		return 0;
41e163cc
 	}
 
2709a6b8
 	/* Spawn a child that will check the system up time. */
 	spawn_sysUpTime_child();
1ae03eb4
 
2709a6b8
 	return 0;
 }
 
94dd7c5f
 /*! This function is called when Kamailio is shutting down. When this happens, we
2709a6b8
  * log a useful message and kill the AgentX Sub-Agent child process */
db0b73b0
 static void mod_destroy(void)
2709a6b8
 {
94dd7c5f
 	LM_INFO("The SNMPStats module got the kill signal\n");
db0b73b0
 
94dd7c5f
 	freeInterprocessBuffer();
98a9961a
 
94dd7c5f
 	LM_INFO("Shutting down the AgentX Sub-Agent!\n");
2709a6b8
 }
 
 
94dd7c5f
 /*! The SNMPStats module forks off a child process to run an snmp command via
1ae03eb4
  * execve(). We need a customized handler to catch and ignore its SIGCHLD when 
  * it terminates. We also need to make sure to forward other processes 
ded7d208
  * SIGCHLD's to Kamailio's usual SIGCHLD handler.  We do this by resetting back
  * Kamailio's own signal handlers after we caught our appropriate SIGCHLD. */
2709a6b8
 static void sigchld_handler(int signal)
 {
 	int pid_of_signalled_process_status;
 	int pid_of_signalled_process;
 
 	/* We need to lookout for the expected SIGCHLD from our
 	 * sysUpTime child process, and ignore it.  If the SIGCHLD is
ded7d208
 	 * from another process, we need to call Kamailio's usual
2709a6b8
 	 * handlers */
db0b73b0
 	pid_of_signalled_process =
2709a6b8
 			waitpid(-1, &pid_of_signalled_process_status, WNOHANG);
 
db0b73b0
 	if(pid_of_signalled_process == sysUpTime_pid) {
2709a6b8
 		/* It was the sysUpTime process which died, which was expected.
 		 * At this point we will never see any SIGCHLDs from any other
ded7d208
 		 * SNMPStats process.  This means that we can restore Kamailio's
2709a6b8
 		 * original handlers. */
 		sigaction(SIGCHLD, &old_sigchld_handler, NULL);
db0b73b0
 	} else {
2709a6b8
 
ded7d208
 		/* We need this 'else-block' in case another Kamailio process dies
2709a6b8
 		 * unexpectantly before the sysUpTime process dies.  If this
 		 * doesn't happen, then this code will never be called, because
ded7d208
 		 * the block above re-assigns Kamailio's original SIGCHLD
2709a6b8
 		 * handler.  If it does happen, then we make sure to call the
 		 * default signal handlers. */
db0b73b0
 		if(old_sigchld_handler.sa_handler != SIG_IGN
 				&& old_sigchld_handler.sa_handler != SIG_DFL) {
2709a6b8
 			(*(old_sigchld_handler.sa_handler))(signal);
 		}
 	}
 }
 
94dd7c5f
 /*!
1ae03eb4
  * This function will spawn a child that retrieves the sysUpTime and stores the
  * result in a file. This file will be read by the AgentX Sub-agent process to
68d7468c
  * supply the kamailioSIPServiceStartTime time. This function never returns,
2709a6b8
  * but it will generated a SIGCHLD when it terminates.  There must a SIGCHLD
  * handler to ignore the SIGCHLD for only this process. (See sigchld_handler
  * above).
  *
94dd7c5f
  * \note sysUpTime is a scalar provided by netsnmp.  It is not the same thing as 
2709a6b8
  *       a normal system uptime. Support for this has been provided to try to
  *       match the IETF Draft SIP MIBs as closely as possible. 
  */
db0b73b0
 static int spawn_sysUpTime_child(void)
2709a6b8
 {
 	struct sigaction new_sigchld_handler;
 
 	char *local_path_to_snmpget = "/usr/local/bin/";
db0b73b0
 	char *snmpget_binary_name = "/snmpget";
 	char *full_path_to_snmpget = NULL;
2709a6b8
 
 	char *snmp_community_string = "public";
9f3b344a
 	char *snmp_version_string = "3";
2709a6b8
 
 	/* Set up a new SIGCHLD handler.  The handler will be responsible for
 	 * ignoring SIGCHLDs generated by our sysUpTime child process.  Every
 	 * other SIGCHLD will be redirected to the old SIGCHLD handler. */
 	sigfillset(&new_sigchld_handler.sa_mask);
db0b73b0
 	new_sigchld_handler.sa_flags = SA_RESTART;
2709a6b8
 	new_sigchld_handler.sa_handler = sigchld_handler;
 	sigaction(SIGCHLD, &new_sigchld_handler, &old_sigchld_handler);
 
1ae03eb4
 	pid_t result_pid = fork();
2709a6b8
 
db0b73b0
 	if(result_pid < 0) {
de7fe5e9
 		LM_ERR("failed to not spawn an agent to check sysUpTime\n");
2709a6b8
 		return -1;
db0b73b0
 	} else if(result_pid != 0) {
2709a6b8
 
 		/* Keep around the PID of the sysUpTime process so that the
 		 * customized SIGCHLD handler knows to ignore the SIGCHLD we
 		 * generate when we terminate. */
 		sysUpTime_pid = result_pid;
 
 		return 0;
 	}
 
 	/* If we are here, then we are the child process.  Lets set up the file
 	 * descriptors so we can capture the output of snmpget. */
db0b73b0
 	int snmpget_fd = open(SNMPGET_TEMP_FILE, O_CREAT | O_TRUNC | O_RDWR,
 			S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2709a6b8
 
 
db0b73b0
 	if(snmpget_fd == -1) {
de7fe5e9
 		LM_ERR("failed to open a temporary file "
db0b73b0
 			   "for snmpget to write to\n");
2709a6b8
 		return -1;
 	}
 
 	/* Redirect Standard Output to our temporary file. */
db0b73b0
 	dup2(snmpget_fd, 1);
2709a6b8
 
db0b73b0
 	if(snmp_community != NULL) {
2709a6b8
 		snmp_community_string = snmp_community;
 	} else {
de7fe5e9
 		LM_INFO("An snmpCommunity parameter was not provided."
db0b73b0
 				"  Defaulting to %s\n",
 				snmp_community_string);
2709a6b8
 	}
 
9f3b344a
 	if(snmp_version != NULL) {
 		snmp_version_string = snmp_version;
 	} else {
 		LM_INFO("A snmpVersion parameter was not provided."
 				"  Defaulting to %s\n",
 				snmp_version_string);
 	}
 
 	char *args[] = {"snmpget", "-v", snmp_version_string, "-Ov", "-c",
 			snmp_community_string, "localhost", SYSUPTIME_OID, (char *)0};
2709a6b8
 
 	/* Make sure we have a path to snmpget, so we can retrieve the
 	 * sysUpTime. */
db0b73b0
 	if(snmpget_path == NULL) {
de7fe5e9
 		LM_INFO("An snmpgetPath parameter was not specified."
db0b73b0
 				"  Defaulting to %s\n",
 				local_path_to_snmpget);
 	} else {
2709a6b8
 		local_path_to_snmpget = snmpget_path;
 	}
 
 	int local_path_to_snmpget_length = strlen(local_path_to_snmpget);
db0b73b0
 	int snmpget_binary_name_length = strlen(snmpget_binary_name);
 
2709a6b8
 	/* Allocate enough memory to hold the path, the binary name, and the
 	 * null character.  We don't use pkg_memory here. */
db0b73b0
 	full_path_to_snmpget = malloc(
 			sizeof(char)
 			* (local_path_to_snmpget_length + snmpget_binary_name_length + 1));
2709a6b8
 
db0b73b0
 	if(full_path_to_snmpget == NULL) {
de7fe5e9
 		LM_ERR("Ran out of memory while trying to retrieve sysUpTime.  ");
db0b73b0
 		LM_ERR("                  kamailioSIPServiceStartTime is "
 			   "defaulting to zero\n");
0b7b291a
 		close(snmpget_fd);
2709a6b8
 		return -1;
db0b73b0
 	} else {
2709a6b8
 		/* Make a new string containing the full path to the binary. */
 		strcpy(full_path_to_snmpget, local_path_to_snmpget);
db0b73b0
 		strcpy(&full_path_to_snmpget[local_path_to_snmpget_length],
2709a6b8
 				snmpget_binary_name);
 	}
 
 	/* snmpget -Ov -c public localhost .1.3.6.1.2.1.1.3.0  */
db0b73b0
 	if(execve(full_path_to_snmpget, args, NULL) == -1) {
 		LM_ERR("snmpget failed to run.  Did you supply the snmpstats module"
 			   " with a proper snmpgetPath parameter? The "
 			   "kamailioSIPServiceStartTime is defaulting to zero\n");
2709a6b8
 		close(snmpget_fd);
 		free(full_path_to_snmpget);
 		exit(-1);
 	}
db0b73b0
 
2709a6b8
 	/* We should never be able to get here, because execve() is never
 	 * supposed to return. */
0b7b291a
 	close(snmpget_fd);
2709a6b8
 	free(full_path_to_snmpget);
 	exit(-1);
 }
 
 
94dd7c5f
 /*! This function is called whenever the kamailio.cfg file specifies the
2709a6b8
  * snmpgetPath parameter.  The function will set the snmpget_path parameter. */
db0b73b0
 int set_snmpget_path(modparam_t type, void *val)
2709a6b8
 {
db0b73b0
 	if(!stringHandlerSanityCheck(type, val, "snmpgetPath")) {
2709a6b8
 		return -1;
 	}
 
 	snmpget_path = (char *)val;
 
 	return 0;
 }
 
 /* Handles setting of the snmp community string. */
db0b73b0
 int set_snmp_community(modparam_t type, void *val)
2709a6b8
 {
db0b73b0
 	if(!stringHandlerSanityCheck(type, val, "snmpCommunity")) {
2709a6b8
 		return -1;
 	}
 
 	snmp_community = (char *)val;
 
 	return 0;
0b7b291a
 }
9f3b344a
 
 /* Handles setting of the snmp version. */
 int set_snmp_version(modparam_t type, void *val)
 {
 	if(!stringHandlerSanityCheck(type, val, "snmpVersion")) {
 		return -1;
 	}
 
 	snmp_version = (char *)val;
 
 	return 0;
 }