辅导Programming留学生、讲解Java编程语言、辅导POSIX threads、讲解Python/C++设计

- 首页 >> Java编程

Programming Assignment 3

Adding More Features to the Shell

Objective

The main objective of this assignment is to adding more functionalities to your shell from Project 2. So the

first step is to get your shell from Project 2 working, or at least enough to implement features for this project,

if it doesn't already work. (A minimally working Project 2 code.) The assignment introduces programming

with POSIX threads (pthreads), including mutual exclusion with pthreads. Experience with some more

system calls and C library functions will also be done to hone your programming skills. Reading of man

pages will also be a must to do well.

Try sample code to experiment more.

The Assignment

Implement the ability to background a 'job' using '&'. This needs to only work for executing external

commands. What you need to do is to identify that the last thing given on the command line is a '&'.

The backgrounded job is executed concurrently with the shell, rather than having the shell wait for it

to complete. The shell should go and print another prompt to wait for the next command. It is possible

that several backgrounded jobs could be running concurrently at the same time.

When a parent process does not (get the chance to) do a wait(2) call for its children, the children will

become zombie processes (marked by <defunct> in Linux or Z) when they exit. To prevent this for

your backgrounded jobs, you need to do a nonblocking waitpid(2) call at some point (by using the

WNOHANG option). See the man page for options to use to reap an entry in a nonblocking fashion. It

is suggested that you do this before printing the next prompt. If the parent has a zombie child, the call

will reap the process entry, otherwise no harm is done either. Check out sample zombie code.

Extra Credit (10 points): Implement the 'fg' built-in command to bring a backgrounded job into the

foreground. With no arguments, a default backgrounded job will be chosen. It should also take an

argument to specify which backgrounded job to bring to the foreground. Add your own test to show

this works and clearly document if you implement this. Your shell should work just like when a job

isn't backgrounded when a job is brought into the foreground. Refer to Sections 9.8 and 9.9 of Stevens

and Rago's APUE book.

Add the watchuser built-in command. This command takes as its first argument a username to keep

track of when the user logs on, similar to the watch shell variable in tcsh (review tcsh shell variables).

This command also takes a second optional argument of "off" to stop watching a user. The first time

watchuser is ran, a (new) thread should be created/started via pthread_create(3), which runs a

while (1) loop with a sleep(3) call for 20 seconds in it to track logins of users. Only one watchuser

thread should ever be running. The thread should get the list of users from a global linked list which the

calling function (of the main thread) will modify by either inserting new users or turning off existing

watched users. For instance,

mysh % watchuser cshen

mysh % watchuser smith

mysh % watchuser joe

mysh % watchuser smith off

Sample code to obtain the list of currently logged in users can be found here .

Since the calling function (of the main thread) and the watchuser thread have access to share data

(e.g., the global linked list of users to be watched), the shared data must be protected with a mutex lock

using pthread_mutex_lock(3) and pthread_mutex_unlock(3). When a user from the watch list logs

on to a new tty, this thread should prin a mesage "USER has logged on TTY from HOST." to the

terminal.

Extra Credit (5 points): Also have your thread notify when the users being watched log off of a tty.

Make it clear that you do this if you do, and document it well.

Add the watchmail built-in command. This command takes as the first argument a name of a file,

