Project #1 Description

Operating Systems, 14012203-4

Week #6, Sunday 10 February 2019

(Total: 15 Marks)

Due Date: Week #9, Friday 8 March 2019

Creating your own shell using

forking in C under Linux

Project assignment is a way to measure your performance in the course. It will evaluate your abilities in Operating Systems programming based on topics and technologies that you learnt during the course. You should build a shell as an C programming language application using Ubuntu Linux and the gcc GNU C compiler. From the kernel's view point, the shell is simply viewed as an interpreter application program that might use system calls to spawn and to terminate other user programs.

1 Problem Scenario

The shell is a command interpreter that provides Unix users with an interface to the underlying operating system. It parses, interprets user commands and executes them as independent processes except for shell’s built-in commands where no new child process is spawned. In general, the shell also allows users to code an interpretive script using simple programming constructs such as if, while and for, etc. With shell scripting, users can create customized automation shell tasks.

The behavior of shell simply repeats the following:

  • Displaying a prompt to indicate that it is ready to accept a next command from its user,
  • Reading a line of keyboard input as a command, and
  • Spawning and having a new process execute the user command.

The prompt symbols frequently used include for example:

hostname$

How does the shell execute a user command? The mechanism follows the steps given below:

  • The shell locates an executable file whose name is specified in the first string given from a keyboard input.
  • It creates a child process by duplicating itself.
  • The duplicated shell overloads its process image with the identified executable file.
  • The overloaded process receives all the remaining strings given from a keyboard input, and starts a command execution.

For instance, assume that your current working directory includes the my_program executable file and you have typed the following line input from your keyboard.

hostname$ ./my_program a b c

This means that the shell duplicates itself and has this duplicated process execute my_program executable program (i.e. after compilation and linking) that receives a, b, and c as its arguments.

Note: Reading the man page for execvp()-like functions, you will see that some of these functions expect to receive an array of pointers to C strings. The exec call has no way to know the length of the array data structure (remember, you’re programming in C, not in a language that would have an array data type which would have an instance field to indicate the number of elements stored). The man page will tell you that this array must be terminated by a NULL element, which serves as the sentinel that marks the end of the array.

Built-in shell commands The shell also has some built-in commands that changes its current status rather than executing a user program. (Note that user programs are distinguished as external commands (such as ps command) from the shell built-in commands). For instance:

hostname$ cd public

changes the shell's current working directory to public. Thus, cd command is one of the shell built-in commands. Note: To recognize if a command is a shell built-in command, you could use the type command as following:

hostname$ type cd cd is a shell builtin hostname$ type ps ps is /bin/ps

I/O redirection and pipeline One of the shell's interesting features is I/O redirection.

hostname$ my_program < file1 > file2 redirects my_program's standard input and output to file1 and file2 respectively, so that my_program reads from file1 and writes to file2 as if it were reading from a keyboard (the original standard input STDIN) and printing out to a monitor (the original standard output STDOUT). dup2 system call could be used to duplicate file identifiers of standard input and output.

Another convenience feature is pipeline. hostname$ command1 | command2 | command3 connects command1's standard output to command2's standard input, and also connects command2's standard output to command3's standard input. For instance,

hostname$ who | wc -l first executes the who command that prints out a list of current users. This output is not displayed but is rather passed to wc's standard input. Thereafter, the wc (i.e. wordcount) command is executed with its -l option. It reads the list of current users and prints out

number of lines of this list to the standard output. As a result, you will get the number of the current users at the end. If the shell receives a sequence of commands, each concatenated with "|", it must recursively create children whose number is the same as that of commands. Which child executes which command is a kind of tricky. The farthest offspring will execute the first command, the grand child will execute the 2nd last, and the child will execute the last command. Their standard input and output must be redirected accordingly using the pipe and dup2 system calls. The following diagrams describe how to execute "who | wc -l", and how to use the pipe system call.

Note: The dup2() system call is similar to dup() but the basic difference between them is that instead of using the lowest-numbered unused file descriptor, it uses the descriptor number specified by the user. int dup2 (int oldfd, int newfd); oldfd: old file descriptor newfd: new file descriptor which is used by dup2() to create a copy.

dup2() system call example: // C program to illustrate the use of dup2()

include

include

include

include

int main() { int file_desc = open("tricky.txt",O_WRONLY | O_APPEND); // here the newfd is the file descriptor of stdout (i.e. 1) dup2(file_desc, 1) ; // All the printf statements will be written in the file // "tricky.txt" instead of the STDOUT (monitor) printf("I will be printed in the file tricky.txt\n"); return 0; }

2 Project Team

Each group could consist of two students at maximum and should provide a separate report and source codes.

3 Project Instructions

Name your C source code program (in Linux) including the group student numbers. So, for example, if the student numbers of the group students are 123456 and 789012, then the program source code as: myshell_123456_789012.c

Compile the C program using the gcc compiler and generate an executable program with the relevant name such as (myshell_123456_789012)

Running the programming from the shell could be as following: hostname$ ./myshell_123456_789012

The program should generate a command prompt like the following to receive user’s commands: 123456_789012$>

The program should loop forever until the user type exit or quit commands from the command line where your shell program should exit successfully to the main shell prompt (hostname$ )

Test the following commands and show the screen outputs of running them in your final report:

hostname$ ./myshell_123456_789012 123456_789012$> ls

. . . . 123456_789012$> ls -al . . . . 123456_789012$> [enter key] 123456_789012$> pwd . . . . 123456_789012$> mkdir public 123456_789012$> cd public 123456_789012$> rmdir public 123456_789012$> ls -al > output_file 123456_789012$> ps -ef >> output_file 123456_789012$> wc < output_file . . . . 123456_789012$> ls -al | wc . . . . 123456_789012$> exit [OR quit] hostname$

You must add comments as much as possible in the C source code program. The more the comments, the better the mark.

Try to decompose your code into several useful and separate functions as much as possible.

Your program code should detect empty command line and thus not run any command or programd and just display the regular command prompt in a new line.

Try to handle in your code all possible errors.

Don’ t use the system() C function to run shell commands.

You might need to use the following system calls in your C program:

printf() fprintf() perror() readline() [you need to install libreadline-dev API library] OR fgets() strtok() // to tokenize the input command line into separate tokens (words) pipe() dup2(), dup() close() malloc() free() fork() execvp() OR execlp () getpid() wait() OR waitpid() strcmp() strcpy() getcwd() // i.e. get current working directory mkdir()

chdir()

rmdir() open() read() write() exit()

4 Evaluation

The total mark of the project will be 15% of the total course mark.

5 Project submission

Please, upload a compressed zip file to the eLearn website (

)

using your account and add the following contents in the compressed zip file:

1- names.txt is a text file that includes the names, student numbers and email addresses of the participating students of the group. Also, describe what are the included files in the zip file and the purpose of each file.

2- Add all your C application source code files.

3- Write a report file (using Microsoft Word) that describes every part/function/section of your code(s) and the purpose of it. Please, include also screenshots of the

output screens for each command you run in your shell. In addition, the report

should explain how to compile and run your source code(s) files.

6 Notes

Each group should work independently. Any copying of any deliverable is considered plagiarism and it will be treated seriously. You could lose the whole mark for cheating or copying from another group or from the Internet.