260 likes | 453 Views
Daemons. Daemon. A daemon is a process that runs in the background and is independent of control from all terminals. Since a daemon does not have a controlling terminal , it needs some way to output message. Daemon. There are numerous ways to start a daemon
E N D
Daemon • A daemon is a process that runs in the background and is independent of control from all terminals. • Since a daemon does not have a controlling terminal, it needs some way to output message
Daemon • There are numerous ways to start a daemon 1. the system initialization scripts (/etc/rc) 2. the inetdsuperserver 3. crondeamon 4. the at command 5. from user terminals
Process Group • process group is a collection of one or more processes, usually associated with the same job • intsetpgid(pid_tpid, pid_tpgid); • pid_tgetpgid(pid_tpid); • It is possible for a process group leader to create a process group, create processes in the group, and then terminate. The process group still exists, as long as at least one process is in the group, regardless of whether the group leader terminates
Process Groups in a Session • The processes in a process group are usually placed there by a shell pipeline • proc1 | proc2 & • proc3 | proc4 | proc5
Creating Session • A process establishes a new session by calling the setsid() function • If the calling process is not a process group leader, this function creates a new session. Three things happen. • The process becomes the session leader of this new session. (A session leader is the process that creates a session.) The process is the only process in this new session. • The process becomes the process group leader of a new process group. The new process group ID is the process ID of the calling process. • The process has no controlling terminal. If the process had a controlling terminal before calling setsid, that association is broken.
setsid • pid_tsetsid(void); • This function returns an error if the caller is already a process group leader. • To ensure this is not the case, the usual practice is to call fork and have the parent terminate and the child continue.
daemon_initFunction void daemon_init(const char *pname, int facility) { inti; pid_tpid; if ( (pid = Fork()) != 0) exit(0); /* parent terminates */ /* 1st child continues */ setsid(); /* become session leader */ Signal(SIGHUP, SIG_IGN); if ( (pid = Fork()) != 0) exit(0); /* 1st child terminates */ /* 2nd child continues */ daemon_proc = 1; /* for our err_XXX() functions */ chdir("/"); /* change working directory */ for (i = 0; i < MAXFD; i++) close(i); openlog(pname, LOG_PID, facility); }
Daemon_init • We first call fork and then the parent terminates, and the child continues. If the process was started as a shell command in the foreground, when the parent terminates, the shell thinks the command is done. This automatically runs the child process in the background. Also, the child inherits the process group ID from the parent but gets its own process ID. This guarantees that the child is not a process group leader, which is required for the next call to setsid • The process becomes the session leader of the new session, becomes the process group leader of a new process group, and has no controlling terminal
Daemon_init • We ignore SIGHUP and call fork again. When this function returns, the parent is really the first child and it terminates, leaving the second child running. The purpose of this second fork is to guarantee that the daemon cannot automatically acquire a controlling terminal should it open a terminal device in the future. When a session leader without a controlling terminal opens a terminal device (that is not currently some other session's controlling terminal), the terminal becomes the controlling terminal of the session leader. But by calling fork a second time, we guarantee that the second child is no longer a session leader, so it cannot acquire a controlling terminal. We must ignore SIGHUP because when the session leader terminates (the first child), all processes in the session (our second child) receive the SIGHUP signal.
syslogddaemon • Berkeley-derived implementation of syslogd perform the following actions upon startup. 1. The configuration file is read, specifying what to do with each type of log message that the daemon can receive. 2. A Unix domain socket is created and bound to the pathname /var/run/log ( /dev/log on some system). 3. A UDP socket is created and bound to port 514 4. The pathname /dev/klog is opened. Any error messages from within the kernel appear as input on this device. • We could send log messages to the syslogd daemon from our daemons by creating a Unix domain datagram socket and sending our messages to the pathname that the daemon has bound, but an easier interface is the syslog function.
syslogd Filesystem /var/log/messages Unix domain socket /dev/log syslogd UDP socket port 514 Console /dev/klog Remote syslogd
#include <syslog.h> void syslog(int priority, const char *message, . . . ); syslogfunction • the priority argument is a combination of a level and a facility. • The message is like a format string to printf, with the addition of a %m specification, which is replaced with the error message corresponding to the current value of errno. Ex) Syslog(LOG_INFO|LOG_LOCAL2, “rename(%s, %s): %m”,file1,file2);
syslogfunction • Log message have a level between 0 and 7.
syslogfunction • A facility to identify the type of process sending the message.
#include <syslog.h> void openlog(const char *ident, int options, int facility); void closelog(void); syslogfunction • Openlogand closelog • openlog can be called before the first call to syslog and closelog can be called when the application is finished sending is finished log messages.
inetdDaemon • A typical Unix system’s problems 1. All these daemons contained nearly identical startup code. 2. Each daemon took a slot in the processtable, but each daemon was asleep most of the time. • inetddaemon fixes the two problems. 1. It simplifies writing daemon processes, since most of the startup details are handled by inetd. 2. It allow a single process(inetd) to be waiting for incoming client requests for multiple services, instead of one process for each service.
inetd service specification • For each service, inetd needs to know: • the socket type and transport protocol • wait/nowait flag. • login name the process should run as. • pathname of real server program. • command line arguments to server program. • Servers that are expected to deal with frequent requests are typically not run from inetd • mail, web, NFS.
/etc/inetd.conf # Syntax for socket-based Internet services: # <service_name> <socket_type> <proto> <flags> <user> <server_pathname> <args> # # comments start with # echo stream tcpnowait root internal echo dgramudp wait root internal chargen stream tcpnowait root internal chargendgramudp wait root internal ftp stream tcpnowait root /usr/sbin/ftpdftpd -l telnet stream tcpnowait root /usr/sbin/telnetdtelnetd finger stream tcpnowait root /usr/sbin/fingerdfingerd # Authentication auth stream tcpnowait nobody /usr/sbin/in.identdin.identd -l -e -o # TFTP tftpdgramudp wait root /usr/sbin/tftpdtftpd -s /tftpboot
wait/nowait • WAIT specifies that inetd should not look for new clients for the service until the child (the real server) has terminated. • TCP servers usually specify nowait - this means inetd can start multiple copies of the TCP server program - providing concurrency • Most UDP services run with inetd told to wait until the child server has died.
Server loaded by Inetd • 3 int • 4 main(intargc, char **argv) • 5 { • 6 socklen_tlen; • 7 structsockaddr *cliaddr; • 8 char buff[MAXLINE]; • 9 time_t ticks; • 10 daemon_inetd(argv[0], 0); • 11 cliaddr = Malloc(sizeof(structsockaddr_storage)); • 12 len = sizeof(structsockaddr_storage); • 13 Getpeername(0, cliaddr, &len); • 14 err_msg("connection from %s", Sock_ntop(cliaddr, len)); • 15 ticks = time(NULL); • 16 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); • 17 Write(0, buff, strlen(buff)); • 18 Close(0); /* close TCP connection */ • 19 exit(0); • 20 }