]> Trent Huber's Code - thus.git/commitdiff
Integrate with TTY device
authorTrent Huber <trentmhuber@gmail.com>
Mon, 5 May 2025 20:38:10 +0000 (16:38 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Mon, 5 May 2025 20:38:10 +0000 (16:38 -0400)
README.md
main.c

index df172fb68ddecf8c5b0215f8f4c915d189aa29e1..fa0c22bef337115bca43d8cf1e31d76df6cb1e7e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1 +1,6 @@
 # Ash
+
+## Resources
+
+[TTY Demystified](http://www.linusakesson.net/programming/tty/)
+[Process Groups and Terminal Signaling](https://cs162.org/static/readings/ic221_s16_lec17.html)
diff --git a/main.c b/main.c
index eab4047ba7df7bf2f3e04ee0f8f47df2f6d1fe25..e707e5572851db300809d2f8fb5f95dee3a784dc 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,3 +1,4 @@
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -7,6 +8,13 @@
 
 #define BUFLEN 1024
 
+void nlprompt(int sig) {
+   (void)sig;
+
+   printf("\n%% ");
+   fflush(stdout);
+}
+
 char **tokenize(char *p) {
    size_t i;
    static char *result[BUFLEN / 2];
@@ -22,12 +30,97 @@ char **tokenize(char *p) {
    return result;
 }
 
+#define BGMAX 128
+struct {
+   pid_t pgids[BGMAX];
+   size_t bp, sp;
+} bgps;
+
+void pushbgps(pid_t pgid) {
+   if ((bgps.sp + 1) % BGMAX == bgps.bp) killpg(bgps.pgids[bgps.bp++], SIGKILL);
+   bgps.pgids[++bgps.sp] = pgid;
+// printf("PUSHED pgid %d to stack: bp = %zu, sp = %zu\n", pgid, bgps.bp, bgps.sp);
+}
+
+pid_t popbgps(void) {
+   return bgps.sp != bgps.bp ? bgps.pgids[bgps.sp--] : -1;
+}
+
+void await(pid_t cpgid) {
+   int status;
+   pid_t pgid;
+
+   if (waitpid(cpgid, &status, WUNTRACED) == -1) {
+       dprintf(STDERR_FILENO, "ash: Unable to await previous command: %s\n",
+               strerror(errno));
+       exit(EXIT_FAILURE);
+   }
+   if (!WIFSTOPPED(status)) { // Reap
+       while (waitpid(-cpgid, NULL, 0) != -1);
+       if (errno != ECHILD) {
+           dprintf(STDERR_FILENO, "ash: Unable to await previous command: %s\n",
+                   strerror(errno));
+           exit(EXIT_FAILURE);
+       }
+       errno = 0;
+   }
+   if ((pgid = getpgid(0)) == -1) {
+       dprintf(STDERR_FILENO, "ash: Unable to get group process id: %s\n",
+               strerror(errno));
+       exit(EXIT_FAILURE);
+   }
+   if (signal(SIGTTOU, SIG_IGN) == SIG_ERR) {
+       dprintf(STDERR_FILENO, "ash: Unable to ignore SIGTTOU signal: %s\n",
+               strerror(errno));
+       exit(EXIT_FAILURE);
+   }
+   if (tcsetpgrp(STDIN_FILENO, pgid) == -1) {
+       dprintf(STDERR_FILENO, "ash: Unable to set tty foreground "
+               "process group: %s\n", strerror(errno));
+       exit(EXIT_FAILURE);
+   }
+   if (signal(SIGTTOU, SIG_DFL) == SIG_ERR) {
+       dprintf(STDERR_FILENO, "ash: Unable to set SIGTTOU signal: %s\n",
+               strerror(errno));
+       exit(EXIT_FAILURE);
+   }
+
+   if (WIFSIGNALED(status)) printf("\n");
+   if (WIFSTOPPED(status)) pushbgps(cpgid);
+}
+
+int builtin(char **tokens) {
+   pid_t fgpgid;
+
+   if (strcmp(tokens[0], "cd") == 0) {
+       if (chdir(tokens[1]) == -1) {
+           dprintf(STDERR_FILENO, "ash: Unable to cd: %s\n", strerror(errno));
+           exit(EXIT_FAILURE);
+       }
+   } else if (strcmp(tokens[0], "fg") == 0) {
+       if ((fgpgid = popbgps()) == -1) return 1;
+       if (tcsetpgrp(STDIN_FILENO, fgpgid) == -1) {
+           dprintf(STDERR_FILENO, "ash: Unable to set tty foreground "
+                   "process group: %s\n", strerror(errno));
+           exit(EXIT_FAILURE);
+       }
+       if (killpg(fgpgid, SIGCONT) == -1) {
+           dprintf(STDERR_FILENO, "ash: Unable to wake up process group "
+                   "%d to wake up: %s\n", fgpgid, strerror(errno));
+           exit(EXIT_FAILURE);
+       }
+       await(fgpgid);
+   } else return 0;
+
+   return 1;
+}
+
 int main(void) {
    char buffer[BUFLEN];
    char **tokens;
-   pid_t cpid;
-   int status;
+   pid_t cpgid;
 
+   signal(SIGINT, nlprompt);
    for (;;) {
        printf("%% ");
        if (fgets(buffer, BUFLEN, stdin) == NULL) {
@@ -42,18 +135,31 @@ int main(void) {
        }
        tokens = tokenize(buffer);
 
-       if ((cpid = fork()) == 0)
+       if (builtin(tokens)) continue;
+
+       if ((cpgid = fork()) == 0) {
            if (execvp(tokens[0], tokens) == -1) {
                dprintf(STDERR_FILENO, "ash: Unable to run command `%s': %s\n",
                        tokens[0], strerror(errno));
                exit(EXIT_FAILURE);
            }
-       if (waitpid(cpid, &status, 0) == -1) {
-           dprintf(STDERR_FILENO, "ash: Unable to await command `%s': %s\n",
-                   tokens[0], strerror(errno));
+       }
+       if (setpgid(cpgid, cpgid) == -1) {
+           dprintf(STDERR_FILENO, "ash: Unable to change child group process id: %s\n",
+                   strerror(errno));
+           exit(EXIT_FAILURE);
+       }
+       if (tcsetpgrp(STDIN_FILENO, cpgid) == -1) {
+           dprintf(STDERR_FILENO, "ash: Unable to set tty foreground "
+                   "process group: %s\n", strerror(errno));
+           exit(EXIT_FAILURE);
+       }
+       if (killpg(cpgid, SIGCONT) == -1) {
+           dprintf(STDERR_FILENO, "ash: Unable to signal child to wake up: %s\n",
+                   strerror(errno));
            exit(EXIT_FAILURE);
        }
-       // TODO: Handle WIFEXITED, WIFSIGNALED, WIFSTOPPED differently
+       await(cpgid);
    }
    return 0;
 }