Use Case: Graceful Termination#

Problem#

  • Program blocks inside a main loop, waiting for something (network packet, command on a named pipe or message queue, …)

  • SIGTERM (and SIGINT, for that matter) should initiate a clean exit ⟶ cleanup, proper database sync, …

  • ⟶ Default disposition (immediate termination at signal arrival) is inappropriate

Solution#

  • Set quit flag in signal handler

  • Main loop’s blocking call is interrupted (EINTR)

  • Flag can be honored

  • sig_atomic_t as per POSIX standard (simply int on Linux)

#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <print>

static volatile sig_atomic_t quit = 0;                 // <-- special "atomic" type

static void handler(int signal)
{
    if (signal == SIGTERM || signal == SIGINT)
        quit = 1;                                      // <-- communicate into main loop
}

int main()
{
    struct sigaction sa = { 0 };
    sa.sa_handler = handler;

    int rv = sigaction(SIGTERM, &sa, nullptr);
    if (rv == -1) {
        perror("sigaction(SIGTERM)");
        return 1;
    }
    rv = sigaction(SIGINT, &sa, nullptr);
    if (rv == -1) {
        perror("sigaction(SIGINT)");
        return 1;
    }

    while (!quit) {                                    // <-- graceful termination
        char c;
        rv = read(STDIN_FILENO, &c, 1);
        if (rv == -1) {
            if (errno == EINTR)                        // <-- restart manually
                continue;
            else {
                perror("read");
                return 1;
            }
        }
    }

    std::println("cleaning up, bye");                  // <-- sync database
    return 0;
}