CIS 4307: Daemons

If I check what are the program running on my Unix system (joda.cis.temple.edu) I see a number of programs [see their definitions using the man pages]:

kloadsrv Kernel module load server
xntpd Network Time Protocol daemon
cron The system clock daemon
nfsiod Local NFS compatible asynchronous I/O daemon
proplistd Daemon that services remote NFS compatible property list requests
nfsd Remote NFS compatible server
mountd Daemon that services remote NFS compatible mount requests
rpc.lockd Network lock daemon
rpc.statd Network status monitor daemon
auditd Audit daemon
httpd HTTP server
inetd Internet super-server
telnetd The DARPA telnet protocol server
binlogd Binary event-log daemon
portmap DARPA port to RPC program number mapper
advfsd Daemon that obtains system information for the AdvFS GUI
lpd Line printer daemon
syslogd Daemon that logs system messages
sendmail Mail SMTP server
xdm X Display Manager
imapd Daemon for the Internet Mail Access Protocol (IMAP)
sshd Secure shell daemon

All these programs are special kinds of servers called daemons. A Daemon is a server that operates as a background job [with a number of restrictions on the way it uses resources], it is just a server which has been programmed more carefully so as to be more reliable and to run in the background. As the list above indicates, a substantial portion of the system's functionality is provided as a daemon.

Comer and D.Stevens discuss at length how to build a server/daemon in a Unix environment. Here we paraphrase thoughts (and full paragraphs, and code) from their Chapter 30: "Practical Hints and Techniques for Unix Servers". Some thoughts are from R.Stevens. The discussion will be demonstrated with a server that implements in the Unix Domain an Echo service. We provide also a simple client.

[ Notice in this code the handler for the SIGCHLD signal
	void sig_chld(int n) {
	    int status;
	    fprintf(stderr, "Child terminated\n");
	    wait(&status); /* So no zombies */
	}
  and the improvement suggested in R.Stevens, Fenner, Rudoff
  to deal with the fact that multiple children may die
  before any SIGCHLD signal is handled:
	void sig_chld(int signo) {
	    pid_t pid;
	    int stat;
	    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) {
		printf("child %d terminated\n", pid);
	    }
	    return;
	}
]
Here are the Comer-D.Stevens recommendations [for each we have a pointer to its expression in the HTML version of the server's code]:
  1. Operate the server as a background processes with as parent the init process [code]
    We just fork the current process, terminate the parent. Continue with the child which assumes init (pid = 1) as parent. We like init as parent because init prevents its children from becoming zombies. If, given a normal program moo, we execute with "moo &", then the resulting job is in the background, but it can easily be brought to the foreground with the "fg" command.
  2. Close all open descriptors of the parent.[code]
    This is done to save system resources.
  3. Detach from controlling terminal.[code]
    This is done so that terminal signals are not directed to the server. It is achieved by becoming session leader, which detaches the controlling terminal.
  4. Move to a Known Safe Directory.[code]
    It is done so that we know where to look for core images and so that we do not keep active without reason the current directory. [If you have root privileges, you may want to use the "chroot" command. That selects for the current process and its descendants a new root directory. Say that we chroot to "/home/gpi/", then the current process and its descendants will have "/" as equivalent to "/home/gpi/" and will be restricted to the files in the subtree starting at /home/gpi/". Great for security.]
  5. Put the server in a new Process Group.[code]
    This is done so that the server does not receive signals directed to its parent.
  6. Reset the standard files to /dev/null.[code]
    In this way, if we use functions that use standard files, they can be used without effect.
  7. Make sure that only one copy of the server is running.[code]
    This is so otherwise the multiple copies interfere with each other. [We use locks on file "/tmp/unixechoserver.pid".]
  8. Save the pid of the server in a standard file.[code]
    This is done so that we can easily send signals to the server, or terminate it. We store the pid in "/tmp/unixechoserver.pid". Then we can terminate the server with
    	kill -9 `cat /tmp/unixechoserver.pid`
    
  9. Set the umask.[code]
    In this way the files created by the server will have the restrictions specified by the umask.
  10. Ignore extraneous signals.[code]
  11. Use syslog to handle error messages.[code]
    In our case, since the standard user has no access to the syslog configuration, we have redirected the standard error file to a log file, /tmp/unixechoserver.log.
  12. Wait for child processes to terminate.[code]
    So they will not become zombies.