How to Daemonize in Linux

來源:互聯網
上載者:User

How to Daemonize in Linux - silent_wind的專欄 - 部落格頻道 - CSDN.NET

How to Daemonize in Linux 分類: UNIX 2010-07-30 14:29 170人閱讀 評論(0) 收藏 舉報signalredirectuserterminalfilelogging

目錄(?)[+]

  1. Simple Example
  2. A More Useful Example

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.

  • The fork() call is used to create a separate process.
  • The setsid() call is used to detach the process from the parent (normally a shell).
  • The file mask should be reset.
  • The current directory should be changed to something benign.
  • The standard files (stdin,stdout and stderr) need to be reopened.

Failure to do any of these steps will lead to a daemon process that can misbehave. The typical symptoms are as follows.

  • Starting the daemon and then logging out will cause the terminal to hang. This is particularly nasty with ssh.
  • The directory from which the daemon was launched remains locked.
  • Spurious output appears in the shell from which the daemon was started.
Simple Example

The following example program performs the bare minimum steps required to launch a daemon process.

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#define EXIT_SUCCESS 0#define EXIT_FAILURE 1static 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;}

 

It has been brought to my attention that a second call to fork() may be required to fully detach the process from the controller terminal (in other words: fork, setsid, fork). This does not seem to be required in Linux. A second fork would not cause any problems, although it would complicate the child/parent signalling below.

 

A More Useful Example

The following program extends the basic daemon by adding the following features.

  • Logs messages to the system log (via syslog).
  • Creates a lock file to prevent the daemon from being run twice.
  • Changes the effective user (drops privileges).
  • Startup errors are reported to the main process.
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <syslog.h>#include <errno.h>#include <pwd.h>#include <signal.h>/* 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 1static 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;}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.