C language Homework
Homework One: Operating Systems Internals and Design Fall Semester 2018
As we discussed in class and in the book, a shell is a type of user interface. Generally speaking,
a CLI (Command-LIne) shell is a text-based interface in which a user types text commands at a prompt, and the shell program reads and executes user commands. Shells can also include a
simple programming language (a scripting language) that a user could also use to automate
tasks that would otherwise require a great deal of typing from the command line. You can read
more about shells in general at:
https://en.wikipedia.org/wiki/Shell_(computing)
A simple CLI shell might have a processing loop that looks something like this:
while (shell_is_not_finished) { read a line of input; tokenize the line of input; if (first token is a built-in command)
{ do what the command says }
else { fork a clone of the shell;
Have the clone load the program named in the first token and pass it the tokens it has them too
} }
In English, what the shell does is this:
Go into an infinite loop of reading and interpreting command line input. For each line read,
FIRST “tokenize” the input. By tokenizing, we mean that we rewrite the input string so that all
white space in the string (space, tab, newline, and carriage return characters) are replaced by
null characters (ASCII code zero) and that we create an array of pointers that point to the first
NON ZERO (null) character of each cluster of non-null characters. This may sound complex, but
consider the following:
A user types “ls -l” at the command line. In this case, the user typed the “ls” command name, three spaces, and then the command line flag “-l” (long list option). In memory, the shell would maintain a buffer of characters typed that would look like this:
l S l-input_line \0
Where input_line is a variable of type pointer to character (char *) that points to the memory location where the FIRST character of the input data is stored. Each subsequent
memory location will hold subsequent characters in the input string. The first step is to change
all the “whitespace” characters to null characters, like this:
This could be done with a simple loop. After this step, you’d create another array, this time of
POINTERS to chars. Each pointer in THAT array would point to the first character in each of the
“separated words” in the input line. That would look something like this:
Now we have a new array. The first element of that array is pointed to by l_argv. Each subsequent slot of l_argv is a pointer to subsequent words in the input string. The array of pointers is itself terminated by a memory location containing NULL. Notice the format of our “l_argv” array is IDENTICAL to that of the argv array you’re already familiar with.
Once the above structure is created, then l_argv[0] will be the string that is the first TOKEN inside of the input. l_argv[1] will be the string that is the second TOKEN in in the input…. and so on.
In short, the process of parsing creates something that looks and acts just like the
char **argv parameter you could pass into main(), except of course it creates a tokenized version of user input instead of system input.
Once things are tokenized, your shell could look at the FIRST token to see if it is a “built-in” or a
“program name”. If it’s a built-in, then it should just call local code to do it. If it is not, it should
fork a process, load the text segment of that process with the program (passing to it any
parameters it should get from the command line) and then wait until the child is done. When
the child is done, then the shell can continue reading, parsing, and doing what it’s told line by
line.
You may want to examine the heavily commented code “sillyshell_template.c” at this point.
The template code will take in commands, parse them, and process a very small collection of
built-ins. It will not actually fork processes and run other programs in them. It will just
l S \0 \0 \0 l- \0input_line
S \0 \0 \0 l- \0l
NULL
l_argv
input_line
complain that it wish it could and return you to regular processing. Before moving on, make
sure you understand the template code.
For your assignment, you will need to complete the following programming tasks. EVERYTHING
you need is either in the book, explicitly mentioned in lecture or one of the in-class examples
you were asked to work, or is explicitly in the template code itself. You will need at least a basic
understanding of everything in those sources to do this assignment. Note that you get to a 60%
JUST by repeating things we did in class.
Task One: Add Simple Program Calling (50 points) For task one, modify the program so that that when the token pointed to by largv[0] is NOT a built-in command, your sillyshell will do the following: a) fork a process b) have the child
process load the program in the file specified by largv[0] and be passed the appropriate command line arguments. c) have the parent wait on the completion of the child, then return
to normal processing of input lines. Note, ALL of modifications you need to make could be
done INSIDE the sillyshell routine called execute(). Also note that this task is nearly identical to a task we did during an in-class activity.
Task Two: Add a Built-In Command that Prints out All Environment Variables (10 points) For task two, add a new built-in command called “printenv” that prints to the screen ALL of the current shell’s environment variables. This will require you to include a slight variation on
code you would have developed during an in-class exercise.
Task Three: Properly Handle Control Codes (20 points) Generally speaking, shells should NOT react to signals in the way that other processes might.
For example, typing control-c USUALLY interrupts a running process. A shell should not shut
down just because someone types control-c. Also, a shell USUALLY “shuts down” when
someone types control-d. The template I gave you goes into an endless loop if you try that
(yes, this is an intentional bug). For this task, you should make silly shell PROPERLY handle both
control-c and control-d. When you are running silly shell, typing control-c should have no effect
when you are at a prompt. Typing control-d should make sillyshell quit. Adding each capability
is worth 10 points each. Note, you’ll want to handle the control-c problem with material you
can find here: https://www.usna.edu/Users/cs/aviv/classes/ic221/s16/lec/19/lec.html
You will want to handle the control-d problem by investigating the fgets() routine and finding out what it returns if anyone types control-d.
Task Four: Putting a Command in the Background (20 points) In many standard shells, typing the & character as the last token for something that is not a
built-in will put the child process “in the background”. This means that the child process
created will NOT block the shell. The child and the shell will run at the same time and the shell
will continue accepting and running command lines even before the child terminates. Actual
shells also have job control commands that enable you to manipulate background jobs using
additional built-in commands. You can see some details at
https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html
Note that for this assignment, I am not requiring job control commands be added to your
sillyshell. For purposes of this task, you can put jobs “in the background” in any number and
you are not required to have any mechanism by which silly shell can interact with them after
they are created. Note, though, that REAL shells would always have such capability.
Task Five: Adding Job Control Capability Do NOT attempt this until you have verified that EVERYTHING ELSE is perfect. In this task, you
should describe a job control mechanism of your design that mimics at least some of the
abilities of the BASH shell’s job control capability. Tell me about your JCL built-ins and what
they do. Provide me with screen dumps showing us their use with real processes. Points will
be assigned according to the completeness of the JCL and will be applied against the NEXT
assignment (I.E. if you don’t get all the points on assignment #2, you can “spend” your bonus
points to make up the difference).
You should turn in a si e i i e that contains your C language source code for your assignment. With TASK FIVE, there should also be a text file that explains what job control capability you attempted. Your code should be HIGHLY commented. Please make your
comments descriptive of your thinking processes so that we are in the best position to give
partial credit if for some reason there are bugs in your code. You should also include in the zip file screen dumps and/or descriptions of how you tested each capability. The more evidence of
functionality you can provide, the better. The instructor and TA will also be compiling your
code and running our own tests. I will link a brief video lecture that explains how I will test your
code. If you can pass all the tests, you’ll get full credit.