mirror of
https://github.com/rkd77/elinks.git
synced 2024-10-09 05:20:36 -04:00
396 lines
7.7 KiB
C
396 lines
7.7 KiB
C
/* Signals handling. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_SYS_WAIT_H
|
|
#include <sys/wait.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "elinks.h"
|
|
|
|
#include "main/main.h"
|
|
#include "main/select.h"
|
|
#include "main/timer.h"
|
|
#include "main/version.h"
|
|
#include "osdep/signals.h"
|
|
#include "terminal/kbd.h"
|
|
#include "terminal/terminal.h"
|
|
#include "util/error.h"
|
|
|
|
|
|
static void unhandle_basic_signals(struct terminal *term);
|
|
|
|
static void
|
|
sig_terminate(struct terminal *term)
|
|
{
|
|
unhandle_basic_signals(term);
|
|
program.terminate = 1;
|
|
program.retval = RET_SIGNAL;
|
|
}
|
|
|
|
#ifdef SIGHUP
|
|
static void
|
|
sig_intr(struct terminal *term)
|
|
{
|
|
unhandle_basic_signals(term);
|
|
|
|
if (!term)
|
|
program.terminate = 1;
|
|
else
|
|
register_bottom_half(destroy_terminal, term);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
sig_ctrl_c(struct terminal *term)
|
|
{
|
|
if (!is_blocked()) kbd_ctrl_c();
|
|
}
|
|
|
|
#ifdef SIGTTOU
|
|
static void
|
|
sig_ign(void *x)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
|
|
#if defined(SIGTSTP) || defined(SIGTTIN)
|
|
static void poll_fg(void *);
|
|
static struct timer *fg_poll_timer = NULL;
|
|
|
|
static void
|
|
sig_tstp(struct terminal *term)
|
|
{
|
|
#ifdef SIGSTOP
|
|
pid_t newpid;
|
|
pid_t pid = getpid();
|
|
|
|
block_itrm();
|
|
#if defined (SIGCONT) && defined(SIGTTOU)
|
|
newpid = fork();
|
|
if (!newpid) {
|
|
sleep(1);
|
|
kill(pid, SIGCONT);
|
|
/* Use _exit() rather than exit(), so that atexit
|
|
* functions are not called, and stdio output buffers
|
|
* are not flushed. Any such things must have been
|
|
* inherited from the parent process, which will take
|
|
* care of them when appropriate. */
|
|
_exit(0);
|
|
}
|
|
#endif
|
|
raise(SIGSTOP);
|
|
#endif
|
|
if (fg_poll_timer != NULL) kill_timer(&fg_poll_timer);
|
|
install_timer(&fg_poll_timer, FG_POLL_TIME, poll_fg, term);
|
|
}
|
|
|
|
static void
|
|
poll_fg(void *t_)
|
|
{
|
|
struct terminal *t = (struct terminal *)t_;
|
|
int r ;
|
|
|
|
fg_poll_timer = NULL;
|
|
r = unblock_itrm();
|
|
if (r == -1) {
|
|
install_timer(&fg_poll_timer, FG_POLL_TIME, poll_fg, t);
|
|
}
|
|
#if 0
|
|
if (r == -2) {
|
|
/* This will unblock externally spawned viewer, if it exists */
|
|
#ifdef SIGCONT
|
|
EINTRLOOP(r, kill(0, SIGCONT));
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef SIGCONT
|
|
static void
|
|
sig_cont(struct terminal *term)
|
|
{
|
|
if (!unblock_itrm()) resize_terminal();
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_BACKTRACE
|
|
static void
|
|
sig_segv(struct terminal *term)
|
|
{
|
|
/* Get some attention. */
|
|
fputs("\a", stderr); fflush(stderr); sleep(1); fputs("\a\n", stderr);
|
|
|
|
/* Rant. */
|
|
fputs( "ELinks crashed. That shouldn't happen. Please report this incident to\n"
|
|
"the developers. If you would like to help to debug the problem you just\n"
|
|
"uncovered, please keep the core you just got and send the developers\n"
|
|
"the output of 'bt' command entered inside of gdb (which you run as:\n"
|
|
"gdb elinks core). Thanks a lot for your cooperation!\n\n", stderr);
|
|
|
|
/* version information */
|
|
fputs(full_static_version, stderr);
|
|
fputs("\n\n", stderr);
|
|
|
|
/* Backtrace. */
|
|
dump_backtrace(stderr, 1);
|
|
|
|
/* TODO: Perhaps offer launching of gdb? Or trying to continue w/
|
|
* program execution? --pasky */
|
|
|
|
/* The fastest way OUT! */
|
|
abort();
|
|
}
|
|
#endif
|
|
|
|
|
|
void
|
|
handle_basic_signals(struct terminal *term)
|
|
{
|
|
#ifdef SIGHUP
|
|
install_signal_handler(SIGHUP, (void (*)(void *)) sig_intr, term, 0);
|
|
#endif
|
|
install_signal_handler(SIGINT, (void (*)(void *)) sig_ctrl_c, term, 0);
|
|
install_signal_handler(SIGTERM, (void (*)(void *)) sig_terminate, term, 0);
|
|
#ifdef SIGTSTP
|
|
install_signal_handler(SIGTSTP, (void (*)(void *)) sig_tstp, term, 0);
|
|
#endif
|
|
#ifdef SIGTTIN
|
|
install_signal_handler(SIGTTIN, (void (*)(void *)) sig_tstp, term, 0);
|
|
#endif
|
|
#ifdef SIGTTOU
|
|
install_signal_handler(SIGTTOU, (void (*)(void *)) sig_ign, term, 0);
|
|
#endif
|
|
#ifdef SIGCONT
|
|
install_signal_handler(SIGCONT, (void (*)(void *)) sig_cont, term, 0);
|
|
#endif
|
|
#ifdef CONFIG_BACKTRACE
|
|
install_signal_handler(SIGSEGV, (void (*)(void *)) sig_segv, term, 1);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
unhandle_terminal_signals(struct terminal *term)
|
|
{
|
|
#ifdef SIGHUP
|
|
install_signal_handler(SIGHUP, NULL, NULL, 0);
|
|
#endif
|
|
install_signal_handler(SIGINT, NULL, NULL, 0);
|
|
#ifdef SIGTSTP
|
|
install_signal_handler(SIGTSTP, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef SIGTTIN
|
|
install_signal_handler(SIGTTIN, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef SIGTTOU
|
|
install_signal_handler(SIGTTOU, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef SIGCONT
|
|
install_signal_handler(SIGCONT, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef CONFIG_BACKTRACE
|
|
install_signal_handler(SIGSEGV, NULL, NULL, 0);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
unhandle_basic_signals(struct terminal *term)
|
|
{
|
|
#ifdef SIGHUP
|
|
install_signal_handler(SIGHUP, NULL, NULL, 0);
|
|
#endif
|
|
install_signal_handler(SIGINT, NULL, NULL, 0);
|
|
install_signal_handler(SIGTERM, NULL, NULL, 0);
|
|
#ifdef SIGTSTP
|
|
install_signal_handler(SIGTSTP, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef SIGTTIN
|
|
install_signal_handler(SIGTTIN, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef SIGTTOU
|
|
install_signal_handler(SIGTTOU, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef SIGCONT
|
|
install_signal_handler(SIGCONT, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef CONFIG_BACKTRACE
|
|
install_signal_handler(SIGSEGV, NULL, NULL, 0);
|
|
#endif
|
|
}
|
|
|
|
struct signal_info {
|
|
void (*handler)(void *);
|
|
void *data;
|
|
int critical;
|
|
int mask;
|
|
};
|
|
|
|
static struct signal_info signal_info[NUM_SIGNALS];
|
|
volatile int critical_section = 0;
|
|
|
|
static void check_for_select_race(void);
|
|
|
|
/* TODO: In order to gain better portability, we should use signal() instead.
|
|
* Highest care should be given to careful watching of which signals are
|
|
* blocked and which aren't then, though. --pasky */
|
|
|
|
static void
|
|
got_signal(int sig)
|
|
{
|
|
struct signal_info *s;
|
|
int saved_errno = errno;
|
|
|
|
if (sig >= NUM_SIGNALS || sig < 0) {
|
|
/* Signal handler - we have no good way how to tell this the
|
|
* user. She won't care anyway, tho'. */
|
|
return;
|
|
}
|
|
|
|
s = &signal_info[sig];
|
|
|
|
if (!s->handler) return;
|
|
|
|
if (s->critical) {
|
|
s->handler(s->data);
|
|
errno = saved_errno;
|
|
return;
|
|
}
|
|
|
|
s->mask = 1;
|
|
check_for_select_race();
|
|
|
|
errno = saved_errno;
|
|
}
|
|
|
|
void
|
|
install_signal_handler(int sig, void (*fn)(void *), void *data, int critical)
|
|
{
|
|
#ifdef HAVE_SIGACTION
|
|
struct sigaction sa;
|
|
#else
|
|
void (*handler)(int) = fn ? got_signal : SIG_IGN;
|
|
#endif
|
|
|
|
/* Yes, assertm() in signal handler is totally unsafe and depends just
|
|
* on good luck. But hey, assert()ions are never triggered ;-). */
|
|
assertm(sig >= 0 && sig < NUM_SIGNALS, "bad signal number: %d", sig);
|
|
if_assert_failed return;
|
|
|
|
#ifdef HAVE_SIGACTION
|
|
/* AIX has problem with empty static initializers. */
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
if (!fn)
|
|
sa.sa_handler = SIG_IGN;
|
|
else
|
|
sa.sa_handler = got_signal;
|
|
|
|
sigfillset(&sa.sa_mask);
|
|
if (!fn) sigaction(sig, &sa, NULL);
|
|
#endif
|
|
|
|
signal_info[sig].handler = fn;
|
|
signal_info[sig].data = data;
|
|
signal_info[sig].critical = critical;
|
|
|
|
#ifdef HAVE_SIGACTION
|
|
if (fn) sigaction(sig, &sa, NULL);
|
|
#else
|
|
signal(sig, handler);
|
|
#endif
|
|
}
|
|
|
|
static volatile int pending_alarm = 0;
|
|
|
|
#ifdef SIGALRM
|
|
static void
|
|
alarm_handler(void *x)
|
|
{
|
|
pending_alarm = 0;
|
|
check_for_select_race();
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
check_for_select_race(void)
|
|
{
|
|
if (critical_section) {
|
|
#ifdef SIGALRM
|
|
install_signal_handler(SIGALRM, alarm_handler, NULL, 1);
|
|
#endif
|
|
pending_alarm = 1;
|
|
#ifdef HAVE_ALARM
|
|
alarm(1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
uninstall_alarm(void)
|
|
{
|
|
pending_alarm = 0;
|
|
#ifdef HAVE_ALARM
|
|
alarm(0);
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef SIGCHLD
|
|
static void
|
|
sig_chld(void *p)
|
|
{
|
|
#ifdef WNOHANG
|
|
while ((int) waitpid(-1, NULL, WNOHANG) > 0);
|
|
#else
|
|
wait(NULL);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void
|
|
set_sigcld(void)
|
|
{
|
|
#ifdef SIGCHLD
|
|
install_signal_handler(SIGCHLD, sig_chld, NULL, 1);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
clear_signal_mask_and_handlers(void)
|
|
{
|
|
memset(signal_info, 0, sizeof(signal_info));
|
|
}
|
|
|
|
int
|
|
check_signals(void)
|
|
{
|
|
int i, r = 0;
|
|
|
|
for (i = 0; i < NUM_SIGNALS; i++) {
|
|
struct signal_info *s = &signal_info[i];
|
|
|
|
if (!s->mask) continue;
|
|
|
|
s->mask = 0;
|
|
if (s->handler)
|
|
s->handler(s->data);
|
|
check_bottom_halves();
|
|
r = 1;
|
|
}
|
|
|
|
return r;
|
|
}
|