Overview

The first project is to build your own implementation of Busybox, which according to its website is the following:

BusyBox combines tiny versions of many common UNIX utilities into a single small executable. It provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc.

You may think of Busybox as a complete Unix userland in a single binary executable. Because of its compact size, it is found in many embedded devices such as printers, network routers, and even some phones. In fact, Alpine Linux, the official Linux distribution for Docker, uses Busybox as its default userland rather than the traditional GNU utilities found on most Linux machines. This means commands such as ls and cat are embedded inside of a single busybox executable rather than as separate programs.

Given the Busybox executable, busybox, you can invoke any of its internal commands (i.e. applets) by specifying the desired applet as the first argument to the program:

$ busybox ls -l     # Execute ls applet with the argument -l

Alteratively, you can symlink the busybox executable to the name of any applet and then execute the applet directly:

$ ln -s busybox ls  # Create symbolic link from ls to busybox

$ ./ls -l           # Execute ls applet with the argument -l

Working individually, you are to utilize low-level system calls to create your own version of Busybox called idlebin that supports multiple internal applets by noon on Saturday, September 14, 2019.

More details about this project and your deliverables are described below.

Learning Goals

By completing this project, you will achieve the following learning goals:

Idlebin

For this project, you are to implement various Unix utilities as applets in a single binary executable called idlebin. Specifically, you must implement a clone of cp, and then optionally implement one or two additional applets for extra credit.

Provided Applets

You are provided with a working implementation of idlebin that contains the following applets:

Applet Arguments
basename NAME [SUFFIX]
cat [-E] [FILES]...
false
ls [-Ra] [PATHS]...
pwd
true
yes [STRING]...

You can utilize the provided applets by calling idlebin as follows:

$ bin/idlebin ls --help     # Display ls applet's usage message
Usage: ls [-Ra] [PATHS]...
    -R    List subdirectories recursively
    -a    Do not ignore entries starting with .

Likewise, the provided Makefile will automatically create symlinks for each individual applet, so you can execute the provided ls applet as follows:

$ bin/ls --help             # Display ls applet's usage message
Usage: ls [-Ra] [PATHS]...
    -R    List subdirectories recursively
    -a    Do not ignore entries starting with .

As with Busybox, the idlebin applets only implement a subset of the functionality (i.e. command-line options) of their traditional GNU counterparts. For instance, the provided ls only supports the -R and -a flags, which allow it to list subdirectories recursively or to display all files, even those that begin with a ..

Required Applet

For this project, you will need to implement a clone of the cp command that supports the following usage:

$ bin/idlebin cp --help     # Display cp applet's usage message
Usage: cp [-Rv] SOURCE... TARGET
    -R    Copy directories recursively
    -v    Explain what is being done

To implement this applet, you will need to review and then utilize some of the following system calls and low-level functions: stat, open, read, write, close, readlink, symlink, opendir, readdir, closedir, and mkdir.

The behavior (in terms of operation and output) of the cp applet must match that of both the GNU version and the reference implementation provided below.

Note: In terms of the output of cp in verbose mode, you should build your version to match the output of the reference implementation when it deviates from the GNU behavior. The provided unit test will check that you match the reference implementation.

Notes

  1. The work of each individual applet must go in a separate git branch that can be later merged into the master branch (ie. make a applet-cp branch, DO NOT COMMIT OR MERGE TO MASTER).

  2. Think carefully about all the different use cases (ie. copy one file to a directory, copy multiple files to a directory, etc.) and how you would detect these situations.

  3. When possible create small helper functions that perform one specific action.

  4. Develop your applet incrementally by supporting one feature or use case at a time.

Optional Applets

Once you have implemented cp and have passed all the provided unit tests, you may program one or two more applets for extra credit.

Here is a table of possible applets to optionally implement:

Applet Arguments
chmod OCTAL-MODE FILE...
ln [-sv] TARGET LINK_NAME
mkdir [-v] DIRECTORY...
rmdir [-v] DIRECTORY...
touch [FILES]...

