From 87a944fb56ba25e845598d8c7a8761affc100964 Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Mon, 5 May 2025 16:38:10 -0400 Subject: [PATCH] Integrate with TTY device --- README.md | 5 +++ main.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 118 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index df172fb..fa0c22b 100644 --- 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 eab4047..e707e55 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -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; } -- 2.51.0