Skip to content

Instantly share code, notes, and snippets.

@tspspi
Last active April 25, 2020 23:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tspspi/900bbd379cc2c024380bb58e3a5321ca to your computer and use it in GitHub Desktop.
Save tspspi/900bbd379cc2c024380bb58e3a5321ca to your computer and use it in GitHub Desktop.
Basic daemon skeleton
/*
Compilation: clang -o basicdaemon -Wall -ansi -std=c99 -Werror -pedantic ./basicdaemon.c -lutil
*/
#include <unistd.h>
#include <libutil.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
/* Used for perror and (f)printf: */
#include <stdio.h>
/*
Currently we employ a simple signal handelr that
sets a flag that will get checked periodically by
the daemon. If SIGTERM is received we will terminate
our wait loop.
*/
static int termSignaled = 0;
static void signalHandlerSIGTERM(int sigio) {
termSignaled = 1;
}
/* Main entry point */
int main(int argc, char* argv[]) {
struct pidfh *pidFileHandle;
struct sigaction sAction;
pid_t otherDaemonPid;
pid_t pidChildProcess;
pid_t pidLeader;
int fileNull;
/*
Note that we start with fork. Maybe one wants to
do some CLI processing or reading of configuration
files before that to allwo modification of startup
behaviour, configuration of PIDs and GIDs to be
used, etc. These will be configured as static constants
in this example.
This code would belong here ...
*/
/* open pidfile */
pidFileHandle = pidfile_open(NULL, 0600, &otherDaemonPid);
if(pidFileHandle == NULL) {
if(errno == EEXIST) {
fprintf(stderr, "Daemon already running, pid: %jd.", (intmax_t)otherDaemonPid);
return -1;
}
perror("Failed to open PID-file");
return -1;
}
/* Now its time for the first fork (moving to background) */
pidChildProcess = fork();
if(pidChildProcess < 0) {
/* The fork failed */
perror("Forking failed\n");
return -1;
} else if(pidChildProcess > 0) {
/* We are the parent. We'll terminate silently */
return 0;
}
#ifdef DEBUG
printf("%s:%u Moved to background\n", __FILE__, __LINE__);
#endif
/*
At this point we are the child that has moved
to the background but is still attached to
the original process group.
Now detach from the session ...
*/
pidLeader = setsid();
if(pidLeader < 0) {
perror("Failed to become session leader\n");
/*
This will happen if the calling
process is already a process group
leader or the process group ID of
another process other than the
calling process matches the process ID
of the calling process.
*/
return -1;
}
#ifdef DEBUG
/*
Note that debug output still works and shows
up in the original terminal since we inherited
the stdin and stdout pipes from our parents ...
*/
printf("%s:%u Moved to new process group\n", __FILE__, __LINE__);
#endif
/*
Now we will ensure detachment from the previous
process leader by forking one last time ...
*/
pidChildProcess = fork();
if(pidChildProcess < 0) {
perror("Forking failed\n");
return -1;
} else if(pidChildProcess > 0) {
/* We are the parent and will terminate silently again */
return 0;
}
/*
Register signal handler for SIGTERM
*/
sAction.sa_sigaction = NULL;
sAction.sa_handler = &signalHandlerSIGTERM;
sAction.sa_flags = SA_RESTART; /* Restart any interrupted operations like file I/O */
sigfillset(&sAction.sa_mask); /* Mask all signals during our signal handler */
if(sigaction(SIGTERM, &sAction, NULL) < 0) {
/* We failed to register our handler ... */
perror("Failed to register SIGTERM handler\n");
pidfile_close(pidFileHandle);
return -1;
}
/*
Write our PID into the PID-file
*/
pidfile_write(pidFileHandle);
/*
At this point we are the child process that
has moved into background and has detached from
the original process group
*/
#ifdef DEBUG
/* Still works, we've inherited stdin, stdout and stderr ... */
printf("%s:%u Greetings from background detached process\n", __FILE__, __LINE__);
#endif
/*
Now we should
* close our standard I/O handles of our parent process
(or do that after the following operations just in
case we want to provide direct error output)
* Do some mandatory access control manipulation if required
* Jail if we want to
* setgid, setuid
* chroot
*/
/* close stdin,stdout and stderr. Now printf won't work any more ... */
close(STDOUT_FILENO);
close(STDERR_FILENO);
close(STDIN_FILENO);
/* Open /dev/null as target for stdout and stderr as well as source for stdin */
fileNull = open("/dev/null", 0);
dup2(fileNull, STDOUT_FILENO);
dup2(fileNull, STDERR_FILENO);
dup2(fileNull, STDIN_FILENO);
/*
Main daemon code goes here ...
*/
/* Now remove our PID-file */
pidfile_remove(pidFileHandle);
/* And terminate process ... */
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment