/* * 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, SIGTSTP, SIGTTIN or SIGTTOU signal. The * handler reaps all available zombie children, but doesn't wait * for any other currently running children to terminate. */ void sigchld_handler(int sig) { pid_t cpid; int status; struct job_t* job; sigset_t mask,prev; //the reason why i dn not use the wrapped function //is that i need the return value while ((cpid = waitpid(-1, &status,WNOHANG|WUNTRACED))>0) { //prepare the mask Sigfillset(&mask); //block the signal because i want to change something Sigprocmask(SIG_BLOCK, &mask, &prev); //get the job by the pid job = getjobpid(job_list, cpid); //STP by any signal //change thet status and print msg if (WIFSTOPPED(status)) { printf("Job [%d] (%d) stopped by signal %d\n",job->jid, cpid, WSTOPSIG(status)); job->state = ST; } //terminate by signal //delete the job and print msg else if (WIFSIGNALED(status)){ printf("Job [%d] (%d) terminated by signal %d\n",job->jid, cpid, WTERMSIG(status)); deletejob(job_list, cpid); } //terminate normally //just delete the job from the job list else deletejob(job_list, cpid); } return; }
/* * builtin_cmd - If the user has typed a built-int * command then execute it immediately. * quit, fg, bg, jobs */ int builtin_cmd(char **argv) { char *cmd = argv[0]; sigset_t mask, prev; /* Blocks all signals while determining * whether command is built in */ Sigfillset(&mask); Sigprocmask(SIG_BLOCK, &mask, &prev); if (!strcmp(cmd, "quit")) { Sigprocmask(SIG_SETMASK, &prev, NULL); exit(0); } if (!strcmp(cmd, "jobs")) { listjobs(jobs); Sigprocmask(SIG_SETMASK, &prev, NULL); return 1; } if (!strcmp(cmd, "bg") || !strcmp(cmd, "fg")) { Sigprocmask(SIG_SETMASK, &prev, NULL); do_bgfg(argv); return 1; } Sigprocmask(SIG_SETMASK, &prev, NULL); return 0; }
/* * 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. */ void sigchld_handler(int sig) { int olderrno = errno; sigset_t mask_all, prev_all; pid_t pid; int status; Sigfillset(&mask_all); while ((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0) { Sigprocmask(SIG_BLOCK, &mask_all, &prev_all); /* Block Signals*/ if (WIFEXITED(status)) { /* Exit normally */ deletejob(jobs, pid); } if (WIFSIGNALED(status)) { /* C-c SIGINT */ printf("Job [%d] (%d) terminated by signal 2\n", pid2jid(pid), pid); deletejob(jobs, pid); /* Note: printf first, then deletejob */ } if (WIFSTOPPED(status)) { /* C-z SIGTSTP */ printf("Job [%d] (%d) stopped by signal 20\n", pid2jid(pid), pid); getjobpid(jobs, pid)->state = ST; } Sigprocmask(SIG_SETMASK, &prev_all, NULL); /* Unblock Signals*/ } errno = olderrno; return; }
/* * 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. */ void eval(char *cmdline) { int bg; /* should the job run in bg or fg? */ struct cmdline_tokens tok; sigset_t mask_one, prev_one, mask_all; int state; pid_t pid; /* Parse command line */ bg = parseline(cmdline, &tok); if (bg == -1) /* parsing error */ return; if (tok.argv[0] == NULL) /* ignore empty lines */ return; /* Fig. 8.40 */ if (tok.builtins == BUILTIN_NONE) { Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); /* Block SIGCHLD */ if ((pid = Fork()) == 0) { Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ if (execve(tok.argv[0], tok.argv, environ) < 0) { printf("%s: Command not found.\n", tok.argv[0]); exit(0); } } /* Parent process */ Sigprocmask(SIG_BLOCK, &mask_all, NULL); /* Block all */ state = bg ? BG : FG; addjob(job_list, pid, state, cmdline); Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ if (state == BG) { printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } else { } // Sigprocmask(SIG_SETMASK, &prev_one, NULL); } return; }
/* * 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. */ void eval(char *cmdline) { char *argv[MAXARGS]; /* Argument list execve() */ char buf[MAXLINE]; /* Holds modified command line */ int bg; /* Should the job run in bg or fg? */ pid_t pid; /* Process id */ sigset_t mask_all, mask_one, prev_one; /* Signal mask */ Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); strcpy(buf, cmdline); bg = parseline(buf, argv); if (argv[0] == NULL) return; /* Ignore empty lines */ if (!builtin_cmd(argv)) { Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); /* Block SIGCHLD */ if ((pid = Fork()) == 0) { /* Child runs user job */ Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ setpgid(0, 0); if (execve(argv[0], argv, environ) < 0) { printf("%s: Command not found\n", argv[0]); exit(0); } } if (!bg) { /* Parent waits for foreground job to terminate */ Sigprocmask(SIG_BLOCK, &mask_all, NULL); addjob(jobs, pid, FG, cmdline); Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ waitfg(pid); } else { /* Background job */ Sigprocmask(SIG_BLOCK, &mask_all, NULL); addjob(jobs, pid, BG, cmdline); printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ } } return; }
/* * 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 (SIGSTP) from the kernel * when we type ctrl-c (ctrl-z) at the keyboard. */ void eval(char *cmdline) { char *argv[MAXARGS], buf[MAXLINE]; int bg, status, state; volatile pid_t pid; int jid; sigset_t mask_all, mask_one, prev_one; Log("EVAL [0]\n", 9); strcpy(buf, cmdline); bg = parseline(buf, argv); if (argv[0] == NULL) { return; } Log("EVAL [1]\n", 9); if (builtin_cmd(argv)) { return; } Log("EVAL [2]\n", 9); Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); pid = Fork(); if (pid == CHILD) { Sigprocmask(SIG_SETMASK, &prev_one, NULL); Setpgid(0, 0); Log("EVAL [3]\n", 9); Execve(argv[0], argv, environ); } Sigprocmask(SIG_BLOCK, &mask_all, NULL); state = bg ? BG : FG; Log("EVAL [4]\n", 9); status = addjob(jobs, pid, state, cmdline); Log("EVAL [5]\n", 9); /* Stores jid while process has not been removed */ jid = getjobpid(jobs, pid)->jid; atomic_fggpid = bg ? 0 : pid; Log("EVAL [5a]\n", 10); Sigprocmask(SIG_SETMASK, &prev_one, NULL); Log("EVAL [5b]\n", 10); /* Handle errors when generating a new job */ if (!status) { return; } /* Parent waits for foreground job to terminate */ if (!bg) { Log("EVAL [6]\n", 9); waitfg(pid); } else { Log("EVAL [7]\n", 9); printf("[%d] (%d) %s", jid, pid, cmdline); } }