From d15a15653e26f608e6e4f29e35af9e79105f9cb9 Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Tue, 20 May 2025 22:15:23 -0400 Subject: [PATCH] Pipes added; extremely messy code... --- main.c | 326 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 201 insertions(+), 125 deletions(-) diff --git a/main.c b/main.c index 101ecec..0c3b80d 100644 --- a/main.c +++ b/main.c @@ -274,9 +274,9 @@ struct pg { struct pgs { size_t len; struct pg *b, *c, *t, data[MAXPG]; -} sus = INITPGS(sus), bg = INITPGS(bg); +} spgs = INITPGS(spgs), bgpgs = INITPGS(bgpgs); -struct pgs *recent = &sus; +struct pgs *recent = &spgs; #define PLUSONE(s, m) ((s).data + ((s).m - (s).data + 1) % (s).len) #define INC(s, m) ((s).m = PLUSONE(s, m)) @@ -330,10 +330,13 @@ int waitfg(pid_t cpgid) { if (waitpid(cpgid, &status, WUNTRACED) != -1 && !WIFSTOPPED(status)) { while (waitpid(-cpgid, NULL, 0) != -1); - if (errno != ECHILD && killpg(cpgid, SIGKILL) == -1) _Exit(EXIT_FAILURE); + if (errno != ECHILD && killpg(cpgid, SIGKILL) == -1) + err(EXIT_FAILURE, "Unable to kill child process group %d", cpgid); } - if (signal(SIGTTOU, SIG_IGN) == SIG_ERR) _Exit(EXIT_FAILURE); - if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE); + if (signal(SIGTTOU, SIG_IGN) == SIG_ERR) + err(EXIT_FAILURE, "Unable to ignore SIGTTOU signal"); + if (tcsetpgrp(STDIN_FILENO, self) == -1) + err(EXIT_FAILURE, "Unable to set self back to foreground process group"); if (signal(SIGTTOU, SIG_DFL) == SIG_ERR) warn("Ignoring signal SIGTTOU"); if (WIFSIGNALED(status)) { @@ -346,8 +349,8 @@ int waitfg(pid_t cpgid) { }; if (tcgetattr(STDIN_FILENO, &pg.config) == -1) warn("Unable to save termios state for stopped process group %d", cpgid); - push(&sus, pg); - recent = &sus; + push(&spgs, pg); + recent = &spgs; result = WSTOPSIG(status); } else result = WEXITSTATUS(status); @@ -357,96 +360,142 @@ int waitfg(pid_t cpgid) { return result; } -int builtin(char **tokens) { +#define BUILTINSIG(name) int name(char **tokens) + +BUILTINSIG(cd) { + if (!tokens[1]) return 1; + if (chdir(tokens[1]) != -1) return 0; + warn("Unable to change directory to `%s'", tokens[1]); + return 1; +} + +BUILTINSIG(fg) { long id; struct pg pg; - int fg; - - if (strcmp(tokens[0], "cd") == 0) { - if (chdir(tokens[1]) == -1) - warn("Unable to change directory to `%s'", tokens[1]); - } else if (strcmp(tokens[0], "fg") == 0) { - if (tokens[1]) { - errno = 0; - if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) { - warn("Invalid process group id"); - return 1; - } - if (find(&sus, (pid_t)id)) pg = *sus.c; - else if (find(&bg, (pid_t)id)) pg = *bg.c; - else { - warn("Unable to find process group %ld", id); - return 1; - } - } else if (!(pg = peek(recent)).id) { - warnx("No processes to bring into the foreground"); - return 1; - } - if (tcsetattr(STDIN_FILENO, TCSANOW, &pg.config) == -1) { - warn("Unable to reload termios state for process group %d", pg.id); - return 1; - } - if (tcsetpgrp(STDIN_FILENO, pg.id) == -1) { - warn("Unable to bring process group %d to foreground", pg.id); - if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) - warn("Unable to set termios state back to raw mode"); + + if (tokens[1]) { + errno = 0; + if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) { + warn("Invalid process group id"); return 1; } - if (killpg(pg.id, SIGCONT) == -1) { - if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE); - warn("Unable to wake up suspended process group %d", pg.id); - if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) - warn("Unable to set termios state back to raw mode"); + if (find(&spgs, (pid_t)id)) pg = *spgs.c; + else if (find(&bgpgs, (pid_t)id)) pg = *bgpgs.c; + else { + warn("Unable to find process group %ld", id); return 1; } - if (tokens[1]) delete(recent); else pull(recent); - waitfg(pg.id); - } else if (strcmp(tokens[0], "bg") == 0) { - if (tokens[1]) { - errno = 0; - if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) { - warn("Invalid process group id"); - return 1; - } - if (!find(&sus, (pid_t)id)) { - warn("Unable to find process group %ld", id); - return 1; - } - pg = *sus.c; - } else if (!(pg = peek(&sus)).id) { - warnx("No suspended processes to run in the background"); + } else if (!(pg = peek(recent)).id) { + warnx("No processes to bring into the foreground"); + return 1; + } + if (tcsetattr(STDIN_FILENO, TCSANOW, &pg.config) == -1) { + warn("Unable to reload termios state for process group %d", pg.id); + return 1; + } + if (tcsetpgrp(STDIN_FILENO, pg.id) == -1) { + warn("Unable to bring process group %d to foreground", pg.id); + if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) + warn("Unable to set termios state back to raw mode"); + return 1; + } + if (killpg(pg.id, SIGCONT) == -1) { + if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE); + warn("Unable to wake up suspended process group %d", pg.id); + if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) + warn("Unable to set termios state back to raw mode"); + return 1; + } + if (tokens[1]) delete(recent); else pull(recent); + waitfg(pg.id); + return 0; +} + +BUILTINSIG(bg) { + long id; + struct pg pg; + + if (tokens[1]) { + errno = 0; + if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) { + warn("Invalid process group id"); return 1; } - if (killpg(pg.id, SIGCONT) == -1) { - warn("Unable to wake up suspended process group %d", pg.id); + if (!find(&spgs, (pid_t)id)) { + warn("Unable to find process group %ld", id); return 1; } - if (tokens[1]) delete(&sus); else pull(&sus); - push(&bg, pg); - recent = &bg; - } else { - return 0; + pg = *spgs.c; + } else if (!(pg = peek(&spgs)).id) { + warnx("No suspended processes to run in the background"); + return 1; + } + if (killpg(pg.id, SIGCONT) == -1) { + warn("Unable to wake up suspended process group %d", pg.id); + return 1; } + if (tokens[1]) delete(&spgs); else pull(&spgs); + push(&bgpgs, pg); + recent = &bgpgs; + return 0; +} + +struct builtin { + char *name; + int (*func)(char **tokens); +}; + +#define BUILTIN(name) {#name, name} +BUILTINSIG(which); +struct builtin builtins[] = { + BUILTIN(cd), + BUILTIN(fg), + BUILTIN(bg), + BUILTIN(which), + BUILTIN(NULL), +}; + +BUILTINSIG(which) { + struct builtin *builtin; + + if (!tokens[1]) return 1; + for (builtin = builtins; builtin->func; ++builtin) + if (strcmp(tokens[1], builtin->name) == 0) { + puts("Built-in command"); + return 0; + } + // TODO: Find command in PATH return 1; } +int checkbuiltin(char **args, int *statusp) { + struct builtin *builtinp; + + for (builtinp = builtins; builtinp->func; ++builtinp) + if (strcmp(*args, builtinp->name) == 0) { + *statusp = builtinp->func(args); + return 1; + } + return 0; +} + void waitbg(int sig) { int status; pid_t pid, pgid; (void)sig; - for (bg.c = bg.b; bg.c != bg.t; INC(bg, c)) - do switch ((pid = waitpid(-bg.c->id, &status, WNOHANG | WUNTRACED))) { + for (bgpgs.c = bgpgs.b; bgpgs.c != bgpgs.t; INC(bgpgs, c)) + do switch ((pid = waitpid(-bgpgs.c->id, &status, WNOHANG | WUNTRACED))) { case -1: if (errno != ECHILD) warn("Unable to wait on some child processes"); case 0: break; default: if (WIFSTOPPED(status)) { - push(&sus, *bg.c); - delete(&bg); - recent = &sus; + push(&spgs, *bgpgs.c); + delete(&bgpgs); + recent = &spgs; pid = 0; } } while (pid > 0); @@ -455,8 +504,9 @@ void waitbg(int sig) { int main(void) { char *input; struct cmd *cmds; - pid_t cpid; - int status; + struct builtin *builtin; + pid_t pipegpid, cpid, id; + int status, cpipe[2], ppipe[2]; readhist(); if (atexit(writehist) == -1) @@ -468,75 +518,101 @@ int main(void) { raw.c_lflag &= ~ICANON & ~ECHO & ~ISIG; raw.c_lflag |= ECHONL; if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) - err(EXIT_FAILURE, "Unable to set termios structure"); + err(EXIT_FAILURE, "Unable to initialize termios structure"); if (atexit(ashexit) == -1) err(EXIT_FAILURE, "Unable to add `ashexit()' to `atexit()'"); if ((self = getpgid(0)) == -1) err(EXIT_FAILURE, "Unable to get pgid of self"); - for (;; waitbg(0)) { + for (pipegpid = 0;; waitbg(0)) { signal(SIGCHLD, waitbg); - if (!(input = getinput())) continue; - signal(SIGCHLD, SIG_DFL); - for (cmds = getcmds(input); (*cmds).args; ++cmds) { - switch ((*cmds).type) { - case PIPE: - break; - case OR: - case BG: - case AND: - case SEMI: - if (!builtin((*cmds).args)) { - if ((cpid = fork()) == -1) { - warn("Unable to fork command `%s'", *(*cmds).args); - goto end; - } else if (cpid == 0) { - if (execvp(*(*cmds).args, (*cmds).args) == -1) { - warn("Unable to run command `%s'", *(*cmds).args); + if (!(input = getinput())) { + signal(SIGCHLD, SIG_DFL); // TODO: Make this not repeated... + continue; + } + signal(SIGCHLD, SIG_DFL); // ...with here + ppipe[1] = ppipe[0] = 0; + for (cmds = getcmds(input); cmds->args; ++cmds) { + if (cmds->type == PIPE) { + if (pipe(cpipe) == -1) warn("Unable to create pipe"); + if ((cpid = fork()) == 0) { + if (dup2(ppipe[0], 0) == -1) warn("Unable to read from pipe"); + if (dup2(cpipe[1], 1) == -1) warn("Unable to write to pipe"); + close(ppipe[0]); + close(ppipe[1]); + close(cpipe[0]); + close(cpipe[1]); + if (checkbuiltin(cmds->args, &status)) _Exit(EXIT_SUCCESS); + if (execvp(*cmds->args, cmds->args) == -1) { + warn("Unable to exec `%s' in pipe", *cmds->args); + _Exit(EXIT_FAILURE); + } + } + if (ppipe[0] && close(ppipe[0]) == -1) + warn("Unable to close read end of previous pipe"); + if (ppipe[1] && close(ppipe[1]) == -1) + warn("Unable to close write end of previous pipe"); + ppipe[0] = cpipe[0]; + ppipe[1] = cpipe[1]; + if (!pipegpid) { + pipegpid = cpid; + push(&bgpgs, (struct pg){.id = pipegpid, .config = canonical,}); + } + setpgid(cpid, pipegpid); + continue; + } else { + if (pipegpid || !checkbuiltin(cmds->args, &status)) { + if ((cpid = fork()) == 0) { + if (pipegpid) { + if (dup2(ppipe[0], 0) == -1) { + warn("Unable to read from pipe"); + _Exit(EXIT_FAILURE); + } + close(ppipe[0]); + close(ppipe[1]); + if (checkbuiltin(cmds->args, &status)) _Exit(EXIT_SUCCESS); + } + if (execvp(*cmds->args, cmds->args) == -1) { + warn("Unable to exec `%s'", *cmds->args); _Exit(EXIT_FAILURE); } } - if (setpgid(cpid, cpid) == -1) { - warn("Unable to set process group of process %d", cpid); - if (kill(cpid, SIGKILL) == -1) - warn("Unable to kill process %d; manual termination required", cpid); - goto end; - } - if ((*cmds).type == BG) { - push(&bg, (struct pg){ - .id = cpid, - .config = canonical, - }); - recent = &bg; - } else { + if (pipegpid) { + id = pipegpid; + close(ppipe[0]); + close(ppipe[1]); + } else id = cpid; + setpgid(cpid, id); + if (cmds->type == BG) recent = &bgpgs; + else { if (tcsetattr(STDIN_FILENO, TCSANOW, &canonical) == -1) warn("Unable to set termios structure"); - if (tcsetpgrp(STDIN_FILENO, cpid) == -1) { - warn("Unable to bring process group %d to foreground", cpid); - if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) - warn("Unable to set termios state back to raw"); - if (killpg(cpid, SIGKILL) == -1) - warn("Unable to kill process group %d; manual termination required", cpid); - goto end; - } - if (killpg(cpid, SIGCONT) == -1) { - if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE); - warn("Process group %d may be blocked; killing it", cpid); - if (killpg(cpid, SIGKILL) == -1) - warn("Unable to kill process group %d; manual termination required", cpid); - else warn("Successfully terminated process group %d", cpid); + if (tcsetpgrp(STDIN_FILENO, id) == -1) { + warn("Unable to bring process group %d to foreground", id); if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) warn("Unable to set termios state back to raw"); - goto end; + if (killpg(id, SIGKILL) == -1) + warn("Unable to kill process group %d; manual termination required", id); + break; } - status = waitfg(cpid); - if ((*cmds).type == AND && status != 0) goto end; - if ((*cmds).type == OR && status == 0) goto end; + // if (killpg(id, SIGCONT) == -1) { // ?? + // if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE); + // warn("Process group %d may be blocked; killing it", id); + // if (killpg(id, SIGKILL) == -1) + // warn("Unable to kill process group %d; manual termination required", id); + // else warn("Successfully terminated process group %d", id); + // if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) + // warn("Unable to set termios state back to raw"); + // break; + // } + if (pipegpid) pull(&bgpgs); + status = waitfg(id); } } + if (cmds->type == AND && status != 0) break; + if (cmds->type == OR && status == 0) break; + if (pipegpid) pipegpid = 0; } } - end:; } - return 0; } -- 2.51.0