I'm trying to control a C daemon program from another userspace program.
- Simple C daemon
This daemon is simply a C program which daemonize itself and log a message every second through syslog.
#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
void bye();
int main()
{
printf("Daemon starting ...\n");
openlog("daemon-test", LOG_PID, LOG_DAEMON);
signal(SIGTERM, bye);
if(0 != daemon(0, 0))
{
syslog(LOG_ERR, "Can't daemonize\n");
return EXIT_FAILURE;
}
syslog(LOG_INFO, "Daemon started !\n");
while(1)
{
syslog(LOG_INFO, "Daemon alive\n");
sleep(1);
}
return EXIT_SUCCESS;
}
void bye()
{
syslog(LOG_INFO, "Daemon killed !\n");
exit(EXIT_SUCCESS);
}
- Launching and killing the daemon from a C test program
For test purpose I have developped a minimal example. I'm using popen
to launch the daemon because I want my program to continue its execution.
After 5 seconds, the test program is supposed to kill the daemon.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define DAEMON_NAME "daemon-test"
int main()
{
FILE* pipe = NULL;
int i = 0;
printf("Launching '%s' program\n", DAEMON_NAME);
if(NULL == (pipe = popen(DAEMON_NAME, "re")))
{
printf("An error occured launching '%s': %m\n", DAEMON_NAME);
return EXIT_FAILURE;
}
printf("Program '%s' launched\n", DAEMON_NAME);
while(i<5)
{
printf("Program alive !\n");
sleep(1);
i++;
}
if(NULL == (pipe = popen("killall " DAEMON_NAME, "re")))
{
printf("An error occured killing '%s' program: %m\n", DAEMON_NAME);
return EXIT_FAILURE;
}
printf("Program '%s' killed\n", DAEMON_NAME);
return EXIT_SUCCESS;
}
Test program log:
$ ./popenTest
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
Program alive !
Program alive !
Program 'daemon-test' killed
Syslog:
Jun 25 13:58:15 PC325 daemon-test[4445]: Daemon started !
Jun 25 13:58:15 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:16 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:17 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:18 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:19 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:20 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:20 PC325 daemon-test[4445]: Daemon killed !
So I'm able to launch and kill the daemon from my C program, however I would like to improve the behaviour in some specific cases.
- Handling daemon crash
The daemon may fail at some point, in that case the control program should be notified so it can be relaunched. My problem is to detect that the daemon has been stopped.
I have though about launching a thread waiting for daemon termination by a call to pclose
, however it won't work as the daemonization has already closed file descriptors and detached the process.
So I'm looking for the best way to have the program notified on daemon exit.
I could poll using linux calls with exec
family (such as pgrep daemon-test
or ps aux | grep daemon-test
) but I think there are more efficient way to achieve that.
- Handling test program error
If the test program is killed or fails before it kills the daemon, at next execution, two instances of the daemon will run at the same time.
Test program log:
$ ./popenTest
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
^C
$ ./popenTest
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
Program alive !
Program alive !
Program 'daemon-test' killed
Syslog:
Jun 25 14:17:25 PC325 daemon-test[4543]: Daemon started !
Jun 25 14:17:25 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:26 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:27 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:28 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:29 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:29 PC325 daemon-test[4547]: Daemon started !
Jun 25 14:17:29 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:30 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:30 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:31 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:31 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:32 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:32 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:33 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:33 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4543]: Daemon killed !
Jun 25 14:17:34 PC325 daemon-test[4547]: Daemon killed !
I want to avoid this situation by checking if there are already daemon instances running. If not I can launch the daemon from control program.
Otherwise if one or several instances of the daemon are running I shall kill them before launching a new one.
This could be achieved by calling killall daemon-test
but calling this command at each execution doesn't satisfy me because it's useless most of the time.
Moreover I would like to explicitly log the situation at each execution and though I want to know exactly how many instances were running in that case.
Once again this can be resolved easilly using linux command calls, but I'm looking for the most efficient way to do it.
Does anybody knows how I could implement daemon process control without having to rely on linux command calls ?
EDIT: June 26th 2018
I should have precised it from the beginning but my aim is to be able to monitor the daemon process without having to modify its code.
So the daemon doesn't write its pid to a file and is always detached from its caller.
Instead of running the program via
popen
why not use the good old POSIXfork
+exec
? It gives you a bit more flexibility.Now, to answer you question:
To do this you have to listen to
SIGCHLD
signal in your parent/controlling process. This is good enough since you directly invoked the process. But if you called a shell script which then forked your daemon, it would get difficult. This is why most daemons write something called apid
file - A file written by the daemon early on with it's PID as the only content in that file. Normally, people have it put it/tmp/mydaemon.pid
or something like that.On Linux, your controlling process can read the PID from this file, then every second you can test if
/proc/<pid>/exe
file exists. If not, you know the daemon died. For example, if your child program's PID is 1234, then/proc/1234/exe
will be a soft link to the actual location of the executable of the child program.Something like this:
In fact, this is roughly how many init systems are implemented. See rc, systemd, upstart etc. for a better understanding of how they implement this in more details.
You can run a socket server in daemon, then use control client as normal CLI. CLI sends test messages or control commands to daemon, and daemon gives response. Based on response from daemon, CLI can watch the status of daemon and control it.