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.
By completing this project, you will achieve the following learning
goals:
Discover how basic Unix utilities implement core services by interacting with the operating system kernel via system calls.
Review using system calls in C99 (in particular those related to I/O, files, and directories) to perform systems programming.
Employ TDD as a development strategy by using tests for verification.
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.
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 .
.
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.
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).
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.
When possible create small helper functions that perform one specific action.
Develop your applet incrementally by supporting one feature or use case at a time.
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]... |
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).
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.
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.
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.
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.
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. |
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.
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:
Make the repository private.
Configure access to the repository.
Make sure you add all the members of the team in addition to the instructional staff.
The Project 01 repository includes a README.md
file with the following
sections:
Student: This should be your name and email address.
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.
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.
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.
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).
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.
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:
Break long functions into smaller functions.
Make sure each function does one thing and does it well.
Abstract, but don't over do it.
Please refer to these Coding Style slides for some tips and guidelines on coding style expectations.
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
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.
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
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:
{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.
{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.
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.
Your project will be scored on the following metrics:
Metric | Points |
---|---|
Source Code
|
20.0
|
Documentation
|
4.0
|
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.
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
).