/*
* signals.c:
* Signal handlers for tpop3d.
*
* Copyright (c) 2001 Chris Lightfoot. All rights reserved.
*
*/
static const char rcsid[] = "$Id: signals.c,v 1.23 2003/07/18 08:26:00 chris Exp $";
#ifdef HAVE_CONFIG_H
#include "configuration.h"
#endif /* HAVE_CONFIG_H */
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <sys/wait.h>
#include "connection.h"
#include "pidfile.h"
#include "signals.h"
#include "util.h"
#ifdef APPALLING_BACKTRACE_HACK
#include <execinfo.h>
/* appalling_backtrace_hack:
* Attempt to read a backtrace of the current stack; can be called from a
* signal handler if the program dies unexpectedly. */
#define BT_LEVELS 16
void appalling_backtrace_hack() {
void *func_addr[BT_LEVELS];
int i, n;
n = backtrace(func_addr, BT_LEVELS);
log_print(LOG_ERR, _("appalling_backtrace_hack: stack trace of program begins"));
for (i = 0; i < n; ++i)
log_print(LOG_ERR, "appalling_backtrace_hack: [%d]: %p", i, func_addr[i]);
log_print(LOG_ERR, _("appalling_backtrace_hack: stack trace of program ends"));
log_print(LOG_ERR, _("appalling_backtrace_hack: use addr2line(1) to resolve the addresses"));
}
#endif /* APPALLING_BACKTRACE_HACK */
/* set_signals:
* Set the relevant signals to be ignored/handled. */
void set_signals() {
int ignore_signals[] = {SIGPIPE, SIGHUP, SIGALRM, SIGUSR1, SIGUSR2, SIGFPE,
#ifdef SIGIO
SIGIO,
#endif
#ifdef SIGVTALRM
SIGVTALRM,
#endif
#ifdef SIGLOST
SIGLOST,
#endif
#ifdef SIGPWR
SIGPWR,
#endif
0};
int terminate_signals[] = {SIGINT, SIGTERM, 0};
int restart_signals[] = {SIGHUP, 0};
int die_signals[] = {SIGQUIT, SIGABRT, SIGSEGV, SIGBUS, SIGILL, 0};
int *i;
struct sigaction sa = {0};
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
for (i = ignore_signals; *i; ++i)
xsignal(*i, SIG_IGN);
for (i = terminate_signals; *i; ++i)
xsignal(*i, terminate_signal_handler);
for (i = restart_signals; *i; ++i)
xsignal(*i, restart_signal_handler);
for (i = die_signals; *i; ++i)
xsignal(*i, die_signal_handler);
/* SIGCHLD is special. */
sa.sa_handler = child_signal_handler;
sa.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &sa, NULL);
}
extern sig_atomic_t foad, restart; /* in netloop.c */
/* terminate_signal_handler:
* Signal handler to handle orderly termination of the program. */
void terminate_signal_handler(const int i) {
foad = i;
}
/* die_signal_handler:
* Signal handler to log a message and quit.
*
* XXX This is bad, because we call out to functions which may use malloc or
* file I/O or anything else. However, we quit immediately afterwards, so it's
* probably OK. Alternatively we would have to siglongjmp out, but that would
* be undefined behaviour too. */
extern connection this_child_connection; /* in main.c */
extern char *pidfile; /* in main.c */
extern int post_fork; /* in main.c */
void die_signal_handler(const int i) {
struct sigaction sa = {0};
/* log_print(LOG_ERR, "quit: %s", sys_siglist[i]); */
log_print(LOG_ERR, _("quit: signal %d post_fork = %d"), i, post_fork); /* Some systems do not have sys_siglist. */
#ifdef APPALLING_BACKTRACE_HACK
appalling_backtrace_hack();
#endif /* APPALLING_BACKTRACE_HACK */
if (this_child_connection) connection_delete(this_child_connection);
if (!post_fork && pidfile)
remove_pid_file(pidfile);
sa.sa_handler = SIG_DFL;
sigaction(i, &sa, NULL);
raise(i);
}
/* child_signal_handler:
* Signal handler to deal with SIGCHLD. */
extern int num_running_children; /* in main.c */
#ifdef AUTH_OTHER
extern pid_t auth_other_child_pid, auth_other_childdied; /* in auth_other.c */
extern int auth_other_childwr, auth_other_childrd, auth_other_childstatus;
#endif /* AUTH_OTHER */
/* Save information about any child which dies with a signal. */
pid_t child_died;
int child_died_signal;
void child_signal_handler(const int i) {
pid_t pid;
int e, status;
/* Save errno. */
e = errno;
while (1) {
pid = waitpid(-1, &status, WNOHANG);
if (pid > 0) {
#ifdef REALLY_UGLY_PAM_HACK
extern auth_pam_child_pid; /* in auth_pam.c */
if (pid == auth_pam_child_pid)
; /* Do nothing; in principle we should check to see if it crashed. */
else
#endif /* REALLY_UGLY_PAM_HACK */
#ifdef AUTH_OTHER
if (pid == auth_other_child_pid) {
auth_other_child_pid = 0;
auth_other_childdied = pid;
auth_other_childstatus = status;
close(auth_other_childwr);
close(auth_other_childrd);
} else
#endif /* AUTH_OTHER */
{
--num_running_children;
/* If the child process was killed by a signal, save its PID
* so that the main daemon can report it. Note that we dont't
* cope with the situation of several children dying nearly
* simultaneously, but this is a `shouldn't happen'
* anyway.... */
if (WIFSIGNALED(status)) {
child_died = pid;
child_died_signal = WTERMSIG(status);
}
}
} else if (pid == 0 || (pid == -1 && errno != EINTR)) {
errno = e;
return;
}
}
}
/* restart_signal_handler:
* Signal handler to restart the server on receiving a SIGHUP. */
void restart_signal_handler(const int i) {
if (!post_fork) {
foad = i;
restart = 1;
}
}
syntax highlighted by Code2HTML, v. 0.9.1