This Is Not The Course Website You Are Looking For

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.

  1. moveit.py: The first utility allows users to interactively rename specified files using their favorite text $EDITOR.

  2. timeit.py: The second utility allows users to compute the elapsed time of an application while also enforcing a timeout (or cut off time).

Set us up the bomb (No!)

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.

Activity 0: Preparation

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.

Frequently Asked Questions

Activity 1: 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.

Skeleton

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()

Hints

Fork It!

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.

Failure is an Option

You must check if the system calls you use fail and handle those situations appropriately.

Testing

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

Activity 2: 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.

Skeleton

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()

Hints

Testing

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

Activity 3: Quiz (2 Points)

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

Guru Point: TROLL (1 Point)

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.

Verification

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.

Self-Service Extension

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.

Submission

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:

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

Merge Request

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.