aboutsummaryrefslogtreecommitdiff
path: root/src/signal
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-06 11:28:06 +0000
committer_ov4 <ov4@ov4.org>2026-10-06 11:28:06 +0000
commitc109164a9f68935098a4915e7237828cafdef5f7 (patch)
treec96582cc3e9e7ffa752631fd68bd084982503224 /src/signal
parentc0dc27b0e94d8aca60fc47c9dee09eb170cb79dd (diff)
fix non-signal-safe exit, and refactor signal handlers
Diffstat (limited to 'src/signal')
-rw-r--r--src/signal/misc.cc61
-rw-r--r--src/signal/sigchld_handler.cc99
-rw-r--r--src/signal/sigint_handler.cc37
-rw-r--r--src/signal/sigquit_handler17
-rw-r--r--src/signal/sigtstp_handler.cc32
5 files changed, 246 insertions, 0 deletions
diff --git a/src/signal/misc.cc b/src/signal/misc.cc
new file mode 100644
index 0000000..e8b780e
--- /dev/null
+++ b/src/signal/misc.cc
@@ -0,0 +1,61 @@
+#include "signal_handler.h"
+
+#include <errno.h>
+
+namespace ov4
+{
+
+void signal_init()
+{
+ /* Install the signal handlers */
+
+ sigemptyset(&block_job);
+ sigemptyset(&block_sig_TTOU);
+ sigemptyset(&block_io);
+
+ sigaddset(&block_job, SIGINT);
+ sigaddset(&block_job, SIGTSTP);
+ sigaddset(&block_job, SIGCHLD);
+
+ sigaddset(&block_sig_TTOU, SIGTTOU);
+
+ sigaddset(&block_io, SIGTTOU);
+ sigaddset(&block_io, SIGTTIN);
+
+ Signal(SIGINT, sigint_handler); /* ctrl-c */
+ Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */
+ Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
+
+ /* This one provides a clean way to kill the shell */
+ Signal(SIGQUIT, sigquit_handler);
+}
+
+/*
+ * Signal - wrapper for the sigaction function
+ */
+handler_t *Signal([[maybe_unused]] int signum, handler_t *handler)
+{
+ struct sigaction action, old_action;
+
+ action.sa_handler = handler;
+ sigemptyset(&action.sa_mask); /* block sigs of type being handled */
+ action.sa_flags = SA_RESTART; /* restart syscalls if possible */
+
+ if (sigaction(signum, &action, &old_action) < 0)
+ unix_error("Signal error");
+ return (old_action.sa_handler);
+}
+
+int block_all(sigset_t *prev)
+{
+ sigset_t set;
+ sigfillset(&set);
+ return sigprocmask(SIG_SETMASK, &set, prev);
+}
+
+int block_handler(sigset_t *prev)
+{
+ return sigprocmask(SIG_BLOCK, &block_job, prev);
+}
+
+} \ No newline at end of file
diff --git a/src/signal/sigchld_handler.cc b/src/signal/sigchld_handler.cc
new file mode 100644
index 0000000..e11ed26
--- /dev/null
+++ b/src/signal/sigchld_handler.cc
@@ -0,0 +1,99 @@
+#include "signal_handler.h"
+
+#include <errno.h>
+
+namespace ov4
+{
+
+/*
+ * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
+ * a child job terminates (becomes a zombie), or stops because it
+ * received a SIGSTOP or SIGTSTP signal. The handler reaps all
+ * available zombie children, but doesn't wait for any other
+ * currently running children to terminate.
+ */
+void sigchld_handler([[maybe_unused]] int sig)
+{
+ int errno_backup = errno;
+
+ sigset_t prev;
+ block_handler(&prev);
+
+ int status, pid;
+ safe_debug("SIGCHLD\n");
+
+ while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
+ {
+ // signal'd or stopped or normally quit
+ // print terminated/stopped message
+ if (WIFSIGNALED(status) || WIFSTOPPED(status))
+ {
+ int jid = pid2jid(pid);
+ safe_output("Job [", jid, "] (", pid, ") ");
+ if (!WIFSTOPPED(status) && WTERMSIG(status) == SIGINT)
+ safe_print("terminated");
+ else if (WIFSTOPPED(status))
+ safe_print("stopped");
+ else
+ safe_print("idk");
+ safe_print(" by signal ");
+ if (WIFSTOPPED(status))
+ safe_print(WSTOPSIG(status));
+ else
+ safe_print(WTERMSIG(status));
+ safe_print("\n");
+ }
+
+ // handle job table
+ // and save exit/signal code
+
+ // stopped
+ if (WIFSTOPPED(status))
+ {
+ safe_output("Process suspended PID: ", pid, "\n");
+ sigset_t prev_inner;
+ block_all(&prev_inner);
+ job_suspend(pid);
+ sigprocmask(SIG_SETMASK, &prev_inner, nullptr);
+ }
+ // other type of signal'd
+ // or normally quit
+ else {
+ safe_output("Process terminated PID: ", pid, "\n");
+ sigset_t prev_inner;
+ block_all(&prev_inner);
+ deletejob(pid);
+ sigprocmask(SIG_SETMASK, &prev_inner, nullptr);
+
+ // terminated normally
+ // so that exit code should be available
+ if (WIFEXITED(status))
+ {
+ safe_output("Exit code: ", WEXITSTATUS(status), "\n");
+ if (pid == exit_required_pid)
+ {
+ exit_code = WEXITSTATUS(status);
+ safe_logger("sigchld_handler: required PID detected: ", pid, "\n");
+ }
+ }
+ // if signal'd
+ else if (WIFSIGNALED(status))
+ {
+ safe_output("Signal code: ", WTERMSIG(status), "\n");
+ if (pid == exit_required_pid)
+ {
+ exit_code = SIGNAL_EXIT_CODE_BASE + WTERMSIG(status);
+ safe_logger("sigchld_handler: required PID detected: ", pid, "\n");
+ }
+ }
+
+ }
+ }
+
+ sigprocmask(SIG_SETMASK, &prev, nullptr);
+
+ errno = errno_backup;
+ return;
+}
+
+} \ No newline at end of file
diff --git a/src/signal/sigint_handler.cc b/src/signal/sigint_handler.cc
new file mode 100644
index 0000000..706c041
--- /dev/null
+++ b/src/signal/sigint_handler.cc
@@ -0,0 +1,37 @@
+#include "signal_handler.h"
+
+#include <errno.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+namespace ov4
+{
+
+/*
+ * sigint_handler - The kernel sends a SIGINT to the shell whenver the
+ * user types ctrl-c at the keyboard. Catch it and send it along
+ * to the foreground job.
+ */
+void sigint_handler([[maybe_unused]] int sig)
+{
+ int errno_backup = errno;
+
+ sigset_t prev;
+ block_handler(&prev);
+
+ int pid = fgpid();
+ if (pid != 0)
+ {
+ kill(-pid, SIGINT);
+ } else {
+ tcflush(STDIN_FILENO, TCIFLUSH);
+ ioctl(STDIN_FILENO, TIOCSTI, "\n");
+ }
+
+ sigprocmask(SIG_SETMASK, &prev, nullptr);
+
+ errno = errno_backup;
+ return;
+}
+
+} \ No newline at end of file
diff --git a/src/signal/sigquit_handler b/src/signal/sigquit_handler
new file mode 100644
index 0000000..73296eb
--- /dev/null
+++ b/src/signal/sigquit_handler
@@ -0,0 +1,17 @@
+#include "signal_handler.h"
+
+#include <errno.h>
+
+namespace ov4
+{
+
+void sigquit_handler([[maybe_unused]] int sig)
+{
+ //printf("Terminating after receipt of SIGQUIT signal\n");
+ safe_output("Terminating after receipt of SIGQUIT signal\n");
+ // UNDERSCORE'd version should be safe
+ // but exit() is unsafe
+ _exit(1);
+}
+
+} \ No newline at end of file
diff --git a/src/signal/sigtstp_handler.cc b/src/signal/sigtstp_handler.cc
new file mode 100644
index 0000000..7b32d31
--- /dev/null
+++ b/src/signal/sigtstp_handler.cc
@@ -0,0 +1,32 @@
+#include "signal_handler.h"
+
+#include <errno.h>
+
+namespace ov4
+{
+
+/*
+ * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
+ * the user types ctrl-z at the keyboard. Catch it and suspend the
+ * foreground job by sending it a SIGTSTP.
+ */
+void sigtstp_handler([[maybe_unused]] int sig)
+{
+ int errno_backup = errno;
+
+ sigset_t prev;
+ block_handler(&prev);
+
+ int pid = fgpid();
+ if (pid != 0)
+ {
+ kill(-pid, SIGTSTP);
+ }
+
+ sigprocmask(SIG_SETMASK, &prev, nullptr);
+
+ errno = errno_backup;
+ return;
+}
+
+} \ No newline at end of file