]> Trent Huber's Code - thus.git/commitdiff
Proper signal handling
authorTrent Huber <trentmhuber@gmail.com>
Wed, 15 Oct 2025 06:14:18 +0000 (02:14 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Wed, 15 Oct 2025 06:14:18 +0000 (02:14 -0400)
src/build.c
src/builtins/bg.c
src/builtins/bg.h
src/builtins/fg.c
src/builtins/fg.h
src/input.c
src/options.c
src/run.c

index 91ca5efd08bcdd63c3960ca4535d81735e963c72..84ad1bdf79676d80c9a6226bffa2f3fc343850d9 100644 (file)
@@ -1,7 +1,7 @@
 #include "../external/cbs/cbs.c"
 
-#define SRC1 "context", "history", "input"
-#define SRC2 "main", "options", "parse", "run", "utils"
+#define SRC1 "context", "history"
+#define SRC2 "input", "main", "options", "parse", "run", "utils"
 
 int main(void) {
    char **src;
index 2f78950521a3e119326d7582ee0569b8f4df2282..eec6fbd7e7391893e72fdf028578d1af690a5982 100644 (file)
@@ -20,10 +20,49 @@ struct bglink {
 static struct {
    struct bglink entries[MAXBG], *active, *free;
 } bgjobs;
+struct sigaction bgaction;
+
+void removebg(pid_t id) {
+   struct bglink *p, *prev;
+   
+   for (prev = NULL, p = bgjobs.active; p; prev = p, p = p->next)
+       if (p->job.id == id) break;
+   if (!p) return;
+
+   if (prev) prev->next = p->next; else bgjobs.active = p->next;
+   p->next = bgjobs.free;
+   bgjobs.free = p;
+}
+
+void sigchldbghandler(int sig) {
+   int e, s;
+   struct bglink *p;
+   pid_t id;
+
+   (void)sig;
+
+   e = errno;
+   p = bgjobs.active;
+   while (p) {
+       while ((id = waitpid(-p->job.id, &s, WNOHANG | WUNTRACED)) > 0)
+           if (WIFSTOPPED(s)) {
+               p->job.suspended = 1;
+               break;
+           }
+       if (id == -1) {
+           id = p->job.id;
+           p = p->next;
+           removebg(id);
+       } else p = p->next;
+   }
+   errno = e;
+}
 
 void initbg(void) {
    size_t i;
 
+   bgaction = (struct sigaction){.sa_handler = sigchldbghandler};
+
    for (i = 0; i < MAXBG - 1; ++i)
        bgjobs.entries[i].next = &bgjobs.entries[i + 1];
    bgjobs.free = bgjobs.entries;
@@ -67,41 +106,6 @@ int searchbg(pid_t id, struct bgjob *job) {
    return 0;
 }
 
-void removebg(pid_t id) {
-   struct bglink *p, *prev;
-   
-   for (prev = NULL, p = bgjobs.active; p; prev = p, p = p->next)
-       if (p->job.id == id) break;
-   if (!p) return;
-
-   if (prev) prev->next = p->next; else bgjobs.active = p->next;
-   p->next = bgjobs.free;
-   bgjobs.free = p;
-}
-
-void waitbg(int sig) {
-   int e, s;
-   struct bglink *p;
-   pid_t id;
-
-   (void)sig;
-   e = errno;
-   p = bgjobs.active;
-   while (p) {
-       while ((id = waitpid(-p->job.id, &s, WNOHANG | WUNTRACED)) > 0)
-           if (WIFSTOPPED(s)) {
-               p->job.suspended = 1;
-               break;
-           }
-       if (id == -1) {
-           id = p->job.id;
-           p = p->next;
-           removebg(id);
-       } else p = p->next;
-   }
-   errno = e;
-}
-
 void deinitbg(void) {
    struct bglink *p;
 
index b933134340e56549490fff6487b7209f19f4f0a8..3d34bbe0481f76c747e7c578e7705dbc5bad51fe 100644 (file)
@@ -4,12 +4,14 @@ struct bgjob {
    int suspended;
 };
 
+extern struct sigaction bgaction;
+
+void removebg(pid_t id);
+void sigchldbghandler(int sig);
 void initbg(void);
 int bgfull(void);
 int pushbg(struct bgjob job);
 int pushbgid(pid_t id);
 int peekbg(struct bgjob *job);
 int searchbg(pid_t id, struct bgjob *job);
-void removebg(pid_t id);
-void waitbg(int sig);
 void deinitbg(void);
index 6da9a07e95632f359e43a023cad3e92d6bec6423..c40db18babe3ee4722c93d12911bbcb58df5a108 100644 (file)
@@ -2,6 +2,7 @@
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/errno.h>
 #include <sys/wait.h>
 #include <termios.h>
 
 #include "bg.h"
 #include "builtin.h"
+#include "context.h"
+#include "input.h"
+#include "options.h"
 #include "utils.h"
 
+int sigquit, sigint;
 static struct {
    pid_t id;
    int status, done;
 } fgjob;
+static sigset_t shellsigmask;
+static struct sigaction fgaction;
+struct sigaction defaultaction;
+static pid_t pgid;
+sigset_t childsigmask;
 struct termios canonical;
 static struct termios raw;
-struct sigaction actbg, actdefault;
-static struct sigaction actall, actignore;
 
-static int setconfig(struct termios *mode) {
-   if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
-       note("Unable to set termios config");
-       return 0;
-   }
-   return 1;
+static void sigquithandler(int sig) {
+   (void)sig;
+
+   sigquit = 1;
+}
+
+static void siginthandler(int sig) {
+   (void)sig;
+
+   sigint = 1;
 }
 
-static void waitall(int sig) {
+static void sigchldfghandler(int sig) {
    int e, s;
    pid_t id;
 
@@ -42,25 +54,59 @@ static void waitall(int sig) {
        }
    }
    if (id == -1) fgjob.done = 1;
-   waitbg(sig);
+   sigchldbghandler(sig);
    errno = e;
 }
 
+void setsig(int sig, struct sigaction *act) {
+   if (sigaction(sig, act, NULL) == -1)
+       fatal("Unable to install %s handler", strsignal(sig));
+}
+
+static int setconfig(struct termios *mode) {
+   if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
+       note("Unable to configure TTY");
+       return 0;
+   }
+   return 1;
+}
+
 void initfg(void) {
+   struct sigaction action;
+   pid_t pid;
+
+   sigemptyset(&shellsigmask);
+   sigaddset(&shellsigmask, SIGTSTP);
+   sigaddset(&shellsigmask, SIGTTIN);
+   sigaddset(&shellsigmask, SIGTTOU);
+
+   action = (struct sigaction){.sa_handler = sigquithandler};
+   setsig(SIGHUP, &action);
+   setsig(SIGQUIT, &action);
+   setsig(SIGTERM, &action);
+
+   action = (struct sigaction){.sa_handler = siginthandler};
+   setsig(SIGINT, &action);
+
+   fgaction = (struct sigaction){.sa_handler = sigchldfghandler};
+
+   defaultaction = (struct sigaction){.sa_handler = SIG_DFL};
+   setsig(SIGTSTP, &defaultaction);
+   setsig(SIGTTOU, &defaultaction);
+   setsig(SIGTTIN, &defaultaction);
+
+   pid = getpid();
+   pgid = getpgrp();
+   if (login && pid != pgid && setpgid(0, pgid = pid) == -1) exit(errno);
+
+   if (sigprocmask(SIG_BLOCK, &shellsigmask, &childsigmask) == -1
+       || tcsetpgrp(STDIN_FILENO, pgid) == -1)
+       exit(errno);
+
    if (tcgetattr(STDIN_FILENO, &canonical) == -1) exit(errno);
    raw = canonical;
-   raw.c_lflag &= ~(ICANON | ECHO | ISIG);
+   raw.c_lflag &= ~(ICANON | ECHO);
    if (!setconfig(&raw)) exit(EXIT_FAILURE);
-
-   actbg.sa_handler = waitbg;
-   actall.sa_handler = waitall;
-   actdefault.sa_handler = SIG_DFL;
-   actignore.sa_handler = SIG_IGN;
-}
-
-void setsigchld(struct sigaction *act) {
-   if (sigaction(SIGCHLD, act, NULL) == -1)
-       fatal("Unable to install SIGCHLD handler");
 }
 
 int runfg(pid_t id) {
@@ -79,18 +125,23 @@ int runfg(pid_t id) {
    }
    removebg(id);
 
-   /* SIGCHLD handler is really the function that reaps the foreground process,
-    * the waitpid() below is just to block the current thread of execution until
-    * the foreground process has been reaped. */
+   /* The handler in `fgaction' is really what reaps the foreground process; the
+    * `sigsuspend()' just blocks the current thread of execution until the
+    * foreground process has been reaped. */
    fgjob.id = id;
-   setsigchld(&actall);
-   while (!fgjob.done) sigsuspend(&actall.sa_mask);
-   setsigchld(&actdefault);
+   setsig(SIGCHLD, &fgaction);
+   while (!fgjob.done) {
+       sigsuspend(&shellsigmask);
+       if (sigquit) {
+           deinit();
+           exit(EXIT_SUCCESS);
+       }
+       if (sigint) sigint = 0;
+   }
+   setsig(SIGCHLD, &defaultaction);
    fgjob.done = errno = 0;
 
-   if (sigaction(SIGTTOU, &actignore, NULL) == -1
-       || tcsetpgrp(STDIN_FILENO, getpgrp()) == -1
-       || sigaction(SIGTTOU, &actdefault, NULL) == -1) {
+   if (tcsetpgrp(STDIN_FILENO, pgid) == -1) {
        deinit();
        exit(errno);
    }
@@ -109,7 +160,7 @@ int runfg(pid_t id) {
            return runfg(id);
        }
    } else status = WTERMSIG(fgjob.status);
-   
+
    return 1;
 }
 
index 2d65274ac7b46472347d3127659da5dd97c9955a..086646203f358fdcffa0e8f0371d907d2f951450 100644 (file)
@@ -1,7 +1,9 @@
+extern int sigquit, sigint;
+extern struct sigaction defaultaction;
+extern sigset_t childsigmask;
 extern struct termios canonical;
-extern struct sigaction actbg, actdefault;
 
+void setsig(int sig, struct sigaction *act);
 void initfg(void);
-void setsigchld(struct sigaction *act);
 int runfg(pid_t id);
 void deinitfg(void);
index 53b9b9c6c9a56e54293dd5f8fb88c69c4bd464c9..b9049bc270ddcb9b769c53f002276c7cc7aa661a 100644 (file)
@@ -7,16 +7,16 @@
 #include <unistd.h>
 
 #include "context.h"
+#include "fg.h"
 #include "history.h"
 #include "utils.h"
 
 enum {
-   CTRLC = '\003',
-   CTRLD,
+   CTRLD = '\004',
    CLEAR = '\014',
    ESCAPE = '\033',
 
-   /* See ESCAPE case in userinput() */
+   /* See `ESCAPE' case in `userinput()' */
    ALT = '2' + 1,
 
    UP = 'A',
@@ -41,7 +41,7 @@ int stringinput(struct context *c) {
    end = c->string;
    while (*end && *end != '\n') ++end;
    l = end - c->string;
-   while (*end == '\n') ++end; /* scriptinput() repeatedly uses stringinput() */
+   while (*end == '\n') ++end; /* `scriptinput()' uses `stringinput()' */
    if (l > MAXCHARS) {
        note("Line too long, exceeds %d character limit", MAXCHARS);
        return 0;
@@ -81,6 +81,10 @@ int scriptinput(struct context *c) {
    c->string = c->map;
    c->input = stringinput;
 
+   /* We want errors from this point forward to be reported by the script, not the
+    * shell. */
+   arglist[0] = c->script;
+
    return c->input(c);
 }
 
@@ -114,12 +118,18 @@ int userinput(struct context *c) {
                for (i = end - cursor; i > 0; --i) putchar('\b');
            }
            break;
-       case CTRLC:
-           putchar('\n');
-           return quit(c);
-       case CTRLD:
-           putchar('\n');
-           return 0;
+       case EOF:
+           if (sigquit) {
+           case CTRLD:
+               putchar('\n');
+               return 0;
+           }
+           if (sigint) {
+               sigint = 0;
+               putchar('\n');
+               prompt();
+           }
+           break;
        case CLEAR:
            fputs("\033[H\033[J", stdout);
            prompt();
index 3276d7dcac45c2dd347a9d21a480c4070c02b213..44b8035728eab64bcba9f817bcd4f478c07600e8 100644 (file)
 int login, interactive;
 
 void options(struct context *context) {
-   int opt;
    char *p, *message = "[file | -c string] [arg ...] [-hl]\n"
                        "    <file> [arg ...]      Run script\n"
                        "    -c <string> [arg ...] Run string\n"
                        "    -h                    Show this help message\n"
                        "    -l                    Run as a login shell";
+   int opt;
 
-   opt = 0;
    if (arglist[0][0] == '-') {
        ++arglist[0];
        login = 1;
@@ -26,6 +25,7 @@ void options(struct context *context) {
    interactive = 1;
    context->input = userinput;
 
+   opt = 0;
    while (opt != 'c' && (opt = getopt(argcount, arglist, ":c:hl")) != -1)
        switch (opt) {
        case 'c':
@@ -52,6 +52,10 @@ void options(struct context *context) {
        interactive = 0;
        context->script = arglist[optind];
        context->input = scriptinput;
+
+       /* Until we're able to load the script, errors need to be reported by the
+        * shell, not the script. */
+       arglist[optind] = arglist[0];
    }
    if (!interactive) {
        argcount -= optind;
index a0452e26e5b40b1f1780d0dbab53c91ba093d3b1..4086592892a92fe5570e490239a61b8eb2813ea8 100644 (file)
--- a/src/run.c
+++ b/src/run.c
@@ -60,6 +60,9 @@ static void redirectfiles(struct redirect *r) {
 static void exec(char *path, struct context *c) {
    redirectfiles(c->redirects);
 
+   if (sigprocmask(SIG_SETMASK, &childsigmask, NULL) == -1)
+       note("Unable to unblock TTY signals");
+
    if (isbuiltin(c->tokens)) exit(status);
    execve(path, c->tokens, environ);
    fatal("Couldn't find `%s' command", c->current.name);
@@ -71,9 +74,9 @@ int run(struct context *c) {
    pid_t cpid, jobid;
    static pid_t pipeid;
 
-   setsigchld(&actbg);
+   setsig(SIGCHLD, &bgaction);
    if (!parse(c)) return 0;
-   setsigchld(&actdefault);
+   setsig(SIGCHLD, &defaultaction);
 
    islist = c->prev.term > BG || c->current.term > BG;
    if (c->t) {