#include #include #include #include #include #include #include "yasnd.h" cfg_t *cfg; // pointer to configuration structure char pidfile[]="/var/run/yasnd.pid"; // pidfile path host_decl* hosts=NULL; // structure with hosts' definitions int hosts_count=0; // count of hosts int debug_flag=0; bool sms_enable=false; // use SMS messages for control and reports? char* phone_device=NULL; // phone device for gammu, for example: "/dev/ttyACM0" char* phone_model=NULL; // gammu phone model, for example: "at" char* phone_connection=NULL; // gammu phone connection type, for example: "at" char* recipient_number=NULL; // recipient of sms alerts bool lpt_enable=false; // control usage of LPT port to reset target devices int lpt_port=0x378; // LPT port in hex(0x378 is usually LPT1) bool loop_locked=false; // flag for locking main loop while config re-reading int failures_first=0; // count of failures while hosts checking, that triggers SMS sending int failures_second=0; // count of failures while hosts checking, that triggers host's reset int 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 void log_debug(const char *message,int verbosity) { if (debug_flag>=verbosity) syslog(LOG_INFO,"%s",message); } void log_event(const char *message) { syslog(LOG_INFO,"%s",message); } void* allocate_memory(int bytes) { void* result=malloc(bytes); if (result==NULL) { log_event("Error: malloc failed"); exit(EXIT_FAILURE); } return result; } /* Function, that turn off all LPT port pins. This is needed to unlock LPT control circuit, when management computer is just booted */ void lpt_init() { if (lpt_enable) { if (ioperm (lpt_port, 3, 1)) { log_event("Error: Unlocking the circuit fails due to LPT port access error"); // disable LPT port usage lpt_enable=false; return; } // Unlock circuit outb (0, lpt_port); } } /* Function, that turn on first LPT port pin and turn off others. This is needed to lock LPT control circuit, when management computer is rebooted */ void lpt_lock() { if (lpt_enable) { if (ioperm (lpt_port, 3, 1)) { log_event("Error: Locking the circuit fails due to LPT port access error"); // disable LPT port usage lpt_enable=false; return; } // Lock circuit outb (1, lpt_port); } } // 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), CFG_SIMPLE_INT("debug", &debug_flag), CFG_SIMPLE_INT("failures_first", &failures_first), CFG_SIMPLE_INT("failures_second", &failures_second), CFG_SIMPLE_INT("failures_third", &failures_third), 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) { log_event("Error parsing config file"); exit(EXIT_FAILURE); } // 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) { 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); } if (recipient_number==NULL) { log_event("Error: no recipient for sms alerts defined in config file"); exit(EXIT_FAILURE); } } // hosts' array must not be empty hosts_count=cfg_size(cfg,"host"); if (hosts_count==0) { log_event("Error: no hosts to check defined in config file"); exit(EXIT_FAILURE); } // allocate memory for hosts array... hosts=allocate_memory(hosts_count*sizeof(host_decl)); // ...and fill it with config file content for (int i=0;ihostname); log_debug(tmp,DEBUG_BASE); execl("/bin/ping","/bin/ping","-c 1","-n",host->hostname,(char*) 0); // STUB: check result of exec call exit(EXIT_SUCCESS); } // Save child's pid host->helper_pid=pid; } /* Function, that turn on only one LPT port pin, defined by it argument, wait a second, and turn off all pins. That is enough to reset specified host. */ void reset_pin(int pin_num) { if (lpt_enable) { if (pin_num==0) return; // there is no LPT control for target host if (pin_num<2 || pin_num>8) { log_event("Error: incorrent LPT pin number"); return; } int pins = 1<<(pin_num-1); if (ioperm (lpt_port, 3, 1)) { log_event("Error: LPT port access error"); return; } // Reset host outb (pins, lpt_port); sleep(1); outb (0, lpt_port); } } int loop_function() { // set default signal handlers signal(SIGTERM,SIG_DFL); signal(SIGHUP,SIG_DFL); // for (int i=0;i 0) { if (WIFEXITED(pid_status)) { char tmp[50]; sprintf(tmp, "Finished child %d with result %d", result, WEXITSTATUS(pid_status)); log_debug(tmp,DEBUG_ALL); // if host is alive - reset failure counter // otherwise - increment it if (WEXITSTATUS(pid_status) == 0) { hosts[i].fail_count=0; hosts[i].alert_sent=false; hosts[i].reaction_obtained=false; } else hosts[i].fail_count++; } } } for (int i=0;ifailures_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) { 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; } } } void signal_handler(int signum) { void free_resources() { // 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"); // lock Main Loop loop_locked=true; // kill Main Loop clone process kill(mainloop_clone_pid,SIGTERM); waitpid(mainloop_clone_pid,NULL,__WCLONE); // 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"); // block LPT control circuit lpt_lock(); // close log decriptor closelog(); // free resources free_resources(); // exit(EXIT_SUCCESS); } } void arg_parse(int argc, char *argv[]) { for (int i=0;i 0) { exit(EXIT_SUCCESS); } /* Change the file mode mask */ umask(0); /* Open any logs here */ openlog("yasnd", LOG_PID|LOG_CONS, LOG_USER); /* Create a new SID for the child process */ sid = setsid(); if (sid < 0) { /* Log the failure */ exit(EXIT_FAILURE); } /* 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(); // Init apropriate structures init(); // Set signal handlers signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); /* The Main Loop */ 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); } } exit(EXIT_SUCCESS); }