From a2b7977866fcd18e3c2d430f036f3d28e3724f89 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Tue, 31 Mar 2026 11:51:41 +0300 Subject: [PATCH] add `daemonizing` function; modify test of copy running --- daemon.c | 67 +++++++++++++------------ examples/CMakeLists.txt | 1 + examples/daemon.c | 105 ++++++++++++++++++++++++++++++++++++++++ usefull_macros.h | 2 + 4 files changed, 143 insertions(+), 32 deletions(-) create mode 100644 examples/daemon.c diff --git a/daemon.c b/daemon.c index 8abc317..86aa13f 100644 --- a/daemon.c +++ b/daemon.c @@ -21,6 +21,7 @@ #include // printf, fopen, ... #include // getpid +#include #include // perror #include // opendir #include // opendir @@ -73,42 +74,19 @@ void WEAK sl_iffound_deflt(pid_t pid){ /** * check wether there is a same running process * exit if there is a running process or error - * Checking have 3 steps: - * 1) lock executable file - * 2) check pidfile (if you run a copy?) - * 3) check /proc for executables with the same name (no/wrong pidfile) - * @param selfname - argv[0] or NULL for non-locking + * Checking have 2 steps: + * 1) check pidfile and its owner (if you run a copy?) + * 2) check /proc for executables with the same name (no/wrong pidfile) + * @param selfname - deprecated, maybe remove in next versions * @param pidfilename - name of pidfile or NULL if none */ -void sl_check4running(char *selfname, char *pidfilename){ +void sl_check4running(char _U_ *selfname, char *pidfilename){ DIR *dir; - FILE *pidfile, *fself; + FILE *pidfile; struct dirent *de; struct stat s_buf; pid_t pid = 0, self; - struct flock fl; char *name, *myname; - if(selfname){ // block self - fself = fopen(selfname, "r"); // open self binary to lock - if(!fself){ - WARN("fopen()"); - goto selfpid; - } - memset(&fl, 0, sizeof(struct flock)); - fl.l_type = F_WRLCK; - if(fcntl(fileno(fself), F_GETLK, &fl) == -1){ // check locking - WARN("fcntl()"); - goto selfpid; - } - if(fl.l_type != F_UNLCK){ // file is locking - exit - sl_iffound_deflt(fl.l_pid); - } - fl.l_type = F_RDLCK; - if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){ - WARN("fcntl()"); - } - } - selfpid: self = getpid(); // get self PID if(!(dir = opendir(PROC_BASE))){ // open /proc directory ERR(PROC_BASE); @@ -120,9 +98,11 @@ void sl_check4running(char *selfname, char *pidfilename){ if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists pidfile = fopen(pidfilename, "r"); if(pidfile){ - if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process - if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0) + if(fscanf(pidfile, "%d", &pid) == 1){ // read PID of (possibly) running process + if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0){ sl_iffound_deflt(pid); + exit(1); // run `exit` if user forgot to do it himself + } } fclose(pidfile); } @@ -131,8 +111,10 @@ void sl_check4running(char *selfname, char *pidfilename){ while((de = readdir(dir))){ // scan /proc if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self continue; - if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0) + if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0){ sl_iffound_deflt(pid); + exit(1); + } } closedir(dir); free(myname); @@ -143,3 +125,24 @@ void sl_check4running(char *selfname, char *pidfilename){ fclose(pidfile); } } + +/** + * @brief sl_daemonize - prepare for daemonize: + * - close stdin/out/err and reopen to /dev/null + * - croot / + * - umask(0) + * - ignore SIGHUP + * @return non-zero if failed + */ +int sl_daemonize(){ + if(chdir("/")) return -1; + umask(0); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + if(open("/dev/null", O_RDWR) < 0) return -1; + if(dup(0) < 0) return -1; + if(dup(0) < 0) return -1; + if(SIG_ERR == signal(SIGHUP, SIG_IGN)) return -1; + return 0; +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e168602..dd513ab 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,3 +14,4 @@ add_executable(fifo fifo.c) add_executable(conffile conffile.c) add_executable(clientserver clientserver.c) add_executable(ringbuffer ringbuffer.c) +add_executable(daemon daemon.c) diff --git a/examples/daemon.c b/examples/daemon.c new file mode 100644 index 0000000..f342b36 --- /dev/null +++ b/examples/daemon.c @@ -0,0 +1,105 @@ +/* + * This file is part of the Snippets project. + * Copyright 2024 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// example of simplest daemon: search for running process, daemonize if all OK and herd its child + +#include +#include +#include +#include + +static pid_t childpid = 0; + +typedef struct{ + int help; + int verbose; + int nodaemon; + char *logfile; + char *pidfile; +} parameters; + +static parameters G = {0}; + +static sl_option_t cmdlnopts[] = { + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"}, + {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), "verbose level (each -v adds 1)"}, + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "log file name (FULL path!!!)"}, + {"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), "PID-file name (FULL path!!!)"}, + {"nodaemon", NO_ARGS, NULL, 0, arg_int, APTR(&G.nodaemon), "don't daemonize"}, + end_option +}; + +void sl_iffound_deflt(pid_t pid){ + WARNX("Another copy of this process found, pid=%d. Exit.", pid); + exit(1); // don't run `signals` to protect foreign PID-file from removal +} + +void signals(int signo){ + if(childpid){ // this is a main process! + LOGERR("Main process exits with status %d", signo); + // main process have nothing to cleanup, just remove PID-file + if(G.pidfile) unlink(G.pidfile); + }else{ // this is child + LOGERR("Killed with status %d, clearing", signo); + // here we can close everything and make cleanup + } + usleep(1000); + LOGERR("Exited"); + exit(signo); +} + +int main(int argc, char **argv){ + sl_init(); + sl_parseargs(&argc, &argv, cmdlnopts); + if(G.help) sl_showhelp(-1, cmdlnopts); + sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR; + if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1; + sl_check4running(NULL, G.pidfile); + LOGMSG("Hello, I'm started"); + if(G.logfile) OPENLOG(G.logfile, lvl, 1); + if(!G.nodaemon){ + green("Daemonize..\n"); + LOGMSG("Daemonize"); + // and ignore SIGHUP + if(sl_daemonize()){ + WARN("sl_daemonize() failed"); + LOGERR("Can't daemonize"); + } + } + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z + while(1){ // guard for dead processes + childpid = fork(); + if(childpid){ + LOGMSG("create child with PID %d\n", childpid); + DBG("Created child with PID %d\n", childpid); + wait(NULL); + WARNX("Child %d died\n", childpid); + LOGWARN("Child %d died\n", childpid); + sleep(1); + }else{ + prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies + break; // go out to normal functional + } + } + LOGMSG("Here is the child process; sleep for 1 second and die"); + sleep(1); + return 0; +} diff --git a/usefull_macros.h b/usefull_macros.h index eebf855..ddcc08b 100644 --- a/usefull_macros.h +++ b/usefull_macros.h @@ -329,6 +329,8 @@ void WEAK sl_iffound_deflt(pid_t pid); void sl_check4running(char *selfname, char *pidfilename); // read name of process by its PID char *sl_getPSname(pid_t pid); +// daemonize: reopen stdin/out/err as /dev/null, chdir to /, umask(0) +int sl_daemonize(); // return -1 if failed, 0 on OK /******************************************************************************\ The original fifo_lifo.h