%% options copyright owner = Dirk Krause copyright year = 2016-xxxx license = bsd %% module /* rshdown -s [-n _number_] [-l _port_] [-b] _address_ _port_ _file_ rshdown [-c _configfile_] Either send shutdown packages (-s option used) or run as daemon in background (no -s option) and wait for shutdown packages. Sending: -n _number_ Number of packages to send -l _port_ Local port number to use Default: Same as remote port -b Flag: Destination address is a broadcast address _address_ Destination address (hostname or IP address) _port_ Port number on destination host _file_ File containing data to send Daemon: -c _configfile_ Configuration file. */ #include "dk4conf.h" #include "dk4types.h" #include #if DK4_HAVE_UNISTD_H #ifndef UNISTD_H_INCLUDED #include #define UNISTD_H_INCLUDED 1 #endif #endif #if DK4_HAVE_STDLIB_H #ifndef STDLIB_H_INCLUDED #include #define STDLIB_H_INCLUDED 1 #endif #endif #if (DK4_CHAR_SIZE == 1) \ && (DK4_HAVE_SIGACTION || DK4_HAVE_SIGSET) \ && (DK4_HAVE_SETSID || DK4_HAVE_SETPGRP) #if DK4_HAVE_MATH_H #ifndef MATH_H_INCLUDED #include #define MATH_H_INCLUDED 1 #endif #endif #if DK4_HAVE_IO_H #ifndef IO_H_INCLUDED #include #define IO_H_INCLUDED 1 #endif #endif #if DK4_HAVE_FCNTL_H #ifndef FCNTL_H_INCLUDED #include #define FCNTL_H_INCLUDED 1 #endif #endif #if DK4_HAVE_STRING_H #ifndef STRING_H_INCLUDED #include #define STRING_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SIGNAL_H #ifndef SIGNAL_H_INCLUDED #include #define SIGNAL_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYSLOG_H #ifndef SYSLOG_H_INCLUDED #include #define SYSLOG_H_INCLUDED 1 #endif #endif #include "dk4mem.h" #include "dk4memrs.h" #include "dk4opt.h" #include "dk4mai8dus.h" #include "dk4mai8dii.h" #include "dk4fopt.h" #include "dk4fopc8.h" #include "dk4isadm.h" #include "dk4sock.h" #include "dk4inst.h" #include "dk4str8.h" #include "dk4mpl.h" #include "dk4dmt.h" #include "dk4vers.h" $!trace-include #ifndef RSHDOWN_BUFFER_SIZE /** Buffer size, should not exceed UDP packet size. */ #define RSHDOWN_BUFFER_SIZE 1460 #endif /** Path name of data file. */ static char file_name[DK4_MAX_PATH]; /** Send and receive buffer. */ static char b1[RSHDOWN_BUFFER_SIZE]; /** Buffer for local secret from file in daemon. */ static char b2[sizeof(b1)]; /** Command to execute on packet arrival. */ static char cmd[1024]; /** Allowed sender peer as text. */ static char asender[1024]; /** Options used by the program. */ static dk4_option_t options[] = { /* 0 Configuration file. */ { { dkT('c'), dkT("config"), DK4_OPT_ARG_STRING }, { NULL }, 0 }, /* 1 Send data. */ { { dkT('s'), dkT("send"), DK4_OPT_ARG_NONE }, { NULL }, 0 }, /* 2 Destination address is a broadcast address. */ { { dkT('b'), dkT("broadcast"), DK4_OPT_ARG_NONE }, { NULL }, 0 }, /* 3 Number of packets to send. */ { { dkT('n'), dkT("number"), DK4_OPT_ARG_UNSIGNED}, { NULL }, 0 }, /* 4 Local port number. */ { { dkT('l'), dkT("local"), DK4_OPT_ARG_UNSIGNED}, { NULL }, 0}, /* 5 Help. */ { { dkT('\0'),dkT("help"), DK4_OPT_ARG_NONE}, { NULL }, 0}, /* 6 Version. */ { { dkT('\0'),dkT("version"), DK4_OPT_ARG_NONE}, { NULL }, 0}, /* 7 License */ { { dkT('\0'),dkT("license"), DK4_OPT_ARG_NONE}, { NULL }, 0} }; /** Help text. */ static const char * const help_text[] = { $!text Remote shutdown sender and recipient rshdown [-c config] rshdown -s [-n number] [-l port] [-b] host port file -c config Configuration file for recipient (daemon). -s Send shutdown data packet. -n number Send packet multiple times. -l port Local port to use for sending. -b Prepare socket for broadcast sending. host Remote host (recipient), host name or IP address. Option -b is required if you specify a broadcast address like 255.255.255.255 or 192.0.2.255 here. port Remote port. file File containing pre-shared secret (shutdown data). --help Show this help text. --version Show version number. --license Show license information. $!end }; /** License conditions text. */ static const char * const license_text[] = { $!text preprocessor This software uses code from the following projects, either directly or as a library: dktools Dirk Krause's tools and libraries. See http://dktools.sourceforge.net/ for more information. DK tools and libraries license ============================== Copyright (c) 2015-2016, 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 copyright 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. $!end }; /** Version information. */ static const char version_text[] = { DKT_VERSION_C8 }; /** Default configuration file name. */ static const char def_conf_name[] = { DK4_INST_DIR_ETC "/rshdown/rshdown.conf" }; /** PID file name. */ static const char pid_file_name[] = { DK4_INST_DIR_VAR "/run/rshdown.pid" }; /** Key names expected in configuration file. */ static const char * const key_names[] = { $!string-table # # 0 # sender # # 1 # sender port # # 2 # receiver port # # 3 # signal # # 4 # command # # 5 # file # # 6 # debug # # 7 # syslog facility $!end }; /** Signal names. */ static const char * const signal_names[] = { $!string-table # 0 1 sighup hup # 2 3 sigint int # 4 5 sigquit quit # 6 7 sigill ill # 8 9 sigtrap trap # 10 11 sigabrt abrt # 12 13 sigiot iot # 14 15 sigbus bus # 16 17 sigfpe fpe # 18 19 sigkill kill # 20 21 sigusr1 usr1 # 22 23 sigsegv segv # 24 25 sigusr2 usr2 # 26 27 sigpipe pipe # 28 29 sigalrm alrm # 30 31 sigterm term # 32 33 sigstkflt stkflt # 34 35 36 37 sigcld cld sigchld chld # 38 39 sigcont cont # 40 41 sigstop stop # 42 43 sigtstp tstp # 44 45 sigttin ttin # 46 47 sigttou ttou # 48 49 sigurg urg # 50 51 sigxcpu xcpu # 52 53 sigxfsz xfsz # 54 55 sigvtalrm vtalrm # 56 57 sigprof prof # 58 59 sigwinch winch # 60 61 sigpoll poll # 62 63 sigio io # 64 65 sigpwr pwr # 66 67 sigsys sys $!end }; /** Syslog facility names. */ static const char * const syslog_facility_names[] = { $!string-table auth authpriv cron daemon ftp kern lpr mail news security syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 $!end }; /** Texts used by the program, not localized. */ static const char * const rshdown_kw[] = { $!string-table # # 0 Program name # rshdown # # 1 # : # # 2 # ERROR: Failed to set broadcast mode on socket!\n # # 3 # ERROR: Failed to bind local address!\n # # 4 5 # ERROR: Failed to bind local address: " "!\n # # 6 # ERROR: Failed to create socket!\n # # 7 8 # ERROR: Failed to find address for: " "!\n # # 9 # ERROR: Failed to bring socket subsystem up!\n # # 10 # ERROR: 3 arguments required: host, port and file!\n # # 11 12 # ERROR: Not a 16 bit unsigned integer: " "!\n # # 13 # ERROR: Port number 0 is not allowed!\n # # 14 15 # ERROR: Failed to open data file:\n" "!\n # # 16 17 # ERROR: Failed to read data from file:\n" "!\n # # 18 # ERROR: Failed to install signal handlers!\n # # 19 # ERROR: Failed to restore signal handlers!\n # # 20 # ERROR: Failed to create child process!\n # # 21 # Failed to detach from terminal! # # 22 # Failed to create child process! # # 23 24 # Failed to install signal handlers! Failed to restore signal handlers! # # 25 # Failed to bring socket subsystem up! # # 26 # Failed to create UDP socket set! # # 27 28 # ERROR: Local port number too large!\n ERROR: Local port number must not be 0!\n # # 29 30 # Failed to set allowed peer: " "! # # 31 32 # ERROR: Illegal signal name: " "!\n # # 33 34 # ERROR: Sender name too long:\n "!\n # # 35 # ERROR: Sender name defined multiple times!\n # # 36 # ERROR: Signal number must be positive!\n # # 37 38 # ERROR: Command too long:\n" "!\n # # 39 # ERROR: Command defined multiple times!\n # # 40 41 # ERROR: File name too long:\n" "!\n # # 42 # ERROR: File name defined multiple times!\n # # 43 # ERROR: Unknown key in configuration file!\n # # 44 # ERROR: Missing value on configuration line!\n # # 45 # ERROR: No local port specified!\n # # 46 # ERROR: Signal number or command required!\n # # 47 # ERROR: No data file specified!\n # # 48 49 # ERROR: Illegal syslog facility: " "!\n # # 50 # Service started. # # 51 # Initiate shutdown. # # 52 # Service finished. # # 53 54 # Packet from " " discarded, wrong sender address! # # 55 56 # Packet from " " discarded, wrong sender port! # # 57 58 # Packet from " " discarded, size mismatch! # # 59 60 # Packet from " " discarded, data mismatch! # # 61 62 # Failed to execute command: " "! $!end }; /** Number of options in the options array. */ static const size_t szoptions = sizeof(options)/sizeof(dk4_option_t); /** Number of bytes in buffer b1. */ static size_t szb1 = 0; /** Number of bytes in buffer b2. */ static size_t szb2 = 0; /** Exit status code. */ static int exval = EXIT_FAILURE; /** Options: Help (1), version (2), license (4). */ static int hvl = 0; /** Flag: Additional debug messages. */ static int debug = 0; #ifdef LOG_DAEMON /** Syslog facility. */ static int syslogf = LOG_AUTH; #else /** Syslog facility. */ static int syslogf = (3<<3); #endif /** Signal to send on arrival of shutdown data packet. */ static int signo = #ifdef SIGPWR SIGPWR #else #ifdef SIGTERM SIGTERM #else 15 #endif #endif ; #ifdef SIGPIPE /** Indicator: SIGPIPE signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_pipe = 0; #endif /** Indicator: SIGINT signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_int = 0; /** Indicator: SIGTERM signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_term = 0; /** Remote port. */ static unsigned short rp = 0U; /** Local port. */ static unsigned short lp = 0U; /** Pass pointer through. Use of this function is recommended by CERT C coding standard to avoid optimizing out. @param ptr Pointer to pass through. @return The ptr argument. */ static DK4_VOLATILE dk4_sig_atomic_t * sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr) { return ptr; } #ifdef SIGPIPE /** Handler for SIGPIPE signal. @param signo Signal number (always SIGPIPE, ignored). */ static void sig_handler_pipe(int signo) { *sig_pass_pointer(&sig_had_pipe) = 1; } #endif /** Handler for SIGINT signal. @param signo Signal number (always SIGINT, ignored). */ static void sig_handler_int(int signo) { *sig_pass_pointer(&sig_had_int) = 1; } /** Handler for SIGTERM signal. @param signo Signal number (always SIGTERM, ignored). */ static void sig_handler_term(int signo) { *sig_pass_pointer(&sig_had_term) = 1; } /** Read value from volatile atomic type. This function is necessary as some compilers mis-optimize direct access to volatile variables (at least if you believe one of the coding standards). @param ap Pointer to volatile atomic variable. @return Contents of the variable. */ static dk4_sig_atomic_t sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap) { return (*ap); } /** Check whether we can continue or must abort due to signal. @param dopipe Flag: Check for SIGPIPE too. @return 1 if we can continue, 0 otherwise. */ static int can_continue(void) { int back = 1; if (0 != sig_read_atomic(&sig_had_term)) { back = 0; } if (0 != sig_read_atomic(&sig_had_int)) { back = 0; } return back; } /** Write log message to stderr, 1 component. @param i Text index in rshdown_kw array. */ static void log_1(size_t i) { fputs(rshdown_kw[0], stderr); fputs(rshdown_kw[1], stderr); fputs(rshdown_kw[i], stderr); fflush(stderr); } /** Write syslog message, 1 component. @param i Index of text in rshdown_kw array. */ static void syslog_1(size_t i) { #if DK4_HAVE_SYSLOG openlog(rshdown_kw[0], LOG_PID, syslogf); syslog(LOG_ERR, "%s", rshdown_kw[i]); closelog(); #endif } /** Write syslog message, 3 components. @param i1 Index of first component in rshdown_kw array. @param i2 Index of third component in rshdown_kw array. @param str Second component. */ static void syslog_3(size_t i1, size_t i2, const char *str) { #if DK4_HAVE_SYSLOG openlog(rshdown_kw[0], LOG_PID, syslogf); if (NULL != str) { syslog(LOG_ERR, "%s%s%s", rshdown_kw[i1], str, rshdown_kw[i2]); } else { syslog(LOG_ERR, "%s%s", rshdown_kw[i1], rshdown_kw[i2]); } closelog(); #endif } /** Write an additional debug message to syslog. @param i1 Index of first component in rshdown_kw array. @param i2 Index of third component in rshdown_kw array. @param sa Socket address, second component. */ static void syslog_debug(size_t i1, size_t i2, dk4_sockaddr_storage_t *sa) { #if DK4_HAVE_SYSLOG char buf[128]; /* Buffer for text respresentation of address */ int res; /* Operation result from conversion */ if (0 != debug) { res = dk4socket_c8_sockaddr_to_text(buf, sizeof(buf), sa, NULL); openlog(rshdown_kw[0], LOG_PID, syslogf); if (DK4_SOCKET_RESULT_SUCCESS == res) { syslog(LOG_DEBUG, "%s%s%s", rshdown_kw[i1], buf, rshdown_kw[i2]); } else { syslog(LOG_DEBUG, "%s%s", rshdown_kw[i1], rshdown_kw[i2]); } closelog(); } #endif } /** Write log message to stderr, 3 components. @param i1 Index of first component in rshdown_kw array. @param i2 Index of third component in rshdown_kw array. @param str Second component. */ static void log_3(size_t i1, size_t i2, const char *str) { fputs(rshdown_kw[0], stderr); fputs(rshdown_kw[1], stderr); fputs(rshdown_kw[i1], stderr); if (NULL != str) { fputs(str, stderr); } fputs(rshdown_kw[i2], stderr); fflush(stderr); } /** Send buffer over network. @param hn Remote host name or IP address as text. @param rp Remote port in host representation. @param lp Local port in host representation. @param sz Number of bytes in b1. */ static void send_network(const char *hn, size_t sz) { char adbuf[128]; /* Address as text */ dk4_sockaddr_storage_t array[2]; /* Address pair */ dk4_um_t num = (dk4_um_t)0UL; size_t szr = 0; /* Rem addr size */ size_t szl = 0; /* Loc addr size */ size_t lgt = 0; /* Length sent */ dk4_socket_t sock = INVALID_SOCKET; int res = 0; /* Operation result */ int ip4 = 0; /* IPv4 preferred */ int af = AF_UNSPEC; int pf = PF_UNSPEC; $? "+ send_network" if (DK4_SOCKET_RESULT_SUCCESS == dk4socket_up(NULL)) { if (options[2].found) { ip4 = 1; } $? ". socket subsystem up" res = dk4socket_c8_udp_addresses_numeric(array, hn, rp, lp, ip4, NULL); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". addresses ok" szr = dk4socket_address_size(array); szl = dk4socket_address_size(&(array[1])); af = dk4socket_address_family(&(array[1])); pf = dk4socket_pf_for_af(af); if ((0 < szr) && (0 < szl) && (AF_UNSPEC != af) && (PF_UNSPEC != pf)) { sock = dk4socket_open(pf, SOCK_DGRAM, 0, NULL); if (INVALID_SOCKET != sock) { $? ". socket ok" exval = EXIT_SUCCESS; (void)dk4socket_option_set_reuse(sock, 1, NULL); if (options[2].found) { $? ". prepare for broadcast" res = dk4socket_option_set_broadcast(sock,1,NULL); if (DK4_SOCKET_RESULT_SUCCESS != res) { /* ERROR: Failed to set broadcast mode */ log_1(2); exval = EXIT_FAILURE; $? "! broadcast preparation" } } res = dk4socket_bind( sock, (struct sockaddr *)(&(array[1])), szl, NULL ); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". address bound" if (0 != options[3].found) { num = options[3].val.u; } if ((dk4_um_t)0UL == num) { num = (dk4_um_t)1UL; } while ((0 != can_continue()) && ((dk4_um_t)0UL < num--)) { lgt = sz; $? ". send attempt" res = dk4socket_sendto( sock, b1, &lgt, 0, (struct sockaddr *)(&(array[0])), szr, 0L, 0L, NULL ); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". sendto ok" if (lgt != sz) { exval = EXIT_FAILURE; } } else { $? "! sendto failed" exval = EXIT_FAILURE; } } } else { $? "! failed to bind local addr" /* ERROR: Failed to bind local address */ res = dk4socket_c8_addr_port_to_text( adbuf, sizeof(adbuf), af, (struct sockaddr *)(&(array[1])), szl, NULL ); if (DK4_SOCKET_RESULT_SUCCESS == res) { log_3(4, 5, adbuf); } else { log_1(3); } } (void)dk4socket_close(sock, NULL); } else { $? "! failed to open socket" /* ERROR: Failed to open socket */ log_1(6); } } else { $? "! something wrong" if (0 == szr) { $? "! remote address size" log_3(7, 8, hn); } if (0 == szl) { $? "! local address size" log_3(7, 8, hn); } if (AF_UNSPEC == af) { $? "! address family" log_3(7, 8, hn); } if (PF_UNSPEC == pf) { $? "! protocol family" log_3(7, 8, hn); } } } else { $? "! addresses not found" /* ERROR: Failed to obtain addresses */ log_3(7, 8, hn); } (void)dk4socket_down(NULL); } else { $? "! socket subsystem" /* ERROR: Failed to bring socket subsystem up */ log_1(9); } $? "- send_network" } /** Send shutdown data packet. @param filenames Command line arguments array, 0=remote host, 1=remote port, 2=filename. @param szfilenames Number of elements in filenames array. */ static void sender_with_signals(const char * const *filenames, size_t szfilenames) { FILE *fipo = NULL; /* Data file */ const char *ep = NULL; /* End of usefull data pointer */ size_t lgt = 0; /* Number of bytes in buffer */ int res = 0; /* Conversion or operation result */ $? "+ sender_with_signals" if (3 == szfilenames) { $? ". szfilenames" res = dk4ma_input_c8_dec_ushort(&rp, filenames[1], &ep, 1, NULL); if (0 != res) { $? ". port number" if (0 != rp) { $? ". port number not 0" lp = rp; if (0 != options[4].found) { $? ". local port given" if ((dk4_um_t)0UL < options[4].val.u) { $? ". lp ok" if ((dk4_um_t)(USHRT_MAX) >= options[4].val.u) { lp = (unsigned short)(options[4].val.u); $? ". lp ok" } else { $? "! overflow" lp = 0; /* ERROR: Local port number exceeds 16 bit unsigned range */ log_1(27); } } else { $? "! lp 0" lp = 0; /* ERROR: Local port number 0 is not allowed */ log_1(28); } } if (0 != lp) { $? ". can run" fipo = dk4fopen_c8(filenames[2], "rb", DK4_FOPEN_SC_IS_REGULAR, NULL); if (NULL != fipo) { $? ". data file opened" lgt = fread(b1, 1, sizeof(b1), fipo); fclose(fipo); if (0 < lgt) { $? ". data from file" /* Send bytes over network */ send_network(filenames[0], lgt); /* Overwrite memory containing data packet */ if (0 == dk4mem_reset_secure(b1, sizeof(b1), NULL)) { exval = EXIT_FAILURE; $? "! reset mem failed" } } else { $? "! no data from file" /* ERROR: No data read from file */ log_3(16, 17, filenames[2]); } } else { $? "! data file" /* ERROR: Failed to open data file */ log_3(14, 15, filenames[2]); } } else { $? "! config" } } else { $? "! port number 0" /* ERROR: Remote port number 0 is not allowed */ log_1(13); } } else { $? "! port number" /* ERROR: Not a 16 bit unsigned number */ log_3(11, 12, filenames[1]); } } else { $? "! szfilenames" /* ERROR: Three arguments required - host, port and file */ log_1(10); } $? "- sender_with_signals" } /** Send shutdown data packet. @param filenames Command line arguments array, 0=remote host, 1=remote port, 2=filename. @param szfilenames Number of elements in filenames array. */ static void sender(const char * const *filenames, size_t szfilenames) { $!trace-init rshdown.deb $? "+ sender" #ifdef DK4_HAVE_SIGACTION #ifdef SIGPIPE struct sigaction opipe; struct sigaction npipe; #endif struct sigaction oint; struct sigaction nint; struct sigaction oterm; struct sigaction nterm; #else #ifdef SIGPIPE dk4_sig_handler_t *opipe = NULL; #endif dk4_sig_handler_t *oterm = NULL; dk4_sig_handler_t *oint = NULL; #endif #ifdef DK4_HAVE_SIGACTION #ifdef SIGPIPE int spipe = 0; #endif int sint = 0; int sterm = 0; int s_f_i = 0; int s_f_r = 0; #endif /* Set signal handlers */ #if DK4_HAVE_SIGACTION #ifdef SIGPIPE DK4_MEMRES(&npipe, sizeof(npipe)); npipe.sa_handler = sig_handler_pipe; npipe.sa_flags = 0; if (0 != sigemptyset(&npipe.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { s_f_i = 1; goto finished; } spipe = 1; #endif DK4_MEMRES(&nterm, sizeof(nterm)); nterm.sa_handler = sig_handler_term; nterm.sa_flags = 0; if (0 != sigemptyset(&nterm.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGTERM, &nterm, &oterm)) { s_f_i = 1; goto finished; } sterm = 1; DK4_MEMRES(&nint, sizeof(nint)); nint.sa_handler = sig_handler_int; nint.sa_flags = 0; if (0 != sigemptyset(&nint.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&nint.sa_mask, SIGINT)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGINT, &nint, &oint)) { s_f_i = 1; goto finished; } sint = 1; #else #ifdef SIGPIPE opipe = sigset(SIGPIPE, sig_handler_pipe); #endif oint = sigset(SIGINT, sig_handler_int); oterm = sigset(SIGTERM, sig_handler_term); #endif /* Send the bytes. */ sender_with_signals(filenames, szfilenames); finished: /* Restore signal handlers */ #if DK4_HAVE_SIGACTION if (0 != s_f_i) { /* ERROR: Failed to install signal handlers */ log_1(18); exval = EXIT_FAILURE; } if (0 != sint) { if (0 != sigaction(SIGINT, &oint, NULL)) { s_f_r = 1; } } if (0 != sterm) { if (0 != sigaction(SIGTERM, &oterm, NULL)) { s_f_r = 1; } } #ifdef SIGPIPE if (0 != spipe) { if (0 != sigaction(SIGPIPE, &opipe, NULL)) { s_f_r = 1; } } #endif if (0 != s_f_r) { /* ERROR: Failed to restore signal handlers */ log_1(19); exval = EXIT_FAILURE; } #else if (NULL != oterm) { sigset(SIGTERM, oterm); } if (NULL != oint ) { sigset(SIGINT, oint ); } #ifdef SIGPIPE if (NULL != opipe) { sigset(SIGPIPE, opipe); } #endif #endif $? "- sender" $!trace-end } /** Run service */ static void run_daemon_loop(void) { dk4_allowed_peer_t peer; /* Allowed remote address */ dk4_sockaddr_storage_t sa; /* Generic socket address */ fd_set rfds; /* Readable file descriptors */ dk4_socket_set_t *sset = NULL; /* Set of sockets to read data from */ struct sockaddr *soa = NULL; /* Minimum socket address */ struct sockaddr_in *so4 = NULL; /* IPv4 socket address */ #if DK4_HAVE_STRUCT_SOCKADDR_IN6 struct sockaddr_in6 *so6 = NULL; /* IPv6 socket address */ #endif size_t szsa = 0; /* Size of sa */ size_t i = 0; /* Traverse socket set */ dk4_socket_t smax = 0; /* Maximum socket fd number */ int cc = 1; /* Flag: Can continue */ int pused = 0; /* Flag: Peer used */ int res = 0; /* Operation result */ int ok = 0; /* Flag: Data packet matches file */ unsigned short frp = 0; /* Found remote port */ $? "+ run_daemon_loop" soa = (struct sockaddr *)(&sa); so4 = (struct sockaddr_in *)(&sa); #if DK4_HAVE_STRUCT_SOCKADDR_IN6 so6 = (struct sockaddr_in6 *)(&sa); #endif if (DK4_SOCKET_RESULT_SUCCESS == dk4socket_up(NULL)) { $? ". socket subsystem" if (0 < strlen(asender)) { $? ". sender specified" res = dk4socket_c8_get_allowed_peer(&peer, asender, NULL); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". peer found" pused = 1; } else { $? "! peer not found" cc = 0; /* SYSLOG ERROR: Invalid peer name */ syslog_3(29, 30, asender); } } if (1 == cc) { $? ". no errors yet" sset = dk4socket_set_udp_directly(lp, 0, 0, NULL); if (NULL != sset) { $? ". socket set ok" /* SYSLOG: Started */ #if DK4_HAVE_SYSLOG openlog(rshdown_kw[0], LOG_PID, syslogf); syslog(LOG_INFO, "%s", rshdown_kw[50]); closelog(); #endif do { if (0 != can_continue()) { FD_ZERO(&rfds); smax = -1; for (i = 0; i < sset->szUsed; i++) { $? ". socket %d", (sset->pSockets)[i] FD_SET((sset->pSockets)[i],&rfds); if (smax < (sset->pSockets)[i]) { smax = (sset->pSockets)[i]; } } res = select((1+smax), &rfds, NULL, NULL, NULL); if (0 != can_continue()) { if (0 < res) { $? ". data available" for (i = 0; ((1 == cc) && (i < sset->szUsed)); i++) { $? ". checking %d", (sset->pSockets)[i] if (FD_ISSET((sset->pSockets)[i],&rfds)) { $? ". ready to read" DK4_MEMRES(&sa, sizeof(sa)); szb2 = sizeof(b2); szsa = sizeof(sa); res = dk4socket_recvfrom( (sset->pSockets)[i], b2, &szb2, 0, (struct sockaddr *)(&sa), &szsa, 0L, 0L, NULL ); if (0 != can_continue()) { $? ". can continue" if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". read" if (0 < szb2) { $? ". %u bytes received", (unsigned)szb2 ok = 1; if (0 != pused) { $? ". check peer" res = dk4socket_allowed_peer_compare(&peer, &sa, 1); if (0 != res) { $? "! address mismatch" ok = 0; /* SYSLOG DEBUG: Address mismatch */ syslog_debug(53, 54, &sa); } } if (0U != rp) { $? ". check port" frp = 0; switch (soa->sa_family) { case AF_INET : { frp = dk4socket_ntohs(so4->sin_port); } break; #if DK4_HAVE_STRUCT_SOCKADDR_IN6 case AF_INET6 : { frp = dk4socket_ntohs(so6->sin6_port); } break; #endif } if (frp != rp) { $? "! port mismatch" ok = 0; /* SYSLOG DEBUG: Port mismatch */ syslog_debug(55, 56, &sa); } } if (0 != ok) { $? ". peer/port ok" ok = 0; if (szb1 == szb2) { $? ". size match" if (0 == DK4_MEMCMP(b1,b2,szb1)) { ok = 1; $? ". data matches" } else { $? "! data mismatch" /* SYSLOG DEBUG: Data mismatch */ syslog_debug(59, 60, &sa); } } else { $? "! size mismatch" /* SYSLOG DEBUG: Size mismatch */ syslog_debug(57, 58, &sa); } } if (0 != ok) { $? ". going to shut down" /* Shutdown */ /* SYSLOG: Initiating shutdown */ #if DK4_HAVE_SYSLOG openlog(rshdown_kw[0], LOG_PID, syslogf); syslog(LOG_INFO, "%s", rshdown_kw[51]); closelog(); #endif if (0 < strlen(cmd)) { $? ". command" if (0 != system(cmd)) { #if DK4_HAVE_SYSLOG openlog(rshdown_kw[0], LOG_PID, syslogf); syslog( LOG_ERR, "%s%s%s", rshdown_kw[61], cmd, rshdown_kw[62] ); closelog(); #endif } } else { $? ". signal" if (0 < signo) { kill((pid_t)1, signo); } else { #ifdef SIGPWR kill((pid_t)1, SIGPWR); #else #ifdef SIGTERM kill((pid_t)1, SIGTERM); #else kill((pid_t)1, 15); #endif #endif } } cc = 0; } } else { $? "! size is 0" } } else { $? "! recvfrom" } } else { $? "! signal" cc = -1; } } else { $? ". not ready to read" } } } else { $? ". no fd ready" } } else { $? "! signal" cc = -1; } } else { $? "! signal" cc = -1; } } while (1 == cc); $? ". loop finished" dk4socket_set_close(sset, NULL); /* SYSLOG: Finished */ #if DK4_HAVE_SYSLOG openlog(rshdown_kw[0], LOG_PID, syslogf); syslog(LOG_INFO, "%s", rshdown_kw[52]); closelog(); #endif } else { $? "! socket set" /* SYSLOG ERROR: Failed to create socket set */ syslog_1(26); } } else { $? "! previous errors" } (void)dk4socket_down(NULL); } else { $? "! socket subsystem" /* SYSLOG ERROR: Failed to bring socket subsystem up */ syslog_1(25); } $? "- run_daemon_loop" } /** Set signal handlers and run service. */ static void run_daemon_with_signals(void) { #ifdef DK4_HAVE_SIGACTION #ifdef SIGPIPE struct sigaction opipe; struct sigaction npipe; #endif struct sigaction oint; struct sigaction nint; struct sigaction oterm; struct sigaction nterm; #else #ifdef SIGPIPE dk4_sig_handler_t *opipe = NULL; #endif dk4_sig_handler_t *oterm = NULL; dk4_sig_handler_t *oint = NULL; #endif #ifdef DK4_HAVE_SIGACTION #ifdef SIGPIPE int spipe = 0; #endif int sint = 0; int sterm = 0; int s_f_i = 0; int s_f_r = 0; #endif $? "+ run_daemon_with_signals" /* Set signal handlers */ #if DK4_HAVE_SIGACTION #ifdef SIGPIPE DK4_MEMRES(&npipe, sizeof(npipe)); npipe.sa_handler = sig_handler_pipe; npipe.sa_flags = 0; if (0 != sigemptyset(&npipe.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { s_f_i = 1; goto finished; } spipe = 1; #endif DK4_MEMRES(&nterm, sizeof(nterm)); nterm.sa_handler = sig_handler_term; nterm.sa_flags = 0; if (0 != sigemptyset(&nterm.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGTERM, &nterm, &oterm)) { s_f_i = 1; goto finished; } sterm = 1; DK4_MEMRES(&nint, sizeof(nint)); nint.sa_handler = sig_handler_int; nint.sa_flags = 0; if (0 != sigemptyset(&nint.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&nint.sa_mask, SIGINT)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGINT, &nint, &oint)) { s_f_i = 1; goto finished; } sint = 1; #else #ifdef SIGPIPE opipe = sigset(SIGPIPE, sig_handler_pipe); #endif oint = sigset(SIGINT, sig_handler_int); oterm = sigset(SIGTERM, sig_handler_term); #endif run_daemon_loop(); finished: /* Restore signal handlers */ #if DK4_HAVE_SIGACTION if (0 != s_f_i) { /* ERROR: Failed to install signal handlers */ syslog_1(23); exval = EXIT_FAILURE; } if (0 != sint) { if (0 != sigaction(SIGINT, &oint, NULL)) { s_f_r = 1; } } if (0 != sterm) { if (0 != sigaction(SIGTERM, &oterm, NULL)) { s_f_r = 1; } } #ifdef SIGPIPE if (0 != spipe) { if (0 != sigaction(SIGPIPE, &opipe, NULL)) { s_f_r = 1; } } #endif if (0 != s_f_r) { /* ERROR: Failed to restore signal handlers */ syslog_1(24); exval = EXIT_FAILURE; } #else if (NULL != oterm) { sigset(SIGTERM, oterm); } if (NULL != oint ) { sigset(SIGINT, oint ); } #ifdef SIGPIPE if (NULL != opipe) { sigset(SIGPIPE, opipe); } #endif #endif $? "- run_daemon_with_signals" } /** Retrieve port number from a configuration file line. @param dest Address of destination variable. @param pv String containing the port number. @param fn File name for diagnostics. @param lineno Line number for diagnostics. @param a0 Flag: Remote port, 0 allowed for arbitrary ports. @return 1 on success, 0 on error. */ static int set_port_number( unsigned short *dest, const char *pv, const char *fn, dk4_um_t lineno, int a0 ) { const char *ep = NULL; int back = 0; unsigned short us = 0U; if (0 != dk4ma_input_c8_dec_ushort(&us, pv, &ep, 1, NULL)) { if (0U != us) { *dest = us; back = 1; } else { if (0 != a0) { *dest = us; back = 1; } else { /* ERROR: Port number 0 not allowed! */ log_1(28); } } } else { /* ERROR: Not a 16 bit unsigned number! */ log_3(11, 12, pv); } return back; } /** Set the signal number. @param pv Text containing the number. @param fn File name for diagnostics. @param lineno Line number for diagnostics. @return 1 on success, 0 on error. */ static int set_signal_number(const char *pv, const char *fn, dk4_um_t lineno) { int back = 1; $? "+ set_signal_number \"%s\"", TR_8STR(pv) switch ( dk4str8_array_index(signal_names, pv, 0) ) { case 0 : case 1 : { #ifdef SIGHUP signo = SIGHUP; #else signo = 1; #endif } break; case 2 : case 3 : { #ifdef SIGINT signo = SIGINT; #else signo = 2; #endif } break; case 4 : case 5 : { #ifdef SIGQUIT signo = SIGQUIT; #else signo = 3; #endif } break; case 6 : case 7 : { #ifdef SIGILL signo = SIGILL; #else signo = 4; #endif } break; case 8 : case 9 : { #ifdef SIGTRAP signo = SIGTRAP; #else signo = 5; #endif } break; case 10 : case 11 : case 12 : case 13 : { #ifdef SIGABRT signo = SIGABRT; #else #ifdef SIGIOT signo = SIGIOT; #else signo = 6; #endif #endif } break; case 14 : case 15 : { #ifdef SIGBUS signo = SIGBUS; #else signo = 7; #endif } break; case 16 : case 17 : { #ifdef SIGFPE signo = SIGFPE; #else signo = 8; #endif } break; case 18 : case 19 : { #ifdef SIGKILL signo = SIGKILL; #else signo = 9; #endif } break; case 20 : case 21 : { #ifdef SIGUSR1 signo = SIGUSR1; #else signo = 10; #endif } break; case 22 : case 23 : { #ifdef SIGSEGV signo = SIGSEGV; #else signo = 11; #endif } break; case 24 : case 25 : { #ifdef SIGUSR2 signo = SIGUSR2; #else signo = 12; #endif } break; case 26 : case 27 : { #ifdef SIGPIPE signo = SIGPIPE; #else signo = 13; #endif } break; case 28 : case 29 : { #ifdef SIGALRM signo = SIGALRM; #else signo = 14; #endif } break; case 30 : case 31 : { #ifdef SIGTERM signo = SIGTERM; #else signo = 15; #endif } break; case 32 : case 33 : { #ifdef SIGSTKFLT signo = SIGSTKFLT; #else signo = 16; #endif } break; case 34 : case 35 : case 36 : case 37 : { #ifdef SIGCLD signo = SIGCLD; #else #ifdef SIGCHLD signo = SIGCHLD; #else signo = 17; #endif #endif } break; case 38 : case 39 : { #ifdef SIGCONT signo = SIGCONT; #else signo = 18; #endif } break; case 40 : case 41 : { #ifdef SIGSTOP signo = SIGSTOP; #else signo = 19; #endif } break; case 42 : case 43 : { #ifdef SIGTSTP signo = SIGTSTP; #else signo = 20; #endif } break; case 44 : case 45 : { #ifdef SIGTTIN signo = SIGTTIN; #else signo = 21; #endif } break; case 46 : case 47 : { #ifdef SIGTTOU signo = SIGTTOU; #else signo = 22; #endif } break; case 48 : case 49 : { #ifdef SIGURG signo = SIGURG; #else signo = 23; #endif } break; case 50 : case 51 : { #ifdef SIGXCPU signo = SIGXCPU; #else signo = 24; #endif } break; case 52 : case 53 : { #ifdef SIGXFSZ signo = SIGXFSZ; #else signo = 25; #endif } break; case 54 : case 55 : { #ifdef SIGVTALRM signo = SIGVTALRM; #else signo = 26; #endif } break; case 56 : case 57 : { #ifdef SIGPROF signo = SIGPROF; #else signo = 27; #endif } break; case 58 : case 59 : { #ifdef SIGWINCH signo = SIGWINCH; #else signo = 28; #endif } break; case 60 : case 61 : case 62 : case 63 : { #ifdef SIGPOLL signo = SIGPOLL; #else #ifdef SIGIO signo = SIGIO #else signo = 29; #endif #endif } break; case 64 : case 65 : { #ifdef SIGPWR signo = SIGPWR; #else signo = 30; #endif } break; case 66 : case 67 : { #ifdef SIGSYS signo = SIGSYS; #else signo = 31; #endif } break; default : { $? "! illegal signal name" back = 0; /* ERROR: Illegal signal name */ log_3(31, 32, pv); } break; } $? "- set_signal_number %d", back return back; } /** Set syslog facility. @param pv Text representation of syslog facility. @return 1 on success, 0 on error. */ static int set_syslog_facility(const char *pv) { int back = 1; if (NULL != pv) { switch ( dk4str8_array_index(syslog_facility_names, pv, 0) ) { #ifdef LOG_AUTH case 0: { syslogf = LOG_AUTH; } break; #endif #ifdef LOG_AUTHPRIV case 1: { syslogf = LOG_AUTHPRIV; } break; #endif #ifdef LOG_CRON case 2: { syslogf = LOG_CRON; } break; #endif #ifdef LOG_DAEMON case 3: { syslogf = LOG_DAEMON; } break; #endif #ifdef LOG_FTP case 4: { syslogf = LOG_FTP; } break; #endif #ifdef LOG_KERN case 5: { syslogf = LOG_KERN; } break; #endif #ifdef LOG_LPR case 6: { syslogf = LOG_LPR; } break; #endif #ifdef LOG_MAIL case 7: { syslogf = LOG_MAIL; } break; #endif #ifdef LOG_NEWS case 8: { syslogf = LOG_NEWS; } break; #endif #ifdef LOG_AUTH case 9: { syslogf = LOG_AUTH; } break; #endif #ifdef LOG_SYSLOG case 10: { syslogf = LOG_SYSLOG; } break; #endif #ifdef LOG_USER case 11: { syslogf = LOG_USER; } break; #endif #ifdef LOG_UUCP case 12: { syslogf = LOG_UUCP; } break; #endif #ifdef LOG_LOCAL0 case 13: { syslogf = LOG_LOCAL0; } break; #endif #ifdef LOG_LOCAL1 case 14: { syslogf = LOG_LOCAL1; } break; #endif #ifdef LOG_LOCAL2 case 15: { syslogf = LOG_LOCAL2; } break; #endif #ifdef LOG_LOCAL3 case 16: { syslogf = LOG_LOCAL3; } break; #endif #ifdef LOG_LOCAL4 case 17: { syslogf = LOG_LOCAL4; } break; #endif #ifdef LOG_LOCAL5 case 18: { syslogf = LOG_LOCAL5; } break; #endif #ifdef LOG_LOCAL6 case 19: { syslogf = LOG_LOCAL6; } break; #endif #ifdef LOG_LOCAL7 case 20: { syslogf = LOG_LOCAL7; } break; #endif default: { back = 0; log_3(48, 49, pv); } break; } } return back; } /** Read configuration file. @return 1 on success, 0 on error. */ static int read_config_file(void) { FILE *fipo = NULL; /* Input file to process */ const char *fn = NULL; /* Input file name */ const char *ep = NULL; /* End pointer after number */ char *pk = NULL; /* Pointer to key string */ char *pv = NULL; /* Pointer to value string */ dk4_um_t lineno = (dk4_um_t)0UL; /* Current line number */ int back = 0; $? "+ read_config_file" if (0 != options[0].found) { fn = options[0].val.t; } if (NULL == fn) { fn = def_conf_name; } fipo = dk4fopen_c8(fn, "r", DK4_FOPEN_SC_IS_REGULAR, NULL); if (NULL != fipo) { back = 1; while( fgets(b1, sizeof(b1), fipo) ) { lineno++; pk = dk4str8_start(b1, NULL); if (NULL != pk) { if ('#' != *pk) { pv = dk4str8_chr(pk, '='); if (NULL != pv) { *(pv++) = '\0'; pv = dk4str8_start(pv, NULL); if (NULL != pv) { dk4str8_normalize(pk, NULL); dk4str8_delnl(pv); switch ( dk4str8_array_index(key_names, pk, 0) ) { case 0: { /* sender address */ if (0 == strlen(asender)) { dk4str8_normalize(pv, NULL); if (0 == dk4str8_cpy_s(asender,sizeof(asender),pv,NULL)) { back = 0; $? "! sender too long" /* ERROR: Sender name too long */ log_3(33, 34, pv); } } else { $? "! redefinition of sender" back = 0; /* ERROR: Redefinition of sender */ log_1(35); } } break; case 1: { /* sender port */ if (0 == set_port_number(&rp, pv, fn, lineno, 1)) { back = 0; $? "! remote port not numeric" } } break; case 2: { /* local port */ if (0 == set_port_number(&lp, pv, fn, lineno, 0)) { back = 0; $? "! local port not numeric" } } break; case 3: { /* signal */ ep = NULL; if (0 != dk4ma_input_c8_dec_int(&signo, pv, &ep, 1, NULL)) { if (1 > signo) { back = 0; $? "! illegal value for signo" /* ERROR: Signal number must be positive */ log_1(36); } } else { dk4str8_normalize(pv, NULL); if (0 == set_signal_number(pv, fn, lineno)) { back = 0; $? "! failed to set signo" } } } break; case 4: { /* command */ if (0 == strlen(cmd)) { if (0 == dk4str8_cpy_s(cmd, sizeof(cmd), pv, NULL)) { back = 0; /* ERROR: Command too long! */ log_3(37, 38, pv); } } else { $? "! command redefined" back = 0; /* ERROR: Redefinition of command */ log_1(39); } } break; case 5: { /* file */ if (0 == strlen(file_name)) { if (0 == dk4str8_cpy_s(file_name,sizeof(file_name),pv,NULL)) { back = 0; /* ERROR: File name too long! */ log_3(40, 41, pv); } } else { $? "! file name redefined" back = 0; /* ERROR: Redefinition of file name */ log_1(42); } } break; case 6: { debug = ((dk4str8_is_on(pv)) ? 1 : 0); } break; case 7: { dk4str8_normalize(pv, NULL); if (0 == set_syslog_facility(pv)) { back = 0; } } break; default : { $? "! unknown key" log_1(43); } break; } } else { $? "! missing value" back = 0; /* ERROR: Missing value */ log_1(44); } } else { $? "! missing value" back = 0; /* ERROR: Missing value */ log_1(44); } } } } fclose(fipo); if ((0 != back) && (0U == lp)) { back = 0; $? "! missing local port" /* ERROR: Local port not specified */ log_1(45); } if (0 != back) { if ((0 == signo) && (0 == strlen(cmd))) { back = 0; $? "! command or signo" /* ERROR: Signal or command required */ log_1(46); } } if (0 != back) { if (0 != strlen(file_name)) { fipo = dk4fopen_c8(file_name, "rb", DK4_FOPEN_SC_IS_REGULAR, NULL); if (NULL != fipo) { szb1 = fread(b1, 1, sizeof(b1), fipo); fclose(fipo); if (0 == szb1) { back = 0; $? "! data file contents" /* ERROR: Failed to read data from file */ log_3(16, 17, file_name); } } else { $? "! open data file" back = 0; /* ERROR: Failed to open data file */ log_3(14, 15, file_name); } } else { $? "! data file name" back = 0; /* ERROR: No data file specified */ log_1(47); } } } else { $? "! failed to open conf file" } $? "- read_config_file %d", back return back; } /** Daemon waiting for shutdown data packet. */ static void run_as_daemon(void) { pid_t cpid; $? "+ run_as_daemon" /* Preparations before we start. */ DK4_MEMRES(cmd, sizeof(cmd)); DK4_MEMRES(asender, sizeof(asender)); DK4_MEMRES(file_name, sizeof(file_name)); if (read_config_file()) { $? ". conf ok" if (0 != dk4dmt_parent_before_fork(rshdown_kw[0],pid_file_name,1,syslogf)) { cpid = fork(); if ((pid_t)0 == cpid) { /* in the child */ if (0 != dk4dmt_intermediate_before_fork(rshdown_kw[0], syslogf)) { cpid = fork(); if ((pid_t)0 == cpid) { /* in the child */ $!trace-init /tmp/rshdown.deb $? "+ daemon process" exval = EXIT_SUCCESS; if (dk4dmt_daemon_after_fork(rshdown_kw[0],pid_file_name,syslogf)) { run_daemon_with_signals(); } dk4dmt_daemon_end(rshdown_kw[0], pid_file_name, syslogf); if (0 == dk4mem_reset_secure(b1, sizeof(b1), NULL)) { exval = EXIT_FAILURE; } if (0 == dk4mem_reset_secure(b2, sizeof(b2), NULL)) { exval = EXIT_FAILURE; } $? "- daemon process %d", exval $!trace-end } else { if ((pid_t)(-1) != cpid) { /* intermediate process */ exval = EXIT_SUCCESS; if (0 == dk4mem_reset_secure(b1, sizeof(b1), NULL)) { exval = EXIT_FAILURE; } if (0 == dk4mem_reset_secure(b2, sizeof(b2), NULL)) { exval = EXIT_FAILURE; } } else { /* error */ if (0 == dk4mem_reset_secure(b1, sizeof(b1), NULL)) { exval = EXIT_FAILURE; } if (0 == dk4mem_reset_secure(b2, sizeof(b2), NULL)) { exval = EXIT_FAILURE; } /* SYSLOG ERROR: Failed to create child process */ syslog_1(22); } } } else { if (0 == dk4mem_reset_secure(b1, sizeof(b1), NULL)) { exval = EXIT_FAILURE; } if (0 == dk4mem_reset_secure(b2, sizeof(b2), NULL)) { exval = EXIT_FAILURE; } /* SYSLOG ERROR: Failed to detach */ syslog_1(21); } } else { if ((pid_t)(-1) != cpid) { /* parent */ exval = EXIT_SUCCESS; } else { /* error */ /* ERROR: Failed to create child process */ log_1(20); } } } } if (0 == dk4mem_reset_secure(b1, sizeof(b1), NULL)) { exval = EXIT_FAILURE; } if (0 == dk4mem_reset_secure(b2, sizeof(b2), NULL)) { exval = EXIT_FAILURE; } $? "- run_as_daemon" } /** Show a text paragraph on standard output. @param txt Text to show. */ static void show_text(const char * const *txt) { if (NULL != txt) { while (NULL != *txt) { fputs(*(txt++), stdout); fputc('\n', stdout); } fflush(stdout); } } /** Handle --help, --version, --license options. */ static void run_hvl(void) { if (0 != (1 & hvl)) { show_text(help_text); } if (0 != (2 & hvl)) { fputs(version_text, stdout); fputc('\n', stdout); fflush(stdout); } if (0 != (4 & hvl)) { show_text(license_text); } } /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { char *filenames[32]; size_t szfilenames = 32; /* Array size */ int res = 0; /* Operation result */ $? "+ main" /* Process command line arguments. */ res = dk4opt_process_argv( options, szoptions, argc, argv, filenames, &szfilenames, 1, 1, NULL ); if (0 == res) { $? "! option processing" goto finished; } /* Check help, version, license. */ if (0 != options[5].found) { hvl |= 1; } if (0 != options[6].found) { hvl |= 2; } if (0 != options[7].found) { hvl |= 4; } if (0 != hvl) { run_hvl(); goto finished; } /* Either send data or run as daemon to receive data. */ if (0 != options[1].found) { $? ". run sender" sender((const char * const *)filenames, szfilenames); } else { $? ". run daemon" run_as_daemon(); } finished: $? "- main %d", exval exit(exval); return exval; } #else /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { fputs("rshdown: ERROR: Build requirements violation!\n", stderr); #if DK4_CHAR_SIZE > 1 fputs("Default character size is larger than 1!\n", stderr); #endif #if !((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) fputs("Neither sigaction() nor sigset() are available!\n", stderr); #endif #if !((DK4_HAVE_SETSID) || (DK4_HAVE_SETPGRP)) fputs("Neither setsid() nor setpgrp() are available!\n", stderr); #endif fflush(stderr); exit(EXIT_FAILURE); return EXIT_FAILURE; } #endif