/** An example daemon in C. * * Derived from : * UNIX Daemon Server Programming Sample Program * Levent Karakas May 2001 * http://www.enderunix.org/docs/eng/daemon.php * http://www.enderunix.org/docs/eng/exampled.c * * and * howto:daemonize * Doug Potter (Nov. 2006) * http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize * * and * Creating a daemon the Python way * Chad J. Schroeder (Oct. 2005) * http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 * From which I took most of the comments. * * There is a good discussion of user IDs by Jan Wolter (Aug. 2004) * `Unix Incompatibility Notes: UID Setting Functions' * http://unixpapa.com/incnote/setuid.html * Basically, setuid() is an irrevocable user ID switch, * while seteuid() temporarily gives you different access permissions. * A user can set(e)uid() to another user if: * - they are root (uid = 0), in which case setuid() changes both the real * and effective uid. * - they are another user, in which case setuid() is equivalent to * seteuid(), and their real uid is not affected. * * The GNU libc intro to Syslog usage is suprisingly concise: * http://www.gnu.org/software/libtool/manual/libc/Syslog.html * * To compile: cc -o exampled exampled.c * To run: ./exampled * To test daemon: ps -ef | grep exampled (or ps -aux on BSD systems) * To test log: tail -f /var/log/user.log * To test signal: kill -HUP `cat /tmp/exampled.lock` * To terminate: kill `cat /tmp/exampled.lock` * or: ./exampled s */ #include /* exit() */ #include /* fprintf(), etc. */ #include /* strlen(), strerror() */ #include /* umask() */ #include /* struct passwd */ #include /* getuid(), geteuid(), setuid(), getdtablesize(), ... */ #include /* open(), lockf() */ #include /* getpwnam() */ #include /* signal(), kill(), SIGHUP, SIGTERM, ... */ #include #include /* errno, ENOENT */ #include /* `Standard' exit codes */ #define RUNNING_DIR "/tmp/" /* the directory the daemon will live in */ #define DAEMON_NAME "mydaemon" /* change to your daemon's name */ #define RUN_AS_USER "daemon" /* change to the user under which to run */ #define LOCK_FILE "exampled.lock" /* so we only have one instance at a time*/ #define UMASK 027 /* complement of allowed file permissions*/ #define PID_MAX_DIGITS 10 /* maximum number of allowed PID digits */ /* our daemon will continue doing it's job as long as stop==0. */ static int stop = 0; /* You could roll your own logging system: * #define LOG_FILE "exampled.log" * void log_message(char *filename, char *message) * { * FILE *logfile; * logfile=fopen(filename,"a"); * if(!logfile) return; * fprintf(logfile,"%s\n",message); * fclose(logfile); * } * ... * log_message(LOG_FILE,"hangup signal caught"); * ... * but in this example we will use syslog instead. */ void signal_handler(int sig) { switch(sig) { case SIGHUP: syslog( LOG_INFO, "hangup signal caught"); break; case SIGTERM: syslog( LOG_INFO, "terminate signal caught"); stop = 1; /* set the stop flag */ break; } } int daemonize() { pid_t pid; /* process id */ int fd, lfd; /* lfd is the lock file descriptor */ char str[PID_MAX_DIGITS+2]; /* buffer for storing PID (+2 for "\n\0" */ int uid; /* user id */ if(getppid()==1) return; /* already a daemon */ /* Fork a child process so the parent can exit. This returns control to * the command-line or shell. It also guarantees that the child will not * be a process group leader, since the child receives a new process ID * and inherits the parent's process group ID. This step is required * to insure that the next call to os.setsid is successful. */ pid=fork(); if (pid<0) { fprintf(stderr, "unable to fork daemon (1st fork), code %d (%s)", errno, strerror(errno) ); exit(EX_OSERR); } else if (pid>0) exit(0); /* parent exits */ /* 1st child has new process ID and inherits parent's process group ID. * Now become the process group leader of a new process group */ if (setsid() < 0) { fprintf(stderr, "unable to create a new session, code %d (%s)", errno, strerror(errno) ); exit(EX_OSERR); } /* Fork a second child and exit immediately to prevent zombies. This * causes the second child process to be orphaned, making the init * process responsible for its cleanup. And, since the first child is * a session leader without a controlling terminal, it's possible for * it to acquire one by opening a terminal in the future (System V- * based systems). This second fork guarantees that the child is no * longer a session leader, preventing the daemon from ever acquiring * a controlling terminal. */ pid=fork(); if (pid<0) { fprintf(stderr, "unable to fork daemon (2nd fork), code %d (%s)", errno, strerror(errno) ); exit(EX_OSERR); } else if (pid>0) exit(0); /* 1st child exits */ /* close all inherited file descriptors */ for (fd=getdtablesize()-1;fd>=0;fd--) close(fd); /* Redirect the standard I/O file descriptors to the specified file. Since * the daemon has no controlling terminal, most daemons redirect stdin, * stdout, and stderr to /dev/null. This is done to prevent side-effects * from reads and writes to the standard I/O file descriptors. */ /* This call to open is guaranteed to return the lowest file descriptor, * which will be 0 (stdin), since it was closed above. */ fd=open("/dev/null",O_RDWR); /* Duplicate standard input to standard output and standard error. */ dup(fd); dup(fd); /* dummy stdout, and stderr */ /* Set up syslog logging, since we've closed stdout and stderr */ openlog(DAEMON_NAME, LOG_PID, LOG_USER); /* Since the current working directory may be a mounted filesystem, we * avoid the issue of not being able to unmount the filesystem at * shutdown time by changing it to the RUNNING_DIR directory. * This also allows daemon processes started in different directories to * find each other's lock files. */ chdir(RUNNING_DIR); /* We probably don't want the file mode creation mask inherited from * the parent, so we give the child complete control over permissions. */ umask(UMASK); /* restrict created file permissions to UMASK's complement */ /* create lock file as the current user */ lfd=open(LOCK_FILE,O_RDWR|O_CREAT,0640); if (lfd<0) { syslog(LOG_ERR, "Could not open %s, code %d (%s)\n", LOCK_FILE, errno, strerror(errno)); exit(EX_OSFILE); /* can not open, lacking permissions? */ } if (lockf(lfd,F_TLOCK,0)<0) { /* F_TLOCK : test and exclusive lock */ syslog(LOG_ERR, "Could not lock %s, code %d (%s)\n", LOCK_FILE, errno, strerror(errno)); exit(EX_OSFILE); } /* The file locks are released on first close by the locking process * of any file descriptor for the file, so hang on to lfd. */ /* now write the PID to the lock file */ sprintf(str,"%*d\n", PID_MAX_DIGITS, getpid()); write(lfd,str,strlen(str)); syslog( LOG_INFO, "wrote pid string '%s' (%d)", str, strlen(str)); /* switch user id if we were run with root priviledges */ uid = getuid(); if ( uid == 0 /* run as root */ || geteuid() == 0 /* effectively root? */) { struct passwd *pw = getpwnam(RUN_AS_USER); if ( pw ) { /* user exists and no error */ syslog( LOG_INFO, "setting user to %s", RUN_AS_USER ); setuid( pw->pw_uid ); } } /* set up signal handlers */ signal(SIGCHLD,SIG_IGN); /* ignore child death */ signal(SIGTSTP,SIG_IGN); /* ignore TTY stop */ signal(SIGTTOU,SIG_IGN); /* ignore TTY output */ signal(SIGTTIN,SIG_IGN); /* ignore TTY input */ signal(SIGHUP,signal_handler); /* catch hangup signal */ signal(SIGTERM,signal_handler); /* catch kill signal */ syslog( LOG_NOTICE, "started by user %d", uid); return lfd; /* file descriptor for our lock file */ } void cleanup_daemon(int lfd) { if (close(lfd) < 0) { syslog( LOG_ERR, "Could not close %s, code %d (%s)\n", LOCK_FILE, errno, strerror(errno)); exit(EX_OSFILE); } if (remove(LOCK_FILE) < 0) { syslog( LOG_ERR, "Could not remove %s, code %d (%s)\n", LOCK_FILE, errno, strerror(errno)); exit(EX_OSFILE); } syslog( LOG_NOTICE, "stopping"); closelog(); } /* Check if the daemon is already running in another process. * If so, terminate that daemon; otherwise, do nothing. */ void stop_daemon() { int pid; /* daemon process id */ int lfd; /* lfd is the lock file descriptor */ char str[PID_MAX_DIGITS+2]={0}; /* buffer for storing PID, +2 for "\n\0" */ ssize_t i; /* Change to the directory that the daemon may be running in. */ chdir(RUNNING_DIR); /* open the lock file */ lfd=open(LOCK_FILE,O_RDONLY); if (lfd<0 && errno != ENOENT) { fprintf(stderr, "Could not open %s, code %d (%s)", LOCK_FILE, errno, strerror(errno)); exit(EX_OSFILE); /* can not open, lacking permissions? */ } else if (errno == ENOENT) { /* the file doesn't exist, so no daemon was running */ return; } /* continuing, the file does exist, read the daemon's pid */ i = read(lfd, str, PID_MAX_DIGITS+1); sscanf(str, "%d", &pid); if (pid < 1) { fprintf(stderr, "Wierd PID in %s, %d\n", LOCK_FILE, pid); exit(EX_UNAVAILABLE); /* don't know what's going on, so bail */ } /* use the pid to kill the daemon */ if (kill((pid_t) pid, SIGTERM) < 0) { fprintf(stderr, "Could not send signal, code %d (%s)\n", errno, strerror(errno)); exit(EX_OSERR); /* can not open, lacking permissions? */ } } int main(int argc, char **argv) { int lfd,i=0; /* lfd is the lock-file file descriptor */ if (argc > 1) { fprintf(stdout, "Stopping any currently running instance\n"); stop_daemon(); } else { fprintf(stdout, "Attempt to start a new deamon instance\n"); lfd=daemonize(); /* Once everything's set up, pick the log verbosity. All messages are * submitted by default. In this case, we submit messages with priorities * higher or equal to LOG_INFO (so everything but LOG_ERROR). * From the GNU man page: * http://www.gnu.org/software/libtool/manual/libc/setlogmask.html * "The unfortunate naming of the (LOG_UPTO) macro is due the fact that, * internally, higher numbers are used for lower message priorities." */ setlogmask(LOG_UPTO(LOG_INFO)); while(stop == 0) { /* do something */ syslog( LOG_INFO, "Working... %d", i++); sleep(1); } cleanup_daemon(lfd); } return 0; }