which must already exist (give an error if it doesn't), to watch for new `mails' in. It can take an optional

second argument of "off" to turn off of watching of mails for that file. It should start up a new thread

using pthread_create(3) for each file to watch for mail in (the name of the file is passed as a

parameter to the thread). This function, to watch for new mail, should run a while (1) loop, which

will do a stat(2) to see if the file got bigger since the last check. A sleep for 1 second should be in the

loop. The st_size member of a stat struct can be used to check the size of the file. When the file

increases in size, the thread, for that file, should print out:

BEEP You've Got Mail in FILE at TIME

The BEEP should be done with a \a in printf(). FILE should be the name of the file that the thread is

watching. And TIME should be the current time in ctime(3) format. To get the current time use

gettimeofday(3) and pass tv_sec of the timeval struct to ctime().

mysh % watchmail mbox

mysh % watchmail /usr/cshen/pobox

mysh % watchmail mbox off

When the "off" parameter is given the thread that is watching that file needs to be canceled with

pthread_cancel(3) . A linked list of active threads and files associated with each one should be

maintained to support this. When an error condition happens in a thread, pthread_exit(3) should be

used. Your program should support watching multiple files. A separate thread will run to check each

file.

Note that these two threaded "watch" commands will print messages when certain things happen,

which could be annoying when using the shell for normal functioning. However if you have multiple

windows open, they could be useful to run in one for notification. As always, document your code well

and explain how things work.

Add support to your shell so the following file redirections will work: (review OSTEP Chapter 5.4

Why Motivating The API for details)

> - redirect standard output of command on the left to file on the right (i.e. command > file)

>& - redirect both standard output and standard error of command on the left to file on the right

(i.e. command >& file).

>> - redirect standard output of command on the left to file on the right (i.e. command >> file),

appending to the file.

>>& - redirect both standard output and standard error of command on the left to file on the right

(i.e. command >>& file), appending to the file.

< - redirect standard input of command on left to come from file on the right (i.e. command <

file).

To do the file redirection requires that you understand file descriptors well and how a child process

inherits file descriptors from its parent. You will need to close and reopen file descriptors 0, 1 and 2.

For this you will need to use the open(), close() and dup() system calls. (Refer to Section 3.12 of

Stevens and Rago's APUE book for dectails of dup().) Please use dup(2) instead of dup2(3). This

doesn't require a lot of code, just the right calls in the right places. To get the normal function of stdin,

stdout and stderr back, opening of /dev/tty will be required. Be sure to use the right options for

open() in each situation (i.e. read/write, truncate/append). Read man pages!

To help out in testing both stdoue and stderr, there is a small program here. Compile as test-1+2 and

use it in test runs.

Code example to redirect stdout to a file:

fid = open(filename, O_WRONLY|O_CREAT|O_TRUNC);

close(1);

dup(fid);

close(fid);


To redirect stdout back to the screen, replace the open() with:

fid = open("/dev/tty", O_WRONLY);


and repeat the other 3 calls.

Additionally, add a noclobber command which will affect how these operators handle file creation. All

it should do is change a variable (tcsh shell variables), noclobber (Prevent accidental overwriting of

existing files), from 0 to 1 or from 1 to 0 and print out the new value of it. This variable should default

to 0 and cause your shell to act the same way as csh/tcsh does with respect to the noclobber variable.

That is when it is 0 > and >& will overwrite existing files and >> and >>& will create the file if it

doesn't exist. When noclobber is 1 the shell should refuse to overwrite existing files, refuse to create a

file for appending and print the same error messages csh/tcsh do in those situations.

[cisc361:/usa/cshen/public_html/361/Proj_3 216] tcsh

cisc361[31] [~/public_html/361/Proj_3/]> touch out-file

cisc361[32] [~/public_html/361/Proj_3/]> set noclobber

cisc361[33] [~/public_html/361/Proj_3/]> echo hello > out-file

out-file: File exists.

cisc361[34] [~/public_html/361/Proj_3/]> unset noclobber

cisc361[35] [~/public_html/361/Proj_3/]> echo hello > out-file

cisc361[36] [~/public_html/361/Proj_3/]> more out-file

hello

cisc361[37] [~/public_html/361/Proj_3/]>

Add support for interprocess communication (IPC). This is adding support for | and |&. The | operator

should pipe standard output of the command on the left to standard input of the command on the right.

(i.e. command1 | command2). For |&, standard error should be piped as well as standard output. It is

only required that this works when the command on the right is an external command, the one on the

left could be either built-in or external. You also need to avoid generating zombie processes!

[cisc361.acad.ece.udel.edu:/usa/cshen 132] tcsh

cisc361[29] [~/]> which echo

echo: shell built-in command.

cisc361[30] [~/]> echo hello world | wc

1 2 12

cisc361[31] [~/]>

cisc361[34] [~/361/2018/apue.3e/intro/]>

cisc361[34] [~/361/2018/apue.3e/intro/]> ls X

X: No such file or directory

cisc361[35] [~/361/2018/apue.3e/intro/]> ls X | wc

X: No such file or directory

0 0 0

cisc361[36] [~/361/2018/apue.3e/intro/]> ls X |& wc

1 6 29

cisc361[37] [~/361/2018/apue.3e/intro/]>

To implement this, you will need to use pipe(2). To implement pipes, file descriptor redirection will

be required again by using close() and dup(). (Refer to Section 3.12 of Stevens and Rago's APUE

book for dectails of dup().) However you will be getting open file descriptors by using pipe(2)

instead of open(). This task is a bit trickier than the previous, but again does not take much code. It

will take some trials and thinking to get right. You will probably need to move your command running

code to a new function which takes info about the style of the pipe (if any) and which side of the pipe

this command will be on. The left side of the pipe needs to not cause the parent to do a blocking wait,

while the right side does.

Once the parent process (your shell) has created a pipe and forked the two child processes that will

write and read from the pipe, make sure the parent explicitly closes the file descriptors for its read and

write access to the pipe. If you fail to do this, the child reading from the pipe will never terminate. This

is because the child reading from the pipe will never get the END-OF-FILE (end-of-pipe) condition

(and hence never terminate) as long as one process (in this case your shell program, by mistake) has an

open write file descriptor for the pipe. The overall result will be a deadlock - your shell waiting for a

child to terminate and the child waiting for the shell to close the pipe, which was inadvertently left

open. (Review Pipes and Section 9.9 of Stevens and Rago's APUE book for dectails.)

This part will also require that your command line processing puts the arguments into two separate

argument vectors. And do not use popen(), you will get no credit.

As always document your code well and explain how things work. It is only necessary that one of &,

the file redirections and piping work at a time for this project. Remember that man pages are your

friends and you may find using truss(1) useful.

Test Runs

To test '&' show running some commands in the background in your script. To test watchmail you can simply

make the file larger in another window a couple of times. Also test for turning off watching a file. And to test

watchuser, come up with your own tests. The TAs will test each feature.

Test your shell by running the following commands in it (in order):

pwd

ls &

ls -l &

cd /

sleep 20 &

ls & ; run before sleep is done

pid

tty

/bin/ps -lfu USERNAME ; replace USERNAME with your own

cd

cd [project test dir of your choosing]

pwd

ls -l

rm -f mail1 mail2

touch mail1 ; create this file

watchmail mail1

echo hi >> mail1

echo HiThere > mail2 ; create another file

watchmail mail2

echo there >> mail1

echo Subject: >> mail2

cat mail1

cat mail2

watchmail mail1 off

echo bye >> mail1

echo bye >> mail2 ; still watching this one

rm -f test1 test2 test3 test4 test5 test6 test7 test8

test-1+2 > test1

test-1+2 >> test2

test-1+2 >& test3

test-1+2 >>& test4

cat test1 test2 test3 test4

test-1+2 > test1

test-1+2 >> test2

test-1+2 >& test3

test-1+2 >>& test4

cat test1 test2 test3 test4

noclobber ; turn noclobber on

test-1+2 > test5

test-1+2 >> test6

test-1+2 >& test7

test-1+2 >>& test8

cat test5 test6 test7 test8

test-1+2 > test5

test-1+2 >> test6

test-1+2 >& test7

test-1+2 >>& test8

cat test5 test6 test7 test8

grep hello < test8

grep error < test8

rm -f test9 test10 test11 test12

noclobber ; turn noclobber off

test-1+2 > test9

test-1+2 >> test10

test-1+2 >& test11

test-1+2 >>& test12

cat test9 test10 test11 test12

ls | fgrep .c ; show pipes works

./test-1+2 | grep hello

./test-1+2 |& grep hello

./test-1+2 | grep output

./test-1+2 |& grep output

./test-1+2 |& grep error

pid ; zombie avoidance checking

/bin/ps -lfu USERNAME | grep defunct ; replace USERNAME with your username


If your file redirection doesn't work then use another shell to modify your mailfiles to test your watchmail

command. The TA will also test each feature.

Grading

20% for adding & support (including avoiding zombies)

15% for watchmail

15% for watchuser (for log ins and mutex locking)

20% for file redirection

20% for interprocess communications

10% documentation and code structure (remember to check error situations and avoid too much

duplicate code)

10% extra credit for fg

5% extra credit for watchuser (for log outs)

Turn In

You need to tar up your source code and submit the tar file so that your shell can be tested and graded. To do

this,

1. Put all the source files of your project #3 into a subdirectory named YourLoginName_3.

2. At the current working directory, do

tar cvf YourLoginName_3.tar YourLoginName_3

To verify that your files are in the tar file take a look at the table of contents of the tar file like:

tar tvf YourLoginName_3.tar

Then submit your tar file to Canvas.


站长地图