For this project, you will take your existing shell and turn it into a multithreaded remote shell server like telnetd.
You should be able to connect to your shell server from any computer with an internet connection. You should use an existing TCP/IP terminal client like telnet or netcat -- do not write your own client. Once connected, your client will appear to behave exactly like your shell from before, except the programs will be run on the remote machine (the server).
In other words, you should implement sshd sans encryption or login.
- use TCP/IP
- multithreaded: handle each connection in a separate thread
- fork-exec-wait to launch child processes (same as before)
- `clients` command: list all connected clients by IP address and elapsed time
- `jobs` command: list all background jobs from all connected clients
- libreadline expects to read/write from a terminal, not a socket. You will probably be unable to get libreadline to behave as you expect. Instead, use read or recv to read bytes from your socket into a buffer. Keep reading this way until you detect that your buffer has a newline sequence, e.g. '\r' or '\n'. At that point, your readline can return everything up to the newline. See the attached code below for a simple implementation of this logic.
- Since all threads will have access to the jobs and clients arrays, you will need to synchronize access to them using mutexes. Otherwise two threads might try to access/modify the arrays at the same time.
- Mixing threads and calls to "fork" is tricky business. Forking causes a copy of the calling process, which includes any mutexes. This means that any locked mutexes will also be locked in your child. However, threads are not copied when you fork. This has a strange side-effect: if a thread locks a mutex in your parent process, there will be no such thread to unlock the mutex in your child process. Moral of the story: you should only fork in order to call execvp -- don't do anything else in your child processes, or you will introduce all manner of synchronization bugs.
- The child processes that your server creates should write to your client socket -- not to your server's stdout. Likewise, they should read from your client socket, not your server's stdin. This means you'll need to use I/O redirection. One way to accomplish this is to close(0) and close(1) in your child process, then dup(ConnectFD) twice, then execvp. This will cause your child's stdin and stdout to be redirected to ConnectFD. Background processes shouldn't get a stdin, so close it and fopen("/dev/null","r") instead.