yasnd/daemon/yasnd.c

393 lines
9.6 KiB
C
Raw Normal View History

2011-11-15 14:41:46 +04:00
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <getopt.h>
2011-11-17 15:45:44 +04:00
#include <confuse.h>
2011-11-15 14:41:46 +04:00
#include "yasnd.h"
#include "yasnd-gammu.h"
#include "yasnd-log.h"
#include "yasnd-lpt.h"
2011-11-15 17:53:12 +04:00
2011-11-25 17:50:47 +04:00
cfg_t *cfg; // pointer to configuration structure
char pidfile[]="/var/run/yasnd.pid"; // pidfile path
2011-11-25 17:35:33 +04:00
host_decl* hosts=NULL; // structure with hosts' definitions
int hosts_count=0; // count of hosts
bool sms_enable=false; // use SMS messages for control and reports?
char* recipient_number=NULL; // recipient of sms alerts
2011-12-19 13:31:40 +04:00
bool loop_locked=false; // flag for locking main loop while config re-reading
long failures_first=0; // count of failures while hosts checking, that triggers SMS sending
long failures_second=0; // count of failures while hosts checking, that triggers host's reset
long failures_third=0; // count of failures while hosts checking, that clean failure statistics
pid_t mainloop_clone_pid; // pid of clone process, that containt main loop
2011-11-15 17:53:12 +04:00
void* allocate_memory(int bytes)
{
void* result=malloc(bytes);
if (result==NULL)
{
log_event("Error: malloc failed");
exit(EXIT_FAILURE);
}
return result;
}
// Previous function, but without ignoring lpt_enable option
void lpt_lock()
{
return lpt_lock_cond(false);
}
// Function, that checks that defined host entry is valid
int cb_validate_host(cfg_t *cfg, cfg_opt_t *opt)
{
return 0; // success
}
void pidfile_init()
{
int pidfile_d=open(pidfile,O_RDWR|O_CREAT,0640);
/* Can not open/create pidfile? */
if (pidfile_d<0)
{
log_event("Error: can not open/create pid file");
exit(EXIT_FAILURE);
}
/* Can not lock pidfile? */
if (lockf(pidfile_d,F_TLOCK,0)<0)
{
log_event("Error: can not lock pid file, maybe another yasnd instance still running?");
exit(EXIT_FAILURE);
}
/* First daemon instance continues */
char pidstr[20];
sprintf(pidstr,"%d\n",getpid());
/* Record pid to pidfile and hold it */
write(pidfile_d,pidstr,strlen(pidstr));
}
void init()
{
static cfg_opt_t host_opts[] = {
CFG_STR("hostname", 0, CFGF_NONE),
CFG_INT("lpt_pin", 0, CFGF_NONE),
CFG_END()
};
// Initialize config parsing library
cfg_opt_t opts[] = {
CFG_SEC("host", host_opts, CFGF_MULTI | CFGF_TITLE),
2011-11-25 17:35:33 +04:00
CFG_SIMPLE_INT("debug", &debug_flag),
2012-01-03 03:05:53 +04:00
CFG_SIMPLE_INT("failures_first", &failures_first),
CFG_SIMPLE_INT("failures_second", &failures_second),
CFG_SIMPLE_INT("failures_third", &failures_third),
2011-12-19 09:56:50 +04:00
CFG_SIMPLE_BOOL("lpt_enable", &lpt_enable),
CFG_SIMPLE_INT("lpt_port", &lpt_port),
CFG_SIMPLE_BOOL("sms_enable", &sms_enable),
CFG_SIMPLE_STR("phone_device", &phone_device),
CFG_SIMPLE_STR("phone_model", &phone_connection),
CFG_SIMPLE_STR("phone_connection", &phone_model),
CFG_SIMPLE_STR("sms_recipient", &recipient_number),
CFG_END()
};
cfg = cfg_init(opts, CFGF_NONE);
// set a validating callback function for host sections
cfg_set_validate_func(cfg, "host", &cb_validate_host);
// check if config file is valid and parse it
if (cfg_parse(cfg, "/etc/yasnd.conf") == CFG_PARSE_ERROR)
{
2011-11-25 17:35:33 +04:00
log_event("Error parsing config file");
exit(EXIT_FAILURE);
}
2012-01-03 03:05:53 +04:00
// failures thresholds must be explicitly defined in config
if (failures_first==0 || failures_second==0 || failures_third==0)
{
log_event("Error: all failures threshold parameters must be defined in config file");
exit(EXIT_FAILURE);
}
// if SMS is enabled, all of phone parameters must be explicitly defined
if (sms_enable)
2011-12-12 13:47:43 +04:00
{
if (phone_model==NULL)
{
log_event("Error: phone model is not defined in config file");
exit(EXIT_FAILURE);
}
if (phone_connection==NULL)
{
log_event("Error: phone connection type is not defined in config file");
exit(EXIT_FAILURE);
}
if (phone_device==NULL)
{
log_event("Error: phone device is not defined in config file");
exit(EXIT_FAILURE);
}
2011-12-12 13:47:43 +04:00
if (recipient_number==NULL)
{
log_event("Error: no recipient for sms alerts defined in config file");
exit(EXIT_FAILURE);
}
}
2011-11-25 17:42:48 +04:00
// hosts' array must not be empty
hosts_count=cfg_size(cfg,"host");
2011-11-25 17:42:48 +04:00
if (hosts_count==0)
{
log_event("Error: no hosts to check defined in config file");
exit(EXIT_FAILURE);
}
// allocate memory for hosts array...
2011-11-25 17:35:33 +04:00
hosts=allocate_memory(hosts_count*sizeof(host_decl));
// ...and fill it with config file content
2011-11-25 17:35:33 +04:00
for (int i=0;i<hosts_count;i++)
{
cfg_t* host = cfg_getnsec(cfg, "host", i);
hosts[i].hostname=cfg_getstr(host,"hostname");
2011-12-19 15:19:18 +04:00
hosts[i].lpt_pin=cfg_getint(host,"lpt_pin");
hosts[i].fail_count=0;
hosts[i].alert_sent=false;
hosts[i].reaction_obtained=false;
}
2011-11-25 18:30:16 +04:00
// initialize gammu structures
if (sms_enable)
gammu_init();
// initialize LPT control circuit
lpt_init();
}
void check_host(int num,host_decl* host)
2011-11-15 17:53:12 +04:00
{
pid_t pid = fork();
if (pid < 0) {
2011-11-25 17:35:33 +04:00
log_event("Error: can not fork child :(");
2011-11-15 14:41:46 +04:00
exit(EXIT_FAILURE);
}
2011-11-15 17:53:12 +04:00
// If pid == 0, then we are in the child
if (pid == 0) {
// Set default signal handlers
signal(SIGTERM, SIG_DFL);
signal(SIGHUP, SIG_DFL);
//
2011-11-15 17:53:12 +04:00
char tmp[50];
sprintf(tmp, "Pinging host %s", host->hostname);
2011-11-25 17:35:33 +04:00
log_debug(tmp,DEBUG_BASE);
2011-11-30 16:21:34 +04:00
execl("/bin/ping","/bin/ping","-c 1","-n",host->hostname,(char*) 0);
2011-11-15 17:53:12 +04:00
// STUB: check result of exec call
exit(EXIT_SUCCESS);
2011-11-15 14:41:46 +04:00
}
2011-11-15 17:53:12 +04:00
// Save child's pid
host->helper_pid=pid;
2011-11-15 17:53:12 +04:00
}
int loop_function()
2011-11-15 17:53:12 +04:00
{
// set default signal handlers
signal(SIGTERM,SIG_DFL);
signal(SIGHUP,SIG_DFL);
//
2011-11-25 17:35:33 +04:00
for (int i=0;i<hosts_count;i++)
2011-11-15 17:53:12 +04:00
{
check_host(i,&hosts[i]);
2011-11-15 17:53:12 +04:00
}
2011-11-25 17:35:33 +04:00
for (int i=0;i<hosts_count;i++)
2011-11-15 17:53:12 +04:00
{
int pid_status;
2011-11-25 17:35:33 +04:00
int result = waitpid(hosts[i].helper_pid, &pid_status, 0);
2011-11-15 17:53:12 +04:00
if (result > 0)
{
if (WIFEXITED(pid_status))
{
char tmp[50];
sprintf(tmp, "Finished child %d with result %d", result, WEXITSTATUS(pid_status));
2011-11-25 17:35:33 +04:00
log_debug(tmp,DEBUG_ALL);
2011-11-15 17:53:12 +04:00
// if host is alive - reset failure counter
// otherwise - increment it
if (WEXITSTATUS(pid_status) == 0)
{
2011-11-15 17:53:12 +04:00
hosts[i].fail_count=0;
hosts[i].alert_sent=false;
hosts[i].reaction_obtained=false;
}
2011-11-15 17:53:12 +04:00
else
hosts[i].fail_count++;
}
}
}
2011-11-30 16:21:34 +04:00
for (int i=0;i<hosts_count;i++)
{
mymsgbuf qbuf;
if (hosts[i].fail_count>failures_second && hosts[i].alert_sent && !hosts[i].reaction_obtained)
{
if (lpt_enable)
{
qbuf.mtype=SMS_SEND_TYPE;
sprintf(qbuf.mtext,"Host %s does not answer and no reaction on this. Trying to reset it(LPT pin %d)",hosts[i].hostname,hosts[i].lpt_pin);
reset_pin(hosts[i].lpt_pin);
hosts[i].reaction_obtained=true;
// send message with SMS text to IPC queue
send_message(qid,&qbuf);
}
continue;
}
if (hosts[i].fail_count>failures_first && !hosts[i].alert_sent)
2011-11-30 16:21:34 +04:00
{
qbuf.mtype=SMS_SEND_TYPE;
sprintf(qbuf.mtext,"Host %s does not answer(LPT pin %d)",hosts[i].hostname,hosts[i].lpt_pin);
// set alert flag to prevent sending more than 1 message
// for unreachable host
hosts[i].alert_sent=true;
// send message with SMS text to IPC queue
send_message(qid,&qbuf);
continue;
2011-11-30 16:21:34 +04:00
}
}
2011-11-15 17:53:12 +04:00
}
void signal_handler(int signum)
2011-11-15 17:53:12 +04:00
{
void free_resources()
{
// lock Main Loop
loop_locked=true;
// kill Main Loop clone process
kill(mainloop_clone_pid,SIGTERM);
waitpid(mainloop_clone_pid,NULL,__WCLONE);
// Stop IPC loop and remove IPC queue
ipc_free();
// free config structure
cfg_free(cfg);
// free gammu structure
gammu_free();
// free hosts structures memory
if (hosts!=NULL)
free(hosts);
// remove pid file
remove(pidfile);
}
if (signum==SIGHUP)
{
log_event("SIGHUP captured, daemon re-read config");
// free all resources
free_resources();
// Init IPC loop again
ipc_init();
// run init again
init();
// sleep 5 seconds and unlock Main Loop
sleep(5);
loop_locked=false;
return;
}
if (signum==SIGTERM)
{
log_event("SIGTERM captured, daemon stopping");
// free resources
free_resources();
// block LPT control circuit
lpt_lock();
// close log decriptor
log_close();
//
exit(EXIT_SUCCESS);
}
2011-11-15 17:53:12 +04:00
}
void arg_parse(int argc, char *argv[])
{
int c;
struct option option_string[] =
{
{"init", no_argument, NULL, 'i'},
};
while ((c = getopt_long(argc, argv,"i", option_string, NULL)) != -1)
{
switch(c)
{
case 'i':
lpt_lock_cond(true);
exit(EXIT_SUCCESS);
break;
default:
break;
}
}
}
int main(int argc, char *argv[])
{
// Parse command line arguments
arg_parse(argc,argv);
2011-11-15 17:53:12 +04:00
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
2011-11-15 17:53:12 +04:00
/* Change the file mode mask */
umask(0);
/* Open any logs here */
log_init();
2011-11-15 17:53:12 +04:00
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
2011-11-15 17:53:12 +04:00
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// create pid file and write current process pid
pidfile_init();
// initialize IPC queue and create process
// that will watch for new messages in it
ipc_init();
2011-11-15 17:53:12 +04:00
// Init apropriate structures
init();
// Set signal handlers
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
/* The Main Loop */
2011-11-15 17:53:12 +04:00
while (1) {
if (!loop_locked)
{
unsigned char child_stack[16384];
mainloop_clone_pid=clone(loop_function,child_stack+8192,CLONE_VM,NULL);
sleep(60); /* wait 60 seconds */
// wait for clone process to prevent zombie
waitpid(mainloop_clone_pid,NULL,__WCLONE);
// log_event("Exiting");
// exit(EXIT_SUCCESS);
}
else
{
// if loop is locked, just wait 1 second and start it again
sleep(1);
}
2011-11-15 17:53:12 +04:00
}
exit(EXIT_SUCCESS);
2011-11-15 14:41:46 +04:00
}