I have this C code to daemonize a systemd service:
static void daemon_me(char *my_name) {
pid_t new_pid;
struct sigaction sig_act;
int i;
int f0, f1, f2;
int my_mask;
umask(0);
if((new_pid = fork()) < 0) {
err_exit("%s: Errore sulla fork", my_name);
}
else if(new_pid != 0) {
exit(0);
}
syslog(LOG_CRIT, "PRINT0 pid %d", new_pid);
setsid();
if((new_pid = fork()) < 0) {
syslog(LOG_CRIT, "PRINT1 pid %d", new_pid);
err_exit("%s: Errore sulla fork", my_name);
}
else if(new_pid != 0) {
syslog(LOG_CRIT, "PRINT2 pid %d", new_pid);
exit(0);
}
syslog(LOG_CRIT, "PRINT3 pid %d", new_pid);
and it doesn't execute the child process after the second fork(). the log is:
PRINT0 pid 0
WAN_Application.out: PRINT2 pid 3009
if i run it manually it works fine:
PRINT0 pid 0
PRINT2 pid 3080
PRINT3 pid 0
Why it happens only using systemd?
It should have the same behaviour
EDIT: full example
#include <string.h> /* strrchr, strerror, strcat, memset */
#include <syslog.h> /* openlog, syslog, closelog, setlogmask */
#include <stdlib.h> /* strtol, exit */
#include <stdio.h> /* fputs, vsnprintf, snprintf, fflush */
#include <sys/types.h> /* umask, open, accept, setsockopt */
#include <sys/stat.h> /* umask, open */
#include <sys/resource.h> /* getrlimit */
#include <sys/socket.h> /* accept, shutdown, setsockopt */
#include <stdarg.h> /* va_start */
#include <unistd.h> /* fork, setsid, chdir. close, dup, sync, sleep */
#include <signal.h> /* sigemptyset, sigaction */
#include <fcntl.h> /* open */
#include <sys/mman.h> /* mlockall */
#include <sys/un.h> /* sockaddr_un */
#include <sys/shm.h> /* shmdt, shmctl */
#define MAX_MSG_SIZE (512)
char main_strbuff[MAX_MSG_SIZE];
static void term_handler(int signo) {
syslog(LOG_CRIT, "term_handler: rcv %d.", signo);
closelog();
exit(0);
} /* term_handler */
static void err_handle(int has_errno, int my_errno, const char *my_args,
va_list my_list) {
char my_string[MAX_MSG_SIZE]; /* Buffer di diagnostica */
vsnprintf(my_string, MAX_MSG_SIZE, my_args, my_list);
if(has_errno) {
snprintf(my_string + strlen(my_string), MAX_MSG_SIZE - strlen(my_string), ":%s", strerror_r(my_errno, main_strbuff, MAX_MSG_SIZE));
}
strcat(my_string, "\n");
fflush(stdout);
fputs(my_string, stderr);
fflush(NULL);
}
static void err_exit(const char *my_args, ...) {
va_list args_list;
va_start(args_list, my_args);
err_handle(0, 0, my_args, args_list);
va_end(args_list);
exit(1);
}
static void err_print(const char *my_args, ...) {
va_list args_list;
va_start(args_list, my_args);
err_handle(0, 0, my_args, args_list);
va_end(args_list);
}
static void daemon_me(char *my_name) {
struct rlimit file_lim;
pid_t new_pid;
struct sigaction sig_act;
int i;
int f0, f1, f2;
int my_mask;
umask(0);
sig_act.sa_handler = SIG_IGN;
sigemptyset(&sig_act.sa_mask);
sig_act.sa_flags = 0;
if(sigaction(SIGHUP, &sig_act, NULL) < 0) {
syslog(LOG_CRIT, "Impossibile ignorare SIGHUP");
exit(1);
}
sig_act.sa_handler = term_handler;
sigemptyset(&sig_act.sa_mask);
sig_act.sa_flags = SA_INTERRUPT;
if(sigaction(SIGTERM, &sig_act, NULL) < 0) {
/* Errore di sistema */
syslog(LOG_CRIT, "Impossibile configurare SIGTERM");
exit(1);
}
if((new_pid = fork()) < 0) {
err_exit("%s: Errore sulla fork", my_name);
}
else if(new_pid != 0) {
exit(0);
}
setsid();
if((new_pid = fork()) < 0) {
err_exit("%s: Errore sulla fork", my_name);
}
else if(new_pid != 0) {
exit(0);
}
}
int main(int argc, char **argv) {
daemon_me("programNAME");
syslog(LOG_CRIT, "Success!");
while(1);
}
systemd service keep restarting itself. Launch it manually and it will work.
EDIT2: OS info /etc/issue: Linux COM-Blade-0 4.19.0-25-amd64 #1 SMP Debian 4.19.289-1 (2023-07-24) x86_64 GNU/Linux
EDIT3: I tried to comment from if(chdir("/") < 0)
to the end of function but nothing changed.
EDIT 4: new example the SIGTERM handling. while lunching with systemd it prints: term_handler: rcv 15
which is SIGTERM
With a Type=forking
service, systemd treats the initial process exiting as an indication that startup is done and the service is now ready – whatever is left becomes the main service process.
At this point, the service is 'active', therefore if that process then exits (even if it's part of double-forking), systemd takes that as an indication of the entire service stopping.
Solution 1: Do not use double-fork in systemd. You literally don't need it. When your process is run from a service manager, it's a daemon from the very beginning – it's already in "background"; there is no tty to detach from; there is no session to setsid() away from; there are no FDs to close – double-forking or daemonizing serves no purpose here.
So you can just #if 0
all of the code and switch the .service unit to Type=simple
.
(Ideally though you should use Type=notify
and call sd_notify(0, "READY=1")
from libsystemd when the daemon is, in fact, ready. This is still the same as Type=simple in that it needs no forking, but has the advantage of more accurate monitoring, needing only minimal changes. Later, you could even add some sd_notify(0, "STATUS=Brewing coffee (%d done)", progress)
and such...)
Solution 2: Create a pipe before forking, then have the parent process wait before exiting until the grandchild sends it a 'ready' indication over the pipe. This is the traditional method and will improve the service's behavior for all service managers (including sysv init), not just systemd.