MP2: Shell

CS 241, Fall 2011


Introduction

In this MP, you will implement a simple Unix shell interpreter (e.g., Terminal, Konsole etc.) called Shell. The basic function of a shell is to accept commands as inputs and execute the corresponding programs in response.

The purpose of this MP is to help you learn the basics of system calls for creating and managing processes as you implement your Shell program. You will write the code for your Shell program in shell.c inside your mp2 directory. The two other files inside your mp2 directory: vector.c and vector.h provide the implementation of a generic data structure vector_t that you may use to implement the history feature of your Shell.

Task

To execute multiple commands, your shell should run in a loop where each loop performs the following actions:

Task 1: Printing the Command Prompt

Your shell prompt MUST use the following format:

Shell (pid = x)y}
You can use printf("Shell (pid = %d)%d} ", x, y) to print out this prompt. Here x is the current process ID and y is the command counter. Use getpid() to determine the process ID of the current process.

The Command Counter, y starts at 1 and is incremented after each command, regardless of whether the command succeeds or fails to execute. The only time when you should NOT increment y is when:

Task 2: Reading the Command from stdin

Read a line from standard input. This line will be your command.

The constant SHELL_BUFFER_SIZE, defined in shell.c, indicates the maximum length of a valid command. This means that the shell allows the user to enter a SHELL_BUFFER_SIZE characters long valid command string. Any command larger than this size should be rejected by your Shell and the shell should output the following error message (without the quotes): "Commands should be within the size of 256 characters"

Notes: While verifying the size of the command, Shell does not count the trailing new line character, '\n' or the null terminator, '\0' after each command to be a part of the buffer.

Task 3: Parsing the Command into Tokens

Parse the command line into an array of tokens. A token should consist of a sequence of non-whitespace characters separated from other tokens by whitespace characters.

The first token is the name of the command and the rest of the tokens are the arguments to that command name. By analyzing the command name, Shell determines the type of the command and executes it accordingly.

Task 4: Executing the Command

Shell supports two types of commands: built-in and non built-in. While built-in commands are executed without creating a new process, a non built-in command MUST create a new process to execute the program for that particular command.

BUILT-IN COMMANDS

Your Shell will support four built-in commands: cd, exit and two commands (!#N and !N) based on a history feature. Your Shell should save all commands (built-in or non built-in) issued during the current session. You should make no assumption about the size of the history.

Note: For your convenience, we have provided the implementation of a data structure vector_t inside vector.c and vector.h. Feel free to use this data structure to implement the history feature for your Shell. The details of this data structure are provided at the end of this specification in the Appendix section.

Assumption:

NON BUILT-IN COMMANDS

If the command is not a Shell built-in (i.e., any command other than cd, exit, !#N and !N), Shell should consider the command name to be the name of a file that contains executable binary code. Such a code must be executed in a process different from the one executing the shell. You can use system() from stdlib.h to execute such a command. Sample codes are available on Linux's man page

Some non built-in UNIX commands that you may try to see whether your Shell works as it should are: ls, pwd, ps, echo hello.

Compiling and Running

To compile and run your shell, run the following commands from a Terminal on a Linux machine:

%> make clean
%> make
%> ./shell

Type "exit" to exit from Shell.

Lab Report

In CS 241, please include answers to:

Appendix

Data Structure: vector_t

We provide you with the implementation of a generic data structure: vector_t. The structure is defined inside vector.h and the functions supported are implemented inside vector.c. Please do NOT modify these two files. Otherwise, the autograder may fail to compile your Shell program. You may use vector_t to store the command history of your Shell. To use vector_t, just include the header file "vector.h" inside your Shell program shell.c. You may also implement your own data structures to keep track of the Shell history instead of using vector_t.

typedef struct _vector {
      void **buffer;
      unsigned int size;
      unsigned int allocSize;
} vector_t;

Elements:

Functions:

To enable better understanding of the vector_t data structure, we provide you with an example code that uses this data structure in testvector.c. Feel free to modify this tester. To make and run the tester for the data structure testvector.c, use:

%> make            # make/compile your program
%> ./testvector    # run the testvector program

Grading, Submission and Other Details

Please fully read cs241.html for more details on grading, submission, and other topics that are shared between all MPs in CS 241.