From 7e6c1147dbae5c6e7296e3cf01690ea959100d2e Mon Sep 17 00:00:00 2001 From: djairoh Date: Sat, 16 Mar 2024 23:07:19 +0100 Subject: [PATCH] feat: implemented pipes and redirects + background operator --- Makefile | 2 +- README.md | 50 ++++++++-- assets/tab_completion.png | Bin 0 -> 28546 bytes cmd.c | 186 ++++++++++++++++++++++++++++---------- cmd.h | 3 + main.c | 55 +++++++++-- scanner.c | 13 ++- scanner.h | 4 + shell | Bin 22496 -> 26680 bytes shell.c | 7 +- 10 files changed, 246 insertions(+), 74 deletions(-) create mode 100755 assets/tab_completion.png diff --git a/Makefile b/Makefile index a6d3928..2808fab 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ shell: gcc -std=c99 -Wall -pedantic main.c scanner.c shell.c cmd.c -o shell bonus: - gcc -std=c99 -Wall -DEXT_PROMPT -pedantic main.c scanner.c shell.c cmd.c -o shell + gcc -std=c99 -Wall -DEXT_PROMPT -pedantic main.c scanner.c shell.c cmd.c -o shell -lreadline clean: rm -f *~ diff --git a/README.md b/README.md index 555aafe..c549522 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Bonuses added for lab 3 are at the bottom of this document, starting [here](#navigable-arrow-keys). + # general code structure The main objective of this assignment was to tokenize given inputs and process the resulting tokens according to the given grammar. We decided to seperate the two steps of tokenizing and processing almost entirely, which decouples the input parser from the execution very neatly. The one drawback of this method is that each input is, more or less, processed twice - but we considered this a fair trade-off, as the input for a shell can generally be expected to not be incredulously large. @@ -23,8 +25,6 @@ Builtin | Result --- | --- status | prints the return status of the last run command. exit | exits the shell. -true | sets status to `0`. -false | sets status to `-1`. cd | changes the current working directory. debug | prints debugging information to `stdout`. @@ -65,11 +65,13 @@ typedef struct CommandList { } CommandList; ``` -The `Chain` struct models one complete chain to be executed. It is currently a bit barebones, but will be expanded when we implement piping, redirection, and running in the background. +The `Chain` struct models one complete chain to be executed. It conains a list of commands to execute, and extra information related to I/O - any redirects. These are set to `NULL` if no redirect was given. ```c typedef struct Chain { CommandList commands; bool runInBackground; + char *in; + char *out; } Chain; ``` @@ -90,8 +92,6 @@ The execution is done within a while loop that runs until the `cpy` list is enti - COMPOSITION - BUILTIN -// TODO: discuss what the process is for each enum type. - If the code encounters any other types at this level, it means that the syntax of the input is incorrect. If this happens, an error message is printed out and `cpy` is set to NULL in order to exit the loop. In the end, we use `free()` and `freeTokenList()` to free the memory. @@ -163,10 +163,40 @@ The end result looks like this: ![Our own (less impressive) prompt](./assets/prompt.png) -## true / false -The smallest of our extra implementations, we have added two `builtin`s which directly modify `status`. - * The builtin `true` will set `status` to `0`. - * The builtin `false` will set `status` to `-1`. +## navigable arrow keys +Another standard shell feature, available in everything from the modern fish and zsh, to shells as far back as sh, is that of the arrow key. Because our shell takes input using `getchar()` the user is unable to navigate back in an unfinished prompt. By switching to the GNU readline library, we can make use of the `readline()` function - allowing us to navigate an unfinished prompt in the expected manner. - Really this was more of an implementation to allow us to do proper debugging, but it's still something we added to the shell that was not specifically part of the assignment, so we decided we may as well document it here. +This is a bit hard to illustrate using text/images, so we recommend you build the shell yourself if you wish to play around with this a little bit. + + +## tab completion +Another perk of the GNU readline library is it quite neatly allows for tab completion, bash-style. Use tab while entering a command and watch as the shell tries its best to complete it for you. + +Note that this only works on filenames - the bash-style tab-completion of variables and executable commands is not implemented. Included below is an example; after entering 'cat' we then pressed tab to showcase the completion, which prints all (matching) files in CWD to the shell. + + +![tab completion on filesnames](./assets/tab_completion.png) + +## background operation +Everyone who has ever used a shell before will be familiar with the background operator, but here's a quick recap anyay: Whenever a command is suffixed with a single ampersand (`&`), it is run in the background and control of the shell is immediately returned to the user. The output streams of the command are still connected to the shell, (ergo, `echo 7 &` will still print '7' to stdout, which might mess with the prompt a little), but the command is run as non-blocking. + +Because of the structured approach we took towards chain/command design, it was trivially easy for us to implement this bonus: a simple if statement (checking for the background operator) and we are good to go! + +The relevant snippet of code lives in `executeChain` and reads as follows: +```c + // wait on children + int stat; + #if EXT_PROMPT + if (chain.runInBackground) return; + #endif + for (int i=0; i < numCommands; i++) { + waitpid(pids[i], &stat, 0); + if (WIFEXITED(stat)) { + *status = WEXITSTATUS(stat); + } + } +``` + + + diff --git a/assets/tab_completion.png b/assets/tab_completion.png new file mode 100755 index 0000000000000000000000000000000000000000..24548a99e1c42aa42f0511debba029fa25189780 GIT binary patch literal 28546 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 literal 0 HcmV?d00001 diff --git a/cmd.c b/cmd.c index 5949947..977062f 100644 --- a/cmd.c +++ b/cmd.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "scanner.h" @@ -35,6 +37,8 @@ Chain _newChain() { Chain chain; chain.commands = _newCommandList(); chain.runInBackground = false; + chain.in = NULL; + chain.out = NULL; return chain; } @@ -122,6 +126,21 @@ void _insertCommand(Command cmd, CommandList *list) { list->commands[list->numCommands++] = cmd; } +void addRedirect(List *lp, Chain *chain) { + // exit on invalid syntax; should be unreachable. + if ((*lp)->type != REDIRECT || (*lp)->next->type != FILENAME) exit(1); + + if ((*lp)->t[0] == '>' ) { + // out redirect + chain->out = (*lp)->next->t; + } else { + // in redirect + chain->in = (*lp)->next->t; + } + *lp = (*lp)->next; + *lp = (*lp)->next; +} + /** * This function build up one chain of commands from a given listPointer. * It parses one 'unit' of executables, meaning any number of commands+options chained together by pipes and redirects. @@ -152,10 +171,11 @@ Chain buildChain(List *lp) { *lp = (*lp)->next; break; - // finding a redirect means a bunch of things we haven't thought out yet. + // finding a redirect means we need to take this into account during chain execution; + // add the redirect to the chain struct, and we can deal with it down the line. case REDIRECT: - //TODO: implement redirect - break; + addRedirect(lp, &chain); + break; // finding a background operator means two things: // 1. we must run this chain in the background. @@ -179,48 +199,113 @@ Chain buildChain(List *lp) { return chain; } -/** - * This function executes a given command in a child process. - * To make this a blocking operation, the parent process will wait until the child exits - simultaneously updating the status variable. - * @param cmd the Command to execute. - * @param status variable which will contain the exit status of the command. - */ -void _executeCommand(Command cmd, int* status) { - pid_t pid = fork(); - - switch (pid) { - // fork failed. - case -1: - printf("ERROR: failed to create child!\n"); - *status = -1; - break; - case 0: - // child process - execvp(cmd.command, cmd.arguments); - //this line is only ever reached if execvp fails (for example, when an executable can't be found). - exit(127); - break; - default:; - // parent process; wait for child and update status. - int stat; - wait(&stat); - if (WIFEXITED(stat)) { - *status = WEXITSTATUS(stat); - } - } -} - -// TODO: extend to include I/O redirection and background operation /** * This function executes all commands in a given chain. * It updates the status as it does so. - * This function is barebones at the moment, but will be expanded to include support for redirection and non-blocking execution. + * The function uses the variables chain.in and chain.out for redirection, if necessary. + * Additionally it supports non-blocking execution through the chain,runInBackground attribute. * @param chain the Chain to execute. * @param status variable which will contain the exit status after execution. */ void executeChain(Chain chain, int* status) { - for (int i=0; i < chain.commands.numCommands; i++) { - _executeCommand(chain.commands.commands[i], status); + // variables for clarity + int numCommands = chain.commands.numCommands; + int numPipes = chain.commands.numCommands-1; + pid_t pids[numCommands]; + Command cmd; + + // error if input and output are identical files. + if (chain.in != NULL && chain.out != NULL && !strcmp(chain.in, chain.out)) { + printf("Error: input and output files cannot be equal!\n"); + *status = 2; + return; + } + + // pipe creation + int *pipes = calloc(numPipes*2, sizeof(int)); + for (int i=0; i < numPipes; i++) { + if (pipe(pipes + i*2) < 0) { + perror("ERROR: failed to create pipes!"); + return; + } + } + + // command execution + for (int i=0; i < numCommands; i++) { + cmd = chain.commands.commands[i]; + + pids[i] = fork(); + switch (pids[i]) { + case -1: + printf("ERROR: failed to create child!\n"); + *status =-1; + break; + case 0: + // child process + + // if not first command, connect input to previous output + if (i > 0) { + if (dup2(pipes[(i-1)*2], 0) < 0) { + perror("ERROR: failed to connect pipes!"); + return; + } + } else if (chain.in != NULL) { + // in redirection + int fd0 = open(chain.in, O_RDONLY, 0); + if (dup2(fd0, 0) < 0) { + perror("ERROR: failed to redirect input!"); + return; + } + close(fd0); + } + + // if not last command, connect output to next input + if (i+1 < numCommands) { + if (dup2(pipes[i*2+1], 1) < 0) { + perror("ERROR: failed to connect pipes!"); + return; + } + } else if (chain.out != NULL) { + // output redirection + int fd1 = open(chain.out, O_WRONLY|O_CREAT, 0755); + if (dup2(fd1, 1) < 0) { + perror("ERROR: failed to redirect output!"); + return; + } + close(fd1); + } + + // close all pipes + for (int i = 0; i < numPipes; i++) { + close(pipes[i*2]); + close(pipes[i*2+1]); + } + + execvp(cmd.command, cmd.arguments); + // this line is only ever reached if execvp fails (for example, when an executable can't be found). + exit(127); + break; + default:; + // parent process + break; + } + } + // close pipes + for (int i = 0; i < numPipes; i++) { + close(pipes[i*2]); + close(pipes[i*2+1]); + } + + // wait on children + int stat; + #if EXT_PROMPT + if (chain.runInBackground) return; + #endif + for (int i=0; i < numCommands; i++) { + waitpid(pids[i], &stat, 0); + if (WIFEXITED(stat)) { + *status = WEXITSTATUS(stat); + } } } @@ -237,26 +322,27 @@ bool executeBuiltin(Command cmd, int *status, bool *debug) { bool executeBuiltin(Command cmd, int *status) { #endif if (!strcmp(cmd.command, "status")) { - printf("The most recent exit code is: %d.\n", *status); + printf("The most recent exit code is: %d\n", *status); } else if (!strcmp(cmd.command, "exit")) { return false; - } else if (!strcmp(cmd.command, "true")) { - *status = 0; - } else if (!strcmp(cmd.command, "false")) { - *status = 1; - #if EXT_PROMPT - } else if (!strcmp(cmd.command, "debug")) { - *debug = ! *debug; - printf("Toggled debug to %d.\n", *debug); } else if (!strcmp(cmd.command, "cd")) { if (cmd.numArguments == 2) { - char *PWD = getenv("HOME"); - *status = chdir(PWD); + printf("Error: cd requires folder to navigate to!\n"); + *status = 2; } else { *status = chdir(cmd.arguments[1]); + if (*status) { + printf("Error: cd directory not found!\n"); + *status = 2; + } } + } + #if EXT_PROMPT + else if (!strcmp(cmd.command, "debug")) { + *debug = ! *debug; + printf("Toggled debug to %d.\n", *debug); + } #endif - }// can be expanded by growing the if/else chain. return true; } diff --git a/cmd.h b/cmd.h index cfa7d33..96948e1 100644 --- a/cmd.h +++ b/cmd.h @@ -29,6 +29,8 @@ typedef struct CommandList { typedef struct Chain { CommandList commands; bool runInBackground; + char *in; + char *out; } Chain; @@ -36,6 +38,7 @@ void freeCommand(Command cmd); void freeChain(Chain chain); Command buildCommand(List *lp); +void addRedirect(List *lp, Chain *chain); Chain buildChain(List *lp); void executeChain(Chain chain, int* status); diff --git a/main.c b/main.c index 0b50332..3be85a6 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -12,11 +11,15 @@ #include "cmd.h" // general TODO: -// * properly bind stdin/stdout for piped commands -// * run chains in bg if given '&' -// * manage < and > redirects // * signal handling // * builtins - 'kill' +// * simple command history +// * run scripts with argv[] +// * redirect stderr with the n> operator +// * allow multiple redirects (turn chain.in and chain.out into char**) +// +// Big rewrite: create a 'shell' struct which is the actual program to run +// this struct would take care of things like command history, signal handling, etc /** * This function skips one COMMAND, including redirects and pipes (if any). @@ -44,7 +47,7 @@ void skipCommand(List *lp) { } /** - * This function checks whether the current COMPOSITION operator passes (and thus should execute the next function). + * This function checks whether the current COMPOSITION operator passes (and thus should execute the next command). * @param s the COMPOSITION operator. * @param status the exit status of the last ran command. * @return whether the COMPOSITION succeeds/whether to execute the next command. @@ -57,17 +60,48 @@ bool compositionPasses(char *s, int status) { return false; } + +#if EXT_PROMPT +int execute_script(char* script) { + FILE* file = fopen(script, "r"); + if (file == NULL) { + printf("Error: provided Scriptfile, '%s', does not exist or is inaccessible!\n", script); + return 1; + } + char line[512]; + + while (fgets(line, sizeof(line), file)) { + printf("%s", line); + //TODO: continue working here on the script functionality later, after the Refactor + } + fclose(file); + return 0; +} +#endif + /** * Driver function. Contains the main loop which collects and parses input, and decides what and how to execute + output. */ int main(int argc, char *argv[]) { + + #if EXT_PROMPT + if (argc >= 2) { + if (argc > 2) { + printf("Error: unexpected number of arguments!\n"); + return 0; + } else { + if (execute_script(argv[1])) return 1; + } + } + + #endif char *inputLine; List tokenList; int status = 0; bool do_loop = true; #if EXT_PROMPT bool debug = false; - char cwd[101], *usr; + char cwd[128], *usr, prompt[256]; #endif // Disable buffering so we don't have to deal with out-of-order prints. @@ -80,10 +114,13 @@ int main(int argc, char *argv[]) { #if EXT_PROMPT getcwd(cwd, sizeof(cwd)); usr = getlogin(); - if (!status) printf("\x1b[33m%s \x1b[36m%s \x1b[32m>\x1b[0m ", usr, cwd); - if ( status) printf("\x1b[33m%s \x1b[36m%s \x1b[31m>\x1b[0m ", usr, cwd); + if (!status) snprintf(prompt, 200*sizeof(char), "\x1b[33m%s \x1b[36m%s \x1b[32m>\x1b[0m ", usr, cwd); + if ( status) snprintf(prompt, 200*sizeof(char), "\x1b[33m%s \x1b[36m%s \x1b[31m>\x1b[0m ", usr, cwd); + inputLine = readInputLine(prompt); + #else + inputLine = readInputLine(); #endif - inputLine = readInputLine(); + // We have modified the readInputLine function to return NULL on EOF. if (inputLine == NULL) { do_loop = false; diff --git a/scanner.c b/scanner.c index ad590b7..a6326f9 100644 --- a/scanner.c +++ b/scanner.c @@ -7,7 +7,17 @@ #include "scanner.h" -//TODO: handle EOF more completely (currently only EOF at the start of the line is handled) +#if EXT_PROMPT +#include + +/** + * Reads an inputline from stdin. + * @return a string containing the inputline. + */ +char *readInputLine(char const *prompt) { + return readline(prompt); +} +#else /** * Reads an inputline from stdin. * @return a string containing the inputline. @@ -43,6 +53,7 @@ char *readInputLine() { s[i] = '\0'; return s; } +#endif /** * The function isOperatorCharacter checks whether the input paramater \param c is an operator. diff --git a/scanner.h b/scanner.h index 68b0496..d5aa933 100644 --- a/scanner.h +++ b/scanner.h @@ -24,7 +24,11 @@ typedef struct ListNode { } ListNode; +#if EXT_PROMPT +char *readInputLine(char const *prompt); +#else char *readInputLine(); +#endif List getTokenList(char *s); diff --git a/shell b/shell index 6568702f05201fb4ac512567b7e8d7b1a36692af..e2bedf0dc1ff64a47443df13ab0cec9acd4d0062 100755 GIT binary patch literal 26680 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 diff --git a/shell.c b/shell.c index d6be059..83544d2 100644 --- a/shell.c +++ b/shell.c @@ -123,6 +123,9 @@ bool _parsePipeline(List *lp) { * @return a bool denoting whether the filename was parsed successfully. */ bool _parseFilename(List *lp) { + if (*lp == NULL) return false; + + //we run a POSIX compliant system, meaning all characters save '/' and NULL are allowed in filenames. //NULL is already taken care of by the List library, and / just means the file is located in a directory. //as a result the only limit we place on filenames are the reserved (operator) characters. @@ -178,11 +181,9 @@ bool _parseBuiltIn(List *lp) { char *builtIns[] = { "exit", "status", - "true", - "false", + "cd", #if EXT_PROMPT "debug", - "cd", #endif NULL };