void execStages(pipeline thePipeline) { pid_t* children = (pid_t*)malloc(sizeof(pid_t) * thePipeline->length); int i; clstage cursor = thePipeline->stage; int** pipes; if(children == NULL) { perror("malloc"); return; } pipes = (int**)malloc(sizeof(int*) * (thePipeline->length - 1)); if(pipes == NULL) { if(thePipeline->length - 1 > 1) { perror("malloc"); free(children); return; } } else { for(i = 0; i < thePipeline->length - 1; ++i) { pipes[i] = (int*)malloc(sizeof(int) * 2); if(pipes[i] == NULL) { int j; perror("malloc"); for(j = 0; j <= i; ++j) { free(pipes[j]); } free(pipes); free(children); return; } } } for(i = 0; i < thePipeline->length - 1; ++i) { if(pipe(pipes[i]) < 0) { int j; perror("pipe"); for(j = 0; j < thePipeline->length - 1; ++j) { free(pipes[j]); } free(pipes); free(children); return; } } for(i = 0; i < thePipeline->length; ++i) { int j; job_item* theJob = (job_item*)malloc(sizeof(job_item)); if(theJob == NULL) { int j; perror("malloc"); for(j = 0; j < thePipeline->length - 1; ++j) { free(pipes[j]); } free(pipes); free(children); return; } cursor = &(thePipeline->stage[i]); if((children[i] = fork()) < 0) { int j; perror("fork"); free(theJob); for(j = 0; j < thePipeline->length - 1; ++j) { free(pipes[j]); } free(pipes); free(children); return; } else if(children[i] == 0) { /* do child stuff for this stage, including exec-ing */ /* if it's not the first child, set it up to read from the * pipe before it */ if(i > 0) { dup2(pipes[i-1][0], STDIN_FILENO); } /* otherwise check to make sure not supposed to read from input * file */ else if(cursor->inname != NULL) { FILE* input = fopen(cursor->inname, "r"); if(input == NULL) { /* may need to be more specific */ perror(cursor->inname); exit(-1); } dup2(fileno(input), STDIN_FILENO); fclose(input); } /* if it's not the last child, set it up to write to the pipe * after it */ if(i < thePipeline->length - 1) { dup2(pipes[i][1], STDOUT_FILENO); } /* otherwise check to see if supposed to write to file */ else if(cursor->outname != NULL) { FILE* output = fopen(cursor->outname, "w"); if(output == NULL) { /* may need to be more specific */ perror(cursor->outname); exit(-1); } dup2(fileno(output), STDOUT_FILENO); fclose(output); } /* close every pipe */ for(j = 0; j < thePipeline->length - 1; ++j) { close(pipes[j][0]); close(pipes[j][1]); } execvp(cursor->argv[0], cursor->argv); perror(cursor->argv[0]); exit(-1); } /* do parent stuff for this stage */ theJob->command = newstr(thePipeline->stage[i].argv[0]); theJob->job_status = FOREGROUND; theJob->pid = children[i]; addJobToList(theJob); foregroundJobNoWait(theJob); } for(i = 0; i < thePipeline->length - 1; ++i) { close(pipes[i][0]); close(pipes[i][1]); } while(areForegroundJobs()) { ; } for(i = 0; i < thePipeline->length - 1; ++i) { free(pipes[i]); } free(pipes); free(children); return; }
/* This takes a pipeline and either execs it or runs the built-in command */ void runPipeline(pipeline thePipeline) { if(executeCommand(thePipeline) == 0) { pid_t childPid; job_item* theJob; int i; /* must check every argv */ int j; for(j = 0; j < thePipeline->length; ++j) { for(i = 1; i < thePipeline->stage[j].argc; ++i) { if(strcmp(thePipeline->stage[j].argv[i], "&") == 0) { /* exec in the background */ if(i < thePipeline->stage[j].argc - 1) { fprintf(stderr, "Junk after '&'.\n"); return; } if(thePipeline->length > 1) { fprintf(stderr, "Pipelines cannot be backgrounded.\n"); return; } if((childPid = fork()) < 0) { perror("fork"); return; } else if(childPid == 0) { /* exec it in the background, ignoring the last argv[] */ thePipeline->stage->argv[thePipeline->stage->argc - 1] = (char*)0; execvp(thePipeline->stage->argv[0], thePipeline->stage->argv); perror(thePipeline->stage->argv[0]); exit(-1); } /* add the new job to the background */ theJob = (job_item*)malloc(sizeof(job_item)); if(theJob == NULL) { perror("malloc"); return; } theJob->command = newstr(thePipeline->cline); theJob->job_status = BACKGROUND; theJob->pid = childPid; addJobToList(theJob); backgroundJob(theJob); return; } } } /* exec the whole pipeline in the foreground */ execStages(thePipeline); } }
int main(void) { static char buf[100]; int fd; //int status; // Assumes three file descriptors open. while((fd = open("console", O_RDWR)) >= 0){ if(fd >= 3){ close(fd); break; } } // Read and run input commands. int jobcntr = 0; jlist = malloc(sizeof(*jlist)); jlist->first = 0; jlist->last = 0; jlist->fgJob = 0; int waitStatus; int bPrintDollar = 1; int cmdLen = 0; while((cmdLen = getcmd(buf, sizeof(buf), bPrintDollar)) >= 0){ clearFinishedJobs(jlist); // Check if there's a forground job running. // If so - the shell should not function, but only transfer the data received from the console to the job's pipe if (jlist->fgJob != 0) { bPrintDollar = 0; write(jlist->fgJob->fd, buf, cmdLen); continue; } bPrintDollar = 1; if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ') { // Clumsy but will have to do for now. // Chdir has no effect on the parent if run in the child. buf[strlen(buf)-1] = 0; // chop \n if(chdir(buf+3) < 0) printf(2, "cannot cd %s\n", buf+3); continue; } if(buf[0] == 'j' && buf[1] == 'o' && buf[2] == 'b' && buf[3] == 's' && (buf[4] == '\n' || buf[4] == ' ')) { struct job* job = jlist->first; while (job != 0) { printjob(job->jid); job = job->next; } if (!jlist->first) printf(1, "There are no jobs.\n"); continue; } if(buf[0] == 'f' && buf[1] == 'g' && (buf[2] == '\n' || buf[2] == ' ')) { char* c = &buf[3]; int jid = atoi(c); struct job* j = jlist->first; while ( j->jid != jid && j != 0) { j = j->next; } jlist->fgJob = j; //fg(jid); continue; } // 0x0A - new line if (buf[0] != 0x0A ) { // create a pipe so the the shell sends the input to jobs_pipe[1] and the new process gets it in jobs_pipe[0] if (pipe(jobs_pipe) == -1) { panic("pipe"); } struct job* job; job = malloc(sizeof(*job)); memset(job, 0, sizeof(*job)); job->jid = ++jobcntr; job->fd = jobs_pipe[1]; //char* s = job->cmd; //char* t = buf; //int i = sizeof(buf); //while(--i > 0 && *t != '\n' && (*s++ = *t++) != 0) ; //*s = 0; struct cmd* command = parsecmd(buf); int childPid = fork1(); if(childPid == 0) { close(0); dup(jobs_pipe[0]); close(jobs_pipe[1]); if(fork1() == 0) runcmd(command); exit(0); } close(jobs_pipe[0]); // The shell doesn't need the READ end. attachjob(childPid, job); // Attach proc to job (Sys call) // shell writes the input to job_buffer ONLY IF the cmd isn't a backgraound job if (command->type != BACK) { jlist->fgJob = job; bPrintDollar = 0; } addJobToList(jlist, job); wait(&waitStatus); } } exit(0); }