linux-sysprog · intermediate · ~12 min

Graceful SIGTERM — set a flag, cleanup in main

Combine handler + flag + main-loop check for clean termination.

Challenge

Implement graceful shutdown: a SIGTERM handler that just sets a flag, and a work loop that checks that flag each iteration and stops cleanly when asked to.

Task

Implement two functions:

int install_graceful_term(void);
int do_work_until_term(int max_iters);

Input

  • install_graceful_term: no parameters. The global volatile sig_atomic_t g_shutdown is provided.
  • do_work_until_term: max_iters, the maximum number of work iterations to run.

Output

  • install_graceful_term installs a SIGTERM handler that sets g_shutdown to 1. Returns 0 on success, -1 on failure.
  • do_work_until_term runs up to max_iters iterations (one unit of work each), but stops early if g_shutdown is set. Returns the number of iterations actually completed (max_iters if no SIGTERM arrived, fewer otherwise).

Example

install_graceful_term()        ->   0,  g_shutdown == 0
do_work_until_term(5)          ->   5   (no SIGTERM, ran fully)
raise(SIGTERM)                 ->   g_shutdown == 1
do_work_until_term(100)        ->   0   (flag already set, exits immediately)

Edge cases

  • If g_shutdown is already set, the loop completes 0 iterations.
  • With no SIGTERM, the loop returns max_iters.

Rules

  • Use sigaction to install the handler; the handler must only set the flag.

Why this matters

systemd sends SIGTERM, waits a grace period, then SIGKILL. Your service has one chance to clean up.

Input format

install_graceful_term takes nothing (uses the global g_shutdown); do_work_until_term takes max_iters.

Output format

install_graceful_term returns 0/-1; do_work_until_term returns the iterations completed before g_shutdown was set (max_iters if never set).

Constraints

Handler sets g_shutdown = 1 only. The work loop checks g_shutdown each iteration and stops when set.

Starter code

#include <signal.h>
#include <stdio.h>
volatile sig_atomic_t g_shutdown = 0;
int install_graceful_term(void) {
    /* TODO */
    return -1;
}
int do_work_until_term(int max_iters) {
    /* TODO */
    return -1;
}

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.