Notes

  1. The work of each individual applet must go in a separate git branch that can be later merged into the master branch (ie. make a applet-touch branch, DO NOT COMMIT OR MERGE TO MASTER).

  2. Consult the appropriate manual pages and the GNU versions of the commands to determine the correct behavior and output your applet requires. In terms of the output, you should build to match the expectation of the unit test when it deviates from the GNU behavior.

  3. To verify the correctness of your individual applet, you will need to write a [unit test] script in Python that provides a reasonable amount of test coverage for the features you implemented.

Reference Implementation

You can find a reference implementation of idlebin in the following location on the student machines:

/escnfs/home/pbui/pub/bin/idlebin

This will be updated periodically as more applets are written and added to the reference implementation.

Deliverables

As noted above, you are to work individually to implement your idlebin. You must use C99 (not C++) as the implementation language for idlebin itself, while the test scripts are to be written in Python.

Timeline

Here is a timeline of events related to this project:

Date Event
Monday, August 26 Project description and repository are available.
Saturday, September 7 Brainstorming should be completed.
Saturday, September 14 Implementation of idlebin must be completed.

Final Submission

For the final submission, please open a Merge Request on your repository (for your applet-cp branch to the master branch) and assign it to your grader from the Reading 02 TA List.

Repository

To start this project, you must fork the Project 01 repository on GitLab:

https://gitlab.com/nd-cse-30341-fa19/cse-30341-fa19-project01

Once this repository has been forked, follow the instructions from Reading 00 to:

  1. Make the repository private.

  2. Configure access to the repository.

    Make sure you add all the members of the team in addition to the instructional staff.

Documentation

The Project 01 repository includes a README.md file with the following sections:

  1. Student: This should be your name and email address.

  2. Brainstorming: This should contain your responses to the provided brainstorming questions.

    To help you get started with the project, the README.md file provides you with a series of brainstorming questions that you are designed to guide you towards thinking about the different design and implementation aspects of your project. That is, the best way to get started with this project is by reading through the code base and then answering these questions.

    Ideally, these questions would be completed before you begin coding, but you are only required to submit your responses at the end of the project.

  3. Applets: This contains the list of applets you are to implement. You should check off any that are completed (ie. [x]) and include any notes such as if there are any known issues or bugs.

  4. Reflection: This should contain your responses to the provided reflection questions.

    To prepare you for future exams, the README.md file also contains a series of reflection questions that you are to complete after you have finished programming and testing your project. These questions are designed to help you connect the work you did in this project with the concepts discussed in class. It is quite possible that questions related to these reflections will appear on a future exam.

Less is More

While it is tempting to write a long response to the brainstorming and reflection questions, please limit yourself to a few sentences at the most for each response.

This will facilitate more efficient grading and it will help you practice distilling ideas into their absolute essentials (which is useful for real world communication and for answering questions on exams).

Source Code

In addition to the README.md file, the Project 01 repository contains the following folder hierarchy:

project01
    \_  Makefile        # This is the project Makefile
    \_  bin             # This contains the executable and symlinks
    \_  include         # This contains the header files
    \_  src             # This contains the C99 source code for idlebin executable
        \_ applets      # This conatins the C99 source code for idlbein applets
    \_  tests           # This contains the Python unit tests

You must maintain this folder structure for your project and place files in their appropriate place.

K.I.S.S.

While the exact organization of the project code is up to you, keep in mind that you will be graded in part on coding style, cleaniness, and organization. This means your code should be consistently formatted, not contain any dead code, have reasonable comments, and appropriate naming among other things:

Please refer to these Coding Style slides for some tips and guidelines on coding style expectations.

Compiling

To help you get started, we have provided a Makefile that will build the idlebin application for you:

$ make                  # Build idlebin executable and create applet symlinks
Compiling src/applets/basename.o
Compiling src/applets/false.o
Compiling src/applets/pwd.o
Compiling src/applets/ls.o
Compiling src/applets/cat.o
Compiling src/applets/yes.o
Compiling src/applets/true.o
Linking bin/idlebin
Symlinking bin/basename
Symlinking bin/false
Symlinking bin/pwd
Symlinking bin/ls
Symlinking bin/cat
Symlinking bin/yes
Symlinking bin/true

