/* launch.c * Application launcher and built-ins for a Minimally Usable SHell (MUSH) * modified by Douglas Jones to solve MP6, May 1, 2018 * modified by Douglas Jones moving substitute() to a new file, Apr 12, 2018 * modified by Douglas Jones to start MP4, Mar 2, 2018 * extracted from mush.c, Feb 25, 2018 * rewritten by Douglas Jones for MP2 in CS:3620, Feb 13, 2018 * originally by Douglas Jones */ /* Note: MP6 did not completely specify several behaviors: * -- this version supports long pipelines like a | b | c | d, not just a | b * -- | a -- gives an error message * -- a | -- no error, but the output of a is lost into a pipe to nowhere * -- exit | a -- does not run a, it just exits mush * -- a | exit -- runs a, with output piped to nowhere as mush exits * * The above behaviors were not the result of design decisions, rather, they * were the result of doing nothing to detect these special cases or deal with * them. */ #include /* needed for fputs, NULL */ #include /* needed for exit(), getenv() */ #include /* needed for fork(), dup2(), close() */ #include /* needed for wait() */ #include /* needed for strcmp() */ #include "globals.h" /* global variables of mush */ /* default file descriptors used by Unix/Linux */ #define defaultin 0 #define defaultout 1 extern char** environ; /* environment passed here by whoever exec'd this */ void launchcmd( char ** args, int infile, int outfile ) /* Execute the command parameter: args, points to an array of command line arguments where args[0] is a command and args[1] and up are the arguments args[i]==NULL marks the end of the argument list infile and outfile, file descriptor numbers for launched command globals: command, not explicitly referenced, but holds the argument text argv, not explicitly referenced, but args refers to (part of) it. note: Except for built-in commands, uses fork-exec to launch applications warning: Does not wait for forked process to terminate! */ { /* built-in commands */ if (args[0] == NULL) return; /* blank command line */ if (args[0][0] == '#') return; /* comment */ if (!strcmp( args[0], "exit" )) { /* this could get exit succes/failure code from args[1] */ exit( EXIT_SUCCESS ); } /* control only reaches here if the command is not built-in */ if (fork() == 0) { /*child*/ /* before launch, do any change of input or output required */ if (infile != defaultin) { dup2( infile, defaultin ); close( infile ); } if (outfile != defaultout) { dup2( outfile, defaultout ); close( outfile ); } /* first try the literal file name given */ execve( args[0], args, environ ); /* if that doesn't work, try prefixes from the search path */ trypath( args[0], args, environ ); /* if that doesn't work, give up */ fputs( progname, stderr ); fputs( ": no such command \"", stderr ); fputs( args[0], stderr ); fputs( "\"\n", stderr ); exit( EXIT_FAILURE ); } /* note again: it is the caller's job to wait for termination! */ } void launch() /* Execute the command line in argv[], possibly consisting of multiple commands */ { char ** arg = argv; /* setup to scan command line */ char ** start = arg; /* we are at the start of a command */ int in = defaultin; /* setup default file, will change if pipe */ int out; /* out file changes with pipes, etc */ int count = 0; /* number of child processes launched */ while (*arg != NULL) { int pipefd[2]; /* used in pipe system call if needed */ arg++; /* look for end of command and setup depending on the end */ while ((*arg != NULL) && strcmp( *arg, "|" )) { arg++; } if (*arg != NULL) { /* equivalently, if *arg is "|" */ *arg = NULL; /* mark end of sub-command */ arg++; /* setup to process next sub-command */ /* create a pipe and setup to use it */ pipe( pipefd ); out = pipefd[1]; } else { /* not a pipe or last command in pipe */ out = defaultout; } /* launch the command */ count = count + 1; launchcmd( start, in, out ); /* clean up after any piped input or output */ if (in != defaultin) { /* we just launched a pipe-reader */ close( in ); /* we don't need that file any more */ } if (out != defaultout) { /* we just launched a pipe-writer */ close( out ); /* we don't need that file any more */ in = pipefd[0]; /* the next one will read this pipe */ } start = arg; } /* now wait for all the forked children to terminate */ while (count > 0) { wait( NULL ); count = count - 1; } }