Operating Systems 2021F: Assignment 2

From Soma-notes
Jump to navigation Jump to search

Please submit the answers to the following questions via Brightspace by October 14, 2021 by 10 AM. There are 20 points in 9 questions.

Submit your answers as a plain text file following this template. Name your answer file "<username>-comp3000-assign2.txt" (where username is your MyCarletonOne username).

Your answers will be parsed by a script in order to help with grading so please preserve the format of the template. Make sure the file remains a plain text file! No other formats will be accepted.

Don't forget to include what outside resources you used to complete each of your answers, including other students, man pages, and web resources. You do not need to list help from the instructor, TA, or information found in the textbook.

Getting Started

First, download and run countdown.sh. You can make it executable after downloading by running "chmod a+x countdown.sh". After this, it should run like any other executable.

Next, download and compile 3000capture.c as usual. Try the following to get an idea of how 3000capture and countdown.sh work:

 ./countdown.sh

 ./3000capture /bin/ls /dev/null -la
 cat 3000capture.log

 cat > bc-input.txt
 4+5
 7*2
 1-6
 ^D   # type ctrl-D to end the file
 
 ./3000capture /usr/bin/bc bc-input.txt -l
 cat 3000capture.log

 ./3000capture ./countdown.sh /dev/null
 cat 3000capture.log

Questions

  1. [2] If we read from /dev/null, what data do we get? How can you verify this?
  2. [2] 3000capture requires commands to be fully qualified pathnames. How can you modify 3000capture so that if a command doesn't start with a /, 3000capture searches the current PATH to find the right program? If PATH isn't defined it should search a default set of directories.
  3. [2] What is the purpose of the fflush() call in parent() at line 36? Why isn't it needed elsewhere in parent()?
  4. [2] How could you fix 3000capture so it sends its output to the file specified by the CAPTURE environment variable? If there is no CAPTURE environment variable, it should use the same default as it does currently.
  5. [2] How many processes does countdown.sh create, and how many executables does it run? How can you verify this using bpftrace?
  6. [2] What is child_exited used for? Why is it needed?
  7. [2] How can you create a shell script that sends a SIGTERM signal to whatever process that ran it (its parent)? What happens when you run 3000capture on it?
  8. [2] How can you verify that the shell script from the previous question is actually sending the right signal using bpftrace?
  9. [4] How could you change 3000capture so that when it receives a SIGUSR1 signal it
    • Prints "SIGUSR1 received, shutting down."
    • Sends a SIGTERM signal to its child
    • Waits at least one second
    • If the child has terminated, it reports how the child has terminated, whether from a signal or whether it returned normally.
    • If the child has not terminated, it reports "Child still running, aborting." and exits with a return value of -10.

Code

3000capture.c

  1 /* 3000capture.c */
  2 
  3 #include <stdio.h>
  4 #include <stdlib.h>
  5 #include <unistd.h>
  6 #include <sys/types.h>
  7 #include <sys/wait.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <string.h>
 11 #include <signal.h>
 12 
 13 int result_pid = -1;
 14 int result_status = 0;
 15 int child_exited = 0;
 16 
 17 void signal_handler(int the_signal)
 18 {
 19         if (the_signal == SIGCHLD) {
 20                 result_pid = wait(&result_status);
 21                 if (result_pid == -1) {
 22                         return;
 23                 }
 24                 
 25                 child_exited = 1;
 26         }
 27 }
 28 
 29 int parent(pid_t child_pid, char *command)
 30 {
 31         int count = 0;
 32         const int bufsize = 256;
 33         char buf[bufsize];
 34 
 35         printf("Waiting.");
 36         fflush(stdout);
 37 
 38         while (!child_exited) {
 39                 sleep(1);
 40                 write(1, ".", 1);
 41         }
 42 
 43         printf("\n");
 44         
 45         if (result_pid == child_pid) {
 46                 count += snprintf(buf + count, bufsize,
 47                                   "Child %d terminated", child_pid);
 48                 if (WIFEXITED(result_status)) {
 49                         count += snprintf(buf + count, bufsize,
 50                                           " normally with status %d",
 51                                           WEXITSTATUS(result_status));
 52                 } else if (WIFSIGNALED(result_status)) {
 53                         count += snprintf(buf + count, bufsize,
 54                                           " with signal %d",
 55                                           WTERMSIG(result_status));
 56                 }
 57                 
 58                 puts(buf);
 59                 return 0;
 60         } else if (result_pid == -1) {
 61                 printf("No child to wait for.\n");
 62                 return -1;
 63         } else {
 64                 printf("wait returned unknown value %d\n", result_pid);
 65                 return result_pid;
 66         }
 67 }
 68 
 69 int child(char *command, char *input_fn, char *output_fn, char *argv[])
 70 {
 71         extern char **environ;
 72         int fd;
 73 
 74         close(1);
 75         fd = creat(output_fn, 0644);
 76         if (fd == -1) {
 77                 fprintf(stderr, "Could not create %s for output.\n",
 78                         output_fn);
 79                 exit(-3);
 80         }
 81         dup2(fd, 1);
 82 
 83         close(0);
 84         fd = open(input_fn, O_RDONLY);
 85         if (fd == -1) {
 86                 fprintf(stderr, "Could not open %s for input.\n",
 87                         output_fn);
 88                 exit(-4);
 89         }
 90         dup2(fd, 0);
 91         
 92         execve(command, argv, environ);
 93         fprintf(stderr, "execve of %s failed.\n", command);
 94         return(-1);
 95 }
 96 
 97 int main(int argc, char *argv[], char *envp[])
 98 {
 99         char *command;
100         char *input_fn;
101         char *output_fn = "3000capture.log";
102         
103         pid_t pid;
104         
105         struct sigaction signal_handler_struct;
106         
107         memset(&signal_handler_struct, 0, sizeof(signal_handler_struct));
108         signal_handler_struct.sa_handler = signal_handler;
109         
110         if (sigaction(SIGCHLD, &signal_handler_struct, NULL)) {
111                 fprintf(stderr, "Couldn't register SIGCHLD handler.\n");
112         }
113 
114         if (argc < 3) {
115                 fprintf(stderr,
116                         "Usage: %s <command> <input file> [command args]\n"
117                         "By default saves data to %s; set CAPTURE to change.\n",
118                         argv[0], output_fn);
119                 exit(-1);
120         }
121         
122         command = argv[1];
123         input_fn = argv[2];
124 
125         pid = fork();
126 
127         if (pid == -1) {
128                 fprintf(stderr, "Fork failed.\n");
129                 exit(-2);
130         } else if (pid == 0) {
131                 argv[2] = command;
132                 return child(command, input_fn, output_fn, argv + 2);
133         } else {
134                 return parent(pid, command);
135         }
136 }

countdown.sh

1 #!/bin/bash
2 
3 for i in `seq 10 -1 1`; do
4     echo ">>>>  $i  <<<<"
5     sleep 1
6 done
7 
8 echo "Blastoff!"

Solutions

Assignment 2 solutions