Lab 6 - Simple Shell Daemon

Due: Monday May 18, 2009 by 1:00pm

Simple Shell Daemon

In Lab 4, one hinderance to having multiple simultaneous connections like Lab 5 was how to interact with more than one connection. Standard in can only be handled by one process at a time. If the parent process handles standard in, how does it tell which child to pass the information to so that the child can pass the data down its network connection? Likewise, how does the child inform the parent of information to pass to standard out and how does the parent organize this information so data from two children does not overlap?

One way to handle this without having to involve the parent process is to bind another program to each connection to handle the user's requests. For example, when you are on a graphical environment, there are seperate programs in each window. Each program can accept data from the keyboard and print data to the screen. The currently active window (program) is the one that receives the data from the keyboard, so in that fashion the keyboard can be shared among multiple programs by changing which window is active. Likewise, each program only prints data to the screen within their window, so standard out can be shared without any entanglement of output.

From the network server's perspective, there is not so much standard in and standard out as there is incoming data on a socket and data to send out over a socket. The bound process and client-side program will handle converting network data into standard in and standard out. By binding a process to each child to handle the data, the network server no longer needs to process the data coming in and out. It can focus purely on delivering the data and managing the children processes. The network server accomplishes this by mapping the data coming into the socket (recv()) to the standard in of the bound process. Likewise, it maps the standard out of the bound process to the data being sent out on the socket (send()). The bound process needs no knowledge of network sockets; it operates purely on standard in and standard out.

In this part of the lab, we will look at how three programs work together to provide such a system. These programs make a small, pared-down, simple shell over telnet. The code can be found in the lab code tarball. Compile the code with the commands:

make shell
make telnet

s_daemon.c is the server program. It accepts and manages connections. It also spawns the simple shell program, s_sh.c, to interact with each connection. It is similar to daemon.c from Lab 5 except it spawns a new program for the children instead of handling the data itself. It uses pipes to communicate with s_sh. The pipes do the mapping of socket data to standard in/out as described above. select() has been expanded to watch both the sockets and pipes. See the function dialog_with_telnet() for these changes. The command to start s_daemon is:

s_daemon [-log]
The optional log option will create a log, similar to daemon.c. Run this command first and note the port number that it binds to.

s_sh.c is a very simple shell. A shell is used to interact with the system. It parses commands and command options, runs them and displays the results. This program can parse a few commands such as cd, dir and pwd. It also supports one * or ? wildcard per line. The comments section at the top of the file lists more details on the capacity of this shell program. From s_sh's perspective, it is operating purely on data received from standard in and writing out to standard out. This is then mapped by the pipes created in s_daemon to the network socket. You do NOT need to run s_sh. When you connect to s_daemon, it will spawn a process for s_sh.

s_tlnt.c is a simple telnet client. You can use the provided telnet on Sleipnir instead of s_tlnt to initiate the connection to s_daemon, but s_tlnt gives you some additional features and control over the processing of the network data. Some interesting things to look at in the s_tlnt.c code are process_esc() and the commandmode jump target in main(). The process_esc() function interprets basic VT100/ANSI escape sequences. VT100 and ANSI are standards to control the placement of the cursor on a text screen, colors, reverse colors, blinking text, clearing the screen and other such functionality needed to create basic text-based graphical interfaces. If you use Pine, you've seen VT100 and ANSI in action. The commandmode jump target can be reached by pressing CTRL-T (the "escape character") while the program is running. This will bring up the s_tlnt prompt. Your connection will still be active in the background while you give commands to the s_tlnt program. You can "close" an active connection, "return" to an active connection, "open" a new connection, set s_tlnt parameters, access a "help" screen and see the "status" at the s_tlnt prompt. The parameters can enable features which will give more information about the data being passed across the network, such as VT100 codes.

The command to start s_tlnt is:

s_tlnt [-v] [-snoopy] hostname port
The v option will turn on verbose, which will give you detailed information about the execution of the program. You can also set "verbose" or "noverbose" at the s_tlnt prompt. The snoopy option activates transparent mode, which gives information about telnet arbitration and shows all control and 8bit characters. You can set "snoopy" or "nosnoopy" at the s_tlnt prompt. The hostname is the name of the host and the port is the port number given by s_daemon when you ran it.

Part 2 - Writeup

Answer these questions as the writeup for your lab. Email them to me by the due date.
  1. Try running the command s_tlnt -v -snoopy helios 23. Do NOT actually log in to Helios as that will send your password in plaintext. Hit CTRL-T and then type "close" when prompted to log in. What do you see?
  2. How does what you see for Question 1 differ from what you see when you connect to s_daemon's port number using s_tlnt? Describe what Helios does when accepting a telnet connection that s_daemon did not do.
  3. What happens when you connect to s_daemon multiple times simultaneously, either with s_tlnt or regular telnet (use a different terminal window for each connection)? Is there any "overlap" between commands typed in the different instances of s_sh?
  4. When connected to s_daemon with s_tlnt, type CTRL-T to reach the s_tlnt prompt and use "close" to end the connection. What do you see in the s_daemon screen?
  5. Compare the dialog_with_telnet() functions in daemon.c and s_daemon.c. Note how the spawnlp (Windows) and execle (Unix) functions are used in s_daemon to spawn s_sh. In daemon.c, this function would not spawn any other process. Also, in daemon, all the function does is call recv() to receive data from the socket and echo it on screen. Describe how s_daemon instead uses select() to both send() and recv() data to/from the socket.
  6. Given what you have learned in this lab, how do you think the SSH daemon on Sleipnir handles an incoming SSH connection from Putty? Hint: look at the command ps x right after you've logged in to Sleipnir and note what processes you have running.