genode/repos/libports/src/lib/libc/signal.cc
Norman Feske bb6eb0f6ea libc: local signal delivery via 'kill'
This patch adds the ability to call 'kill' with the own PID to trigger
the execution of the handler of the specified POSIX signal. This is used
by 'bash', e.g., when cancelling the input of a command via control-c.

Related to issue #3546
2019-11-19 14:45:38 +01:00

202 lines
5.0 KiB
C++

/*
* \brief POSIX signals
* \author Emery Hemingway
* \date 2015-10-30
*
* Signal related procedures to be overidden by Noux
*/
/*
* Copyright (C) 2006-2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* libc includes */
extern "C" {
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
}
/* libc-internal includes */
#include <internal/types.h>
#include <internal/init.h>
#include <internal/signal.h>
#include <internal/errno.h>
using namespace Libc;
static Libc::Signal *_signal_ptr;
void Libc::init_signal(Signal &signal)
{
_signal_ptr = &signal;
}
void Libc::Signal::_execute_signal_handler(unsigned n)
{
if (signal_action[n].sa_flags & SA_SIGINFO) {
signal_action[n].sa_sigaction(n, 0, 0);
return;
}
if (signal_action[n].sa_handler == SIG_DFL) {
switch (n) {
case SIGCHLD:
case SIGWINCH:
/* ignore */
break;
default:
/*
* Trigger the termination of the process.
*
* We cannot call 'exit' immediately as the exiting code (e.g.,
* 'atexit' handlers) may potentially issue I/O operations such
* as FD sync and close. Hence, we just flag the intention to
* exit and issue the actual exit call at the end of
* 'Signal::execute_signal_handlers'
*/
_exit = true;
_exit_code = (n << 8) | EXIT_FAILURE;
}
} else if (signal_action[n].sa_handler == SIG_IGN) {
/* do nothing */
} else {
signal_action[n].sa_handler(n);
}
}
extern "C" __attribute__((weak))
int sigprocmask(int how, const sigset_t *set, sigset_t *old_set)
{
/* no signals should be expected, so report all signals blocked */
if (old_set != NULL)
sigfillset(old_set);
if (set == NULL)
return 0;
switch (how) {
case SIG_BLOCK: return 0;
case SIG_SETMASK: return 0;
case SIG_UNBLOCK: return 0;
}
errno = EINVAL;
return -1;
}
extern "C"
int __sys_sigprocmask(int how, const sigset_t *set, sigset_t *old) {
return sigprocmask(how, set, old); }
extern "C"
int __libc_sigprocmask(int how, const sigset_t *set, sigset_t *old) {
return sigprocmask(how, set, old); }
/* wrapper from text-relocated i386-assembler call to PLT call */
extern "C" int __i386_libc_sigprocmask(int how, const sigset_t *set, sigset_t *old) __attribute__((visibility("hidden")));
extern "C" int __i386_libc_sigprocmask(int how, const sigset_t *set, sigset_t *old)
{
return __libc_sigprocmask(how, set, old);
}
extern "C" int sigaction(int, const struct sigaction *, struct sigaction *) __attribute__((weak));
extern "C" int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
{
if ((signum < 1) || (signum > NSIG)) {
errno = EINVAL;
return -1;
}
if (oldact)
*oldact = _signal_ptr->signal_action[signum];
if (act)
_signal_ptr->signal_action[signum] = *act;
return 0;
}
extern "C" int _sigaction(int, const struct sigaction *, struct sigaction *) __attribute__((weak, alias("sigaction")));
extern "C" int __sys_sigaction(int, const struct sigaction *, struct sigaction *) __attribute__((weak, alias("sigaction")));
extern "C" int __libc_sigaction(int, const struct sigaction *, struct sigaction *) __attribute__((weak, alias("sigaction")));
extern "C" int kill(pid_t pid, int signum)
{
if (_signal_ptr->local_pid(pid)) {
_signal_ptr->charge(signum);
_signal_ptr->execute_signal_handlers();
return 0;
} else {
warning("submitting signals to remote processes via 'kill' not supported");
return Libc::Errno(EPERM);
}
}
extern "C" int _kill(pid_t, int) __attribute__((weak, alias("kill")));
extern "C" int __sys_kill(pid_t, int) __attribute__((weak, alias("kill")));
extern "C" int __libc_kill(pid_t, int) __attribute__((weak, alias("kill")));
extern "C" pid_t wait(int *istat) {
return wait4(WAIT_ANY, istat, 0, NULL); }
extern "C" pid_t waitpid(pid_t pid, int *istat, int options) {
return wait4(pid, istat, options, NULL); }
extern "C" pid_t _waitpid(pid_t pid, int *istat, int options) {
return wait4(pid, istat, options, NULL); }
extern "C" __attribute__((weak))
pid_t wait6(idtype_t, id_t, int*, int, struct __wrusage*, siginfo_t*)
{
warning(__func__, " not implemented");
errno = ENOSYS;
return -1;
}
extern "C"
pid_t __sys_wait6(idtype_t idtype, id_t id, int *status, int options,
struct __wrusage *wrusage, siginfo_t *infop) {
return wait6(idtype, id, status, options, wrusage, infop); }
extern "C" int waitid(idtype_t idtype, id_t id, siginfo_t *info, int flags)
{
int status;
pid_t ret = wait6(idtype, id, &status, flags, NULL, info);
/*
* According to SUSv4, waitid() shall not return a PID when a
* process is found, but only 0. If a process was actually
* found, siginfo_t fields si_signo and si_pid will be
* non-zero. In case WNOHANG was set in the flags and no
* process was found those fields are set to zero using
* memset() below.
*/
if (ret == 0 && info != NULL)
*info = siginfo_t { 0 };
else if (ret > 0)
ret = 0;
return (ret);
}