COMP 346 Winter 2000: PA-1

Task Deadline
Systems Programs, System Calls and Processes Thursday, Feb. 3, 2000

COMP 346 Programming Assignment 1

This assignment provides practice in writing systems programs that make Unix system calls.

Problem Specification

In this assignment, you will develop a micro shell. Like a normal shell, the micro shell will 1) display a prompt, 2) accept user commands, and 3) execute them. The shell will be like a real Unix shell (e.g., Csh), but will be much simpler.
The overview of the shell functioning is as follows: The shell will handle a variety of commands, which are similar to Unix shell commands. The code that you write should also be similar to that in Unix shells. The commands to be implemented are given below:

Basic Commands

The basic commands require little programming effort since there are system calls to do the work. You must "bullet-proof" the commands to handle error checking. You are not allowed to use system() in your implementation.
  1. display filename
    prints the contents of a file on the screen
  2. copy source destination
    source is the name of a pre-existing file; destination is a filename different from source. This creates a new file with the name given in destination and the same contents as the source file.
  3. fileinf filename
    displays the essential information about the file specified, or of the current directory if invoked without argument; it must list at least the following:
  4. dirinf directoryname
    displays the essential information of the directory specified, or of the current directory if invoked without argument; it must list at least the following:
  5. deldir directoryname
    deletes the directory and all its contents; since this is a potentially disastrous operation, the user should be asked to confirm the operation
  6. quit
    this will quit the shell.

Implementation Notes

Each of the shell commands should be implemented as a procedure (function). The main program is a loop: wait for the next command, parse it, and execute it. You must implement error handling. Note that the error-handling aspect of the problem is not precisely specified, i.e., the specification is left open.
Nonetheless, it is expected that your code will consider all error conditions and take take appropriate action. There are two key considerations here: 1) the shell should not crash when the input is invalid, i.e., your shell program must be bullet proof, and 2) the shell's response to errors must be user friendly and intuitive. You may use the perror call for reporting errors, but may want to catch and handle certain kind of errors directly.

Directory Listing

The next command you will implement will be the directory-listing command, which has the following syntax:
dir [-l]
The command works like the ls command in Unix, and has a similar output. The output should be formatted to look like the output of /bin/ls without the optional -l, and like /bin/ls -gF with the optional -l. For the directory listing, you need only worry about regular files and directories, not special files. This command will require you to make use of various system and library calls. You will need the stat system call to get information about a file. You will also need to consult the include file /usr/include/sys/stat.h, which has some useful macros for this command. You will also need the getpwuid library call to find information about a user.

Executing Programs

So far, our microshell can only execute "built-in" commands. One of the tasks of a shell is also to execute user programs. This is also a useful mechanism for extending the functionality of the shell, since you can write programs that execute specific commands, and these programs extend the capability of the shell. In order to execute user programs, the shell will use the fork and execlp (or execvp) system calls. When the command line does not represent one of the built-in commands of the shell, then the shell should attempt to see if the command is a user program. If so, it should execute the command in a separate child process, using a combination of fork() and exec(). The shell, which is the parent process after fork(), should wait for the child process to terminate before displaying the prompt again. The shell should also check for errors in finding and executing the child program and report them appropriately to the user.

Process Synchronization

This is a small example (separate from the shell above) that helps you understand the coordination of the activities performed by two processes. Two processes P1 and P2 are running concurrently on the same machine. P1 and P2 communicate through a shared memory. First P1, sends P2 a file name (of an ASCII file) that P2 opens and reads. As P2 reads the file, it sends its content to P1 10 characters at a time. Since the two processes have different execution speeds, they should be synchronized (in your implementation, insert some artificial delays in P1 or P2 to make make sure that their speeds differ substantially). To synchronize them, use a semaphore. When the transfer is over, P1 or P2 stops the communication (you should know which process stops it).
Last modified: Thu Jan 20 12:45:39 EST 2000