/* If dsh is running interactively as the foreground job * then set the process group and signal handlers * */ void init_dsh(){ // in batch mode, STDIN_FILENO is not associated to terminal input. dsh_terminal_fd = STDIN_FILENO; dsh_is_interactive = isatty(dsh_terminal_fd); if(dsh_is_interactive) { /* Loop until we are in the foreground. */ DEBUG("dsh run as foreground"); while(tcgetpgrp(dsh_terminal_fd) != (dsh_pgid = getpgrp())){ // SIGTTIN: (SIGnal due to TeleType INput) kill(-dsh_pgid, SIGTTIN); /* Request for terminal input */ } /* Ignore interactive and job-control signals. * If tcsetpgrp() is called by a member of a background process * group in its session, and the calling process is not blocking or * ignoring SIGTTOU, a SIGTTOU signal is sent to all members of * this background process group. */ // register signal handler. ignore all the // SIGTTOU (SIGnal due to TeleType OUtput) is the corresponding signal when a backgrounded (nohuped, etc) job tries to output to the terminal. signal(SIGTTOU, SIG_IGN); dsh_pgid = getpid(); if(setpgid(dsh_pgid, dsh_pgid) < 0) { perror("Couldn't put the dsh in its own process group"); exit(EXIT_FAILURE); } seize_tty(dsh_pgid); printf("\n--- Hi, you are using dsh shell, have fun! ---\n\n"); } else{ DEBUG("dsh run as background"); } }
void init_dsh() { dsh_terminal_fd = STDIN_FILENO; /* isatty(): test whether a file descriptor refers to a terminal * isatty() returns 1 if fd is an open file descriptor referring to a * terminal; otherwise 0 is returned, and errno is set to indicate the error. * */ dsh_is_interactive = isatty(dsh_terminal_fd); /* See if we are running interactively. */ if(dsh_is_interactive) { /* Loop until we are in the foreground. */ while(tcgetpgrp(dsh_terminal_fd) != (dsh_pgid = getpgrp())) kill(- dsh_pgid, SIGTTIN); /* Request for terminal input */ /* Ignore interactive and job-control signals. * If tcsetpgrp() is called by a member of a background process * group in its session, and the calling process is not blocking or * ignoring SIGTTOU, a SIGTTOU signal is sent to all members of * this background process group. */ signal(SIGTTOU, SIG_IGN); /* Put the dsh in our own process group. */ dsh_pgid = getpid(); if(setpgid(dsh_pgid, dsh_pgid) < 0) { perror("Couldn't put the dsh in its own process group"); exit(EXIT_FAILURE); } seize_tty(dsh_pgid); } }
/* Creates the context for a new child by setting the pid, pgid and tcsetpgrp */ void new_child(job_t *j, process_t *p, bool fg) { /* establish a new process group, and put the child in * foreground if requested */ /* Put the process into the process group and give the process * group the terminal, if appropriate. This has to be done both by * the dsh and in the individual child processes because of * potential race conditions. * */ p->pid = getpid(); /* also establish child process group in child to avoid race (if parent has not done it yet). */ set_child_pgid(j, p); if(fg) // if fg is set seize_tty(j->pgid); // assign the terminal /* Set the handling for job control signals back to the default. */ signal(SIGTTOU, SIG_DFL); }
/* * builtin_cmd - If the user has typed a built-in command then execute * it immediately. */ bool builtin_cmd(job_t *last_job, int argc, char **argv) { /* check whether the cmd is a built in command */ if (!strcmp("quit", argv[0])) { /* Your code here */ exit(EXIT_SUCCESS); } else if (!strcmp("jobs", argv[0])) { /* Your code here */ job_t* currentJob = active_jobs_head; job_t* deletedJob = NULL; while(currentJob != NULL) { // print active jobs and then change notified to 0 //if((currentJob->first_process)->completed == true && currentJob->notified == false) { //need to check if every process has completed for a job to be complete, not just the first process if (job_is_completed(currentJob) == true) { printf("%d (Completed): %s\n", currentJob->pgid, currentJob->commandinfo); deletedJob = currentJob; //currentJob->notified = true; } //otherwise it is stopped else if (job_is_stopped(currentJob) == true) { printf("%d (Stopped): %s\n", currentJob->pgid, currentJob->commandinfo); } else { printf("%d (Running): %s\n", currentJob->pgid, currentJob->commandinfo); } currentJob = currentJob->next; // delete job after it is completed, don't need to save notified if (deletedJob != NULL) { if (deletedJob == active_jobs_head) { active_jobs_head = deletedJob->next; free_job(deletedJob); //TBD warning about this? } else { delete_job(deletedJob, active_jobs_head); } deletedJob = NULL; } } return true; } else if (!strcmp("cd", argv[0])) { int chdir_return = chdir(argv[1]); if(chdir_return == -1) { printf("cd: %s: No such file or directory\n", argv[1]); } return true; } else if (!strcmp("bg", argv[0])) { /* Your code here */ job_t* currentJob = active_jobs_head; process_t* p2; if(argv[1] == NULL) { // continue most recent stopped job //use find_last_job currentJob = find_last_job(active_jobs_head); continue_job(currentJob); seize_tty(currentJob->pgid); p2 = currentJob->first_process; while(p2 != NULL) { p2->stopped = false; struct sigaction action; action.sa_sigaction = sighandler; sigfillset(&action.sa_mask); action.sa_flags = SA_SIGINFO; sigaction(SIGCHLD, &action, NULL); p2 = p2->next; } seize_tty(getpid()); } else { pid_t job_number = atoi(argv[1]); while(currentJob != NULL) { // Need to eventually iterate through all processes? if((job_is_stopped(currentJob)) && currentJob->pgid == job_number) { //seize_tty(currentJob->pgid); continue_job(currentJob); seize_tty(currentJob->pgid); p2 = currentJob->first_process; while (p2 != NULL) { p2->stopped = false; struct sigaction action; action.sa_sigaction = sighandler; sigfillset(&action.sa_mask); action.sa_flags = SA_SIGINFO; sigaction(SIGCHLD, &action, NULL); p2 = p2->next; } seize_tty(getpid()); break; } else if (currentJob->pgid == job_number) { printf("%s\n", "This process wasn't stopped`"); } else { printf("%s\n", "This job number is not the requested one"); } currentJob = currentJob->next; } } return true; } else if (!strcmp("fg", argv[0])) { /* Your code here */ job_t* currentJob = active_jobs_head; process_t* p2; if(argv[1] == NULL) { // continue most recent stopped job //use find_last_job currentJob = find_last_job(active_jobs_head); continue_job(currentJob); seize_tty(currentJob->pgid); p2 = currentJob->first_process; while(p2 != NULL) { waiting(p2); p2 = p2->next; } seize_tty(getpid()); } else { pid_t job_number = atoi(argv[1]); while(currentJob != NULL) { if((job_is_stopped(currentJob)) && currentJob->pgid == job_number) { //seize_tty(currentJob->pgid); continue_job(currentJob); seize_tty(currentJob->pgid); p2 = currentJob->first_process; while (p2 != NULL) { waiting(p2); p2 = p2->next; } seize_tty(getpid()); break; } else if (currentJob->pgid == job_number) { printf("%s\n", "This process wasn't stopped`"); } else { printf("%s\n", "This job number is not the requested one"); } currentJob = currentJob->next; } } return true; } /* not a builtin command */ return false; }
void spawn_job(job_t *j, bool fg) { pid_t pid; process_t *p; int countProcess = 0; int loopCounter = -1; int i; //count number of processes in the job for(p = j->first_process; p; p = p->next) { countProcess++; } //printf("count process: %d \n", countProcess); int pipefd[countProcess-1][2]; //initialize 2-dimensional array [number of process][input/output] //printf("test"); //open all pipes if(countProcess > 1) { for(i = 0; i < (countProcess-1); i++) { //printf("piped %d \n", i); pipe(pipefd[i]); } } for(p = j->first_process; p; p = p->next) { /* YOUR CODE HERE? */ /* Builtin commands are already taken care earlier */ loopCounter++; switch (pid = fork()) { // int status; case -1: /* fork failure */ perror("fork"); exit(EXIT_FAILURE); case 0: /* child process */ p->pid = getpid(); new_child(j, p, fg); /* YOUR CODE HERE? Child-side code for new process. */ if (p->ifile != NULL) { int fd = open(p->ifile, O_RDONLY); dup2(fd, STDIN_FILENO); } if (p->ofile != NULL) { int fd = open(p->ofile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); dup2(fd, STDOUT_FILENO); } //pipe if(countProcess > 1) { //printf("begin piping \n"); //first process is output //need to loop through number of process and close all pipes if(p == j->first_process) { dup2(pipefd[loopCounter][1], 1); //duplicate output as output for next process closePipe(pipefd, countProcess); } //last process else if (p->next == NULL) { dup2(pipefd[loopCounter-1][0], 0); closePipe(pipefd, countProcess); } //for middle: dup both input and output else { dup2(pipefd[loopCounter][1], 1); dup2(pipefd[loopCounter-1][0], 0); closePipe(pipefd, countProcess); } } printf("%d (Launched): %s\n", j->pgid, j->commandinfo); execvp(p->argv[0], p->argv); perror("New child should have done an exec"); exit(EXIT_FAILURE); /* NOT REACHED */ break; /* NOT REACHED */ default: /* parent */ /* establish child process group */ p->pid = pid; set_child_pgid(j, p); /* YOUR CODE HERE? Parent-side code for new process. */ if (p->next == NULL) //if last process { process_t *p2; p2 = j->first_process; while(p2 != NULL) { //printf("waiting for process \n"); //if fg true waiting, else bg stuff if(fg == true) { waiting(p2); } else { struct sigaction action; action.sa_sigaction = sighandler; sigfillset(&action.sa_mask); action.sa_flags = SA_SIGINFO; sigaction(SIGCHLD, &action, NULL); } closePipe(pipefd, countProcess); p2 = p2->next; } } //wait for child to finish } /* YOUR CODE HERE? Parent-side code for new job.*/ //printf("assign terminal back to dsh\n"); seize_tty(getpid()); // assign the terminal back to dsh } }