516 lines
12 KiB
C
516 lines
12 KiB
C
/* sig.c - interface for shell signal handlers and signal initialization. */
|
|
|
|
/* Copyright (C) 1994 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Bash, the Bourne Again SHell.
|
|
|
|
Bash is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 2, or (at your option) any later
|
|
version.
|
|
|
|
Bash 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 General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with Bash; see the file COPYING. If not, write to the Free Software
|
|
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
|
|
|
|
#include "config.h"
|
|
|
|
#include "bashtypes.h"
|
|
|
|
#if defined (HAVE_UNISTD_H)
|
|
# ifdef _MINIX
|
|
# include <sys/types.h>
|
|
# endif
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
|
|
#include "shell.h"
|
|
#if defined (JOB_CONTROL)
|
|
#include "jobs.h"
|
|
#endif /* JOB_CONTROL */
|
|
#include "siglist.h"
|
|
#include "sig.h"
|
|
#include "trap.h"
|
|
|
|
#include "builtins/common.h"
|
|
|
|
#if defined (READLINE)
|
|
# include "bashline.h"
|
|
#endif
|
|
|
|
#if defined (HISTORY)
|
|
# include "bashhist.h"
|
|
#endif
|
|
|
|
extern int last_command_exit_value;
|
|
extern int return_catch_flag;
|
|
extern int loop_level, continuing, breaking;
|
|
extern int parse_and_execute_level, shell_initialized;
|
|
extern int interactive, interactive_shell, login_shell, startup_state;
|
|
|
|
/* Non-zero after SIGINT. */
|
|
int interrupt_state;
|
|
|
|
/* The environment at the top-level R-E loop. We use this in
|
|
the case of error return. */
|
|
procenv_t top_level;
|
|
|
|
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
|
/* The signal masks that this shell runs with. */
|
|
sigset_t top_level_mask;
|
|
#endif /* JOB_CONTROL */
|
|
|
|
/* When non-zero, we throw_to_top_level (). */
|
|
int interrupt_immediately = 0;
|
|
|
|
static void initialize_shell_signals ();
|
|
|
|
void
|
|
initialize_signals ()
|
|
{
|
|
initialize_shell_signals ();
|
|
initialize_job_signals ();
|
|
#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL)
|
|
initialize_siglist ();
|
|
#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */
|
|
}
|
|
|
|
void
|
|
reinitialize_signals ()
|
|
{
|
|
initialize_shell_signals (1);
|
|
initialize_job_signals ();
|
|
}
|
|
|
|
/* A structure describing a signal that terminates the shell if not
|
|
caught. The orig_handler member is present so children can reset
|
|
these signals back to their original handlers. */
|
|
struct termsig {
|
|
int signum;
|
|
SigHandler *orig_handler;
|
|
};
|
|
|
|
#define NULL_HANDLER (SigHandler *)SIG_DFL
|
|
|
|
/* The list of signals that would terminate the shell if not caught.
|
|
We catch them, but just so that we can write the history file,
|
|
and so forth. */
|
|
static struct termsig terminating_signals[] = {
|
|
#ifdef SIGHUP
|
|
SIGHUP, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGINT
|
|
SIGINT, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGILL
|
|
SIGILL, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGTRAP
|
|
SIGTRAP, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGIOT
|
|
SIGIOT, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGDANGER
|
|
SIGDANGER, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGEMT
|
|
SIGEMT, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGFPE
|
|
SIGFPE, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGBUS
|
|
SIGBUS, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGSEGV
|
|
SIGSEGV, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGSYS
|
|
SIGSYS, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGPIPE
|
|
SIGPIPE, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGALRM
|
|
SIGALRM, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGTERM
|
|
SIGTERM, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGXCPU
|
|
SIGXCPU, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGXFSZ
|
|
SIGXFSZ, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGVTALRM
|
|
SIGVTALRM, NULL_HANDLER,
|
|
#endif
|
|
|
|
#if 0
|
|
#ifdef SIGPROF
|
|
SIGPROF, NULL_HANDLER,
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef SIGLOST
|
|
SIGLOST, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGUSR1
|
|
SIGUSR1, NULL_HANDLER,
|
|
#endif
|
|
|
|
#ifdef SIGUSR2
|
|
SIGUSR2, NULL_HANDLER,
|
|
#endif
|
|
};
|
|
|
|
#define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig))
|
|
|
|
#define XSIG(x) (terminating_signals[x].signum)
|
|
#define XHANDLER(x) (terminating_signals[x].orig_handler)
|
|
|
|
static int termsigs_initialized = 0;
|
|
|
|
/* Initialize signals that will terminate the shell to do some
|
|
unwind protection. For non-interactive shells, we only call
|
|
this when a trap is defined for EXIT (0). */
|
|
void
|
|
initialize_terminating_signals ()
|
|
{
|
|
register int i;
|
|
#if defined (HAVE_POSIX_SIGNALS)
|
|
struct sigaction act, oact;
|
|
#endif
|
|
|
|
if (termsigs_initialized)
|
|
return;
|
|
|
|
/* The following code is to avoid an expensive call to
|
|
set_signal_handler () for each terminating_signals. Fortunately,
|
|
this is possible in Posix. Unfortunately, we have to call signal ()
|
|
on non-Posix systems for each signal in terminating_signals. */
|
|
#if defined (HAVE_POSIX_SIGNALS)
|
|
act.sa_handler = termination_unwind_protect;
|
|
act.sa_flags = 0;
|
|
sigemptyset (&act.sa_mask);
|
|
sigemptyset (&oact.sa_mask);
|
|
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
|
sigaddset (&act.sa_mask, XSIG (i));
|
|
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
|
{
|
|
sigaction (XSIG (i), &act, &oact);
|
|
XHANDLER(i) = oact.sa_handler;
|
|
/* Don't do anything with signals that are ignored at shell entry
|
|
if the shell is not interactive. */
|
|
if (!interactive_shell && XHANDLER (i) == SIG_IGN)
|
|
{
|
|
sigaction (XSIG (i), &oact, &act);
|
|
set_signal_ignored (XSIG (i));
|
|
}
|
|
#if defined (SIGPROF) && !defined (_MINIX)
|
|
if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
|
|
sigaction (XSIG (i), &oact, (struct sigaction *)NULL);
|
|
#endif /* SIGPROF && !_MINIX */
|
|
}
|
|
|
|
#else /* !HAVE_POSIX_SIGNALS */
|
|
|
|
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
|
{
|
|
XHANDLER(i) = signal (XSIG (i), termination_unwind_protect);
|
|
/* Don't do anything with signals that are ignored at shell entry
|
|
if the shell is not interactive. */
|
|
if (!interactive_shell && XHANDLER (i) == SIG_IGN)
|
|
{
|
|
signal (XSIG (i), SIG_IGN);
|
|
set_signal_ignored (XSIG (i));
|
|
}
|
|
#ifdef SIGPROF
|
|
if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
|
|
signal (XSIG (i), XHANDLER (i));
|
|
#endif
|
|
}
|
|
|
|
#endif /* !HAVE_POSIX_SIGNALS */
|
|
|
|
termsigs_initialized = 1;
|
|
}
|
|
|
|
static void
|
|
initialize_shell_signals ()
|
|
{
|
|
if (interactive)
|
|
initialize_terminating_signals ();
|
|
|
|
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
|
/* All shells use the signal mask they inherit, and pass it along
|
|
to child processes. Children will never block SIGCHLD, though. */
|
|
sigemptyset (&top_level_mask);
|
|
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask);
|
|
# if defined (SIGCHLD)
|
|
sigdelset (&top_level_mask, SIGCHLD);
|
|
# endif
|
|
#endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */
|
|
|
|
/* And, some signals that are specifically ignored by the shell. */
|
|
set_signal_handler (SIGQUIT, SIG_IGN);
|
|
|
|
if (interactive)
|
|
{
|
|
set_signal_handler (SIGINT, sigint_sighandler);
|
|
set_signal_handler (SIGTERM, SIG_IGN);
|
|
}
|
|
}
|
|
|
|
void
|
|
reset_terminating_signals ()
|
|
{
|
|
register int i;
|
|
#if defined (HAVE_POSIX_SIGNALS)
|
|
struct sigaction act;
|
|
#endif
|
|
|
|
if (termsigs_initialized == 0)
|
|
return;
|
|
|
|
#if defined (HAVE_POSIX_SIGNALS)
|
|
act.sa_flags = 0;
|
|
sigemptyset (&act.sa_mask);
|
|
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
|
{
|
|
/* Skip a signal if it's trapped or handled specially, because the
|
|
trap code will restore the correct value. */
|
|
if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
|
|
continue;
|
|
|
|
act.sa_handler = XHANDLER (i);
|
|
sigaction (XSIG (i), &act, (struct sigaction *) NULL);
|
|
}
|
|
#else /* !HAVE_POSIX_SIGNALS */
|
|
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
|
{
|
|
if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
|
|
continue;
|
|
|
|
signal (XSIG (i), XHANDLER (i));
|
|
}
|
|
#endif /* !HAVE_POSIX_SIGNALS */
|
|
}
|
|
#undef XSIG
|
|
#undef XHANDLER
|
|
|
|
/* What to do when we've been interrupted, and it is safe to handle it. */
|
|
void
|
|
throw_to_top_level ()
|
|
{
|
|
int print_newline = 0;
|
|
|
|
if (interrupt_state)
|
|
{
|
|
print_newline = 1;
|
|
DELINTERRUPT;
|
|
}
|
|
|
|
if (interrupt_state)
|
|
return;
|
|
|
|
last_command_exit_value |= 128;
|
|
|
|
/* Run any traps set on SIGINT. */
|
|
run_interrupt_trap ();
|
|
|
|
/* Cleanup string parser environment. */
|
|
while (parse_and_execute_level)
|
|
parse_and_execute_cleanup ();
|
|
|
|
#if defined (JOB_CONTROL)
|
|
give_terminal_to (shell_pgrp);
|
|
#endif /* JOB_CONTROL */
|
|
|
|
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
|
/* This should not be necessary on systems using sigsetjmp/siglongjmp. */
|
|
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
|
|
#endif
|
|
|
|
reset_parser ();
|
|
|
|
#if defined (READLINE)
|
|
if (interactive)
|
|
bashline_reinitialize ();
|
|
#endif /* READLINE */
|
|
|
|
#if defined (PROCESS_SUBSTITUTION)
|
|
unlink_fifo_list ();
|
|
#endif /* PROCESS_SUBSTITUTION */
|
|
|
|
run_unwind_protects ();
|
|
loop_level = continuing = breaking = 0;
|
|
return_catch_flag = 0;
|
|
|
|
if (interactive && print_newline)
|
|
{
|
|
fflush (stdout);
|
|
fprintf (stderr, "\n");
|
|
fflush (stderr);
|
|
}
|
|
|
|
/* An interrupted `wait' command in a script does not exit the script. */
|
|
if (interactive || (interactive_shell && !shell_initialized) ||
|
|
(print_newline && signal_is_trapped (SIGINT)))
|
|
jump_to_top_level (DISCARD);
|
|
else
|
|
jump_to_top_level (EXITPROG);
|
|
}
|
|
|
|
/* This is just here to isolate the longjmp calls. */
|
|
void
|
|
jump_to_top_level (value)
|
|
int value;
|
|
{
|
|
longjmp (top_level, value);
|
|
}
|
|
|
|
sighandler
|
|
termination_unwind_protect (sig)
|
|
int sig;
|
|
{
|
|
if (sig == SIGINT && signal_is_trapped (SIGINT))
|
|
run_interrupt_trap ();
|
|
|
|
#if defined (HISTORY)
|
|
if (interactive_shell && sig != SIGABRT)
|
|
maybe_save_shell_history ();
|
|
#endif /* HISTORY */
|
|
|
|
#if defined (JOB_CONTROL)
|
|
if (interactive && sig == SIGHUP)
|
|
hangup_all_jobs ();
|
|
end_job_control ();
|
|
#endif /* JOB_CONTROL */
|
|
|
|
#if defined (PROCESS_SUBSTITUTION)
|
|
unlink_fifo_list ();
|
|
#endif /* PROCESS_SUBSTITUTION */
|
|
|
|
run_exit_trap ();
|
|
set_signal_handler (sig, SIG_DFL);
|
|
kill (getpid (), sig);
|
|
|
|
SIGRETURN (0);
|
|
}
|
|
|
|
/* What we really do when SIGINT occurs. */
|
|
sighandler
|
|
sigint_sighandler (sig)
|
|
int sig;
|
|
{
|
|
#if defined (MUST_REINSTALL_SIGHANDLERS)
|
|
signal (sig, sigint_sighandler);
|
|
#endif
|
|
|
|
/* interrupt_state needs to be set for the stack of interrupts to work
|
|
right. Should it be set unconditionally? */
|
|
if (interrupt_state == 0)
|
|
ADDINTERRUPT;
|
|
|
|
if (interrupt_immediately)
|
|
{
|
|
interrupt_immediately = 0;
|
|
throw_to_top_level ();
|
|
}
|
|
|
|
SIGRETURN (0);
|
|
}
|
|
|
|
/* Signal functions used by the rest of the code. */
|
|
#if !defined (HAVE_POSIX_SIGNALS)
|
|
|
|
#if defined (JOB_CONTROL)
|
|
/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
|
|
sigprocmask (operation, newset, oldset)
|
|
int operation, *newset, *oldset;
|
|
{
|
|
int old, new;
|
|
|
|
if (newset)
|
|
new = *newset;
|
|
else
|
|
new = 0;
|
|
|
|
switch (operation)
|
|
{
|
|
case SIG_BLOCK:
|
|
old = sigblock (new);
|
|
break;
|
|
|
|
case SIG_SETMASK:
|
|
sigsetmask (new);
|
|
break;
|
|
|
|
default:
|
|
internal_error ("Bad code in sig.c: sigprocmask");
|
|
}
|
|
|
|
if (oldset)
|
|
*oldset = old;
|
|
}
|
|
#endif /* JOB_CONTROL */
|
|
|
|
#else
|
|
|
|
#if !defined (SA_INTERRUPT)
|
|
# define SA_INTERRUPT 0
|
|
#endif
|
|
|
|
#if !defined (SA_RESTART)
|
|
# define SA_RESTART 0
|
|
#endif
|
|
|
|
SigHandler *
|
|
set_signal_handler (sig, handler)
|
|
int sig;
|
|
SigHandler *handler;
|
|
{
|
|
struct sigaction act, oact;
|
|
|
|
act.sa_handler = handler;
|
|
act.sa_flags = 0;
|
|
#if 0
|
|
if (sig == SIGALRM)
|
|
act.sa_flags |= SA_INTERRUPT; /* XXX */
|
|
else
|
|
act.sa_flags |= SA_RESTART; /* XXX */
|
|
#endif
|
|
sigemptyset (&act.sa_mask);
|
|
sigemptyset (&oact.sa_mask);
|
|
sigaction (sig, &act, &oact);
|
|
return (oact.sa_handler);
|
|
}
|
|
#endif /* HAVE_POSIX_SIGNALS */
|