/* * eval - Evaluate the command line that the user has just typed in * * If the user has requested a built-in command (quit, jobs, bg or fg) * then execute it immediately. Otherwise, fork a child process and * run the job in the context of the child. If the job is running in * the foreground, wait for it to terminate and then return. Note: * each child process must have a unique process group ID so that our * background children don't receive SIGINT (SIGTSTP) from the kernel * when we type ctrl-c (ctrl-z) at the keyboard. */ voideval(char *cmdline) { char *argv[MAXARGS]; int bg = parseline(cmdline, argv); /* background job? */ int builtin = builtin_cmd(argv); /* is built-in command? */
/* * builtin_cmd - If the user has typed a built-in command then execute * it immediately. */ intbuiltin_cmd(char **argv) { if (argv[0] == NULL) return1;
/* * waitfg - Block until process pid is no longer the foreground process */ voidwaitfg(pid_t pid) { sigset_t mask; Sigemptyset(&mask); while (fgpid(jobs) == pid) sigsuspend(&mask);
/* * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever * a child job terminates (becomes a zombie), or stops because it * received a SIGSTOP or SIGTSTP signal. The handler reaps all * available zombie children, but doesn't wait for any other * currently running children to terminate. */ voidsigchld_handler(int sig) { int olderrno = errno; sigset_t mask, prev; Sigfillset(&mask);
pid_t pid; int status; while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { Sigprocmask(SIG_BLOCK, &mask, &prev); /* block all */ if (WIFEXITED(status)) /* job exit */ deletejob(jobs, pid); if (WIFSIGNALED(status)) { /* job interrupt */ printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(jobs, pid); } if (WIFSTOPPED(status)) { /* job suspend */ structjob_t *j = getjobpid(jobs, pid); if (j) { j->state = ST; printf("Job [%d] (%d) stopped by signal %d\n", j->jid, pid, WSTOPSIG(status)); } else printf("cannot find a job that corresponding to pid: %d", pid); } Sigprocmask(SIG_SETMASK, &prev, NULL); /* unblock all */ }
/* waitpid sets errno to ECHILD if there's no more zombie process */ /* be careful: we have to ignore EINTR too */ if (errno != ECHILD && errno != EINTR) unix_error("waitpid error");
/* * sigint_handler - The kernel sends a SIGINT to the shell whenver the * user types ctrl-c at the keyboard. Catch it and send it along * to the foreground job. */ voidsigint_handler(int sig) { int olderrno = errno;
pid_t pid = fgpid(jobs);
/* we don't need to response when there's no foreground job */ if (pid == 0) { errno = olderrno; return; } sigset_t mask, prev; Sigfillset(&mask); Sigprocmask(SIG_BLOCK, &mask, &prev); /* block all signals */
/* send signal to all the process in the same group to pid */ Kill(-pid, sig);
Sigprocmask(SIG_SETMASK, &prev, NULL); /* unblock all signals */
/* * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever * the user types ctrl-z at the keyboard. Catch it and suspend the * foreground job by sending it a SIGTSTP. */ voidsigtstp_handler(int sig) { int olderrno = errno;