/* simpleshell.c Jeff Ondich, 5/9/08 This shell executes pipelines like this: command1 | command2 | command3 ... The full intent of this shell is to illustrate how you can string together a pipeline of commands. Thus, I've simplified it by: -- Assuming that no command has any command-line arguments -- Not implementing >, <, or & -- Not looping back to the prompt to do another command. */ #include #include #include #include #include #include #include #include void trim(char *s); char **splitPipeline(char *pipeline, int *pNCommands); void freeCommandList(char **commandList, int nCommands); const int kPipelineBufferSize = 200; int main() { int k, nCommands, stdinFD, stdoutFD, status; int pipeFD[2]; char **commandList; char pipeline[kPipelineBufferSize]; /* Get the pipeline of commands from the user, and split it into command strings. */ printf("Please enter a sequence of one-token commands, separated by |'s.\n"); printf("> "); fgets(pipeline, kPipelineBufferSize, stdin); commandList = splitPipeline(pipeline, &nCommands); stdinFD = dup(STDIN_FILENO); stdoutFD = dup(STDOUT_FILENO); /* Launch the commands. */ for( k=0; k + 1 < nCommands; k++ ) { if( pipe(pipeFD) < 0 ) { perror("Trouble creating a pipe.\n"); break; } dup2(pipeFD[1], STDOUT_FILENO); close(pipeFD[1]); if( fork() == 0 ) { close(pipeFD[0]); fprintf(stderr, "Launching command %s with pid 0x%x\n", commandList[k], getpid()); execlp(commandList[k], commandList[k], NULL); } dup2(pipeFD[0], STDIN_FILENO); close(pipeFD[0]); } if( nCommands > 0 ) { if( fork() == 0 ) { dup2(stdoutFD, STDOUT_FILENO); fprintf(stderr, "Launching command %s with pid 0x%x\n", commandList[k], getpid()); execlp(commandList[k], commandList[k], NULL); } } /* Wait for the commands to exit. */ dup2(stdinFD, STDIN_FILENO); dup2(stdoutFD, STDOUT_FILENO); for( k=0; k < nCommands; k++ ) { wait(&status); fprintf(stderr, "Process ended with status 0x%x\n", status); } freeCommandList(commandList, nCommands); return 0; } void trim(char *s) { char *src, *dest; assert(s != NULL); /* Scan forward to the first non-space character. */ src = s; while( isspace(*src) ) src++; /* Shift the rest of the string to eliminate the left-side spaces. */ dest = s; while( *src != '\0' ) { *dest = *src; src++; dest++; } /* Scan backwards from the right side to eliminate spaces. */ dest--; while( dest >= s && isspace(*dest) ) dest--; dest++; *dest = '\0'; } char **splitPipeline(char *pipeline, int *pNCommands) { char *p; char **commandList; int countedThisCommand; int startCommandIndex, endCommandIndex, commandNumber; assert(pipeline != NULL); assert(pNCommands != NULL); /* Count the commands */ *pNCommands = 0; countedThisCommand = 0; for( p=pipeline; *p != '\0'; p++ ) { if( *p == '|' ) countedThisCommand = 0; else if( !isspace(*p) && !countedThisCommand ) { countedThisCommand = 1; (*pNCommands)++; } } /* Allocate space for the array of pointers to the commands. */ commandList = NULL; if( *pNCommands > 0 ) { commandList = (char **)malloc(*pNCommands * sizeof(char *)); if( commandList == NULL ) { fprintf(stderr, "Fatal memory allocation error in splitPipeline.\n"); exit(1); } /* Trim and store the distinct commands. */ commandNumber = 0; startCommandIndex = 0; endCommandIndex = 0; for( commandNumber=0; commandNumber < *pNCommands; commandNumber++ ) { while( pipeline[endCommandIndex] != '\0' && pipeline[endCommandIndex] != '|' ) endCommandIndex++; commandList[commandNumber] = (char *)malloc(endCommandIndex - startCommandIndex + 1); if( commandList[commandNumber] == NULL ) { fprintf(stderr, "Fatal memory allocation error in splitPipeline (command %d).\n", commandNumber); exit(1); } strncpy(commandList[commandNumber], &pipeline[startCommandIndex], endCommandIndex - startCommandIndex); commandList[commandNumber][endCommandIndex - startCommandIndex] = '\0'; trim(commandList[commandNumber]); endCommandIndex++; startCommandIndex = endCommandIndex; } } return commandList; } void freeCommandList(char **commandList, int nCommands) { int k; assert(nCommands == 0 || (nCommands > 0 && commandList != 0)); for( k=0; k < nCommands; k++ ) free(commandList[k]); free(commandList); }