/* signalcatcher - signal analysis partly taken from stdio-common/psiginfo.c (glibc) For the signal analysis parts (function 'signal_handler'): Copyright (C) 2009-2016 Free Software Foundation, Inc. For the rest of signalcatcher.c: Copyright (C) 2016 AVM Audiovisuelles Marketing und Computersysteme GmbH, nsc This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include struct args { int daemon; const char *log; int log_fd; }; static struct args args = { .log = "/dev/stderr", .log_fd = -1, }; static void astrappend(char **strp, const char *format, ...) __attribute__((nonnull(1))) __attribute__((format(printf, 2, 3))); static void astrappend(char **strp, const char *format, ...) { va_list ap; char *new_format, *old_string; const char *const_old_string = ""; va_start(ap, format); old_string = *strp ? : (char *)const_old_string; asprintf(&new_format, "%s%s", old_string, format); vasprintf(strp, new_format, ap); free(new_format); if (old_string != const_old_string) free(old_string); va_end(ap); } static void pr_log(const char *format, ...) { va_list ap; char *new_format; if (args.log_fd < 0) return; va_start(ap, format); asprintf(&new_format, "%s: %s\n", program_invocation_short_name, format); vdprintf(args.log_fd, new_format, ap); free(new_format); va_end(ap); } static void signal_handler(int sig, siginfo_t *pinfo, void *context __attribute__((unused))) { const char *base = NULL; const unsigned char *offarr = NULL; size_t offarr_len = 0; const char *str = NULL; char *output = NULL; if (offarr != NULL && pinfo->si_code >= 1 && pinfo->si_code <= (int)offarr_len) str = base + offarr[pinfo->si_code - 1]; else switch (pinfo->si_code) { case SI_USER: str = "Signal sent by kill()"; break; case SI_QUEUE: str = "Signal sent by sigqueue()"; break; case SI_TIMER: str = "Signal generated by the expiration of a timer"; break; case SI_ASYNCIO: str = "Signal generated by the completion of an asynchronous I/O request"; break; case SI_MESGQ: str = "Signal generated by the arrival of a message on an empty message queue"; break; #ifdef SI_TKILL case SI_TKILL: str = "Signal sent by tkill()"; break; #endif #ifdef SI_ASYNCNL case SI_ASYNCNL: str = "Signal generated by the completion of an asynchronous name lookup request"; break; #endif #ifdef SI_SIGIO case SI_SIGIO: str = "Signal generated by the completion of an I/O request"; break; #endif #ifdef SI_KERNEL case SI_KERNEL: str = "Signal sent by the kernel"; break; #endif } if (str != NULL) astrappend(&output, "%s ", str); else astrappend(&output, "%d ", pinfo->si_code); if (pinfo->si_signo == SIGILL || pinfo->si_signo == SIGFPE || pinfo->si_signo == SIGSEGV || pinfo->si_signo == SIGBUS) astrappend(&output, "[%p]", pinfo->si_addr); else if (pinfo->si_signo == SIGCHLD) astrappend(&output, "%ld %d %ld", (long int) pinfo->si_pid, pinfo->si_status, (long int) pinfo->si_uid); else if (pinfo->si_signo == SIGPOLL) astrappend(&output, "%ld", (long int) pinfo->si_band); else astrappend(&output, "%ld %ld", (long int) pinfo->si_pid, (long int) pinfo->si_uid); pr_log("received signal %d (%s) (%s)", sig, strsignal(sig), output); free(output); } static int register_signal(int sig) { int r; struct sigaction sa = { .sa_sigaction = signal_handler, .sa_flags = SA_SIGINFO, }; if ((r = sigaction(sig, &sa, NULL))) pr_log("%s: sigaction/%s: %m", __func__, strsignal(sig)); else pr_log("%s: registered signal handler for signal %s (%d)", __func__, strsignal(sig), sig); return r; } static void register_all_signals(void) { int sig; for (sig = 1; sig <= SIGRTMAX; sig++) { pr_log("%s: registering signal %s (%d)", __func__, strsignal(sig), sig); register_signal(sig); } } static int handle_args(int argc, char **argv) { int opt; struct option longopts[] = { { .name = "daemon", .val = 'd', }, { .name = "log", .has_arg = required_argument, .val = 'l', }, { NULL, }, }; while ((opt = getopt_long(argc, argv, "dl:", longopts, NULL)) != -1) { switch(opt) { case 'd': args.daemon = 1; break; case 'l': args.log = optarg; break; default: fprintf(stderr, "Usage: %s [--daemon] [--log=FILE] [SIGNAL]...\n", program_invocation_short_name); exit(EXIT_FAILURE); } } return optind; } static int do_fork(void) { pid_t pid; pid = fork(); switch(pid) { case -1: fprintf(stderr, "%s: fork: %m\n", program_invocation_short_name); return errno; case 0: return 0; default: _exit(0); } } static int daemonize(void) { int r; if ((r = do_fork())) return r; if (setsid() < 0) return fprintf(stderr, "%s: setsid: %m\n", program_invocation_short_name), errno; return do_fork(); } int main(int argc, char **argv) { int optind = argc; if (argc > 1) optind = handle_args(argc, argv); if (optind < argc) /* register_signals_explicitly(argv+optind); */ pr_log("unable to register signal explicitly"); else register_all_signals(); if (args.log) { args.log_fd = open(args.log, O_WRONLY|O_APPEND|O_CREAT, 0644); if (args.log_fd < 0) return fprintf(stderr, "%s: open: %s: %m\n", program_invocation_short_name, args.log), EXIT_FAILURE; pr_log("Opened '%s' for logging", args.log); } if (args.daemon) { pr_log("Attempting to daemonize"); if (daemonize()) return fprintf(stderr, "%s: unable to daemonize: %m\n", program_invocation_short_name), EXIT_FAILURE; pr_log("Daemonized."); } pr_log("Endlessly sleeping... (pid: %d)", getpid()); while (1) sleep(3600); } /* vim: set cinoptions=\:0l1(0: */