Homework 3 Due: Sept.. 26 [*** For submission instructions, see help.txt ***] In addition, when you submit, make sure you have: * make clean * make build * make check * README - state what works; and what doesn't work, if anything. ===================================================== AI Policy: Please read carefully the file 000-AI-POLICY.txt . To summarize, you must still write the first draft of all of your code by yourself, without the help of AI. You then have two options for doing the assignment. You can choose not to use any AI. In that case, include in your submission a file named AI.txt that says: I did not use any AI. Alternatively, you may choose to use AI to help you _learn_. When you are debugging your assignment, you may wish to ask AI questions about how Linux system calls work. That is entirely acceptable. You may also ask the AI questions about bugs you are seeing. That is also acceptable. However, absolutely every conversatoin between you and an AI agent must be copy-pasted into an additional file that you submit, AI.txt In addition, you must add a file: AI-implementation.txt write _in your own words_ what your program does, and how your own implementation works, with references to relevant function names and variables. Then start a _new_ session with your AI agent, and upload all of your files for the assignment, and then your _own_ description of what your program dow and how your implementation works. Then, ask the AI agent if your documentation is correct. And finally, copy into AI-implementation.txt this conversation (your description of your implementation) and your question to the AI about your documentation, and the answer that the AI agent gives you. Our graders will be reading your AI-implementation.txt file as well as your code. If they feel that you don't show a true understanding of your own code, then we can call you in for a one-on-one oral quiz to see how well you understand your own code. If you don't understand your own code sufficiently, you will receive a zero for that assignment. The policy of submitting AI.txt and AI-implementation.txt is to help you in your own career. In the past, some students have relied on "prompt engineering", without actually understanding the code that the AI helped them to debug. In your future career, you must learn how an AI can accelerate your productivity -- but also the pitfalls when you submit code that you yourself don't understand. ===================================================== In this homework, you will create a small shell. As before, please create a Makefile with the targets: build, check, and clean. For the 'check' target, it should simply start executing the shell. [help.txt contains more details about "make"] ========================================================================= PART 1 ========================================================================= You must first write a simple shell. The initial shell should simply read a command line, and execute it, within a loop. You can assume that there will be exactly one space between each pair of words on the command line, in order to make parsing easy. You can base your code on the pseudo-code described in class, and reproduced here: LOOP: print prompt read command line fork a child process (using 'fork') if child process, execute the command line (using 'execvp') if parent process, wait for the child (using 'waitpid') goto LOOP In a lower-level pseudo-code, this might look something like: while (1) { char *command[100]; // For commands with up to 100 words printf("% \n"); // Read a string from command line: /* fgets creates null-terminated string. stdin is pre-defined C constant * for standard intput. feof(stdin) tests for file:end-of-file. */ char buffer[100]; // For commands with up to 100 characters if (fgets(buffer, MAXLINE, stdin) == NULL && feof(stdin)) { printf("Couldn't read from standard input. End of file? Exiting ...\n"); exit(1); /* any non-zero value for exit means failure. */ } // Fill in 'command' with pointers to words (char *), followed // by a NULL pointer. 'execvp' requires that the last pointer be NULL. parse_command(buffer /* input */, command /* output */); int childpid = fork(); // See 'man fork' if (childpid == ) { // if child execvp(command[0], command); // See 'man execvp' } else { // else parent waitpid(childpid); // See 'man waitpid' } } ========================================================================= PART 2 ========================================================================= Now that you have a working shell, add some simple features to it. [Make sure to save your current work before you move on to this part.] You will add four features: * output redirection * background jobs * pipes * handling of ^C (control-C) to kill the command, but not the shell process Add pipes and stdout redirection and background jobs to myshell. Also, support the user typing ^C (control-C) to interrupt a command. You only need to support one of the operators '>', '|', or '&' in a single command line. Assume that we will also have a space between each operator (word) and a neighboring word. % ls -l > FILES.txt The output of "ls" command is written into "FILES.txt" instead of standard-output. % sleep 10 & The "sleep 10" command runs in background, and the shell immediately prints a new prompt and accepts a new command. % ls | wc This expression "pipes" the output of "ls" command to "wc" command. The "wc" command then prints the number of lines, words and characters. This is a quick way to find out the total number of files in the current directory. % sleep 60 % ^C (User interrupts a command) Under Linux, "man pipe" provides description and an example for the pipe command. Source code in pipe-example.c provides an example of how to setup a pipe for "ls | wc" example in UNIX. In order to support interrupts, you will want to register a signal handler for SIGINT in your shell. Your signal handler should then kill the child process (given by 'childpid'). If you don't add a signal handler, then when the user types ^C, this will cause the operating system to send a SIGINT signal to your shell process. If your shell process doesn't have a signal handler, then it will be killed. As you look over the examples, be sure to often check the UNIX man pages: man open, man close, man pipe, man dup, man dup2, man perror, man kill, etc. We will test each of the I/O redirection and background features in isolation. You don't need to support combinations of them. Also, you can assume that we will place one or moer spaces around any single-character tokens. This should make it easier for you to parse.