CS2106 2220 Lab2
CS2106 2220 Lab2
IMPORTANT
The deadline of submission through LumiNUS: 5th March, 2023, 11.59 PM Saturday
The total weightage is 8% (+2% bonus):
• Exercise 1: 3% [Lab demo exercise]
• Exercise 2: 2%
• Exercise 3: 3%
• Exercise 4: 2% (Bonus)
You must ensure the exercises work properly on the VM provided or on the SoC Cluster.
1 Introduction
As programmers, command-line interpreters, also known as shells, are an important and ubiquitous part
of our lives. A command line interpreter (or command prompt, or shell) allows the execution of multiple
user commands with various number of arguments. The user inputs the commands one after another, and
the commands are executed by the command line interpreter.
A shell is actually just another program implemented using the process-related system calls discussed in
the lectures. In this lab, you are going to implement a simple shell with the following functionalities:
• Running commands in foreground and background
• Chaining commands
• Redirecting input, output, and error streams
• Terminating commands
• Managing the processes launched by this shell
The main purpose of this lab is to familiarize you with:
• advanced aspects of C programming,
• system calls,
• process operations in Unix-based operating systems.
1
AY22/23 S2 CS2106
2 Exercises in Lab 2
struct PCBTable {
pid_t pid;
int status; // 4: Stopped, 3: Terminating, 2: Running, 1: exited
int exitCode; // -1 not exit, else exit code status
};
where
• pid is the process id
• status indicates the status of the process ; (e.g., 2: Running, 1: Exited)
• exitCode indicate the exit status of process, -1 if still running
Please use the PCBTable to maintain the details of all the processes you fork. Define a structure variable
of type PCBTable, which can be an array, a linked list or other containers. You can assume there won’t
be more than 50 PCBTables.
You should implement the three functions in the file myshell.c:
• my_init() is called when your shell starts up, before it starts accepting user commands. It should
initialise your shell appropriately.
• my_process_command() is called in the main loop. It should handle every user command except
the quit command. The function accepts two arguments:
– tokens is an array of tokens with the last element of the array being NULL.
– size is the size of that array, including the NULL.
You may want to print out the tokens array or look at the driver.c file for your own understand-
ing. You can also call other user-defined functions to make the program more modular (more details
later).
• my_quit() is called when user inputs the quit command.
We have also provided some executable programs with various runtime behaviours in the programs
folder to aid your testing later. You can also create your own programs and run them with your shell.
• programs/goingtosleep: Prints “Good night!” and then prints “Going to sleep” 10 times with
a 2-second sleep between each print.
• programs/lazy: Wake up, echo “Good morning...” and loops for 2 minutes. It takes a while (at
least 5 seconds) to respond to the SIGTERM signal.
• programs/result: Takes in a single number x and exits with a return status of x.
2
AY22/23 S2 CS2106
One you have finished testing the code, please DO NOT forget to exit out the shell.
3
AY22/23 S2 CS2106
File Structure
When you unzip lab2.tar.gz, you should find the following files:
Note: We have provided some skeleton functions to help you make the program more modular and easy
to debug. You may or may not use. Your implementation will NOT be tested for its structure or modularity.
Terminologies
The Table 1 contains information about some terminologies used in this lab document.
4
AY22/23 S2 CS2106
Let us implement a simple shell with limited features in this first exercise.
The shell should accept any of the following commands, in a loop:
{program} (args...)
Example commands:
/bin/ls
/bin/cat -n file.txt
info option
If option is 0
• Print details of all processes in the order in which they were run. You will need to print their
process IDs, their current status (Exited or Running)
• For Exited processes, print their exit codes.
• Please check the sample output for the printing format (??).
If option is 1
• Print the number of exited process.
If option is 2
• Print the number of running process.
For all other cases print “Wrong command” to stderr.
5
AY22/23 S2 CS2106
quit
• Terminate all RUNNING processes by sending SIGTERM signal to them, and print “Killing
[pid]” for each terminated process.
• Do not wait for the child processes. Print “Goodbye” and exit the shell.
Notes:
• Read Section 2.4 to find out additional details about these requirements.
• Note that info can only display a return result after a process has exited.
• You should look at the following C system calls and library functions:
– access
– fork
– execv
– wait
– waitpid
– kill
• You can use the skeleton functions provided in myshell.c to make your code modular and under-
stand where to use suggested system calls and library functions.
Sample of this exercise:
See it at https://asciinema.org/a/NZAYfBa6ZvG2TVGlaXbYzu0ZD
1 myshell> info 0
2 myshell> info
3 Wrong command
4 myshell> info -1
5 Wrong command
6 myshell> /bin/echo hello
7 hello
8 myshell> info 0
9 [3281554] Exited 0
10 myshell>
11 myshell> /bin/notaprogram
12 /bin/notaprogram not found
13 myshell>
14 myshell> info 0
15 [3281554] Exited 0
16 myshell> /bin/sleep 10 &
17 Child [3281571] in background
18 myshell> info 0
19 [3281554] Exited 0
20 [3281571] Running
21 myshell> info 2
22 Total running process: 1
23 myshell> info 0
24 [3281554] Exited 0
6
AY22/23 S2 CS2106
25 [3281571] Running
26 myshell> info 0
27 [3281554] Exited 0
28 [3281571] Exited 0
29 myshell> info 1
30 Total exited process: 2
31 myshell>
32 myshell> ./programs/result 7
33 myshell> info 0
34 [3281554] Exited 0
35 [3281571] Exited 0
36 [3281579] Exited 7
37 myshell> info 2
38 Total running process: 0
39 myshell> info 1
40 Total exited process: 3
41 myshell> ./programs/result 256
42 myshell> info 0
43 [3281554] Exited 0
44 [3281571] Exited 0
45 [3281579] Exited 7
46 [3281585] Exited 0
47 myshell> ./programs/showCmdArg 5 23 1 &
48 Child [3281597] in background
49 myshell> [Arg 0]: 5
50 [Arg 1]: 23
51 [Arg 2]: 1
52
58 Goodbye
7
AY22/23 S2 CS2106
wait {PID}
Example command:
wait 226
{PID} is a process id created using “{program} (args...) &” syntax and has not yet been
waited for before.
If the process indicated by the process id is RUNNING, wait for it.
Else, continue accepting user commands.
No output should be produced.
terminate {PID}
Example command:
terminate 226
Example command:
; is an operator that allows multiple “{program} (args...)” to be chained together and ex-
ecuted sequentially, and in the foreground.
1. If {program1} exists and is readable and executable:
• Run and wait for {program1}.
• There might be error output from the program if it fails, which is fine.
2. Else:
• Print “{program} not found” to stderr.
3. Go back to step 1 with the next {program2}.
Note: There will always be spaces around “;”. It would be helpful to start with 2 chained com-
mands, before extending your implementation to any number of chained commands. Last com-
mand will not have a “;”
8
AY22/23 S2 CS2106
info option
Should now have an additional status “Terminating”, in addition to the original “Running” and “Ex-
ited”.
If option is 0
• Print details of all processes in the order in which they were run. You will need to print their
process IDs, their current status (Exited or Running)
• For Exited processes:
– If the child process ended normally, print the exit code of the child process.
– If the child process ended abnormally, print which signal (the signal number) caused
the child process to exit (link).
If option is 3
• Print the number of terminating process
Notes:
• Read Section 2.4 to find out additional details about these requirements.
• You should look at the following C system calls:
– wait
– kill
Sample of this exercise:
See it at https://asciinema.org/a/K2Fk0SkjysZQdWF6LcY2CB2qk
10 Goodbye
See it at https://asciinema.org/a/Fb8y1yXa1TVbDq1bW2c5gw6w1
9
AY22/23 S2 CS2106
4 [1004267] Running
5 myshell> terminate 1004267
6 myshell> info 0
7 [1004267] Exited 15
8 myshell> info 2
9 Total running process: 0
10 myshell>
11 myshell> ./programs/lazy &
12 Child [1005093] in background
13 myshell> Good morning...
14 terminate 1005093
15 myshell> Give me 5 more seconds
16
17 myshell> info 0
18 [1004267] Exited 15
19 [1005093] Terminating
20 myshell> info 3
21 Total terminating process: 1
22 myshell> info 0
23 [1004267] Exited 15
24 [1005093] Exited 0
25 myshell> info 3
26 Total terminating process: 0
27 myshell> quit
28
29 Goodbye
See it at https://asciinema.org/a/1Nye0hABvhcQG7M8os5TIdh0G
10
AY22/23 S2 CS2106
18 myshell> info 2
19 Total running process: 0
20 myshell>
21 myshell> quit
22
23 Goodbye
11
AY22/23 S2 CS2106
In this exercise, we will implement the redirection operators for our shell by extending the {program}
(args...), the {program} (args...) & commands, and the ; operator from exercises 1 and 2.
Implement the following user commands:
(< {file}), (> {file}), and (2> {file}) are optional and may or may not be present.
If there are more than 1 present, all {file}s will be different, i.e., no reading and writing to the
same file.
Example commands
/bin/cat a.txt > b.txt
/bin/sort < test.txt > sorted.txt
/bin/sleep 2 2> error.log
Note the & symbol at the end of the user command. Same behaviour as above, except that the
command will be run in the background instead, i.e., your shell should continue accepting user
commands.
12
AY22/23 S2 CS2106
Example command
/bin/printf hello\nworld\n > test.txt ; /bin/sort < test.txt >
,→ sorted.txt ; /bin/cat sorted.txt
The rest of the ; operator’s function remains the same as in exercise 2. Note that the same file can
be read/written across different programs, as shown above with test.txt.
If a file is not found for (< {file}), just print “{file} does not exist” to stderr.
Again, continue executing the rest of the programs even if one of the program fails.
Notes:
• For simplicity, you may assume that (< {file}) always appears before (> {file}), and both
will always appear before (2> {file}). Also, the redirection operators will always appear after
{program} (args...). This differs from the actual Bash shell where even something like
> a.out < a.in cat
Have you wondered how piping actually works? Find out more here!
• Read Section 2.4 to find out additional details about these requirements.
• You should look at the following C system calls and library functions:
– fopen / open
– dup2
• A flow chart about how the chained commands with input redirection (only) should be handled is
shown in Figure 1.
Sample of this exercise: See it at https://asciinema.org/a/ZoqYgvqeezLsZ5UdkMIWABs3G
13
AY22/23 S2 CS2106
14
AY22/23 S2 CS2106
64 myshell> quit
65
66 Goodbye
15
AY22/23 S2 CS2106
16
AY22/23 S2 CS2106
This section will be long and verbose, but it is essential as we need to limit the scope of the lab by defining
some constraints. Please read through it, perhaps after you have read through the exercises. The details
below apply to all exercises in this lab, unless otherwise stated.
• Any command that does not satisfy the correct syntax details will not be tested on your shell.
• Any command that does not satisfy the formats specified in the various tables shown in sections
2.1 – 2.6 will not be tested on your shell. To give an example, “/bin/ls & -a” does not satisfy
our command formats, because firstly, the ampersand must appear at the end of the command,
and secondly, if we interpret the ampersand as an argument, it will be syntactically invalid by our
definitions.
• It is good practice to check the return values of all syscalls (and indeed, all functions) and handle
any errors that occur.
• In total, during the entire lifetime of the program, no more than 50 (<= 50) processes will be run
(counting both currently running processes and processes that have exited).
• Programs with the same names as the user commands will not be run, i.e., there will be no name
collisions.
• Programs that require interactive input will not be tested on your shell. For example, programs such
as the Python shell, or your shell itself.
• Notice that background processes may sometimes mess up the display on your shell, which may
cause your output to be slightly different from the sample session. This is fine. Other than these
cases, your output, excluding the PID numbers, should match the sample sessions exactly.
• At any point of time, if a file is being read from or executed, we will not be running a command that
writes to that same file.
• You may assume that there will be no permission issues for files that already exist (files that are not
created by your shell). Your shell will have read, write, and execute permissions, where necessary,
for these files that already exist. Similarly, there will be no permission issues for directories as well.
• To illustrate what a valid file path is for the purposes of this lab, we split a path up into two parts.
The first part is the path to the containing directory (we will call it <dir>), such that running the
command cd <dir> in a bash shell always succeeds. The implications of this are that every di-
rectory along the path to our file exists and is accessible. This first part is optional, and if omitted
defaults to the current working directory. The second part (non-optional) is the file name, which can
only contain the following characters: a-z, A-Z, 0-9, dash (-), and period (.). In <dir>, there might
or might not be a file name that matches our second part, but either way it is still considered a valid
file path.
• You will have to check if the process can execute the programs or not.
• For simplicity, only regular files/directories will be used in our testing. No symbolic links will be used.
17
AY22/23 S2 CS2106
Right now, when you press Ctrl-Z or Ctrl-C while running your shell, it gets suspended or interrupted re-
spectively. For this exercise, you will intercept the SIGTSTP and SIGINT signals, which correspond to Ctrl-Z
and Ctrl-C respectively (these keys may differ if you’re on a Mac).
Please do this bonus using a copy of your Exercise 1-3 solutions, in a separate folder as instructed in sec-
tion 3. The bonus and the main parts will be graded separately.
<Ctrl-Z>
If there is a currently running program that your shell is waiting for, send the SIGTSTP signal to it,
and print “[PID] stopped”.
Else, do nothing and continue accepting user input.
<Ctrl-C>
If there is a currently running program that your shell is waiting for, send the SIGINT signal to it,
and print “[PID] interrupted”.
Else, do nothing and continue accepting user input.
fg {PID}
info option
Should now have an additional status “Stopped” , in addition to the original “Running”, “Exited”,
and “Terminating”.
If option is 4
• Print the number of stopped process
quit
18
AY22/23 S2 CS2106
1 myshell> ./programs/lazy
2 Good morning...
3 ^C[3507490] interrupted
4 myshell> info 0
5 [3507490] Exited 2
6 myshell>
7 myshell> ./programs/goingtosleep
8
9 Good night!
10 Going to sleep
11 ^Z[3507594] stopped
12 myshell> info 0
13 [3507490] Exited 2
14 [3507594] Stopped
15 myshell> info 4
16 Total stopped process: 1
17 myshell> fg 3507594
18 [3507594] resumed
19 Going to sleep
20 Going to sleep
21 Going to sleep
22 Going to sleep
23 Going to sleep
24 ^Z[3507594] stopped
25 myshell> info 0
26 [3507490] Exited 2
27 [3507594] Stopped
28 myshell> fg 3507594
29 [3507594] resumed
30 Going to sleep
31 Going to sleep
32 Going to sleep
33 Going to sleepmyshell> quit
34
35 Goodbye
19
AY22/23 S2 CS2106
Before you submit your lab assignment, run our check archive script named check_zip.sh.
The script checks the following:
a. The name or the archive you provide matches the naming convention mentioned in section 3.
b. Your zip file can be unarchived, and the folder structure follows the structure presented in section 3.
c. All files for each exercise with the required names are present.
d. Each exercise can be compiled.
You have the zip files on cluster nodes as follows:
Once you have the zip file, you will be able to check it by doing:
$ chmod +x ./check_zip.sh
$ ./check_zip.sh E0123456.zip % replace with your zip file name
or
$ ./check_zip.sh E0123456_E0123457.zip
During execution, the script prints if the checks have been successfully conducted, and which checks
failed. Successfully passing checks ensures that we can grade your assignment.
20
AY22/23 S2 CS2106
Zip the following files as E0123456.zip (use your NUSNET id, NOT your student no A012...B, and use
capital ‘E’ as prefix):
Do not add any additional folder structure during zipping.
E0123456.zip contains 1 file, with 1 extra folder if the bonus section is attempted, following this file struc-
ture:
E0123456
E0123456/myshell.c
E0123456/bonus
E0123456/bonus/myshell.c
E0123456
E0123456/myshell.c
You can check your file structure matches the above format by using the following command:
If submitting as a pair, only one member needs to submit. The folder name should be both partners’ NUS-
NET ids separated by an underscore, i.e., E0123456_E0123457 instead of E0123456. Resultant zip
file should be E0123456_E0123457.zip instead of E0123456.zip.
Upload the zip file to the “Lab 2” folder on Canvas. Note the deadline for the submission is 5th March,
2023, 11.59 PM Saturday.
Please ensure that you follow the instructions carefully (output format, how to zip the files etc.). Deviations
will be penalized.
21