【csapp lab】shell lab

hazel1313發表於2020-11-19
/*
 * 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;
    pid_t pid;


    sigset_t mask_child,mask_all,prev_all;
    sigfillset(&mask_all);
    sigemptyset(&mask_child);
    sigaddset(&mask_child,SIGCHLD);

    strcpy(buf,cmdline);
    bg=parseline(buf,argv);
    if(argv[0]==NULL)
        return;

    if(!builtin_cmd(argv)){
	sigprocmask(SIG_BLOCK, &mask_child, &prev_all);
        if((pid=fork())<0){
            sigprocmask(SIG_SETMASK, &prev_all, NULL);
            fprintf(stderr,"fork error: %s\n",strerror(errno));
            exit(0);
        }
	else if(pid==0){  //child
	    setpgid(0,0);//last hint,很重要!!!
	    sigprocmask(SIG_SETMASK, &prev_all, NULL);
            if(execve(argv[0],argv,environ)<0){

                printf("%s: Command not found.\n",argv[0]);
                exit(0);		
            }
        }
        //parent
	
	sigprocmask(SIG_BLOCK, &mask_all, NULL);
	addjob(jobs,pid,bg?BG:FG,cmdline);
        sigprocmask(SIG_SETMASK, &prev_all, NULL);//csapp8.5.6
        if(!bg){
	flag=0;
            //int status;
            //if(waitpid(pid,&status,WUNTRACED)<0)
                //unix_error("waitfg:waitpid error");
	    waitfg(pid);
	    //if(getjobpid(jobs, pid) && getjobpid(jobs, pid)->state != ST)
                //deletejob(jobs, pid);
        }
        else{
	   // sigprocmask(SIG_SETMASK, &prev_all, NULL);
            printf("[%d] (%d) %s",getjobpid(jobs, pid)->jid,pid,cmdline);}
    }
    
    return;
}

/*
 * parseline - Parse the command line and build the argv array.
 *
 * Characters enclosed in single quotes are treated as a single
 * argument.  Return true if the user has requested a BG job, false if
 * the user has requested a FG job.  
 */
int parseline(const char *cmdline, char **argv)
{
    static char array[MAXLINE]; /* holds local copy of command line */
    char *buf = array;          /* ptr that traverses command line */
    char *delim;                /* points to first space delimiter */
    int argc;                   /* number of args */
    int bg;                     /* background job? */

    strcpy(buf, cmdline);
    buf[strlen(buf)-1] = ' ';  /* replace trailing '\n' with space */
    while (*buf && (*buf == ' ')) /* ignore leading spaces */
	buf++;

    /* Build the argv list */
    argc = 0;
    if (*buf == '\'') {
	buf++;
	delim = strchr(buf, '\'');
    }
    else {
	delim = strchr(buf, ' ');
    }

    while (delim) {
	argv[argc++] = buf;
	*delim = '\0';
	buf = delim + 1;
	while (*buf && (*buf == ' ')) /* ignore spaces */
	       buf++;

	if (*buf == '\'') {
	    buf++;
	    delim = strchr(buf, '\'');
	}
	else {
	    delim = strchr(buf, ' ');
	}
    }
    argv[argc] = NULL;
    
    if (argc == 0)  /* ignore blank line */
	return 1;

    /* should the job run in the background? */
    if ((bg = (*argv[argc-1] == '&')) != 0) {
	argv[--argc] = NULL;
    }
    return bg;
}

/*
 * builtin_cmd - If the user has typed a built-in command then execute
 *    it immediately.  
 */
int builtin_cmd(char **argv)
{
    if(!strcmp(argv[0],"quit"))
       exit(0);
    if(!strcmp(argv[0],"jobs")){
	listjobs(jobs);
	return 1;
	}
    if(!strcmp(argv[0],"bg")||!strcmp(argv[0],"fg")){
	do_bgfg(argv);
	return 1;
        }
    return 0;     /* not a builtin command */
}

/*
 * do_bgfg - Execute the builtin bg and fg commands
 */
void do_bgfg(char **argv)
{
    pid_t pid;
    int jid;
    struct job_t* job;
    if(argv[1]==NULL){
	printf("%s command requires PID or %%jobid argument\n",argv[0]);
	return;
    }
    if(*argv[1]=='%'){
	if(sscanf (argv[1],"%%%d",&jid)==0){
		printf("%s: argument must be a PID or %%jobid\n",argv[0]);
		return;
		}
	job=getjobjid(jobs, jid);
	if(job)
	    pid=job->pid;
	else {
	printf("%s: No such job\n",argv[1]); 	
	return;
	}
    }
    else {
	if(sscanf (argv[1],"%d",&pid)==0){
		printf("%s: argument must be a PID or %%jobid\n",argv[0]);
		return;
		}
	job=getjobpid(jobs,pid);
	if(!job){
	printf("(%s): No such process\n",argv[1]); 	
	return;
	}
	}


    if(!strcmp(argv[0],"fg")){
	    if(job-> state==ST)
	    kill(-pid,SIGCONT);
	    job->state=FG;
            //int status;
            //if(waitpid(pid,&status,WUNTRACED)<0)
                //unix_error("waitfg:waitpid error");
	    waitfg(pid);}
    if(!strcmp(argv[0],"bg")){
	    if(job->state==ST)
	    kill(-pid,SIGCONT);
	    job->state=BG;
            printf("[%d] (%d) %s",job->jid,pid,job->cmdline);
	}
    return;
}

/*
 * waitfg - Block until process pid is no longer the foreground process
 */
void waitfg(pid_t pid)
{
	
    sigset_t mask_child,prev;
    sigemptyset(&mask_child);
    sigaddset(&mask_child,SIGCHLD);

  sigprocmask(SIG_BLOCK,&mask_child,&prev);
    while(getjobpid(jobs,pid)&&getjobpid(jobs,pid)->state==FG){
    //while(pid==fgpid(jobs)){
   // while(!flag){
	sigsuspend(&prev);}//csapp8.5.7
	///sigsuspend(&mask_empty);}
    sigprocmask(SIG_SETMASK,&prev,NULL);   

    return;
}

/*****************
 * Signal handlers
 *****************/

/*
 * 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;

    pid_t pid;
    int status;
    //ctermi=fgpid(jobs);
    while((pid=waitpid(-fgpid(jobs),&status,WNOHANG|WUNTRACED))>0){//
    //while((pid=waitpid(-1,&status,WNOHANG|WUNTRACED))>0){
    //if(pid==fgpid(jobs)) flag=1;
    if(WIFSTOPPED(status)){
        printf("Job [%d] (%d) stopped by signal 20\n",getjobpid(jobs, pid)->jid,pid);
        getjobpid(jobs, pid)-> state=ST;
	//ctermi=pid;
	}
    else if(WIFSIGNALED(status)){
        printf("Job [%d] (%d) terminated by signal 2\n",getjobpid(jobs, pid)->jid,pid);
	deletejob(jobs, pid);
	//ctermi=pid;
    }
    else if(WIFEXITED(status))
	deletejob(jobs,pid);}
    //pid=waitpid(-1,NULL,0);
	//app_error("waitpid error");    
    //ctermi=pid;

    errno=olderrno;

    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 fpid;
    fpid=fgpid(jobs);
    if(fpid){
	kill(-fpid,sig);
    }
    return;
}

/*
 * 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 fpid;
    fpid=fgpid(jobs);
    if(fpid>0){
	kill(-fpid,SIGTSTP);
	
    }
    return;
}
/*********************
 * End signal handlers
 *********************/

相關文章