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]:
- 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.
- Close all open descriptors of the parent.[code]
This is done to save system resources.
- 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.
- 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.]
- Put the server in a new Process Group.[code]
This is done so that the server does not receive signals directed
to its parent.
- 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.
- 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".]
- 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`
- Set the umask.[code]
In this way the files created by the server will have the restrictions specified by the umask.
- Ignore extraneous signals.[code]
- 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.
- Wait for child processes to terminate.[code]
So they will not become zombies.