Traditional SysV Daemons
- NOTE - systemd doesn’t need a service to be daemonized like this but it can be. Systemd uses ‘simple’ type to handle regular non-daemonized scripts/binaries and ‘forking’ type to handle scripts/binaries that daemonize themselves. Daemonizing can be useful if you want to run without systemd or on a system without systemd. So if you daemonize, you can run on a system with or without systemd.
Steps to Daemonize
- fork() - so parent can exit, also guaranteed not to be a process group leader after this
- setsid() - become a process group and session group leader, detatch from any terminal
- fork() - again, so parent ( session group leader ) can exit, new proc can never regain controlling terminal
-
exit() - in first child proc
- chdir(“/”) - or a daemon specific dir, don’t want any FS held and unmountable
-
umask(0) - optional, gain control over permissions
- close() fds 0, 1, and 2 ( STDIN, STDOUT, STDERR ) ( connect to /dev/null or somewhere else to log console )
-
close all possible file descriptors - free up file descriptors, don’t prevent files from being deleted * iterate through /proc/self/fd * fallback methoc: iterate 3 to getrlimit()
- set all signal handlers to their default
-
Reset the signal mask using sigprocmask()
-
handle SIGCLD or SIGCLD ??? ( so zombines aren’t created when child proc terminates )
-
Sanitize the environment block
- Implement using a PID file ( optional )
-
Drop privileges ( optional )
- Send signal to original process for sucess ( use IPC, ex: unnamed pipe )
Daemon Written in Bash
Someone else’s code:
#!/bin/bash
DEBUG=false
# This part is for fun, if you consider shell scripts fun- and I do.
trap process_USR1 SIGUSR1
process_USR1() {
echo 'Got signal USR1'
echo 'Did you notice that the signal was acted upon only after the sleep was done'
echo 'in the while loop? Interesting, yes? Yes.'
exit 0
}
# End of fun. Now on to the business end of things.
print_debug() {
whatiam="$1"; tty="$2"
[[ "$tty" != "not a tty" ]] && {
echo "" >$tty
echo "$whatiam, PID $$" >$tty
ps -o pid,sess,pgid -p $$ >$tty
tty >$tty
}
}
me_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
me_FILE=$(basename $0)
cd /
#### CHILD HERE --------------------------------------------------------------------->
if [ "$1" = "child" ] ; then # 2. We are the child. We need to fork again.
shift; tty="$1"; shift
$DEBUG && print_debug "*** CHILD, NEW SESSION, NEW PGID" "$tty"
umask 0
$me_DIR/$me_FILE XXrefork_daemonXX "$tty" "$@" </dev/null >/dev/null 2>/dev/null &
$DEBUG && [[ "$tty" != "not a tty" ]] && echo "CHILD OUT" >$tty
exit 0
fi
##### ENTRY POINT HERE -------------------------------------------------------------->
if [ "$1" != "XXrefork_daemonXX" ] ; then # 1. This is where the original call starts.
tty=$(tty)
$DEBUG && print_debug "*** PARENT" "$tty"
setsid $me_DIR/$me_FILE child "$tty" "$@" &
$DEBUG && [[ "$tty" != "not a tty" ]] && echo "PARENT OUT" >$tty
exit 0
fi
##### RUNS AFTER CHILD FORKS (actually, on Linux, clone()s. See strace -------------->
# 3. We have been reforked. Go to work.
exec >/tmp/outfile
exec 2>/tmp/errfile
exec 0</dev/null
shift; tty="$1"; shift
$DEBUG && print_debug "*** DAEMON" "$tty"
# The real stuff goes here. To exit, see fun (above)
$DEBUG && [[ "$tty" != "not a tty" ]] && echo NOT A REAL DAEMON. NOT RUNNING WHILE LOOP. >$tty
$DEBUG || {
while true; do
echo "Change this loop, so this silly no-op goes away." >/dev/null
echo "Do something useful with your life, young padawan." >/dev/null
sleep 10
done
}
$DEBUG && [[ "$tty" != "not a tty" ]] && sleep 3 && echo "DAEMON OUT" >$tty
exit # This may never run. Why is it here then? It's pretty.
# Kind of like, "The End" at the end of a movie that you
# already know is over. It's always nice.
Daemon Written in C
Someone else’s code:
#include
#include
#include
#include
#include <sys/types.h>
#include <sys/stat.h>
#include
static void skeleton_daemon()
{
pid_t pid;
/* Fork off the parent process */
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* On success: The child process becomes session leader */
if (setsid() < 0)
exit(EXIT_FAILURE);
/* Catch, ignore and handle signals */
/*TODO: Implement a working signal handler */
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* Fork off for the second time*/
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* Set new file permissions */
umask(0);
/* Change the working directory to the root directory */
/* or another appropriated directory */
chdir("/");
/* Close all open file descriptors */
int x;
for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
{
close (x);
}
/* Open the log file */
openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
skeleton_daemon();
while (1)
{
//TODO: Insert daemon code here.
syslog (LOG_NOTICE, "First daemon started.");
sleep (20);
break;
}
syslog (LOG_NOTICE, "First daemon terminated.");
closelog();
return EXIT_SUCCESS;
}
</code></pre>
## Other Tools
* BSD daemon() function not recommended because it doesn't implement everything needed
Another daemonize tool exists, still need to look into this:
sudo apt install daemonize
daemonize ....
## Quick Adhoc
This will run in the background, disconnect from terminal, close stdin, and redirect output. It is not a real daemon though.
nohup ./myscript 0<&- &> my.admin.log.file &
## Reference
* [nice document](https://www.freedesktop.org/software/systemd/man/latest/daemon.html)
* [GitHub C Daemon](https://github.com/pasce/daemon-skeleton-linux-c)