mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
701 lines
12 KiB
C++
701 lines
12 KiB
C++
/** \file signal.c
|
|
|
|
The library for various signal related issues
|
|
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef HAVE_SIGINFO_H
|
|
#include <siginfo.h>
|
|
#endif
|
|
|
|
#include "common.h"
|
|
#include "fallback.h"
|
|
#include "util.h"
|
|
|
|
#include "wutil.h"
|
|
#include "signal.h"
|
|
#include "event.h"
|
|
#include "reader.h"
|
|
#include "proc.h"
|
|
|
|
|
|
/**
|
|
Struct describing an entry for the lookup table used to convert
|
|
between signal names and signal ids, etc.
|
|
*/
|
|
struct lookup_entry
|
|
{
|
|
/**
|
|
Signal id
|
|
*/
|
|
int signal;
|
|
/**
|
|
Signal name
|
|
*/
|
|
const wchar_t *name;
|
|
/**
|
|
Signal description
|
|
*/
|
|
const wchar_t *desc;
|
|
};
|
|
|
|
/**
|
|
The number of signal blocks in place. Increased by signal_block, decreased by signal_unblock.
|
|
*/
|
|
static int block_count=0;
|
|
|
|
|
|
/**
|
|
Lookup table used to convert between signal names and signal ids,
|
|
etc.
|
|
*/
|
|
static const struct lookup_entry lookup[] =
|
|
{
|
|
#ifdef SIGHUP
|
|
{
|
|
SIGHUP,
|
|
L"SIGHUP",
|
|
N_(L"Terminal hung up")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGINT
|
|
{
|
|
SIGINT,
|
|
L"SIGINT",
|
|
N_(L"Quit request from job control (^C)")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGQUIT
|
|
{
|
|
SIGQUIT,
|
|
L"SIGQUIT",
|
|
N_(L"Quit request from job control with core dump (^\\)")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGILL
|
|
{
|
|
SIGILL,
|
|
L"SIGILL",
|
|
N_(L"Illegal instruction")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGTRAP
|
|
{
|
|
SIGTRAP,
|
|
L"SIGTRAP",
|
|
N_(L"Trace or breakpoint trap")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGABRT
|
|
{
|
|
SIGABRT,
|
|
L"SIGABRT",
|
|
N_(L"Abort")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGBUS
|
|
{
|
|
SIGBUS,
|
|
L"SIGBUS",
|
|
N_(L"Misaligned address error")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGFPE
|
|
{
|
|
SIGFPE,
|
|
L"SIGFPE",
|
|
N_(L"Floating point exception")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGKILL
|
|
{
|
|
SIGKILL,
|
|
L"SIGKILL",
|
|
N_(L"Forced quit")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGUSR1
|
|
{
|
|
SIGUSR1,
|
|
L"SIGUSR1",
|
|
N_(L"User defined signal 1")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGUSR2
|
|
{
|
|
SIGUSR2, L"SIGUSR2",
|
|
N_(L"User defined signal 2")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGSEGV
|
|
{
|
|
SIGSEGV,
|
|
L"SIGSEGV",
|
|
N_(L"Address boundary error")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGPIPE
|
|
{
|
|
SIGPIPE,
|
|
L"SIGPIPE",
|
|
N_(L"Broken pipe")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGALRM
|
|
{
|
|
SIGALRM,
|
|
L"SIGALRM",
|
|
N_(L"Timer expired")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGTERM
|
|
{
|
|
SIGTERM,
|
|
L"SIGTERM",
|
|
N_(L"Polite quit request")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGCHLD
|
|
{
|
|
SIGCHLD,
|
|
L"SIGCHLD",
|
|
N_(L"Child process status changed")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGCONT
|
|
{
|
|
SIGCONT,
|
|
L"SIGCONT",
|
|
N_(L"Continue previously stopped process")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGSTOP
|
|
{
|
|
SIGSTOP,
|
|
L"SIGSTOP",
|
|
N_(L"Forced stop")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGTSTP
|
|
{
|
|
SIGTSTP,
|
|
L"SIGTSTP",
|
|
N_(L"Stop request from job control (^Z)")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGTTIN
|
|
{
|
|
SIGTTIN,
|
|
L"SIGTTIN",
|
|
N_(L"Stop from terminal input")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGTTOU
|
|
{
|
|
SIGTTOU,
|
|
L"SIGTTOU",
|
|
N_(L"Stop from terminal output")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGURG
|
|
{
|
|
SIGURG,
|
|
L"SIGURG",
|
|
N_(L"Urgent socket condition")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGXCPU
|
|
{
|
|
SIGXCPU,
|
|
L"SIGXCPU",
|
|
N_(L"CPU time limit exceeded")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGXFSZ
|
|
{
|
|
SIGXFSZ,
|
|
L"SIGXFSZ",
|
|
N_(L"File size limit exceeded")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGVTALRM
|
|
{
|
|
SIGVTALRM,
|
|
L"SIGVTALRM",
|
|
N_(L"Virtual timer expired")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGPROF
|
|
{
|
|
SIGPROF,
|
|
L"SIGPROF",
|
|
N_(L"Profiling timer expired")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGWINCH
|
|
{
|
|
SIGWINCH,
|
|
L"SIGWINCH",
|
|
N_(L"Window size change")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGWIND
|
|
{
|
|
SIGWIND,
|
|
L"SIGWIND",
|
|
N_(L"Window size change")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGIO
|
|
{
|
|
SIGIO,
|
|
L"SIGIO",
|
|
N_(L"I/O on asynchronous file descriptor is possible")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGPWR
|
|
{
|
|
SIGPWR,
|
|
L"SIGPWR",
|
|
N_(L"Power failure")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGSYS
|
|
{
|
|
SIGSYS,
|
|
L"SIGSYS",
|
|
N_(L"Bad system call")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGINFO
|
|
{
|
|
SIGINFO,
|
|
L"SIGINFO",
|
|
N_(L"Information request")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGSTKFLT
|
|
{
|
|
SIGSTKFLT,
|
|
L"SISTKFLT",
|
|
N_(L"Stack fault")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGEMT
|
|
{
|
|
SIGEMT,
|
|
L"SIGEMT",
|
|
N_(L"Emulator trap")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGIOT
|
|
{
|
|
SIGIOT,
|
|
L"SIGIOT",
|
|
N_(L"Abort (Alias for SIGABRT)")
|
|
}
|
|
,
|
|
#endif
|
|
#ifdef SIGUNUSED
|
|
{
|
|
SIGUNUSED,
|
|
L"SIGUNUSED",
|
|
N_(L"Unused signal")
|
|
}
|
|
,
|
|
#endif
|
|
{
|
|
0,
|
|
0,
|
|
0
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/**
|
|
Test if \c name is a string describing the signal named \c canonical.
|
|
*/
|
|
static int match_signal_name(const wchar_t *canonical,
|
|
const wchar_t *name)
|
|
{
|
|
if (wcsncasecmp(name, L"sig", 3)==0)
|
|
name +=3;
|
|
|
|
return wcscasecmp(canonical+3,name) == 0;
|
|
}
|
|
|
|
|
|
int wcs2sig(const wchar_t *str)
|
|
{
|
|
int i;
|
|
wchar_t *end=0;
|
|
|
|
for (i=0; lookup[i].desc ; i++)
|
|
{
|
|
if (match_signal_name(lookup[i].name, str))
|
|
{
|
|
return lookup[i].signal;
|
|
}
|
|
}
|
|
errno=0;
|
|
int res = fish_wcstoi(str, &end, 10);
|
|
if (!errno && res>=0 && !*end)
|
|
return res;
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
const wchar_t *sig2wcs(int sig)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; lookup[i].desc ; i++)
|
|
{
|
|
if (lookup[i].signal == sig)
|
|
{
|
|
return lookup[i].name;
|
|
}
|
|
}
|
|
|
|
return _(L"Unknown");
|
|
}
|
|
|
|
const wchar_t *signal_get_desc(int sig)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; lookup[i].desc ; i++)
|
|
{
|
|
if (lookup[i].signal == sig)
|
|
{
|
|
return _(lookup[i].desc);
|
|
}
|
|
}
|
|
|
|
return _(L"Unknown");
|
|
}
|
|
|
|
/**
|
|
Standard signal handler
|
|
*/
|
|
static void default_handler(int signal, siginfo_t *info, void *context)
|
|
{
|
|
if (event_is_signal_observed(signal))
|
|
{
|
|
event_fire_signal(signal);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Respond to a winch signal by checking the terminal size
|
|
*/
|
|
static void handle_winch(int sig, siginfo_t *info, void *context)
|
|
{
|
|
common_handle_winch(sig);
|
|
default_handler(sig, 0, 0);
|
|
}
|
|
|
|
/**
|
|
Respond to a hup signal by exiting, unless it is caught by a
|
|
shellscript function, in which case we do nothing.
|
|
*/
|
|
static void handle_hup(int sig, siginfo_t *info, void *context)
|
|
{
|
|
if (event_is_signal_observed(SIGHUP))
|
|
{
|
|
default_handler(sig, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
reader_exit(1, 1);
|
|
}
|
|
}
|
|
|
|
/** Handle sigterm. The only thing we do is restore the front process ID, then die. */
|
|
static void handle_term(int sig, siginfo_t *info, void *context)
|
|
{
|
|
restore_term_foreground_process_group();
|
|
signal(SIGTERM, SIG_DFL);
|
|
raise(SIGTERM);
|
|
}
|
|
|
|
/**
|
|
Interactive mode ^C handler. Respond to int signal by setting
|
|
interrupted-flag and stopping all loops and conditionals.
|
|
*/
|
|
static void handle_int(int sig, siginfo_t *info, void *context)
|
|
{
|
|
reader_handle_int(sig);
|
|
default_handler(sig, info, context);
|
|
}
|
|
|
|
/**
|
|
sigchld handler. Does notification and calls the handler in proc.c
|
|
*/
|
|
static void handle_chld(int sig, siginfo_t *info, void *context)
|
|
{
|
|
job_handle_signal(sig, info, context);
|
|
default_handler(sig, info, context);
|
|
}
|
|
|
|
void signal_reset_handlers()
|
|
{
|
|
int i;
|
|
|
|
struct sigaction act;
|
|
sigemptyset(& act.sa_mask);
|
|
act.sa_flags=0;
|
|
act.sa_handler=SIG_DFL;
|
|
|
|
for (i=0; lookup[i].desc ; i++)
|
|
{
|
|
sigaction(lookup[i].signal, &act, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Sets appropriate signal handlers.
|
|
*/
|
|
void signal_set_handlers()
|
|
{
|
|
struct sigaction act;
|
|
|
|
if (get_is_interactive() == -1)
|
|
return;
|
|
|
|
sigemptyset(& act.sa_mask);
|
|
act.sa_flags=SA_SIGINFO;
|
|
act.sa_sigaction = &default_handler;
|
|
|
|
/*
|
|
First reset everything to a use default_handler, a function
|
|
whose sole action is to fire of an event
|
|
*/
|
|
sigaction(SIGINT, &act, 0);
|
|
sigaction(SIGQUIT, &act, 0);
|
|
sigaction(SIGTSTP, &act, 0);
|
|
sigaction(SIGTTIN, &act, 0);
|
|
sigaction(SIGTTOU, &act, 0);
|
|
sigaction(SIGCHLD, &act, 0);
|
|
|
|
/*
|
|
Ignore sigpipe, it is generated if fishd dies, but we can
|
|
recover.
|
|
*/
|
|
sigaction(SIGPIPE, &act, 0);
|
|
|
|
if (get_is_interactive())
|
|
{
|
|
/*
|
|
Interactive mode. Ignore interactive signals. We are a
|
|
shell, we know whats best for the user. ;-)
|
|
*/
|
|
|
|
act.sa_handler=SIG_IGN;
|
|
|
|
sigaction(SIGINT, &act, 0);
|
|
sigaction(SIGQUIT, &act, 0);
|
|
sigaction(SIGTSTP, &act, 0);
|
|
sigaction(SIGTTIN, &act, 0);
|
|
sigaction(SIGTTOU, &act, 0);
|
|
|
|
act.sa_sigaction = &handle_int;
|
|
act.sa_flags = SA_SIGINFO;
|
|
if (sigaction(SIGINT, &act, 0))
|
|
{
|
|
wperror(L"sigaction");
|
|
FATAL_EXIT();
|
|
}
|
|
|
|
act.sa_sigaction = &handle_chld;
|
|
act.sa_flags = SA_SIGINFO;
|
|
if (sigaction(SIGCHLD, &act, 0))
|
|
{
|
|
wperror(L"sigaction");
|
|
FATAL_EXIT();
|
|
}
|
|
|
|
#ifdef SIGWINCH
|
|
act.sa_flags = SA_SIGINFO;
|
|
act.sa_sigaction= &handle_winch;
|
|
if (sigaction(SIGWINCH, &act, 0))
|
|
{
|
|
wperror(L"sigaction");
|
|
FATAL_EXIT();
|
|
}
|
|
#endif
|
|
|
|
act.sa_flags = SA_SIGINFO;
|
|
act.sa_sigaction= &handle_hup;
|
|
if (sigaction(SIGHUP, &act, 0))
|
|
{
|
|
wperror(L"sigaction");
|
|
FATAL_EXIT();
|
|
}
|
|
|
|
// SIGTERM restores the terminal controlling process before dying
|
|
act.sa_flags = SA_SIGINFO;
|
|
act.sa_sigaction= &handle_term;
|
|
if (sigaction(SIGTERM, &act, 0))
|
|
{
|
|
wperror(L"sigaction");
|
|
FATAL_EXIT();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Non-interactive. Ignore interrupt, check exit status of
|
|
processes to determine result instead.
|
|
*/
|
|
act.sa_handler=SIG_IGN;
|
|
|
|
sigaction(SIGINT, &act, 0);
|
|
sigaction(SIGQUIT, &act, 0);
|
|
|
|
act.sa_handler=SIG_DFL;
|
|
|
|
act.sa_sigaction = &handle_chld;
|
|
act.sa_flags = SA_SIGINFO;
|
|
if (sigaction(SIGCHLD, &act, 0))
|
|
{
|
|
wperror(L"sigaction");
|
|
exit_without_destructors(1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void signal_handle(int sig, int do_handle)
|
|
{
|
|
struct sigaction act;
|
|
|
|
/*
|
|
These should always be handled
|
|
*/
|
|
if ((sig == SIGINT) ||
|
|
(sig == SIGQUIT) ||
|
|
(sig == SIGTSTP) ||
|
|
(sig == SIGTTIN) ||
|
|
(sig == SIGTTOU) ||
|
|
(sig == SIGCHLD))
|
|
return;
|
|
|
|
sigemptyset(&act.sa_mask);
|
|
if (do_handle)
|
|
{
|
|
act.sa_flags = SA_SIGINFO;
|
|
act.sa_sigaction = &default_handler;
|
|
}
|
|
else
|
|
{
|
|
act.sa_flags = 0;
|
|
act.sa_handler = SIG_DFL;
|
|
}
|
|
|
|
sigaction(sig, &act, 0);
|
|
}
|
|
|
|
void get_signals_with_handlers(sigset_t *set)
|
|
{
|
|
sigemptyset(set);
|
|
for (int i=0; lookup[i].desc ; i++)
|
|
{
|
|
struct sigaction act = {};
|
|
sigaction(lookup[i].signal, NULL, &act);
|
|
if (act.sa_handler != SIG_DFL)
|
|
sigaddset(set, lookup[i].signal);
|
|
}
|
|
}
|
|
|
|
void signal_block()
|
|
{
|
|
ASSERT_IS_MAIN_THREAD();
|
|
sigset_t chldset;
|
|
|
|
if (!block_count)
|
|
{
|
|
sigfillset(&chldset);
|
|
VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, NULL));
|
|
}
|
|
|
|
block_count++;
|
|
// debug( 0, L"signal block level increased to %d", block_count );
|
|
}
|
|
|
|
void signal_unblock()
|
|
{
|
|
ASSERT_IS_MAIN_THREAD();
|
|
sigset_t chldset;
|
|
|
|
block_count--;
|
|
|
|
if (block_count < 0)
|
|
{
|
|
debug(0, _(L"Signal block mismatch"));
|
|
bugreport();
|
|
FATAL_EXIT();
|
|
}
|
|
|
|
if (!block_count)
|
|
{
|
|
sigfillset(&chldset);
|
|
VOMIT_ON_FAILURE(pthread_sigmask(SIG_UNBLOCK, &chldset, 0));
|
|
}
|
|
// debug( 0, L"signal block level decreased to %d", block_count );
|
|
}
|
|
|
|
bool signal_is_blocked()
|
|
{
|
|
return !!block_count;
|
|
}
|
|
|