CSC 552 UNIX System Programming, Fall 2015 Dr. Dale E. Parson, Final exam programming project. 20% of course grade per first day handout. This assignment is due via make turnitin from the final552pipeline/ directory by 11:59 PM on Saturday December 12. I will not accept a late assignment. Perform the following steps to get my handout from any Linux machine. The makefile requires you to ssh into one of luna, dumbledore, or hermione for testing. cd $HOME # or start out in your login directory cd ./UnixSysProg cp ~parson/UnixSysProg/final552pipeline.problem.zip final552pipeline.problem.zip unzip final552pipeline.problem.zip cd ./final552pipeline PART 1, Worth 80% of exam. There are 4 tests in the makefile within final552pipeline, each worth 20% of the exam. You can run “make test1” “make test2” “make test3” or “make test4” to run individual tests, or “make test” to run them all. Examine the documentation comments and function header in file include/pipeline.h. In the existing pipelinelib/ subdirectory, you must create file pipelinelib/pipelinecmds.c and implement function void runpipeline(int argc, char *argv[]) as documented in include/pipeline.h. You can consult ~parson/UnixSysProg/runcat2/runcatlib/run1cmd.c1 to see my solution to assignment 2 that uses fork(), exec(), pipe(), and other system calls related to this project. This project does not need to read() or write() any file descriptors. All reading and writing takes place in exec()’d children processes. Here is the full list of library functions and system calls that I used in my exam solution: perror fprintf exit pipe fork execl dup2 close wait, WIFEXITED, WEXITSTATUS man –s3 perror man –s3 fprintf man –s3 exit man –s2 pipe man –s2 fork man –s3 execl man –s2 dup2 man –s2 close man –s2 wait Here is a snapshot of make test from my solution to the project, interspersed with dataflow diagrams of the pipelines constructed by runpipeline(). The pipelines do not show the parent process that forks each of the children processes (circles or ellipses) appearing in the dataflow diagrams. The file descriptors (STDIN_FILENO, STDOUT_FILENO and pipes) appear as external entities (boxes) in the dataflow diagrams. 1 Also contained in ~parson/UnixSysProg/runcat2.solution.zip. page 1 /bin/bash -c "ls pipelinelib" > test1.ref ./pipeline "ls pipelinelib" > test1.out diff test1.out test1.ref > test1.dif # This shell command generates the reference file. # This command tests your program. # Compare their output, “cat test1.dif” to see diffs. /bin/bash -c "ls pipelinelib include | wc -l" > test2.ref ./pipeline "ls pipelinelib include" "wc -l" > test2.out diff test2.out test2.ref > test2.dif /bin/bash -c "cat ./pipeline.c | pr -n | wc -c" > test3.ref ./pipeline "cat ./pipeline.c" "pr -n" "wc -c" > test3.out diff test3.out test3.ref > test3.dif bash -c './pipeline "ls bogusFileName" "wc -l" ; echo STATUS $? > test4.out' ls: cannot access bogusFileName: No such file or directory 0 Child exited with status 2. diff test4.out test4.ref > test4.dif When function runpipeline() in the parent process detects a non-0 exit() value from the any child process via wait(), the parent prints a message to stderr using fprintf and exit()s immediately; the parent process exits with the same exit() value as the child. In the above test, the child exit()ed with a value of 2, which page 2 is the value appearing in test4.ref. The following interactive shell command shows the same exit value; the shell uses variable $? to hold the exit value of the most recently completed command. $ ls bogusFileName ls: cannot access bogusFileName: No such file or directory [:-) ~/.../solutions/final552pipeline] echo $? 2 After you get each of test1 through test4 to pass correctly, you can cp pipelinecmds.c pipelinecmdsN.bak to save a working copy of your code to that point, where N is 1 or 2 or 3 or 4, depending on which test passes. Alternatively, you can complete the assignment without creating these backup file copies. PART 2, Worth 20% of exam. In the final552pipeline/wordcaInC3/ is my working solution to assignment 3, which is the single-threaded cellular automaton that uses the select() system call within the serverLoop function. In WordCA.c function main() you must add a call to the signal system call (man –s2 signal; ignore the comments about avoiding it; it works). It must catch signal number 6 (which is SIGABRT according to man –s7 signal) by invoking a helper function that you must write. Your function must set variable isShutdown to 1 (which is true). Here is what the handout code prints when I invoke make tests to start an interactive server, and then exit by hitting control-D for EOF. aX,Y,(a|d|/|\),TEXT or rI(se[c[s]]|ms[ec[s]]|st[ates]) or p(l|s): FINAL VALUE OF isShutdown is 0. 0.00user 0.00system 0:04.09elapsed 0%CPU (0avgtext+0avgdata 2944maxresident)k 80inputs+0outputs (1major+214minor)pagefaults 0swaps /bin/rm -f /tmp/CSC552_*_parson.* That output is correct, since serverLoop() does not set isShutdown to anything, leaving it at 0. However, if I make tests and then run ps –fu parson in another window (use YOUR userid, not parson), and find the process ID of the WordCA process: $ ps -fu parson UID PID PPID C STIME TTY TIME CMD … parson 14757 14756 0 18:38 pts/3 00:00:00 ./WordCA - 10 10 4 Running kill -6 14757 (or more generally, kill -6 PID) to send the signal 6 (SIGABRT) to the WordCA process should result in this output, once the signal() catcher is working: aX,Y,(a|d|/|\),TEXT or rI(se[c[s]]|ms[ec[s]]|st[ates]) or p(l|s): Server cannot select.: Interrupted system call FINAL VALUE OF isShutdown is 1. 0.00user 0.00system 2:06.03elapsed 0%CPU (0avgtext+0avgdata 3040maxresident)k 80inputs+0outputs (1major+220minor)pagefaults 0swaps /bin/rm -f /tmp/CSC552_*_parson.* page 3 The original handout code does not shutdown cleanly on signal 6. The signal aborts it, giving this error: aX,Y,(a|d|/|\),TEXT or rI(se[c[s]]|ms[ec[s]]|st[ates]) or p(l|s): 0.00user 0.01system 0:11.60elapsed 0%CPU (0avgtext+0avgdata 2912maxresident)k 80inputs+0outputs (1major+212minor)pagefaults 0swaps make: *** [tests] Error 6 Once you have everything above working correctly, run make test from the main final directory one last time, then run make turnitin from that directory. I will not accept late assignments, I will not help to debug this assignment, and I will not answer questions about any of the above after the December 9 class. The only question I will answer after that class is if someone finds a statement above so ambiguous that it demands clarification. In that case, I will email the reply to the class and post it in a D2L forum for the final exam. This project is an exam. CAVEATS for runpipeline(): A. A parent or child process that does not use a read or write side of a pipe() should close() that side of the pipe(). It is critical to close() the write side of a pipe() if a process has it open and does not plan to write to it. Otherwise, a reader or readers of the pipe() will never see EOF (end of file). They may block indefinitely trying to read the pipe. B. A Unix process that exit()s automatically closes all files. C. You do NOT have to check the exit status of your close() system calls that are guaranteed to work on obviously open fd’s. Otherwise, file include/pipeline.h states, “If any of the system calls within runpipeline fails, it must perror() an error message and then exit(1) without returning.” Doing so is extremely helpful during debugging. D. My call to execl() uses “/bin/bash” as the command path and also uses “/bin/bash” as the first command line argument; it uses “-c” as the next command line argument, just as assignment 2. page 4