/* * 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 tmppid = 1; int st; while( tmppid > 0 ) { // default tmppid is 1. tmppid = waitpid(-1, &st, WNOHANG | WUNTRACED); // wait all children and with WNOHANG | WUNTRACED. if(tmppid > 0) { // When child processes returns PID. if(WIFEXITED(st)) // When the child exits. deletejob(jobs, tmppid); else { if(WIFSIGNALED(st)) { if(WTERMSIG(st) == 2) // If signal is 2(that is SIGINT) printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(tmppid), tmppid, WTERMSIG(st)); deletejob(jobs, tmppid); } else if(WIFSTOPPED(st)) { // When child processes are stopped. getjobpid(jobs, tmppid)->state = ST; // Change state. printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(tmppid), tmppid, WSTOPSIG(st)); } } } } }
/* * 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 pid; int status; struct job_t* job; while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { if (WIFSIGNALED(status)) { if (WTERMSIG(status) == SIGINT) { printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(job_list, pid); } } else if (WIFSTOPPED(status)) { job = getjobpid(job_list, pid); job->state = ST; printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); } else deletejob(job_list, pid); } if (pid == -1 && errno != ECHILD) unix_error("waitpid error"); return; }
/* * 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 pid; int status; while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0){ if(pid == fgpid(job_list)){ if(tcgetpgrp(STDIN_FILENO) != shell_pid) { tcsetpgrp(STDIN_FILENO, shell_pid); } } if(WIFEXITED(status)){ deletejob(job_list, pid); } else if(WIFSIGNALED(status)){ printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(job_list, pid); } else if (WIFSTOPPED(status)) { printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); getjobpid(job_list, pid)->state = ST; } } return; }
/* * 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 status; pid_t pid; while ((pid = waitpid(fgpid(jobs), &status, WNOHANG|WUNTRACED)) > 0) { if (WIFSTOPPED(status)){ //change state if stopped getjobpid(jobs, pid)->state = ST; int jid = pid2jid(pid); printf("Job [%d] (%d) Stopped by signal %d\n", jid, pid, WSTOPSIG(status)); } else if (WIFSIGNALED(status)){ //delete is signaled int jid = pid2jid(pid); printf("Job [%d] (%d) terminated by signal %d\n", jid, pid, WTERMSIG(status)); deletejob(jobs, pid); } else if (WIFEXITED(status)){ //exited deletejob(jobs, pid); } } return; }
/* * 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; }
void builtincont(struct cmdline_tokens *tok, int state){ if (tok->argc < 2){ // app_error("Usage: %s [%jid/pid]\n", tok->argv[0]); return; } pid_t pid; /* JID */ if (tok->argv[1][0]=='%'){ int jid = atoi((char *)(tok->argv[1] + sizeof(char))); pid = jid2pid(jid); if (!pid) app_error("No such jid \n"); } else{ /* PID case */ pid = atoi(tok->argv[1]); if (!pid2jid(pid)) app_error("No such pid \n"); } /* Common to both JID & PID */ Kill(-pid, SIGCONT); changestate(job_list, pid, state); if (state==BG){ printf("[%d] (%d) %s\n", pid2jid(pid), pid, getjobpid(job_list,pid)->cmdline); } if (state==FG){ wait_for_fg(pid); } }
/* * 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) { int status; pid_t pid; while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { if(WIFEXITED(status)) { /* if a child job terminated normally */ deletejob(job_list, pid); } else if(WIFSIGNALED(status)) { /* if a child job terminated by signal */ printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(job_list, pid); } else if(WIFSTOPPED(status)) { /* if a child job is stopped, we should also update its status */ printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); getjobpid(job_list, pid) -> state = ST; } } return; }
/* * 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 pid,status; while ((pid=waitpid(-1, &status, WNOHANG | WUNTRACED))>0) { /*If there is any child which is still running then parent will not wait for them and retuens the pids and status of reaped child with MACRO "WNOHANG".*/ /* Also if there are any stopped ot terminated child then parent will reap them immediately with MACRO "WUNTRACED"*/ if (WIFEXITED(status))/* MACRO "WIFEXITED" returns true if child terminated normally*/ { deletejob(jobs, pid); /* delete job from jobtable when terminated*/ } else if (WIFSIGNALED(status))/* MACRO "WIFSIGNALED" returns true if reaped child is terminated*/ { printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status));/*MACRO "WTERMSIG" gives by which signal child is terminated*/ deletejob(jobs, pid); /* delete job from jobtable when terminated*/ } else if (WIFSTOPPED(status))/* MACRO "WIFSTOPPED" returns true if reaped child is stopped*/ { getjobpid(jobs, pid)->state = ST; /* If child process receives stop signal then it will change state of child process*/ printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); /*MACRO "WSTOPSIG" gives by which signal child is stopped*/ } } return; }
/* * 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) { int status; pid_t pid; while((pid = waitpid(-1, &status, WUNTRACED | WNOHANG)) > 0) { // If child exited normally, delete job from list if(WIFEXITED(status)) deletejob(job_list, pid); // If child terminated because of uncaught signal, delete job from list else if(WIFSIGNALED(status)) { printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(job_list, pid); } // If child was stopped change status to ST else if(WIFSTOPPED(status)) { printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); getjobpid(job_list, pid) -> state = ST; } } return; }
void sigchld_handler(int sig) { pid_t pid = 0; struct job_t *nowjob = 0; int status; while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)//handle all stopped or terminated children { if(WIFEXITED(status)) { // printf("pid [%d] terminated normally\n",pid); deletejob(jobs,pid); } else if(WIFSIGNALED(status)) { printf("Job [%d] (%d) terminated by signal %d\n",pid2jid(pid),pid,WTERMSIG(status)); deletejob(jobs,pid); } else if(WIFSTOPPED(status)) { printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); nowjob = getjobpid(jobs,pid); nowjob->state = ST; } } if((errno != ECHILD) && (errno != 0)) { unix_error("waitpid error"); } return; }
/* * 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) { pid_t pid; int status; // Reap all the child process. while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { //Handle with the 3 situation in this handler together. if (WIFSIGNALED(status)) { printf("Job [%d] (%d) terminated by Signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(jobs, pid); } else if ( WIFSTOPPED(status)) { struct job_t *job = getjobpid(jobs, pid); if (job == NULL) { printf("sigchld: Get job error.\n"); return; } job->state = ST; printf("Job [%d] (%d) stopped by Signal %d\n", pid2jid(pid), pid, WEXITSTATUS(status)); } else if ( WIFEXITED(status)) { deletejob(jobs, pid); } //printf("Handler reaped child %d\n", (int)pid); } return; }
void sigchld_handler(int sig) { int pid=0; int status=-1; do{ //continually reap as many children as possible, deleting them from the job list pid=waitpid(-1,&status,WNOHANG|WUNTRACED); if(pid>0)//first time { if(WIFEXITED(status)){//child terminated normally, easy to handle, just delete it from the job list deletejob(jobs,pid); } else if(WIFSIGNALED(status)){ //what if the process was terminated by another process? if(WTERMSIG(status)==2) printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(jobs,pid); } else if(WIFSTOPPED(status)){ //what if the process was stopped by another process? getjobpid(jobs, pid)->state=ST; printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); } } } while(pid>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, 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) { int status; pid_t pid; int olderrno = errno; // store errno in handler /* * Reap child with the pid if the child is stopped or terminated * If a child is terminated normally, delete the child from the job list * If a child is stopped by a signal, set the job status as stopped * If a child is terminated by a signal that was not caught, delete the child from the job list */ while ((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0) { if (WIFEXITED(status)) { deletejob(job_list, pid); // the child ternimated normally } else if (WIFSTOPPED(status)) { /* * Avoid printf() in handler, use helper function to write the output to stdout */ sio_puts("Job ["); sio_putl(pid2jid(pid)); sio_puts("] ("); sio_putl(pid); sio_puts(") stopped by signal "); sio_putl(WSTOPSIG(status)); sio_puts("\n"); getjobpid(job_list, pid) -> state = ST; // the child was stopped by a signal } else if (WIFSIGNALED(status)) { // the child was terminated by a signal that was not caught /* * Avoid printf() in handler, use helper function to write the output to stdout */ sio_puts("Job ["); sio_putl(pid2jid(pid)); sio_puts("] ("); sio_putl(pid); sio_puts(") terminated by signal "); sio_putl(WTERMSIG(status)); sio_puts("\n"); deletejob(job_list, pid); } } errno = olderrno; return; }
/* * 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 status; waitpid(-1, &status, 0); return; */ //printf("...in sigchild\n"); fflush(stdout); int status = 1; pid_t pid; while ( ( pid = waitpid(-1, &status, WNOHANG | WUNTRACED) ) > 0 ) { if (WIFSTOPPED(status)) { int jid = pid2jid(pid); getjobpid(jobs, pid)->state = ST; printf("%s [%d] (%d) %s %d %s", "Job", jid, pid, "stopped by signal", SIGSTOP, "\n"); } else { int jid = pid2jid(pid); int jdel = deletejob(jobs, pid); if (jdel) { if (sig != 17) { //printf("MADE IT!\n"); printf("%s [%d] (%d) %s %d %s", "Job", jid, pid, "terminated by signal", SIGINT, "\n"); } } else { printf("error deleting job\n"); fflush(stdout); exit(1); } } } if (pid == -1) { //printf("...no processes\n"); } else if (pid < -1) { printf("error in sigchild\n"); fflush(stdout); exit(1); } return; }
/* * 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 status; if (verbose) printf ("sigchld_handler: entering\n"); /* wait until some child is terminated */ pid_t pid_ch, jid_ch ; while ((pid_ch = waitpid (-1, &status, WUNTRACED | WNOHANG)) > 0) { // wait any terminated child jid_ch = pid2jid (pid_ch); if (verbose) printf ("sigchld_handler: Job [%d] (%d) deleted\n", jid_ch, pid_ch); if (WIFEXITED (status)) { // case 1: child process terminated normally if (verbose) printf ("sigchld_handler: Job [%d] (%d) terminates OK (status 0)\n", jid_ch, pid_ch); deletejob (jobs, pid_ch); } else if (WIFSIGNALED (status)) { // case 2: child process terminated via a signal that was not caught deletejob (jobs, pid_ch); printf ("Job [%d] (%d) terminated by signal %d\n", jid_ch, pid_ch, WTERMSIG (status)); } else if (WIFSTOPPED (status)) { // case 3: child process stopped struct job_t *stopped = getjobpid (jobs, pid_ch); stopped->state = ST; printf ("Job [%d] (%d) stopped by signal %d\n", jid_ch, pid_ch, WSTOPSIG (status)); } } if (verbose) printf ("sigchld_handler: exiting\n"); return; }
/* * waitfg - Block until process pid is no longer the foreground process */ void waitfg(pid_t pid) { int status; if ((pid = waitpid(pid, &status, WUNTRACED)) < 0) { perror("waitfg error:"); exit(0); } else { if (WIFSTOPPED(status)) { int jid = pid2jid(pid); printf("Job [%d] %d stopped by signal: Stopped\n",jid,(int)pid); } else if (WIFEXITED(status)) { deletejob(jobs,pid); } else if (WIFSIGNALED(status)) { printf("Job %d terminated by signal: Interrupt\n",(int)pid); deletejob(jobs,pid); } } 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]; int bg=parseline(cmdline,argv); int p=builtin_cmd(&argv[0]); int childpid; bg++; //because the background process is defined as 2 and foreground as 1 sigset_t set; sigemptyset(&set); sigaddset(&set,SIGCHLD); sigaddset(&set,SIGINT); sigaddset(&set,SIGTSTP); if(!p) { if((childpid=fork())==0) { setpgid(0,0); //Changing group id execve(argv[0], argv, NULL); printf("Command not found\n");//If incase there is no command exit(0); } else { sigprocmask(SIG_BLOCK,&set,NULL); //masking if incase child executes first addjob(jobs,childpid,bg,cmdline); sigprocmask(SIG_UNBLOCK,&set,NULL);//remove mask if(bg==1) waitfg(childpid); else printf("[%d] (%d) %s",pid2jid(childpid),childpid,cmdline); } } 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]; /* argv for execve() */ int bg; pid_t pid; bg = parseline(cmdline, argv); if(argv[0] == NULL) return; /* ignore empty lines */ if(!builtin_cmd(argv)) { if((pid = fork()) < 0) unix_error("eval(): fork() error"); if(pid == 0) /* child runs user job */ { if(setpgid(0,0) < 0) unix_error("eval(): setpgid() error"); if(execve(argv[0], argv, environ) < 0) { printf("%s: Command not found.\n", argv[0]); exit(0); } } addjob(jobs,pid,(bg == 1 ? BG : FG),cmdline); sigprocmask(SIG_UNBLOCK,&mask,NULL); if(!bg) waitfg(pid); else printf("[%d] (%d) %s",pid2jid(pid),pid,cmdline); } 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]; /* int ground = parseline(cmdline, (char **)&argv); */ char buf[MAXLINE]; // Holds modified command line strcpy(buf, cmdline); int ground = parseline(buf, argv); if (argv[0] == NULL) return; if (!builtin_cmd((char **)&argv)) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigprocmask(SIG_BLOCK, &mask, NULL); pid_t pid = __fork(); if (pid != 0) { addjob(jobs, pid, ground ? BG : FG, cmdline); sigprocmask(SIG_UNBLOCK, &mask, NULL); if (!ground) waitfg(pid); else printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } else { __setpgid(0, 0); sigprocmask(SIG_UNBLOCK, &mask, NULL); if (execve(argv[0], argv, environ) < 0) { printf("%s: Command not found\n", argv[0]); exit(0); } } } return; }
/* * 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 pid; int status; struct job_t *job; int old_err = errno; while((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0 ) { if(WIFSTOPPED(status)) { job = getjobpid(job_list, pid); //Assume that pointer job is not NULL safe_printf("Job [%d] (%d) stopped by signal %d\n", job->jid, job->pid, WSTOPSIG(status)); job->state = ST; } else if(WIFSIGNALED(status)) { safe_printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(job_list,pid); } else if(WIFEXITED(status)) deletejob(job_list,pid); } if( pid < 0 && errno != ECHILD && errno != EINTR ) { safe_printf("waitpid error: %s\n", strerror(errno)); _exit(1); } errno = old_err; return; }
/* * 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 pid; int jid; int status; while((pid=waitpid(-1,&status,WNOHANG | WUNTRACED))>0) { jid=pid2jid(pid); if(WIFEXITED(status)) { deletejob(job_list,pid); } if(WIFSIGNALED(status)) { printf("Job [%d] (%d) terminated by signal 2\n",jid,pid); deletejob(job_list,pid); } if(WIFSTOPPED(status)) { printf("Job [%d] (%d) stopped by signal 20\n",jid,pid); getjobpid(job_list,pid)->state=ST; } } 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]; char buf[MAXLINE]; int bg; sigset_t mask; pid_t pid; strcpy(buf, cmdline); bg = parseline(buf, argv); if(argv[0] == NULL) return; if(!builtin_cmd(argv)){ //block signal if(sigemptyset(&mask) < 0) unix_error("sigemptyset error"); if(sigaddset(&mask, SIGCHLD) < 0) unix_error("sigaddset error"); if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0) unix_error("sigprocmask error"); if((pid = fork()) == 0){ //set pid group to be the same as the current pid if(setpgid(0,0) < 0) unix_error("eval: set pid group error"); //unblock siganl if(sigprocmask(SIG_UNBLOCK, &mask, NULL) < 0) unix_error("sigprocmask error"); //execute program if(execve(argv[0], argv, environ)<0){ printf("%s: Command not found.\n", argv[0]); exit(1); } } if(!bg){ addjob(jobs, pid, FG, cmdline); //unblock siganl if(sigprocmask(SIG_UNBLOCK, &mask, NULL) < 0) unix_error("sigprocmask error"); waitfg(pid); }else{ addjob(jobs, pid, BG, cmdline); //unblock siganl if(sigprocmask(SIG_UNBLOCK, &mask, NULL) < 0) unix_error("sigprocmask error"); printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } } 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. * * Requires: * "cmdline" is a NUL ('\0') terminated string with a trailing * '\n' character. "cmdline" must contain less than MAXARGS * arguments. * * Effects: * A built-in command is executed immediately. Otherwise, it attempts * to fork a child process and execute the job. If necessary, the * executable program is searched through the directories of the * search path. If not found, an error is thrown. If the job is * running as a foreground job, it waits until completion. */ static void eval(const char *cmdline) { char *argv[MAXARGS]; int bg = parseline(cmdline, argv); pid_t pid; sigset_t mask; // Checks command line is not empty. if (!argv[0]) { return; } // Checks that the command is not a built-in command. if(!builtin_cmd(argv)){ sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigprocmask(SIG_BLOCK, &mask, NULL); // A child is forked. pid = fork(); if (pid == 0) { // Put child in new group whose ID is identical to child’s PID. setpgid(0, 0); sigprocmask(SIG_UNBLOCK, &mask, NULL); if (execve(argv[0], argv, environ) == -1) { int index = 0; // Run through directories in search path to execute program. while (argv[0][0] != '.' && index < directoryCount) { if (execve(strcat(directories[index],argv[0]), argv, environ) != -1) { break; } index++; } // Command not found. char *print; asprintf(&print, "%s: Command not found\n", argv[0]); sio_error(print); } } if (bg == 0) { addjob(jobs,pid,FG,cmdline); sigprocmask(SIG_UNBLOCK, &mask, NULL); // Wait for foreground jobs to complete. waitfg(pid); } else { addjob(jobs,pid,BG,cmdline); sigprocmask(SIG_UNBLOCK, &mask, NULL); printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } } return; }
/* * do_bgfg - Execute the builtin bg and fg commands */ void do_bgfg(char **argv) { struct job_t* job = NULL; sigset_t mask; int jid = 0; int fg = !strcmp(argv[0],"fg"); if(argv[1] == NULL){ printf("%s command requires PID or %%jobid argument\n", argv[0]); return; } //used to save a bit of repetition when returning after an error int done = 0; //SIGCHLD handler could delete the job before it is or read edited //giving us a seg fault so we must protect it while reading and editing state sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigprocmask(SIG_BLOCK, &mask, NULL); if(isdigit(argv[1][0])){ pid_t pid = atoi(argv[1]); jid = pid2jid(pid); if(jid == 0){ printf("(%d): No such process\n", pid); done = 1; } }else if(argv[1][0]=='%'){ argv[1]++; jid = atoi(argv[1]); if(getjobjid(jobs, jid) == NULL){ printf("%%%s: No such job\n", argv[1]); done = 1; } }else{ printf("%s: argument must be a PID or %%jobid\n", argv[0]); done = 1; } if(done){ sigprocmask(SIG_UNBLOCK, &mask, NULL); return; } job = getjobjid(jobs, jid); if(kill(-job->pid,SIGCONT) < 0){ printf("Could not continue child process\n"); sigprocmask(SIG_UNBLOCK, &mask, NULL); return; } //sets state to 1 if fg 2 if bg job->state = !fg + 1; if(fg){ sigprocmask(SIG_UNBLOCK, &mask, NULL); waitfg(job->pid); }else{ printf("[%d] (%d) %s",job->jid,job->pid, job->cmdline); sigprocmask(SIG_UNBLOCK, &mask, NULL); } return; }
/* * 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 status; pid_t pid; while((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0){ if(WIFEXITED(status)) deletejob(jobs, pid); else if(WIFSTOPPED(status)){ //if it's stopped... getjobpid(jobs, pid)->state = ST; printf("Job [%d] (%d) stopped by signal %d\n",pid2jid(pid),pid,SIGTSTP); } else if(WIFSIGNALED(status)){ //if it's terminated... printf("Job [%d] (%d) terminated by signal %d\n",pid2jid(pid),pid,SIGINT); deletejob(jobs, pid); } } 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]; int bg = parseline(cmdline, argv); int rbic = builtin_cmd(argv); if (!rbic) { pid_t child = fork(); //printf("forked\n"); if (child) { //PARENT if (bg) { int ret = addjob(jobs, child, BG, cmdline); //returns an int if (ret) { int jid = pid2jid(child); printf("[%d] (%d) %s", jobs[jid-1].jid, jobs[jid-1].pid, jobs[jid-1].cmdline); } else { printf("addjobfailed\n"); } } else { int ret = addjob(jobs, child, FG, cmdline); //returns an int if (!ret) { printf("addjobfailed\n"); } waitfg(child); } } else if( child < 0 ) { //ERROR exit(1); } else { //CHILDS setpgid( 0, 0 ); int rc = execv( argv[0], argv ); if (rc == -1) { printf( "execv error %d\n", errno); exit(1); } } } return; }
/* * 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. */ void sigint_handler(int sig) { pid_t fg_pid = fgpid(jobs); if(fg_pid == 0) return ; printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(fg_pid), fg_pid, sig); kill(-fg_pid, SIGINT); deletejob(jobs, fg_pid); return; }
/* * This helper function can handle the "bg" command * 1. Get the pid and jid. * 2. Set the job state to BG. * 3. Print the infomation. * 4. Send the SIGCONT signal. */ void dobg(char **argv) { pid_t pid; int jid; pid = getargvpid(argv); jid = pid2jid(pid); job_list[jid - 1].state = BG; printf("[%d] (%d) %s\n", jid, pid, job_list[jid - 1].cmdline); Kill(pid, SIGCONT); }
/* * 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. */ void sigtstp_handler(int sig) { pid_t pid = fgpid(jobs); if(pid != 0) { //check if it is the shell or a child process printf("Job [%d] (%d) stopped by signal 20 \n", pid2jid(pid), pid); getjobpid(jobs, pid)->state = ST; //job state changed to ST (stopped) kill(-pid, SIGTSTP); //send SIGTSTP signal to stop the process } 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 prev, mask_all; int state; pid_t pid; struct job_t *job_ins; /* 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 (!builtin_command(&tok)) { Sigemptyset(&mask_all); Sigaddset(&mask_all, SIGCHLD); Sigaddset(&mask_all, SIGINT); Sigaddset(&mask_all, SIGTSTP); Sigprocmask(SIG_BLOCK, &mask_all, &prev); /* Block at beginning */ if ((pid = Fork()) == 0) { /* Child process */ Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock inside child */ Setpgid(0, 0); /* one proc and shell in pgrp */ open_dup2_in(tok.infile); open_dup2_out(tok.outfile); if (execve(tok.argv[0], tok.argv, environ) < 0) { printf("%s: Command not found.\n", tok.argv[0]); exit(0); } } /* Parent process */ state = bg ? BG : FG; addjob(job_list, pid, state, cmdline); job_ins = getjobpid(job_list, pid); Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock 3 signals */ if (bg) { printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } else { wait_fg_job(job_ins -> state); } } return; }