/* 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: */