Operating Systems 2020W: Assignment 1
Please submit the answers to the following questions via CULearn by 2:30 PM on February 7, 2020 11:55 PM on February 9, 2020. There are 20 points in 8 questions.
Submit your answers as a gzipped tarball "<username>-comp3000-assign1.tar.gz" (where username is your MyCarletonOne username).
The tarball you submit must contain the following:
- A plaintext file containing your solutions to all questions, including explanations. Further details are provided below.
- A README.txt file listing the contents of your submission as well as any commands the TAs should use to compile your code.
- The source code for your modified version of 3000run. This file should include all required changes.
No other formats will be accepted. Submitting in another format will likely result in your assignment not being graded and you receiving no marks for this assignment. In particular do not submit an MS Word, OpenOffice, or PDF file as your answers document!
Empty or corrupted tarballs may be given a grade of zero, so please double check your submission by downloading and extracting it.
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.
Contents of Plaintext File
The plaintext file you submit with your assignment must include a detailed explanation of your reasoning for all questions.
For coding questions, explain the changes you made to the original file, including why and how these changes work. Your explanations should make reference to the source file you include with your submission. You may wish to include diffs against the original code to augment your explanation. Your grade for coding questions will be based partly on the provided explanation, and partly on whether your code compiles, runs, and does what it is supposed to do.
For all questions, answers should be detailed and reflect an understanding of course material.
Creating a Gzipped Tarball
You can create gzipped tarballs on the course VM with the command tar cvzf dest.tar.gz source
.
For example, if my name was John Doe and I had an assignment folder named johndoe-assignment1
,
I could create a gzipped tarball of that folder as follows:
tar cvzf johndoe-comp3000-assign1.tar.gz johndoe-assignment1
Before submitting your assignment, you should test to see that your gzipped tarball actually has the correct contents.
Following the above John Doe example, I can extract my tarball by running tar xvzf johndoe-comp3000-assign1.tar.gz
and then inspect the contents.
Background
The questions in this assignment are based on 3000run.c. Its code is listed below. 3000run takes a program binary (full pathname), a filename for standard input, and a filename for standard output. It then runs the specified binary in a new process and waits for it to finish, outputting the results of execution.
For example, to run ls, with standard input coming from file A and standard output coming from file B, do the following:
./3000run /bin/ls A B
(Note that the current version does not implement redirecting standard input.)
Questions
- [1] Complete support for redirecting standard input.
- [1] To run nano with standard in and out being set to A and B, we can type "./3000run /bin/nano A B" in a terminal. What should A and B be so that nano is run and interacts properly with the current terminal?
- [1] How does envp[] in main() compare to environ in child()? Be precise. How did you compare them?
- [5] Which library function calls in 3000run.c generate one or more system calls? List each library call and what system call (if any) it produces (once main() starts and until main() exits). Note the library calls are as follows: close, creat, dup2, execve, exit, fork, fprintf, printf, puts, snprintf, wait
- [2] Add support for running binaries without specifying the full pathname. Implement only using code and methods shown in class (lecture or tutorial) except for any execve variants (don't change the call to execve). Other solutions will not be accepted.
- [2] Add support for reporting when the child terminates with a signal and the signal number that caused the termination. (Note that processes with custom signal handlers may terminate normally using exit, so be sure to test with a program that actually terminates with a signal.)
- [2] Add support for command line arguments. For example, to run ls -l /tmp with input from A and output to B, you would type "./3000run /bin/ls A B -l /tmp".
- [6] Change the parent process so that it only allows the child to run for three seconds before sending it a SIGTERM, as follows:
- [1] Replace the call to wait in parent() with a sleep for three seconds.
- [2] Call wait in a signal handler for SIGCHLD. This signal handler stores result_status and result_pid in global variables. It also sets a global flag child_exited to 1 (this flag defaults to 0).
- [2] If the child has not exited after the sleep (as indicated by child_exited), send the child a SIGTERM signal, then sleep for another three seconds.
- [1] The parent terminates as before, conditionally on result_pid (but it is now global, not local).
Code
/* 3000run.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int parent(pid_t child_pid, char *command, char *input_fn, char *output_fn)
{
int result_status;
pid_t result_pid;
int count = 0;
const int bufsize = 256;
char buf[bufsize];
result_pid = wait(&result_status);
if (result_pid == child_pid) {
count += snprintf(buf + count, bufsize,
"Child %d terminated", child_pid);
if (WIFEXITED(result_status)) {
count += snprintf(buf + count, bufsize,
" normally with status %d",
WEXITSTATUS(result_status));
}
puts(buf);
return 0;
} else if (result_pid == -1) {
printf("No child to wait for.\n");
return -1;
} else {
printf("wait returned unknown value %d\n", result_pid);
return result_pid;
}
}
int child(char *command, char *input_fn, char *output_fn)
{
extern char **environ;
char *argv[2];
int fd;
argv[0] = command;
argv[1] = NULL;
close(1);
fd = creat(output_fn, 0644);
if (fd == -1) {
fprintf(stderr, "Could not create %s for output.\n",
output_fn);
exit(-3);
}
dup2(fd, 1);
execve(command, argv, environ);
fprintf(stderr, "execve of %s failed.\n", command);
return(-1);
}
int main(int argc, char *argv[], char *envp[])
{
char *command;
char *input_fn;
char *output_fn;
pid_t pid;
if (argc < 4) {
fprintf(stderr,
"Usage: %s <command> <input file> <output file>\n",
argv[0]);
exit(-1);
}
command = argv[1];
input_fn = argv[2];
output_fn = argv[3];
pid = fork();
if (pid == -1) {
fprintf(stderr, "Fork failed.\n");
exit(-2);
} else if (pid == 0) {
return child(command, input_fn, output_fn);
} else {
return parent(pid, command, input_fn, output_fn);
}
}