/* Copyright (c) 2002-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #if DK_HAVE_LIMITS_H #include #else #if DK_HAVE_SYS_PARAM_H #include #endif #endif #if DK_HAVE_STDLIB_H #include #endif #if DK_HAVE_UNISTD_H #include #endif #if DK_HAVE_SIGNAL_H #include #endif #if DK_HAVE_SOCKET_H #include #endif #if DK_HAVE_NETINET_IN_H #include #endif #if DK_HAVE_NETDB_H #include #endif #if DK_HAVE_STRING_H #include #endif #if DK_HAVE_STRINGS_H #include #endif #if DK_HAVE_SYSLOG_H #include #endif #if DK_HAVE_ERRNO_H #include #endif #include "dktools-version.h" $(trace-include) #ifndef MAXPATHLEN #ifdef PATH_MAX #define MAXPATHLEN (PATH_MAX) #else #define MAXPATHLEN 1024 #endif #endif static #if DK_HAVE_VOLATILE volatile #endif int sighup_received = 0; static #if DK_HAVE_VOLATILE volatile #endif int sigint_received = 0; static #if DK_HAVE_VOLATILE volatile #endif int sigterm_received = 0; static #if DK_HAVE_VOLATILE volatile #endif int sigpipe_received = 0; dk_signal_ret_t handler_sighup DK_P1(int, signo) { dksignal_refresh(signo, handler_sighup); sighup_received = 1; } dk_signal_ret_t handler_sigint DK_P1(int, signo) { dksignal_refresh(signo, handler_sigint); sigint_received = 1; } dk_signal_ret_t handler_sigterm DK_P1(int, signo) { dksignal_refresh(signo, handler_sigterm); sigterm_received = 1; } dk_signal_ret_t handler_sigpipe DK_P1(int, signo) { dksignal_refresh(signo, handler_sigpipe); sigpipe_received = 1; } static int flags; static int exval = 0; static char progname[] = { "rshdown" }; static int syslog_facility = LOG_DAEMON; static char versnumb[] = { VERSNUMB }; static unsigned long lineno = 0UL; #ifndef SYSCONFDIR #define SYSCONFDIR "/etc" #endif #ifndef RSHDOWN_BUFFER_SIZE #define RSHDOWN_BUFFER_SIZE 512 #endif #define FLAG_HAVE_LOCAL_IP 1 #define FLAG_HAVE_LOCAL_PORT 2 #define FLAG_HAVE_REMOTE_IP 4 #define FLAG_HAVE_REMOTE_PORT 8 #define FLAG_HAVE_LOCAL_DATA 16 #define FLAG_HAVE_SCRIPT 32 #define FLAGS_REQUIRED 31 static char buffer_for_filedata[RSHDOWN_BUFFER_SIZE]; static char buffer_for_network[RSHDOWN_BUFFER_SIZE]; static char script_name[MAXPATHLEN]; static char *command_to_execute = NULL; static size_t buffer_used; static char *keywords[] = { "sender-ip", "sender-port", "receiver-ip", "receiver-port", "file", "action", "syslog", NULL }; static struct sockaddr_in sender_addr; static struct sockaddr_in this_addr; static struct sockaddr_in receiver_addr; $!trace-code #define IP1(x) ((x >> 24) & 0x000000FFUL) $!trace-code #define IP2(x) ((x >> 16) & 0x000000FFUL) $!trace-code #define IP3(x) ((x >> 8) & 0x000000FFUL) $!trace-code #define IP4(x) (x & 0x000000FFUL) static unsigned long dotted_string_to_ip DK_P1(char *, hn) { unsigned long back = 0UL; unsigned long u1, u2, u3, u4, u; int ende, state; char *ptr; if(hn) { state = 0; u = u1 = u2 = u3 = u4 = 0UL; ptr = hn; ende = 0; while(!ende) { if(*ptr) { if(isdigit(*ptr)) { u = 0UL; switch(*ptr) { case '0': u = 0UL; break; case '1': u = 1UL; break; case '2': u = 2UL; break; case '3': u = 3UL; break; case '4': u = 4UL; break; case '5': u = 5UL; break; case '6': u = 6UL; break; case '7': u = 7UL; break; case '8': u = 8UL; break; case '9': u = 9UL; break; } switch(state) { case 0: u1 = 10UL * u1 + u; break; case 1: u2 = 10UL * u2 + u; break; case 2: u3 = 10UL * u3 + u; break; case 3: u4 = 10UL * u4 + u; break; } } else { if(*ptr == '.') { state++; if(state >= 4) { ende = 1; } } } ptr++; } else { ende = 1; } } } u1 = u1 << 24; u1 = u1 & 0xFF000000UL; u2 = u2 << 16; u2 = u2 & 0x00FF0000UL; u3 = u3 << 8; u3 = u3 & 0x0000FF00UL; u4 = u4 & 0x000000FFUL; back = u1 | u2 | u3 | u4; return back; } static unsigned long string_to_ip DK_P1(char *,str) { unsigned long back = 0UL; struct hostent *hp; unsigned long *lp; back = dotted_string_to_ip(str); if(back) { back = htonl(back); } else { hp = gethostbyname(str); if(hp) { if(hp->h_addr_list) { if(hp->h_length) { lp = (unsigned long *)(*(hp->h_addr_list)); if(lp) { back = *lp; } else { /* ERROR: Empty host list */ } } else { /* ERROR: Empty address list */ } } else { /* ERROR: Empty address list */ } } else { /* ERROR: Host name not found */ } } return back; } static int read_config_file DK_P1(FILE *,file) { int back = 0; char buffer[MAXPATHLEN+256], *p1, *p2; int cc, act, i; unsigned long ul; unsigned u; unsigned short us; $(trace-init rshdown.deb) $? "- read_config_file" cc = back = 1; flags = 0; sender_addr.sin_family = AF_INET; receiver_addr.sin_family= AF_INET; sender_addr.sin_port = 0; receiver_addr.sin_port = 0; sender_addr.sin_addr.s_addr = 0UL; receiver_addr.sin_addr.s_addr = 0UL; while(cc && back) { if(fgets(buffer,sizeof(buffer),file)) { dkstr_delcomm(buffer, '#'); $!trace-code dkstr_chomp(buffer, NULL); lineno++; $? ". %lu %s", lineno, buffer p1 = dkstr_start(buffer, NULL); if(p1) { p2 = dkstr_next(p1, NULL); if(p2) { dkstr_chomp(p2, NULL); $? ". \"%s\" \"%s\"", p1, p2 act = dkstr_array_index(keywords, p1, 1); $? ". act = %d", act switch(act) { case 0: { $? ". sender-ip" ul = string_to_ip(p2); if(ul) { sender_addr.sin_addr.s_addr = ul; flags |= FLAG_HAVE_REMOTE_IP; $? ". sender-ip ok" } else { back = 0; fprintf(stderr, "Host \"%s\" not found!\n", p2); fflush(stderr); } } break; case 1: { $? ". sender-port" if(sscanf(p2, "%u", &u) ==1) { us = u; us = htons(us); sender_addr.sin_port = us; flags |= FLAG_HAVE_REMOTE_PORT; } else { back = 0; fprintf(stderr, "Illegal port number \"%s\"!\n", p2); fflush(stderr); } } break; case 2: { $? ". receiver-ip" ul = string_to_ip(p2); if(ul) { receiver_addr.sin_addr.s_addr = ul; flags |= FLAG_HAVE_LOCAL_IP; $? ". receiver-ip ok" } else { back = 0; fprintf(stderr, "Host \"%s\" not found!\n", p2); fflush(stderr); } } break; case 3: { $? ". receiver-port" if(sscanf(p2, "%u", &u) ==1) { us = u; us = htons(us); receiver_addr.sin_port = us; flags |= FLAG_HAVE_LOCAL_PORT; } else { back = 0; fprintf(stderr, "Illegal port number \"%s\"!\n", p2); fflush(stderr); } } break; case 4: { FILE *file2; $? ". file" file2 = dksf_fopen(p2, "r"); if(file2) { buffer_used = fread( buffer_for_filedata, 1, (sizeof(buffer_for_filedata)), file2 ); if(buffer_used > 0) { $? ". file ok %lu", (unsigned long)buffer_used flags |= FLAG_HAVE_LOCAL_DATA; } else { back = 0; fprintf(stderr, "Failed to read data from file \"%s\"!\n", p2); fflush(stderr); } fclose(file2); } else { back = 0; fprintf(stderr, "Failed to open file \"%s\"!\n", p2); fflush(stderr); } } break; case 5: { $? ". action" if(strlen(p2) < sizeof(script_name)) { strcpy(script_name, p2); flags |= FLAG_HAVE_SCRIPT; } else { back = 0; fprintf(stderr, "Script name \"%s\" too long!\n", p2); fflush(stderr); } } break; case 6: { $? ". syslog" i = dkslsupp_get_facility(p2); if(i) { syslog_facility = i; } else { fprintf(stderr, "Unknown syslog facility \"%s\"!\n", p2); fflush(stderr); } } break; default: { back = 0; fprintf(stderr, "Unknown configuration keyword \"%s\"!\n", p1); fflush(stderr); } break; } } else { } } else { } } else { cc = 0; } } if(back) { if((flags & FLAGS_REQUIRED) != FLAGS_REQUIRED) { back = 0; fprintf(stderr, "Incomplete configuration!\n"); fflush(stderr); } } $? "- read_config_file %d", back $(trace-end) return back; } typedef struct sockaddr_in SAIN; #define SA(x) ((struct sockaddr *)x) #define SZSAIN sizeof(SAIN) static int run_the_service DK_P0() { int back = 0; int sock; int sz; ssize_t bytes_read; int cc, action; action = 0; sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock > -1) { if(bind(sock, SA(&receiver_addr), SZSAIN) == 0) { cc = 1; back = 1; #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_NOTICE, "Service starting. (%s %s)", progname, versnumb); closelog(); #endif while(cc) { if(sighup_received) cc = 0; if(sigint_received) cc = 0; if(sigterm_received) cc = 0; if(sigpipe_received) cc = 0; if(cc) { sz = SZSAIN; DK_MEMCPY(&this_addr,&sender_addr,SZSAIN); bytes_read = recvfrom(sock, buffer_for_network, sizeof(buffer_for_network), 0, SA(&this_addr), &sz); #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_NOTICE, "Received %lu bytes!",(unsigned long)bytes_read); closelog(); #endif if(bytes_read > 0) { if((size_t)bytes_read == buffer_used) { if(sz == SZSAIN) { if(this_addr.sin_family == sender_addr.sin_family) { if(this_addr.sin_addr.s_addr == sender_addr.sin_addr.s_addr) { if(this_addr.sin_port = sender_addr.sin_port) { if(DK_MEMCMP(buffer_for_network,buffer_for_filedata,buffer_used) == 0) { cc = 0; action = 1; } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Illegal data!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid sender address port!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid sender address ip!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid sender address family!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid sender address length!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid data packet size!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Error while receiving data!"); closelog(); #endif } } } #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_NOTICE, "Service going down (%s %s)",progname,versnumb); closelog(); #endif } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Failed to bind local address!"); closelog(); #endif } close(sock); } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Failed to obtain socket (errno=%d)!",errno); closelog(); #endif } if(back) { if(action) { if(flags & FLAG_HAVE_SCRIPT) { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_EMERG, "Running power-fail script!"); syslog(LOG_EMERG, "Script name %s", script_name); closelog(); #endif system(script_name); } else { #if DK_HAVE_SYSLOG_H #endif #ifdef SIGPWR openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_EMERG, "Sending power-fail signal SIGPWR=%d to init process!",SIGPWR); closelog(); kill(1, SIGPWR); #else #ifdef SIGINFO openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_EMERG, "Sending power-fail SIGINFO=%d signal to init process!",SIGINFO); closelog(); kill(1, SIGINFO); #else openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_EMERG, "Sending power-fail signal 9 to init process!"); closelog(); kill(1, 9); #endif #endif } } } return back; } static int run_daemon DK_P0() { int back = 0; int maxfiles, i; dk_signal_disp_t disp_hup, disp_int, disp_term, disp_pipe; pid_t pid; chdir("/"); umask(077); maxfiles = (int)dksf_get_maxfiles(); for(i = 0; i < maxfiles; i++) (void)close(i); disp_hup = disp_int = disp_term = disp_pipe = NULL; sighup_received = sigint_received = sigterm_received = sigpipe_received = 0; pid = fork(); if(pid == 0) { #if DK_HAVE_SETSID setsid(); #else #if DK_HAVE_SETPGRP setpgrp(); #endif #endif disp_hup = dksignal_set(SIGHUP,handler_sighup); pid = fork(); if(pid == 0) { disp_int = dksignal_set(SIGINT,handler_sigint); disp_term = dksignal_set(SIGTERM,handler_sigterm); disp_pipe = dksignal_set(SIGPIPE,handler_sigpipe); back = run_the_service(); if(disp_pipe) { dksignal_set(SIGPIPE,disp_pipe); } if(disp_term) { dksignal_set(SIGTERM,disp_term); } if(disp_int) { dksignal_set(SIGINT,disp_int); } } else { if(pid > (pid_t)(-1)) { back = 1; } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Failed to create new process!"); closelog(); #endif } } if(disp_hup) { dksignal_set(SIGHUP,disp_hup); } } else { if(pid > (pid_t)(-1)) { back = 1; } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Failed to create new process!"); closelog(); #endif } } return back; } static int run_for_config_file DK_P1(char *,filename) { int back = 0; FILE *file; file = dksf_fopen(filename, "r"); if(file) { if(read_config_file(file)) { back = run_daemon(); } fclose(file); } else { /* ERROR: Failed to open config file */ fprintf(stderr, "Failed to open configuration file!\n"); fflush(stderr); } return back; } static char default_config_file_name[] = { SYSCONFDIR "/rshdown.conf" }; #ifdef DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { if(argc > 1) { exval = run_for_config_file(argv[1]); } else { exval = run_for_config_file(default_config_file_name); } exval = (exval ? 0 : 1); exit(exval); return exval; }