分类: LINUX
2009-05-18 15:06:23
One of the things I keep running across is Linux daemons that don’t properly daemonize themselves. To properly daemonize, the following steps must be followed.
Failure to do any of these steps will lead to a daemon process that can misbehave. The typical symptoms are as follows.
The following example program performs the bare minimum steps required to launch a daemon process.
#include#include #include #include #include #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 static void daemonize(void) { pid_t pid, sid; /* already a daemon */ if ( getppid() == 1 ) return; /* Fork off the parent process */ pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } /* If we got a good PID, then we can exit the parent process. */ if (pid > 0) { exit(EXIT_SUCCESS); } /* At this point we are executing as the child process */ /* Change the file mode mask */ umask(0); /* Create a new SID for the child process */ sid = setsid(); if (sid < 0) { exit(EXIT_FAILURE); } /* Change the current working directory. This prevents the current directory from being locked; hence not being able to remove it. */ if ((chdir("/")) < 0) { exit(EXIT_FAILURE); } /* Redirect standard files to /dev/null */ freopen( "/dev/null", "r", stdin); freopen( "/dev/null", "w", stdout); freopen( "/dev/null", "w", stderr); } int main( int argc, char *argv[] ) { daemonize(); /* Now we are a daemon -- do the work for which we were paid */ return 0; }
The following program extends the basic daemon by adding the following features.
#include#include #include #include #include #include #include #include #include #include #include /* Change this to whatever your daemon is called */ #define DAEMON_NAME "mydaemon" /* Change this to the user under which to run */ #define RUN_AS_USER "daemon" #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 static void child_handler(int signum) { switch(signum) { case SIGALRM: exit(EXIT_FAILURE); break; case SIGUSR1: exit(EXIT_SUCCESS); break; case SIGCHLD: exit(EXIT_FAILURE); break; } } static void daemonize( const char *lockfile ) { pid_t pid, sid, parent; int lfp = -1; /* already a daemon */ if ( getppid() == 1 ) return; /* Create the lock file as the current user */ if ( lockfile && lockfile[0] ) { lfp = open(lockfile,O_RDWR|O_CREAT,0640); if ( lfp < 0 ) { syslog( LOG_ERR, "unable to create lock file %s, code=%d (%s)", lockfile, errno, strerror(errno) ); exit(EXIT_FAILURE); } } /* Drop user if there is one, and we were run as root */ if ( getuid() == 0 || geteuid() == 0 ) { struct passwd *pw = getpwnam(RUN_AS_USER); if ( pw ) { syslog( LOG_NOTICE, "setting user to " RUN_AS_USER ); setuid( pw->pw_uid ); } } /* Trap signals that we expect to recieve */ signal(SIGCHLD,child_handler); signal(SIGUSR1,child_handler); signal(SIGALRM,child_handler); /* Fork off the parent process */ pid = fork(); if (pid < 0) { syslog( LOG_ERR, "unable to fork daemon, code=%d (%s)", errno, strerror(errno) ); exit(EXIT_FAILURE); } /* If we got a good PID, then we can exit the parent process. */ if (pid > 0) { /* Wait for confirmation from the child via SIGTERM or SIGCHLD, or for two seconds to elapse (SIGALRM). pause() should not return. */ alarm(2); pause(); exit(EXIT_FAILURE); } /* At this point we are executing as the child process */ parent = getppid(); /* Cancel certain signals */ signal(SIGCHLD,SIG_DFL); /* A child process dies */ signal(SIGTSTP,SIG_IGN); /* Various TTY signals */ signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */ signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */ /* Change the file mode mask */ umask(0); /* Create a new SID for the child process */ sid = setsid(); if (sid < 0) { syslog( LOG_ERR, "unable to create a new session, code %d (%s)", errno, strerror(errno) ); exit(EXIT_FAILURE); } /* Change the current working directory. This prevents the current directory from being locked; hence not being able to remove it. */ if ((chdir("/")) < 0) { syslog( LOG_ERR, "unable to change directory to %s, code %d (%s)", "/", errno, strerror(errno) ); exit(EXIT_FAILURE); } /* Redirect standard files to /dev/null */ freopen( "/dev/null", "r", stdin); freopen( "/dev/null", "w", stdout); freopen( "/dev/null", "w", stderr); /* Tell the parent process that we are A-okay */ kill( parent, SIGUSR1 ); } int main( int argc, char *argv[] ) { /* Initialize the logging interface */ openlog( DAEMON_NAME, LOG_PID, LOG_LOCAL5 ); syslog( LOG_INFO, "starting" ); /* One may wish to process command line arguments here */ /* Daemonize */ daemonize( "/var/lock/subsys/" DAEMON_NAME ); /* Now we are a daemon -- do the work for which we were paid */ /* Finish up */ syslog( LOG_NOTICE, "terminated" ); closelog(); return 0; }