Where we are ------------ I have finished signals. We talked about how to block and unblock signals on Wednesday. Next week I will go through a longer signal example, and then introduce sockets. The main topic: -------------- The basic idea is to go through some of the ideas of implementing a shell. The ideas are mostly taken from chapter 7 of Robbins and Robbins which involve how to implement a shell. This is a good overview of fork, exec, wait, and signals. (I have a copy of Robbins and Robbins if you would like to borrow it for the tutorial, but you may not need to.) I would suggest not going line by line through the example, but developing it with them in small chunks. I would also suggest going through steps 1, 2, and 3 fairly quickly and concentrating on steps 5 and 6. Step 1: We need a loop that prints a prompt and reads a command line Step 2: Parse the command line (I would hint at how to do it and then skip it.) The key here is setting up the arguments so you can call exec. Step 3: Fork and exec. Step 4: Parent waits and prints out child's exit status. Step 5: How to run background processes? Find the background process character (using strchr) in the command line and parent calls waitpid(-1, NULL, WNOHANG). Step 6: Handling ctrl-C. The example I have included in the code is the same as that in section 7.4. When a process is running in the foreground, you ignore the signals in the parent, and use the default behaviour in the child. Here's an extremely rough cut at the code I was thinking of. I have not fully tested it, and I didn't write the string parsing function. It probably isn't very useful to put up the whole piece of code (even if it were cleaned up). It would be better to talk about what the pieces are and write the snippets of code. For example, setting it up to wait for a background process is a bit tricky. If anyone makes any improvements to this code please check it back into the repository. #include #include #include #include #include #include #define MAX_BUFFER 256 void makeargs(char *inbuf, char *buf[]); void run(char *inbuf); int main(void) { int status, pid, code, inbackground, child_pid; char inbuf[MAX_BUFFER]; char *backp; struct sigaction ignorehd; struct sigaction defaulthd; sigset_t blockmask; /* Set up the handlers for prompt and default */ ignorehd.sa_handler = SIG_IGN; sigemptyset(&ignorehd.sa_mask); ignorehd.sa_flags = 0; defaulthd.sa_handler = SIG_DFL; sigemptyset(&defaulthd.sa_mask); defaulthd.sa_flags = 0; if ((sigaction(SIGINT, &ignorehd, NULL) < 0) || (sigaction(SIGQUIT, &ignorehd, NULL) < 0)) { perror("Shell failed to install signal handlers"); exit(1); } /* Set up a mask to block SIGINT and SIGQUIT */ sigemptyset(&blockmask); sigaddset(&blockmask, SIGINT); sigaddset(&blockmask, SIGQUIT); sigprocmask(SIG_BLOCK, &blockmask, NULL); /* Keep checking for commands from the command line */ while(1) { printf("$ "); /* print a prompt */ fgets(inbuf, MAX_BUFFER, stdin); /* read a command from stdin*/ if((backp = strchr(inbuf, '&')) == NULL) { inbackground = 0; } else { inbackground = 1; *(backp) = '\0'; } if(strncmp(inbuf, "exit", strlen("exit")) == 0) { exit(0); } else { if((child_pid = fork()) == 0) { if ((sigaction(SIGINT, &defaulthd, NULL) < 0) || (sigaction(SIGQUIT, &defaulthd, NULL) < 0)) { perror("Child could not restore default handlers"); exit(1); } sigprocmask(SIG_UNBLOCK, &blockmask, NULL); run(inbuf); printf("Error: couldn't run command\n"); } } /* Version that handles background processes */ if( !inbackground) { while((pid = waitpid(-1, NULL, 0)) > 0 ) if(pid == child_pid) break; } else { while(waitpid(-1, NULL, WNOHANG) > 0) ; } /* First version that only handles foreground processes */ /* pid = wait(&status);*/ } } void run(char *inbuf) { char *buf[2]; makeargs(inbuf, buf); if(execvp(buf[0], buf) == -1){ perror("Invalid command\n"); exit(1); } } /* I was trying to get this code working quickly and didn't have time * to write the string parsing function */ void makeargs(char *inbuf, char *buf[]) { /* pull out the strings */ buf[0] = "who"; buf[1] = NULL; }