Homework 2 EXTENDED DEADLINE: Due Jan. 26 OBSOLETE: Due Jan. 23 ====== HOW TO SUBMIT hw2 (same as hw1): The method for submitting will be used by this and most future homework assignments. Even though future homeworks will not always describe the details of submission, please consider these instructions to be the default. These instructions assume that your hw2 directory is on login.ccs.neu.edu. 1. You should always test if your homework is working, by using the command 'make check' _on the computer login.ccs.neu.edu_. The graders will also be running 'make check' on login.ccs.neu.edu. 2. You must include in your hw2 directory a README file. If everything works, write that in the README file. If some elements of your homework don't work, then you must document the portion of your homework that doesn't work. If some portion of your homework doesn't work, and you didn't document it in your README file, then the grader will deduct extra points. 3. Next, you are ready to create a "tarball" for your homework. You can easily create a tarball by typing the command 'make dist'. 4. You must then submit your homework. /course/cs3650/homework/submit-cs3650-hw2 ../hw2.tar.gz As an example, here is what happens when I create a tarball and submit the tarball to the "handin" directory. LOGIN-STUDENTS:hw2% make dist ... -rw-r--r--. 1 gene faculty 3918 Jan 12 12:30 ../hw2.tar.gz LOGIN-STUDENTS:hw2% /course/cs3650/homework/submit-cs3650-hw2 ../hw2.tar.gz dir: /net/course/cs3650/homework/hw2.handin to_file: /net/course/cs3650/homework/hw2.handin/gene-hw2.tar.gz.93326561 ... ====== In this homework, you will write a command, 'record'. You will need to modify the Makefile to correspond. Recall that the grader will use your 'make check', as well as looking for your file, 'README'. There are two parts to this assignment. A. The command 'record' will: (i) read its command line, (ii) execute the arguments as a new command, and (iii) save the stdout of that execution in a file, 'record.out'. B. The command 'record' may also have a flag '-v' (verbose). In that case, the file record.out should have a prefix on each line: 'record.out> ' You will want to use the system calls 'man 3 execvp', 'man 2 open', and 'man 2 dup2'. You will change the stdout to 'record.out'. In Case A, above, you can do this by something like: // Read 'man 2 open to decide what to use for FLAGS and MODE: int fd = open("record.out", FLAGS, MODE); close(1); // close stdout dup2(fd, 1); // Replace stdout by the new 'fd'. close(fd); // BUT BEWARE: This can cause an infinite loop. // You'll need to create your own logic so that the // program 'record' recognizes if this is the // parent or child process. // Create 'char *args[]' for the new command, based on argv[] from main. execvp("./record", args}; // Probably replace "./record" by prgm on cmd line. In Case B, above, you will also need the systme call 'man 2 pipe'. And you will need to create a second program, 'filter'. The program 'filter' should take its standard input, and then insert the string 'record> ' in front of every line. Recall that '\n' will create a new line. In Case B, You will then do something like: int pipefd[2]; pipe(pipefd); int childpid1 = fork(); if (childpid1 = 0) { // If this is the first child process. close(1); // close stdout dup2(pipefd[XXX], 1); // Read 'man 2 pipe' to decide what is 'XXX'.(Hint: 1) // Create 'char *args[]' for the new command, based on argv[] from main. execvp("./record", args}; // replace "./record" by the appropriate cmd line } int childpid2 = fork(); if (childpid2 = 0) { // If this is the second child process. # Now change stdin close(0); // close stdin dup2(pipefd[XXX], 0); // Read 'man 2 pipe' to decide what is 'XXX'. # Now change stdout int fd = open("record.out", FLAGS, MODE); close(1); // close stdout dup2(fd, 1); // Replace stdout by the new 'fd'. close(fd); # Now execvp to filter execvp("./filter", args}; // We are replacing "./record" by "./filter" } Remember. Google is your friend. It's fine to look up examples of the usage of 'pipe()', 'open()', 'dup2()', 'execvp()', etc. Here is an example of running your program 'record': % ./record ls -l /usr/local/bin [ And the file record.out might look something like the following. ] total 12864 -rwxr-xr-x. 1 root root 6582168 Jan 10 16:47 3700-ftp-password* -rwxr-xr-x. 1 root root 6582168 Jan 12 16:55 4700-ftp-password* lrwxrwxrwx. 1 root root 38 Aug 7 16:05 compile-larceny -> /usr/local/lib/larceny/compile-larceny* lrwxrwxrwx. 1 root root 36 Aug 7 16:05 compile-stale -> /usr/local/lib/larceny/compile-stale* lrwxrwxrwx. 1 root root 30 Aug 7 16:05 larceny -> /usr/local/lib/larceny/larceny* lrwxrwxrwx. 1 root root 50 Aug 1 19:24 node_exporter -> /opt/node_exporter-1.0.1.linux-amd64/node_exporter* lrwxrwxrwx. 1 root root 36 Aug 7 16:05 scheme-script -> /usr/local/lib/larceny/scheme-script* -rwxr-xr-x. 1 root root 536 Sep 1 09:58 sudo* drwxr-xr-x. 10 root root 4096 Aug 7 16:16 texlive/ Here is another example: % ./record -v ls -l /usr/local/bin [ And the file record.out might look something like the following. ] record.out> total 12864 record.out> -rwxr-xr-x. 1 root root 6582168 Jan 10 16:47 3700-ftp-password* record.out> -rwxr-xr-x. 1 root root 6582168 Jan 12 16:55 4700-ftp-password* record.out> lrwxrwxrwx. 1 root root 38 Aug 7 16:05 compile-larceny -> /usr/local/lib/larceny/compile-larceny* record.out> lrwxrwxrwx. 1 root root 36 Aug 7 16:05 compile-stale -> /usr/local/lib/larceny/compile-stale* record.out> lrwxrwxrwx. 1 root root 30 Aug 7 16:05 larceny -> /usr/local/lib/larceny/larceny* record.out> lrwxrwxrwx. 1 root root 50 Aug 1 19:24 node_exporter -> /opt/node_exporter-1.0.1.linux-amd64/node_exporter* record.out> lrwxrwxrwx. 1 root root 36 Aug 7 16:05 scheme-script -> /usr/local/lib/larceny/scheme-script* record.out> -rwxr-xr-x. 1 root root 536 Sep 1 09:58 sudo* record.out> drwxr-xr-x. 10 root root 4096 Aug 7 16:16 texlive/