aboutsummaryrefslogtreecommitdiff
path: root/src/signal/sigchld_handler.cc
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/sigchld_handler.cc
parentc0dc27b0e94d8aca60fc47c9dee09eb170cb79dd (diff)
fix non-signal-safe exit, and refactor signal handlers
Diffstat (limited to 'src/signal/sigchld_handler.cc')
-rw-r--r--src/signal/sigchld_handler.cc99
1 files changed, 99 insertions, 0 deletions
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