//parses a pipeline, returning command struct //modifies global variable t CMD *pipeline() { CMD *cmd = 0, *tmp = 0; cmd = stage(); while (t && ISPIPE(t->type)) { if (!cmd) { DIE("Parse: null command\n"); freeCMD(cmd); return 0; } if (cmd->toType != NONE) { DIE("Parse: two output redirects\n"); freeCMD(cmd); return 0; } tmp = cmd; cmd = mallocCMD(); cmd->type = t->type; cmd->left = tmp; t = t->next; cmd->right = pipeline(); if (!cmd->right) { DIE("Parse: null command\n"); freeCMD(cmd); return 0; } } return cmd; }
// Executes a <pipeline> rooted with cmd and returns the status of the last // command executed. int processPipeline(CMD* cmd) { assert(cmd); assert(ISPIPE(cmd->type) || cmd->type == SIMPLE || cmd->type == SUBCMD); if(ISPIPE(cmd->type)) { int pipeStatus = execPipe(cmd); updateStatusVar(pipeStatus); return pipeStatus; } else { return processStage(cmd); } }
CMD *parsePipeline(token **lstHead) { CMD *cmd = parseStage(lstHead); if(cmd->type == ERROR) { //propagate an error return cmd; } //check to make sure that end of linked list not reached and cmd not NULL! if(cmd && *lstHead && ISPIPE((*lstHead)->type)) { CMD *stageCMD = cmd; cmd = mallocCMD(); cmd->type = (*lstHead)->type; cmd->left = stageCMD; *lstHead = (*lstHead)->next; if(*lstHead == NULL) { //error..incomplete pipeline! fprintf(stderr,"%s\n","Error: Incomplete pipeline command"); return errorCMD(cmd); } cmd->right = parsePipeline(lstHead); if(cmd->right == NULL) { //case of no right argument for pipe fprintf(stderr,"%s\n","Error: Incomplete pipeline command"); return errorCMD(cmd); } if(cmd->right->type == ERROR) { //propagate along an error return cmd->right; } if(stageCMD->toFile != NULL || cmd->right->fromFile != NULL) { //case of multiple redirections involving a pipeline fprintf(stderr,"%s\n","Error: Multiple redirections"); return errorCMD(cmd); } } return cmd; }
CMD* make_stage(token_list** list_ref) { token_list* list = *list_ref; if (!list) return make_error_cmd("could not form stage (no tokens)"); CMD* stage_tree = mallocCMD(); // Current type is NONE int next_type = NONE; // Hold the type of the head token of list bool redir_in = false; // Has there been a previous input redirection? bool redir_out = false; // Loop until hit a pipe or a separator; raise an error if multiple redirect // or both a command and subcommand while ((next_type = type(list)) != -1 && !ISSEP(next_type) && !ISPIPE(next_type) && next_type != PAR_RIGHT) { bool error = false; // Signals whether the current action results in an // error if (next_type == SIMPLE && stage_tree->type != SUBCMD) { int argc = stage_tree->argc + 1; char** argv = stage_tree->argv; argv = realloc(argv, (argc + 1) * sizeof(char*)); // +1 for trailing // NULL required // by freeCMD argv[argc - 1] = strdup(text(list)); argv[argc] = NULL; stage_tree->argc = argc; stage_tree->argv = argv; stage_tree->type = SIMPLE; } else if ((next_type == RED_IN || next_type == RED_HERE) && !redir_in) { error = make_redirect(stage_tree, &redir_in, next_type, &list); } else if (ISREDOUT(next_type) && !redir_out) { error = make_redirect(stage_tree, &redir_out, next_type, &list); } else if (next_type == PAR_LEFT && stage_tree->type == NONE) { // Can't have two subcommands in a stage advance(&list); // Advance past left parentheses CMD* subcmd_tree = make_cmd(&list); error = subcmd_tree->type == ERROR || type(list) != PAR_RIGHT; stage_tree->type = SUBCMD; stage_tree->left = subcmd_tree; } else { error = true; } if (error) { freeCMD(stage_tree); return make_error_cmd("Invalid stage"); } advance(&list); } // Make sure we didn't double-redirect if (ISPIPE(next_type) && redir_out) { freeCMD(stage_tree); return make_error_cmd("Double redirect - pipe and >"); } // Make sure there was actually an argument if (stage_tree->type == NONE) { freeCMD(stage_tree); return make_error_cmd("No arguments for simple command"); } assert(stage_tree->type != NONE); *list_ref = list; return stage_tree; }
// Executes a pipeline and returns the exit status of the pipe. The arg // pipeRoot is the PIPE or PIPE_ERR command at the root of the pipeline. // This function draws upon code from Professor Stan Eisenstat at Yale // University int execPipe(CMD* pipeRoot) { // count the number of stages in the pipeline int numStages = 1; for(CMD* cmd = pipeRoot; ISPIPE(cmd->type); cmd = cmd->right, numStages++); // create table to hold pid and exit status of all stages in the pipe struct { int pid, status; } processTable[numStages]; int fd[2]; // holds file descriptors for the pipe int pid, status; // the pid and status of a single stage int fdIn = STDIN_FD; // the read end of the last pipe, or the original // stdin CMD* cmd = pipeRoot; for(int i = 0; ISPIPE(cmd->type); cmd = cmd->right, i++) { if(pipe(fd) < 0 || (pid = fork()) < 0) { perror(EXEC_NAME); return errno; } else if(pid == 0) { // child close(fd[0]); // redirect stdin to the last pipe read (if there was a last pipe) if(fdIn != STDIN_FD) { dup2(fdIn, STDIN_FD); close(fdIn); } bool shouldCloseFD1 = false; // redirect stdout to the new pipe write (if it's not stdout) if(fd[1] != STDOUT_FD) { dup2(fd[1], STDOUT_FD); shouldCloseFD1 = true; } // if this is a PIPE_ERR, redirect stderr to the new pipe write // (if it's not stderr) if(cmd->type == PIPE_ERR && fd[1] != STDERR_FD) { dup2(fd[1], STDERR_FD); shouldCloseFD1 = true; } if(shouldCloseFD1) close(fd[1]); exit(processStage(cmd->left)); } else { // parent processTable[i].pid = pid; // close the read end of the last pipe if it's not the orig stdin if(i > 0) { close(fdIn); } fdIn = fd[0]; // remember the read end of the new pipe close(fd[1]); } } // cmd is now the right child of last PIPE or PIPE_ERR, the last stage of // the pipeline // if the last stage is a built-in command, it should affect the parent // shell, so execute it here instead of forking off a process if(cmd->type == SIMPLE && IS_BUILTIN(cmd->argv[0])) { processTable[numStages - 1].pid = -1; // unused pid processTable[numStages - 1].status = processSimple(cmd, false); close(fdIn); } else if((pid = fork()) < 0) { perror(EXEC_NAME); return errno; } else if(pid == 0) { // child if(fdIn != STDIN_FD) { dup2(fdIn, STDIN_FD); close(fdIn); } exit(processStage(cmd)); } else { // parent processTable[numStages - 1].pid = pid; close(fdIn); } // wait for children to die signal(SIGINT, SIG_IGN); for(int i = 0; i < numStages; ) { pid = wait(&status); int j; for(j = 0; j < numStages && processTable[j].pid != pid; j++); // only add to the processTable if the child's pid is in the table; // that is, ignore zombies if(j < numStages) { processTable[j].status = status; i++; } } signal(SIGINT, SIG_DFL); for(int i = 0; i < numStages; i++) { if(GET_STATUS(processTable[i].status) != 0) { return GET_STATUS(processTable[i].status); } } return 0; }