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

max_iters is maximum work iterations; returns count of completed iterations.

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;
}

Common mistakes

Using non-atomic variable types for signal flags or performing unsafe operations inside the handler.

Edge cases to handle

Signal received before loop starts, signal received during loop iteration, loop runs to max_iters without signal.

Background lessons

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