summaryrefslogtreecommitdiff
path: root/support/dktools/rshdown.ctr
diff options
context:
space:
mode:
Diffstat (limited to 'support/dktools/rshdown.ctr')
-rw-r--r--support/dktools/rshdown.ctr2379
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
+
+