From 7b0771659e2193b57f1c4cf77ef8bd810887cc7b Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 11 Oct 2019 15:23:22 +0200 Subject: [PATCH] libc: trigger SIGWINCH by watching .terminal/info Issue #3544 --- repos/libports/src/lib/libc/dummies.cc | 1 - repos/libports/src/lib/libc/internal/init.h | 6 + repos/libports/src/lib/libc/internal/kernel.h | 10 ++ repos/libports/src/lib/libc/internal/signal.h | 108 ++++++++++++++++++ .../src/lib/libc/internal/vfs_plugin.h | 10 ++ repos/libports/src/lib/libc/kernel.cc | 25 ++++ repos/libports/src/lib/libc/signal.cc | 73 +++++++++++- 7 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 repos/libports/src/lib/libc/internal/signal.h diff --git a/repos/libports/src/lib/libc/dummies.cc b/repos/libports/src/lib/libc/dummies.cc index 40f25bc12..d3321d6de 100644 --- a/repos/libports/src/lib/libc/dummies.cc +++ b/repos/libports/src/lib/libc/dummies.cc @@ -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 *)); diff --git a/repos/libports/src/lib/libc/internal/init.h b/repos/libports/src/lib/libc/internal/init.h index eb9a5b078..eaea096ef 100644 --- a/repos/libports/src/lib/libc/internal/init.h +++ b/repos/libports/src/lib/libc/internal/init.h @@ -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_ */ diff --git a/repos/libports/src/lib/libc/internal/kernel.h b/repos/libports/src/lib/libc/internal/kernel.h index 3d873ab99..107bc09f3 100644 --- a/repos/libports/src/lib/libc/internal/kernel.h +++ b/repos/libports/src/lib/libc/internal/kernel.h @@ -38,6 +38,7 @@ #include #include #include +#include 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> _terminal_resize_handler { }; + + void _handle_terminal_resize(); + + Signal _signal; + Reconstructible> _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(); } } diff --git a/repos/libports/src/lib/libc/internal/signal.h b/repos/libports/src/lib/libc/internal/signal.h new file mode 100644 index 000000000..3aca36380 --- /dev/null +++ b/repos/libports/src/lib/libc/internal/signal.h @@ -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 +#include + +/* libc includes */ +#include + +/* libc-internal includes */ +#include + +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> _charged_signals[NSIG + 1]; + + Registry> _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_ */ diff --git a/repos/libports/src/lib/libc/internal/vfs_plugin.h b/repos/libports/src/lib/libc/internal/vfs_plugin.h index 886cec13c..d2016e852 100644 --- a/repos/libports/src/lib/libc/internal/vfs_plugin.h +++ b/repos/libports/src/lib/libc/internal/vfs_plugin.h @@ -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 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); /* diff --git a/repos/libports/src/lib/libc/kernel.cc b/repos/libports/src/lib/libc/kernel.cc index a7efc7d96..f75433de4 100644 --- a/repos/libports/src/lib/libc/kernel.cc +++ b/repos/libports/src/lib/libc/kernel.cc @@ -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(); diff --git a/repos/libports/src/lib/libc/signal.cc b/repos/libports/src/lib/libc/signal.cc index f9d380a1c..5a84cafe5 100644 --- a/repos/libports/src/lib/libc/signal.cc +++ b/repos/libports/src/lib/libc/signal.cc @@ -21,15 +21,57 @@ extern "C" { #include } -/* Genode includes */ -#include - /* libc-internal includes */ #include +#include +#include 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); }