libc: trigger SIGWINCH by watching .terminal/info

Issue #3544
This commit is contained in:
Norman Feske 2019-10-11 15:23:22 +02:00 committed by Christian Helmuth
parent e9762ee25f
commit 7b0771659e
7 changed files with 229 additions and 4 deletions

View File

@ -195,7 +195,6 @@ __SYS_DUMMY(int, -1, truncate, (const char *, off_t))
DUMMY(int, -1, sigblock, (int))
DUMMY(int, -1, thr_kill2, (pid_t pid, long id, int sig));
__SYS_DUMMY_SILENT(int, -1, sigaction, (int, const struct sigaction *, struct sigaction *));
__SYS_DUMMY(int, -1, sigsuspend, (const sigset_t *))
__SYS_DUMMY(int, -1, sigtimedwait, (const sigset_t *, siginfo_t *, const struct timespec *));
__SYS_DUMMY(int, -1, sigwaitinfo, (const sigset_t *, siginfo_t *));

View File

@ -35,6 +35,7 @@ namespace Libc {
struct Clone_connection;
struct Kernel_routine_scheduler;
struct Watch;
struct Signal;
/**
* Support for shared libraries
@ -129,6 +130,11 @@ namespace Libc {
*/
void init_execve(Genode::Env &, Genode::Allocator &, void *user_stack,
Reset_malloc_heap &);
/**
* Signal handling
*/
void init_signal(Signal &);
}
#endif /* _LIBC__INTERNAL__INIT_H_ */

View File

@ -38,6 +38,7 @@
#include <internal/current_time.h>
#include <internal/kernel_timer_accessor.h>
#include <internal/watch.h>
#include <internal/signal.h>
namespace Libc { class Kernel; }
@ -117,6 +118,13 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
Config_attr const _rtc_path = _libc_env.libc_config().attribute_value("rtc", Config_attr());
/* handler for watching the stdout's info pseudo file */
Constructible<Watch_handler<Kernel>> _terminal_resize_handler { };
void _handle_terminal_resize();
Signal _signal;
Reconstructible<Io_signal_handler<Kernel>> _resume_main_handler {
_env.ep(), *this, &Kernel::_resume_main };
@ -279,6 +287,7 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
_switch_to_kernel();
} else {
_valid_user_context = false;
_signal.execute_signal_handlers();
}
/*
@ -439,6 +448,7 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
} else {
_valid_user_context = false;
_dispatch_pending_io_signals = false;
_signal.execute_signal_handlers();
}
}

View File

@ -0,0 +1,108 @@
/*
* \brief POSIX signal handling
* \author Norman Feske
* \date 2019-10-11
*/
/*
* Copyright (C) 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.
*/
#ifndef _LIBC__INTERNAL__SIGNAL_H_
#define _LIBC__INTERNAL__SIGNAL_H_
/* Genode includes */
#include <util/noncopyable.h>
#include <base/registry.h>
/* libc includes */
#include <signal.h>
/* libc-internal includes */
#include <internal/types.h>
namespace Libc { struct Signal; }
struct Libc::Signal : Noncopyable
{
public:
struct sigaction signal_action[NSIG + 1] { };
private:
struct Pending
{
virtual ~Pending() { }
unsigned const n;
Pending(unsigned n) : n(n) { }
};
Constructible<Registered<Pending>> _charged_signals[NSIG + 1];
Registry<Registered<Pending>> _pending_signals { };
void _execute_signal_handler(unsigned n);
unsigned _count = 0;
bool _exit = false;
unsigned _exit_code = 0;
unsigned _nesting_level = 0;
public:
void charge(unsigned n)
{
if (n > NSIG)
return;
_charged_signals[n].construct(_pending_signals, n);
_count++;
}
void execute_signal_handlers()
{
/*
* Prevent nested execution of signal handlers, which may happen
* if I/O operations are executed by a signal handler.
*/
if (_nesting_level > 0) {
warning("attempt to nested execution of signal handlers");
return;
}
_nesting_level++;
_pending_signals.for_each([&] (Pending &pending) {
_execute_signal_handler(pending.n);
_charged_signals[pending.n].destruct();
});
_nesting_level--;
/*
* Exit application due to a signal such as SIGINT.
*/
if (_exit)
exit(_exit_code);
}
/**
* Return number of triggered signals
*
* The value is intended to be used for tracking whether a signal
* occurred during a blocking operation ('select').
*/
unsigned count() const { return _count; }
};
#endif /* _LIBC__INTERNAL__SIGNAL_H_ */

View File

@ -44,9 +44,19 @@ class Libc::Vfs_plugin : public Plugin
/**
* Return path to pseudo files used for ioctl operations of a given FD
*
* The 'fd' argument must feature a valid 'fd.fd_path' member.
* This assumption can be violated by the stdout, stdin, or stderr FDs
* if the <libc> configuration lacks the corresponding attribute.
*/
static Absolute_path ioctl_dir(File_descriptor const &fd)
{
if (!fd.fd_path) {
error("Libc::Vfs_plugin::ioctl_dir: fd lacks path information");
class Fd_lacks_path : Exception { };
throw Fd_lacks_path();
}
Absolute_path path(fd.fd_path);
/*

View File

@ -168,6 +168,30 @@ void Libc::Kernel::_init_file_descriptors()
for (unsigned fd = 0; fd <= 2; fd++)
file_descriptor_allocator()->preserve(fd);
}
/*
* Watch stdout's 'info' pseudo file to detect terminal resize events
*/
File_descriptor const * const stdout_fd =
file_descriptor_allocator()->find_by_libc_fd(STDOUT_FILENO);
if (stdout_fd && stdout_fd->fd_path) {
Absolute_path dir = Vfs_plugin::ioctl_dir(*stdout_fd);
dir.append_element("info");
_vfs.with_root_dir([&] (Directory &root_dir) {
if (root_dir.file_exists(dir.string()))
_terminal_resize_handler.construct(root_dir, dir.string(),
*this, &Kernel::_handle_terminal_resize);
});
}
}
void Libc::Kernel::_handle_terminal_resize()
{
_signal.charge(SIGWINCH);
_resume_main();
}
@ -326,6 +350,7 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap)
init_select(*this, *this, *this);
init_socket_fs(*this);
init_passwd(_passwd_config());
init_signal(_signal);
_init_file_descriptors();

View File

@ -21,15 +21,57 @@ extern "C" {
#include <sys/wait.h>
}
/* Genode includes */
#include <base/log.h>
/* libc-internal includes */
#include <internal/types.h>
#include <internal/init.h>
#include <internal/signal.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)
{
@ -68,6 +110,31 @@ extern "C" int __i386_libc_sigprocmask(int how, const sigset_t *set, sigset_t *o
}
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" pid_t wait(int *istat) {
return wait4(WAIT_ANY, istat, 0, NULL); }