Skip to main content

Daemon control

What is a daemon?

A daemon is a service process that runs in the background and supervises the system or provides functionality to other processes. On UNIX-like systems, a well-formed daemon detaches from the parent process, after the original parent exits, the child is re-parented to PID 1 (init/systemd). For this project, a minimal approach is sufficient: fork the process, close inherited descriptors in the parent to avoid duplicate output, and exit the parent so the child continues in the background.

To create a daemon:

  1. fork
  2. close the parent's process's file descriptors to avoid undefined behaviors like having the logs written twice
  3. terminate the parent

Your child will be assigned to init (or systemd).

#include <stdio.h>
#include <unistd.h>

int main(void)
{
pid_t val = fork();

/* check fork errors */

if (!val)
{
/* inside daemon */
}
else
{
printf("%d\n", val);
}

return 0;
}

In this example, the parent will terminate and the child will be running in the background.

tip

For more information about daemons, please look at the daemon(7) man page.

Signals

In HTTPd, you need to communicate with the daemon to tell it to stop or restart by sending signals. Signals are notifications delivered to processes running on the system. For example, the kill command is used to send a specific signal to a process, and pressing Ctrl + C in a shell sends the SIGINT signal to the foreground program.

For example:

kill -s SIGINT 187956

This command will send the signal SIGINT to the process which has the PID 187956.

tip

For more information about signals, please look at the signal(7) man page.

Catching a signal

Now you are probably asking yourself how can we catch signals in your C program.

Here is an example:

#define _POSIX_C_SOURCE 200809L

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

// this function will be called each time our program gets a SIGINT signal
static void signal_handler(int signal)
{
printf("signal received: %d\n", signal);
switch (signal)
{
case SIGINT: {
printf("I love HTTPd!\n");
fflush(stdout);
exit(0);
}
}
}

int main(void)
{
struct sigaction siga;
siga.sa_flags = 0; // nothing special to do
siga.sa_handler = signal_handler; // we link our handler function to the struct

// initialize mask
if (sigemptyset(&siga.sa_mask) < 0)
{
// handle error here
}

if (sigaction(SIGINT, &siga, NULL) == -1)
return 1;
while (1) // we do not want our program to stop because we want to send it signals
{
continue;
}
return 0;
}

If you try to run this code, you will see that it never stops by itself. If you try to Ctrl + C you will see that it prints signal received: 2 and then I love HTTPd! before exiting.

info

One of the reasons we use sigaction over signal is because signal does not necessarily block incoming signals while another signal is being treated.