diff options
Diffstat (limited to 'support/dktools/rshdown.ctr')
-rw-r--r-- | support/dktools/rshdown.ctr | 2379 |
1 files changed, 2379 insertions, 0 deletions
diff --git a/support/dktools/rshdown.ctr b/support/dktools/rshdown.ctr new file mode 100644 index 0000000000..7e7edc2b51 --- /dev/null +++ b/support/dktools/rshdown.ctr @@ -0,0 +1,2379 @@ +%% 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 <stdio.h> + +#if DK4_HAVE_UNISTD_H +#ifndef UNISTD_H_INCLUDED +#include <unistd.h> +#define UNISTD_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_STDLIB_H +#ifndef STDLIB_H_INCLUDED +#include <stdlib.h> +#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 <math.h> +#define MATH_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_IO_H +#ifndef IO_H_INCLUDED +#include <io.h> +#define IO_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_FCNTL_H +#ifndef FCNTL_H_INCLUDED +#include <fcntl.h> +#define FCNTL_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_STRING_H +#ifndef STRING_H_INCLUDED +#include <string.h> +#define STRING_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_SYS_TYPES_H +#ifndef SYS_TYPES_H_INCLUDED +#include <sys/types.h> +#define SYS_TYPES_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_SIGNAL_H +#ifndef SIGNAL_H_INCLUDED +#include <signal.h> +#define SIGNAL_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_SYSLOG_H +#ifndef SYSLOG_H_INCLUDED +#include <syslog.h> +#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 + + |