Project 4
Fall 2005
Due Date: Beginning of class, Wednesday, 30 November.
References.
- "Understanding Unix/Linux Programming", Chapters 8 and 9. Bruce Molay.
- "Kernel Projects for Linux", Gary Nutt, exercise 12, handout.
Note!
Historically some people have reported that the code for the parseCommand program in the
handout has a problem. The code compiled and ran fine for me, but if you're having problems
with the strsep( ) function (it's in the string.h library), then try using the function (also
defined in string.h):
char *strtok(char *str, const char *set)
This function takes two strings as paramters. The second string contains a string containing
the characters you want to use as separators. The first time you call the function, pass the
string that you want parsed as the first argument. After the first call, pass NULL as the
first argument, and strtok will continue to parse the string you passed in originally.
Note that you cannot modify the original string in your program if you want strtok to continue
to parse it.
Requirements.
- Complete the two Shell Lab exercises given in the handout.
- You will receive partial credit for each part that you complete.
- Your program must exhibit the same functionality as the launch program
given in the handout (section 2.4), i.e., your shell must be able to start up with a command line
argument. The shell must also be able to start up without a command line argument.
- Your shell must be user friendly. I know that Linux shells use arcane commands, and that's
ok. But the shell must, in general, be intuitive and easy to use.
Details
You do have to include all of the features of the shell described in the
handouts. Particular constraints to your
solution are as follows.
- You must program in C.
- You may not use the system function call.
- You may not use the execp( ) system call, instead
you must use the execv( ) system call to execute all commands.
Note that the code in the Molay book uses execp.
- You may use ideas from chapters 8 and 9 of the Understanding Unix/Linux Programming
book in your solution, but you may not copy any code.
- Your shell must take over the command line prompt and respond to
any valid command (including running user created executable files).
- Your shell must terminate gracefully when the "exit" command is entered. It
must also respond gracefully when a command is entered that cannot be found or
executed.
- (addition to the exercise). You must make up an environmental variable
named PMT (this variable does not currently exist) that will contain the
string that will be used in your prompt. So if PMT contains the string "frodo >"
then you shell prompt must be "frodo >". If the string contains "\u", then your
shell must substitute the user name at that point. If my user name was "pippen",
then if PMT contained "\u >" then my shell would use the prompt "pippen >".
- You do not have to implement shell programming constructs like
if.
- Your shell must implement the cd command internally (i.e., in the
shell code). See below for more details on exactly what commands must be
implemented in the shell.
- Your shell must allow quoted command line arguments. For example a commmand
like vi "A long file name" contains two command line arguments.
- When a CTRL-D is hit while running a program, the program should quit, but
your shell should not quit. If only your shell is running, then a CTRL-D should
quit your shell.
- Environmental variables.
Since your shell will be run on top of some other shell (e.g.,
Bash), the environmental variables will be the same in your shell program as for the original
shell (Bash). Make sure that your shell executes the set command correctly. This command
allows a user to change the value in an environmental variable (in the bourne shell which the
project says you must replicate. The bash shell (the default in Linux) uses the export command. You may
implement either). Since your shell is actually running on top of the BASH shell,
the syntax that you must use is export variable=value (no spaces). The Bourne shell syntax is
set variable=value The Beginning Linux Programming book has the details of the
setenv and getenv system calls for getting and setting environmental variables.
- You do not have to store your own environmental variables as is done in
the Understanding Unix/Linux Programming book.
- Note that you are required to use an execv call to create new processes.
Note, however, that not
all commands that are issued at the command line are separate commands. Some commands are
built into the shell. The best example is the cd command. You cannot exec this
command since it is not a program; rather it is a command built into the bash shell. Do
a "man bash" to find out more about bash built-in commands. You do not have to implement all
of the commmands that bash implements, but you do need to implement cd and will have to
keep track of the current directory. You must also implement echo and pwd (print
working directory, ie, print the path to the current directory).
Note: When you recgonize the "cd" command in your shell you should do a chdir(const char *)
system call. Don't fork off a child and do this system call (it would change the directory in the
child's shell not the parent's shell), rather do the system call right in the code for your shell.
This will change the directory correctly.
- Another generic problem that you may come across in the project is the use of dynamic
memory. You cannot pass any of the character arrays pointed to by argv. This means that you
cannot create a new variable char *newVar and then assign it to some argv like
newVar = argv[2] and then pass newVar to some system call. It won't work. You
have to allocate fresh memory to newVar, then copy the argv element into this
new memory before you pass newVar to a system call.
- For an overview of the system calls necessary for this lab, see the 312-210 systems
labs available here.
Especially appropriate are Practicums 11, 12, and 13. Code examples from this course
are available
here
- Pay careful attention to the & sign in your shell. This calls for different
type of behavior than a normal command. See page 78 of the Nutt Linux Projects book.
- Redirection using pipes and the dup system call are described in the "Kernel Projects
for Linux" book and details of the system calls are given in the "Beginning Linux Programming"
book. Basically, you have to create a pipe and overwrite (using the dup system call) standard
output for the first file and standard input for the second file.
- You must do simple redirection of input/output, i.e., the < and >
operators. You should, again, use pipes and the dup system call to accomplish this. You
must also be able to combine pipes and redirection such as
ls | grep ".c" > myFile
and
grep "printf" < myFile > yourFile
Note that there will be two slight problems with pipes.
- Using the techniques discussed in class, you
can cause a programs stdout to become another programs stdin. If you were to do this with programs
like ls and more you would expect the output of ls (its stdout) to be the input of
more (its stdin). However, after more displays a screen of text the user normally hits the enter
key to advance to the next page. Thus more is getting part of it's input directly
from the terminal and
part from the ls program.
There are several ways to handle this. An easy way is to split the command into pieces using
a temporary file. Of course there are always dangers when using a temporary file (you could overwrite
a file that already exists), but if you use a strange enough file name (or if you use the /tmp
directory; you can write to this directory even if you are not root) you can avoid most collisions.
A more robust solution is to read directly from the terminal (
/dev/tty). See page 23 of the Understanding Unix/Linux Programming book.
- Next, people tend to make a common error with the pipe system call. If you fork off
a child and have that child read a pipe, the child will block if there is nothing in the pipe. It
will remain blocked until all of the write ends of the pipe are closed. When a process finishes, all
of it's open pipes are automatically closed (though you really should close them before exiting).
However, if the pipe was created in the parent process then the parent also has a write pipe open.
If you don't close the parent's write end of the pipe, the child that is reading that pipe will
block indefinitely when it tries to read the pipe.
ls | more
becomes
ls > tempFile
more tempFile
rm tempFile
Debugging Hint: There is a systems program named strace that will trace all
of the signals and system calls that a program receives. See the man page.
Administrative concerns.
- Every file in your solution must contain a comment
giving your name and the assignment number. This comment must list every function
that is defined in the file and the purpose of each.
- There must also be a separate commnent before the main function. This
comment should briefly describe the purpose of the program, describe
how it is implemented, given the major interface elements, etc.
- Finally, each function (including the main function) must have a beginning
comment that includes the following components:
- The name of the function
- The function inputs and outputs
- Changes to any global variables or kernel data structures
- System calls made from the function
- Your program must also have appropriate comments. You do not have to comment each line
of the program, but you must comment each section, feature, and functional unit of the code.
- Your program must also be appropriately formatted with tabs, spaces, etc. You will be
penalized if your code is not easy to read.
Deliverables.
You must demonstrate your program to me.
You must hand in a hard copy of all code that you use in
your solution.
You must also put a copy of all source code that you use in
your solution on the Linux Server.
Revision History
| Date |
Revision |
| 14 November |
PS posted |
|
|