Gawk

The provided Makefile utilizes the GNU version of awk, aka. gawk. On macOS, you will need to use Homebrew to install it if you don't have it already.

Running

Once you have the idlebin executable, you can invoke the various applets in the same way that you do with Busybox:

$ bin/idlebin           # List all applets
Usage: idlebin applet [ARGUMENTS]...
Applets:
    basename
    false
    pwd
    ls
    cat
    yes
    true

$ bin/idlebin pwd       # Execute pwd applet
/home/pbui/src/teaching/cse.30341.fa19/project01

$ bin/pwd               # Execute pwd applet
/home/pbui/src/teaching/cse.30341.fa19/project01

$ bin/pwd --help        # Display pwd applet usage
Usage: pwd

Extending

You can create additional applets to be embedded into idlebin by placing C99 source files in the src/applets folder and defining at least the following two items:

  1. {APPLET}_USAGE: This must be a const char array that contains the usage message for the new APPLET you are creating; it may consist of multiple lines.

  2. {APPLET}_applet: This is a function that receives the command-line arguments (int argc, char *argv[]), performs the desired operations, and then returns an int corresponding to the applet's exit status.

Here is a simple template to follow:

/* {APPLET}.c: {APPLET} applet */

#include "idlebin.h"

/* Usage */

const char {APPLET}_USAGE[] = "Usage: {APPLET}";

/* Applet */

int {APPLET}_applet(int argc, char *argv[]) {
    return EXIT_SUCCESS;
}

The provided Makefile will automatically detect any additional applets placed in the src/applets folder and will embed them into the idlebin executable for you.

Testing

To verify the correctness of the applets in idlebin, we have provided a series of unit tests implemented in Python. These are located in the tests directory and can be executed all at once with the following command:

$ make test                 # Run all test scripts
Testing basename...
test_00_help (test_basename.BasenameTestCase) ... ok
test_01_usage (test_basename.BasenameTestCase) ... ok
test_02_basename (test_basename.BasenameTestCase) ... ok
...
----------------------------------------------------------------------
Ran 81 tests in 17.488s

FAILED (failures=25, errors=10)
make: *** [Makefile:54: test] Error 1

Note, the first time you run make test you will have failures since the ls and cp applets have not been implemented yet.

You may execute all the unit tests of a specific command by running the test Python script directly:

$ ./tests/test_pwd.py -v    # Run pwd test script
Testing pwd...
test_00_help (__main__.PwdTestCase) ... ok
test_01_curdir (__main__.PwdTestCase) ... ok
test_01_curdir_valgrind (__main__.PwdTestCase) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.570s

OK

To add your own tests, simply create the corresponding test_{APPLET}.py Python Script in the tests folder. It is recommended that you model your unit tests after the existing ones, which means utilizing Python's unittest module for creating unit tests.

Rubric

Your project will be scored on the following metrics:

Metric Points
Source Code
  1. Functionality
  2. Error Handling
  3. Coding Style
  4. Commit History
20.0
  1. 15.0
  2. 2.0
  3. 2.0
  4. 1.0
Documentation
  1. Brainstorming
  2. Reflection
4.0
  1. 2.0
  2. 2.0

Commit History

To encourage you to work on the project regularly (rather than leaving it until the last second) and to practice performing incremental development, part of your grade will be based on whether or not you have regular and reasonably sized commits in your git log.

That is, you will lose a point if you commit everything at once near the project deadline.

Error Handling

In addition to meeting the functional requirements of the project (as described above), your program must not exhibit any memory leaks or invalid memory accesses as would be detected by Valgrind.

Additionally, because system calls can fail, your program must implement robust error handling and ensure your code checks the status of system calls when reasonable.

Moreover, you code must compile without any warnings (and -Wall must be one of the CFLAGS).