CMSC 421: Principles of Operating Systems

Homework 2: Elections Have Consequences

This homework is due on Tuesday, February 23, at 11:59:59 PM (Eastern standard time). You must use submit to turn in your homework like so: submit cs421_jtang hw2 evote.c candidate.c

Your programs must be named evote.c and candidate.c, and they will be compiled on Ubuntu 14.04 as follows:

  gcc --std=c99 -Wall -O2 -o evote evote.c
  gcc --std=c99 -Wall -O2 -o candidate candidate.c
There must not be any compilation warnings in your submission; warnings will result in grading penalties. In addition, each code file must be properly indented and have a file header comment, as described on the coding conventions page.

Due to UMBC policy, you may not use the GL systems for this assignment, because you will be using the fork() system call.

In this homework, you are writing an e-voting system for the upcoming presidential primaries. The system consists of two programs. The user interface is handled by evote. The candidates that may be voted for are represented by multiple instances of candidate.

Part 1: Create Processes

The evote program will be divided into several parts as follows. Begin by inspecting the command line arguments. There must be exactly four arguments (plus the program name itself); if not then display an error message and quit. These four arguments are the names of candidates to appear on the ballot.

Next, create four unnamed pipes by calling the pipe() function four times. Store those file descriptors somewhere convenient.

Third, call fork() four times, to create four (and only four!) child processes. Each child is associated with one of the unnamed pipes; it is up to you to figure out how to do this.

In the parent process, close the reading end of all four pipes and then display the PIDs of its child processes; use the %ld specifier when printing the value.

In the child process, close the writing the ends of all unnamed pipes. Second, use dup2() to both close stdin and duplicate the reading end of its pipe. Third close the original reading end of its pipe. (That pipe can still be read via the file descriptor that now occupies stdin.) Four, call execlp() to execute the candidate program. While executing the program, pass the name of one of the candidates from the original command line. In candidate.c, set up a signal handler for signals SIGUSR1 and SIGUSR2, and set a global variable (representing number of votes received) to zero. Then within a loop, read from the stdin (which is really its unnamed pipe); this will cause the child processes to block.

Part 2: Main Menu

Within the parent process, in evote.c, display the following menu:

Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns

Read from the user a menu option, and then perform the requested action as below.

Part 3: Cast Ballot

In the parent process, in evote.c, display the four candidates' names in a menu. Then read from the user a number, one through four. Send a SIGUSR1 signal to the candidate process with that name. If a child process has already terminated (see Part 4 below), then do not send the signal.

In the child process, in candidate.c, in its signal handler for SIGUSR1, increment by one the number of votes that candidate has received.

Afterwards, in the parent process, return to the main menu.

Part 4: Suspend Campaign

In the parent process, in evote.c, display the four candidates' names in a menu. Then read from the user a number, one through four. For that child process, close its unnamed pipe. If the child's pipe has already been closed, display an error message and return to the main menu.

In the child process, in candidate.c, its attempt to read from stdin will result in returning an end-of-file value. Although the proper term is "suspending a campaign", in reality the candidate is quitting the election. Thus, in the child process call exit() to terminate.

Back in the parent process, call waitpid() with the child's PID, so as to reap the child. Afterwards, return to the main menu.

Part 5: Show Vote Counts

In the parent process, in evote.c, display each child's PID and its process state. To get the process state, open and read from the file /proc/child_pid/stat, where child_pid is the PID of that child process. The state is the third field. Next send a SIGUSR2 signal to each child. If a child has already terminated (see Part 4 above), display the string "N/A" instead of its PID and process state, and do not send a signal.

In the child process, in candidate.c, in its signal handler for SIGUSR2, display both that process's candidate name (from its command line argument) and the number of votes received.

Afterwards, in the parent process, return to the main menu.

Process states are represented by various letters. Use your favorite search engine to look up the meaning of each possible letter.

Part 6: Commit Voter Fraud

One benefit of using computerized voting is that voter fraud is easily achieved, and this program is no different.

In the parent process, in evote.c, display the four candidates' names in a menu. Then read from the user a number, one through four. Then get from the user the number of votes to fraudulently add. Write to that child process's unnamed pipe a number of characters (it does not matter which one). If a child process has already terminated (see Part 4 above), then do not perform the write.

In the child process's reading loop, in candidate.c, increment its vote count for each character received.

Afterwards, in the parent process, return to the main menu.

Part 7: End All Campaigns

In the parent process, in evote.c, for each child that is still alive, send it a kill message (as per Part 5 above). Finally, quit the program.

After you finish the code, as an experiment, temporarily modify evote.c so that child processes do not close the unnamed pipes' writing ends. Instead, have it just close its own pipe and not affect other pipes. Now try choosing this last menu option, and note that child processes are no longer terminating. In your evote.c file, add a comment block describing your observations. Then explain why a child process needs to close all writing ends, not just its own pipe, so that it can terminate. (Make sure you revert your changes prior to submitting your work.)

Sample Output

Here is a sample output from running the evote. Observe that because multiple processes are trying to write to the terminal simultaenously, their outputs are interleaved in non-obvious ways.

$ ./evote "Ronald Clump" "Raphael Cruise" "Mallory Klingon" "Ernie Flanders"
Candidate "Ronald Clump" is pid 3371.
Candidate "Raphael Cruise" is pid 3372.
Candidate "Mallory Klingon" is pid 3373.
Candidate "Ernie Flanders" is pid 3374.
Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns
Choose an option> (User enters 1)
0. Ronald Clump
1. Raphael Cruise
2. Mallory Klingon
3. Ernie Flanders
Choose a candidate> (User enters 1)
Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns
Choose an option> (User enters 1)
0. Ronald Clump
1. Raphael Cruise
2. Mallory Klingon
3. Ernie Flanders
Choose a candidate> (User enters 3)
Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns
Choose an option> (User enters 1)
0. Ronald Clump
1. Raphael Cruise
2. Mallory Klingon
3. Ernie Flanders
Choose a candidate> (User enters 3)
Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns
Choose an option> (User enters 3)
Ronald Clump: pid 3371 state S
Raphael Cruise: pid 3372 state S
Mallory Klingon: pid 3373 state S
Ernie Flanders: pid 3374 state S
Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns
Choose an option> I am Mallory Klingon and I have 0 vote(s).
I am Raphael Cruise and I have 1 vote(s).
I am Ronald Clump and I have 0 vote(s).
I am Ernie Flanders and I have 2 vote(s).
(User enters 4)
0. Ronald Clump
1. Raphael Cruise
2. Mallory Klingon
3. Ernie Flanders
Choose a candidate> (User enters 0)
Number of votes to add> 999
Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns
Choose an option> (User enters 2)
0. Ronald Clump
1. Raphael Cruise
2. Mallory Klingon
3. Ernie Flanders
Choose a candidate> (User enters 1)
Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns
Choose an option> (User enters 3)
Ronald Clump: pid 3371 state S
Raphael Cruise: N/A
Mallory Klingon: pid 3373 state S
Ernie Flanders: pid 3374 state S
Main Menu:
1. Cast ballot
2. Suspend campaign
3. Show vote counts
4. Commit voter fraud
5. End all campaigns
Choose an option> I am Ronald Clump and I have 999 vote(s).
I am Ernie Flanders and I have 2 vote(s).
I am Mallory Klingon and I have 0 vote(s).
(User enters 5)

Other Hints and Notes

Extra Credit

Sorry, there is no extra credit available for this assignment.