You are to write a remote shell system, where a server process resides on another machine on the network and the user provides commands to the server via a client process on a local machine. The client sends the commands to the server, the server executes them, and sends the output back to the client.
A main goal of this part is to re-enforce some system calls and
process manipulation facilities from an Operating System. Then,
provide a basic introduction to OS support for sockets
,
while building upon some of your previous OS knowledge. You will
write a basic remote shell in which a user specifies an
arbitrary shell command to be executed, and it is sent over socket
connection and executed on a remote server.
You will write a server and a client:
Server: The server will run on the remote machine. It
will bind to a Unix socket at a port known to the client. When it
receives a connection, it fork()
s a child process to
handle the connection. The parent process loops back to wait for more
connections. The child process first verifies that the client is
valid via a (clear-text) password. Then, it executes the given
shell command, returning all stdout
and stderr
to the client. The server can assume the shell command does not
use stdin
. Upon completing the command, the server
will exit.
Client: The client will run on the local machine. From
the command line, the user will specify the host where the server
resides and the command to be executed. The client will then connect
to the server via a socket and transmit the password and the command.
The client will display any output received from the server to
the stdout
and then exit. Thus, the client does one
command at a time and is not an "interactive" shell.
Once implemented, you will evaluate the performance of your remote shell system through a series of experiments.
The samples code on the course web page contains some samples that may be useful. In particular:
talk.tcp.c
and listen.tcp.c
-
helpful samples for doing socket code.
fork.c
-
showing the simple use of the fork()
call.
execl.c
-
showing simple use of the execl()
call.
get-opt.c
-
code that parses command line arguments (fairly)
painlessly.
To get help information about specific Unix commands, use the "man" command. For instance, entering "man tcsh" will display the manual page entry for the tcsh. If you specify a number after the word "man", it looks in the indicated section number.
The following system calls for setting up your sockets may be helpful (you do not necessarily need to use them all):
connect()
accept()
socket()
listen()
bind()
close()
send()
recv()
getservbyname()
gethostname()
gethostbyname()
gethostbyaddr()
The following system calls for the "shell" part and communication through the sockets may be helpful (you do not necessarily need to use them all):
fork()
execvp()
strtok()
dup2()
fdopen()
In particular, dup2()
makes a new file descriptor be
the copy of an old file descriptor (closing the new file descriptor
first, if necessary). You can use dup2()
to make stdout
(STDOUT_FILENO
, declared in <unistd.h>) a copy of
the connected socket. This will close the stdout (to the display, by
default) and make it instead the socket, causing all output to go down
the socket.
Here are some examples. The server:
claypool 94 zeus% ./server -h remote shell server usage: server [flags], where flags are: -p # port to serve on (default is 6013) -d dir directory to serve out of (default is /home/claypool/msh) -h this help message claypool 95 zeus% ./server remote shell server activating. port: 6013 dir: /home/claypool/msh Socket created! Accepting connections. Connection request received. forked child received password password ok command: ls executing command... Connection request received. forked child received password password ok command: ls -l executing command... Connection request received. forked child received password password ok command: cat Makefile executing command...
The client from the same session:
claypool 49 tethys% ./remsh -h remote shell client usage: remsh [flags] < command >, where flags are: {-c < command >} command to execute remotely {-s < host >} host server is on [-p #] port server is on (default is 6013) [-h] this help message claypool 41 tethys% ./remsh -c "ls" -s zeus Makefile client.c remsh remsh.c index.html server server.c server.h sock.c sock.h claypool 42 tethys% ./remsh -c "ls -l" -s zeus total 37 -rw-r----- 1 claypool users 212 Sep 7 22:19 Makefile -rw-r----- 1 claypool users 997 Sep 1 09:27 client.c -rwxrwx--- 1 claypool users 6918 Sep 9 00:04 remsh -rw-r----- 1 claypool users 3790 Sep 9 00:03 remsh.c -rw-r----- 1 claypool users 5374 Sep 8 23:50 index.html -rwxrwx--- 1 claypool users 7919 Sep 9 00:09 server -rw-r----- 1 claypool users 4383 Sep 9 00:09 server.c -rw-r----- 1 claypool users 240 Sep 7 22:19 server.h -rw-r----- 1 claypool users 2638 Sep 1 09:36 sock.c -rw-r----- 1 claypool users 614 Sep 1 09:27 sock.h claypool 43 tethys% ./remsh -c "cat Makefile" -s zeus # # # CC = gcc all: server remsh server: server.c server.h $(CC) -o server server.c remsh: remsh.c server.h $(CC) -o remsh remsh.c clean: /bin/rm -f remsh server core *.o *~
A useful program for maintaining large programs that have been broken
into many software modules is make. The program uses a file,
usually named Makefile, that resides in the same directory as
the source code. This file describes how to compile a number of
targets specified. To use, simply type "make" at the command line.
WARNING: The operation line(s) for a target MUST begin with a TAB (do
not use spaces). See the man page for make
and
gcc
for additional information.
# # Makefile for the macro shell program # CC = gcc CFLAGS = LIBFLAGS = all: remsh server server: server.c $(CC) $(CFLAGS) server.c server.o -o server $(LIBFLAGS) remsh: remsh.c $(CC) $(CFLAGS) remsh.c remsh.o -o remsh $(LIBFLAGS) clean: /bin/rm -rf *.o core remsh server
You will design experiments to evaluate some basic performance parameters of your remote shell:
ftp
or wget
.
For all experiments, you should repeat the measurements more than one time in order to account for uncontrolled system variance. A suggested minimum is 3 times.
When your experiments are complete, you must turn in a brief (1-2 page) write-up with the following sections:
In addition to the evaluation writeup, you must answer the following questions when you turn in your project:
Please include a README file giving some project information. The main information I'd like you to have from the documentation standard is: author, date, project id, language, OS dependencies, description and building information. Do note, however, that this information does not take the place of normal comments in your code! These comments are to make it easier to assess a grade if there are difficulties in the program.
Here is a sample of the information you should have:
Author: Mark Claypool Date: 9/15/05 Project ID: Project 3 CS Class: CS3013 Programming Language: C OS/Hardware dependencies: Unix, gcc Problem Description: This program implements a distributed shell that does blah, blah, blah How to build the program: "make"
To hand in project 3:
Be sure the answers to the Questions are in a file called "questions.txt" in the directory with your project code.
Also, before you use turnin tar
up (with
gzip
) your files. For example:
mkdir proj3 cp * proj3 /* copy all your files to submit to proj3 directory */ tar -czf proj3.tgz proj3
Then copy your files from your Fossil client to your CCC account:
scp proj3.tgz login_name@ccc:~/ /* will ask your ccc passwd */ ssh login_name@ccc /* will ask your ccc passwd */ /cs/bin/turnin submit cs3013 proj3 proj3.tgz
Use turnin:
/cs/bin/turnin submit cs3013 proj3 proj3.tgz
Send all project questions to the cs3013-staff at cs.wpi.edu mailing list.
Send all Fossil administrative questions to the fossil at cs.wpi.edu mailing list.