This course website is from a previous semester. If you are currently in the class, please make sure you are viewing the latest course website instead of this old one.
The goal of this homework assignment is to allow you to practice using
system calls involving processes and signals in Python by
implementing two new Unix utilities: moveit.py
and timeit.py
.
moveit.py
: The first utility allows users to interactively rename
specified files using their favorite text $EDITOR
.
timeit.py
: The second utility allows users to compute the elapsed time
of an application while also enforcing a timeout (or cut off time).
Be careful with the fork system call. To prevent a fork bomb, start off by always putting a sleep after a fork, that way you have time to kill the processes.
If you still manage to create a fork bomb, do not simply go to another machine and run the same program. Notify csehelp@nd.edu and explain what happened.
For this assignment, record your source code and any responses to the
following activities in the homework07
folder of your assignments
GitLab repository and push your work by 11:59 AM Saturday, March 28.
Before starting this homework assignment, you should first perform a git
pull
to retrieve any changes in your remote GitLab repository:
$ cd path/to/repository # Go to assignments repository $ git checkout master # Make sure we are in master branch $ git pull --rebase # Get any remote changes not present locally
Next, create a new branch for this assignment:
$ git checkout -b homework07 # Create homework07 branch and check it out
Once this is done, download the Makefile
and test scripts:
# Go to homework07 folder $ cd homework07 # Download the Makefile $ curl -LO https://gitlab.com/nd-cse-20289-sp20/cse-20289-sp20-assignments/raw/master/homework07/Makefile # Add and commit starter code $ git add Makefile $ git commit -m "homework07: Add Makefile" # Download the test scripts $ make test-scripts
Note, you do not need to add and commit the test scripts since the
Makefile
. will automatically download them again whenever you run make.
You are now ready to work on the activities below.
moveit.py
(5 Points)For the first activity, write a script, moveit.py
, that reads in file names
from the command line arguments, stores them to a temporary file, opens an
$EDITOR
on the temporary file, allows the user to modify the filenames, and
then renames the files based on the information in the temporary file as
shown below:
Note: Your program must remove the temporary file with the unlink system call.
To help you get started, the instructor has provided you with the following moveit.py skeleton code:
#!/usr/bin/env python3 import atexit import os import sys import tempfile # Functions def usage(status=0): ''' Display usage message for program and exit with specified status. ''' print(f'Usage: {os.path.basename(sys.argv[0])} files...') sys.exit(status) def save_files(files): ''' Save list of files to a temporary file and return the name of the temporary file. ''' pass def edit_files(path): ''' Fork a child process to edit the file specified by path using the user's default EDITOR (use "vim" if not set). Parent waits for child process and returns whether or not the child was successful. ''' return False def move_files(files, path): ''' For each file in files and the corresponding information from the file specified by path, rename the original source file to the new target path (if the names are different). Return whether or not all files were successfully renamed. ''' return False def main(): ''' Parse command line arguments, save arguments to temporary file, allow the user to edit the temporary file, move the files, and remove the temporary file. ''' # TODO: Parse command line arguments pass # TODO: Save files (arguments) pass # TODO: Register unlink to cleanup temporary file pass # TODO: Edit files stored in temporary file pass # TODO: Move files stored in temporary file pass # Main Execution if __name__ == '__main__': main()
For save_files
, you should consider using tempfile.NamedTemporaryFile
with a with statement:
with tempfile.NamedTemporaryFile(delete=False) as tf: ...
For edit_files
, you must use os.fork to create a new process.
To read environment variables, remember that you can access os.environ.
The child process should use os.execlp to execute the text editor.
The parent process should use os.wait to get the child's exit status.
To translate the process exit status, you should use os.WEXITSTATUS.
For move_files
, consider using zip to iterate through two sequences
simultaneously and use os.rename to move files.
For edit_files
and move_files
, you must perform error checking. In
Python, you can use try/except
to handle this:
try: pid = os.fork() except OSError: ... # Handle error
In main
, you will want to use atexit.register on os.unlink to make
sure the temporary file is removed anytime we exit the program.
For this assignment, you must use low-level system calls for processes such as fork, exec, wait, and kill.
You may not use system or popen or subprocess.
You must check if the system calls you use fail and handle those situations appropriately.
To aid you in testing the moveit.py
script, we are providing you with
test_moveit.sh
, which you can use as follows:
$ make test-moveit Testing moveit.py... system calls ... Success usage ... Success usage (no arguments) ... Success deadpool spidey rogue -> deadpool spidey rogue ... Success deadpool spidey rogue -> deadpool spidey rogue (vim) ... Success deadpool spidey rogue -> deadpool spidey rogue (nano) ... Success deadpool spidey rogue -> deadpool spidey rogue (emacs) ... Success deadpool spidey rogue -> deadpool spidey rogue (NOPE) ... Success deadpool spidey rogue -> batman superman wonderwoman ... Success deadpool spidey rogue -> batman superman wonderwoman (NOPE) ... Success batman superman wonderwoman -> deadpool batman rogue ... Success batman superman wonderwoman -> deadpool batman rogue (NOPE) ... Success batman superman -> deadpool spidey rogue ... Success batman superman -> deadpool spidey rogue (NOPE) ... Success deadpool spidey rogue -> batman superman ... Success deadpool spidey rogue -> batman superman (NOPE) ... Success doom -> thing ... Success doom -> thing (rm) ... Success doom -> thing (false) ... Success Score 5.00
timeit.py
(5 Points)For the second activity, write a script, timeit.py
, that executes the given
command until a timeout is reached or the program terminates as show below:
Note: If the time limit is exceeded, the parent should kill the child and wait for it. Moreover, the parent should always return the child's exit status as its own exit status.
To help you get started, the instructor has provided you with the following timeit.py skeleton code:
#!/usr/bin/env python3 import os import signal import sys import time # Functions def usage(status=0): ''' Display usage message for program and exit with specified status. ''' progname = os.path.basename(sys.argv[0]) print(f'''Usage: {progname} [options] command... Options: -t SECONDS Timeout duration before killing command (default is 10) ''') sys.exit(status) def error(message, status=1): ''' Display error message and exit with specified status. ''' print(message, file=sys.stderr) sys.exit(status) def alarm_handler(signum, frame): ''' Alarm handler that raises InterruptedError ''' pass def timeit(argv, timeout=10): ''' Run command specified by argv for at most timeout seconds. - After forking, the child executes the specified command. - After forking, the parent does the following: - Registers a handler for SIGALRM - Set alarm for specified timeout - Waits for child process - If wait is interrupted, kills the child process and wait again - Prints total elapsed time - Exits with the status of the child process ''' pass def main(): ''' Parse command line options and then execute timeit with specified command and timeout. ''' pass # Main Execution if __name__ == '__main__': main()
For alarm_handler
, you must raise an InterruptedError.
For timeit
, you must use os.fork to create a new process.
The child process should use os.execvp to execute the command.
The parent process should use signal.signal to register a signal
handler for signal.SIGALRM
.
The parent process should use signal.alarm to set an alarm.
The parent process should use os.wait to wait for the child process.
Note: A InterruptedError exception is raised by the
alarm_handler
if the signal.alarm is triggered.
The parent process should use os.kill with signal.SIGKILL
to
terminate the child process if it exceeds its time limit.
Note: A ProcessLookupError is raised if you call os.kill on child process that is no longer running.
The parent process should use time.time to keep track of timestamps in order to compute elapsed time.
The parent process should use os.WEXITSTATUS, os.WIFEXITED, and os.WTERMSIG to determine the actual exit status of the child process.
Note: If the child process was killed by the parent process, then the status should be the signal used to kill the *child process.
To aid you in testing the timeit.py
script, we are providing you with
test_timeit.sh
, which you can use as follows:
Testing timeit.py... system calls ... Success usage (-h) ... Success usage (no arguments) ... Success usage (-t 5, no command) ... Success sleep ... Success sleep 1 ... Success sleep 1 (output) ... Success false ... Success false (output) ... Success date ... Success date (output) ... Success sleep 5 ... Success sleep 5 (output) ... Success -t 1 sleep 5 ... Success -t 1 sleep 5 (output) ... Success -t 5 sleep 1 ... Success -t 5 sleep 1 (output) ... Success -t 2 sleep 4 ... Success -t 2 sleep 4 (output) ... Success -t 4 sleep 2 ... Success -t 4 sleep 2 (output) ... Success Score 5.00
Once you have completed all the activities above, you are to complete the following reflection quiz:
As with Reading 01, you will need to store your answers in a
homework07/answers.json
file. You can use the form above to generate the
contents of this file, or you can write the JSON by hand.
To test your quiz, you can use the test_quiz.py
script:
$ ./test_quiz.py Submitting homework07 quiz ... Q01 0.60 Q02 0.60 Q03 0.60 Q04 0.20 Score 2.00
For extra credit, you are to use Python and system calls to implement
your own version of the TROLL
from Homework 01. Recall, that the TROLL
was a process that intercepted signals such as SIGINT
and SIGTERM
and
taunted you when you tried to terminate it. Your version of the TROLL
should do something similar (prevent easy termination)... but its taunts and
other aesthetic details are up to you.
To get credit for this Guru Point, you must show either the instructor or TA
a demonstration of your TROLL
. You have up until one week after this
assignment is due to verify your Guru Point.
Remember that you can always forgo this Guru Point for two extra days to do the homework. That is, if you need an extension, you can simply skip the Guru Point and you will automatically have until Monday to complete the assignment for full credit.
Just leave a note on your Merge Request of your intensions.
To submit your assignment, please commit your work to the homework07
folder
of your homework07
branch in your assignments GitLab repository.
Your homework07
folder should only contain the following files:
Makefile
README.md
answers.json
moveit.py
timeit.py
Note: Don't include any of the test scripts.
#-------------------------------------------------- # BE SURE TO DO THE PREPARATION STEPS IN ACTIVITY 0 #-------------------------------------------------- $ cd homework07 # Go to Homework 07 directory ... $ git add Makefile # Mark changes for commit $ git add moveit.py # Mark changes for commit $ git commit -m "homework07: moveit" # Record changes ... $ git add Makefile # Mark changes for commit $ git add timeit.py # Mark changes for commit $ git commit -m "homework07: timeit" # Record changes ... $ git add answers.json # Mark changes for commit $ git commit -m "homework07: quiz" # Record changes ... $ git push -u origin homework07 # Push branch to GitLab
Remember to create a merge request and assign the appropriate TA from the Reading 08 TA List.
DO NOT MERGE your own merge request. The TAs use open merge requests to keep track of which assignments to grade. Closing them yourself will cause a delay in grading and confuse the TAs.