|
| 1 | +#!/bin/sh |
| 2 | +# Verify transitive dependency chain during reload and crash |
| 3 | +# |
| 4 | +# Three services in a chain: A → B → C, where B depends on pid/A |
| 5 | +# and C depends on pid/B. B is placed in a sub-config file so |
| 6 | +# we can use 'initctl touch' on it. B uses <!pid/svc_a> (with |
| 7 | +# leading '!') so it does not support SIGHUP, causing a full |
| 8 | +# stop/start cycle on 'initctl touch svc_b.conf' + reload. |
| 9 | +# |
| 10 | +# Test 1 - touch + reload: |
| 11 | +# After 'initctl touch svc_b.conf' + 'initctl reload': |
| 12 | +# - A should be unaffected (same PID) |
| 13 | +# - B is restarted (config was touched, noreload) |
| 14 | +# - C must be restarted (transitive, depends on pid/B) |
| 15 | +# |
| 16 | +# Test 2 - crash (kill -9): |
| 17 | +# When B is killed with SIGKILL the crash path (RUNNING → |
| 18 | +# HALTED) bypasses STOPPING, where cond_clear() used to be |
| 19 | +# the only call site. The pidfile plugin only watches for |
| 20 | +# IN_CLOSE_WRITE, so neither the pidfile removal (IN_DELETE) |
| 21 | +# nor a pidfile touch (IN_ATTRIB) triggers an inotify event. |
| 22 | +# Without the fix in service_cleanup(), pid/B is never |
| 23 | +# invalidated and C is never restarted. |
| 24 | + |
| 25 | +set -eu |
| 26 | + |
| 27 | +TEST_DIR=$(dirname "$0") |
| 28 | + |
| 29 | +test_teardown() |
| 30 | +{ |
| 31 | + say "Running test teardown." |
| 32 | + run "rm -f $FINIT_RCSD/svc_b.conf" |
| 33 | +} |
| 34 | + |
| 35 | +pidof() |
| 36 | +{ |
| 37 | + texec initctl -j status "$1" | jq .pid |
| 38 | +} |
| 39 | + |
| 40 | +test_setup() |
| 41 | +{ |
| 42 | + run "cat >> $FINIT_CONF" <<EOF |
| 43 | +service log:stdout notify:pid name:svc_a serv -np -i svc_a -- Chain root |
| 44 | +service log:stdout notify:pid <pid/svc_b> name:svc_c serv -np -i svc_c -- Needs B |
| 45 | +EOF |
| 46 | + run "echo 'service log:stdout notify:pid <!pid/svc_a> name:svc_b serv -np -i svc_b -- Needs A' > $FINIT_RCSD/svc_b.conf" |
| 47 | +} |
| 48 | + |
| 49 | +# shellcheck source=/dev/null |
| 50 | +. "$TEST_DIR/lib/setup.sh" |
| 51 | + |
| 52 | +sep "Configuration" |
| 53 | +run "cat $FINIT_CONF" |
| 54 | +run "cat $FINIT_RCSD/svc_b.conf" |
| 55 | + |
| 56 | +say "Reload Finit to start all services" |
| 57 | +run "initctl reload" |
| 58 | + |
| 59 | +say "Wait for full chain to start" |
| 60 | +retry 'assert_status "svc_c" "running"' 10 1 |
| 61 | + |
| 62 | +run "initctl status" |
| 63 | +run "initctl cond dump" |
| 64 | + |
| 65 | +# ―――――――――――――――――――――――――――――――――――――――――――――――――――――― |
| 66 | +# Test 1: touch + reload |
| 67 | +# ―――――――――――――――――――――――――――――――――――――――――――――――――――――― |
| 68 | +sep "Test 1: Touch B and global reload" |
| 69 | + |
| 70 | +pid_a=$(pidof svc_a) |
| 71 | +pid_b=$(pidof svc_b) |
| 72 | +pid_c=$(pidof svc_c) |
| 73 | +say "PIDs before: A=$pid_a B=$pid_b C=$pid_c" |
| 74 | + |
| 75 | +run "initctl touch svc_b.conf" |
| 76 | +run "initctl reload" |
| 77 | + |
| 78 | +say "Wait for chain to settle" |
| 79 | +retry 'assert_status "svc_c" "running"' 15 1 |
| 80 | + |
| 81 | +run "initctl status" |
| 82 | +run "initctl cond dump" |
| 83 | + |
| 84 | +new_pid_a=$(pidof svc_a) |
| 85 | +new_pid_b=$(pidof svc_b) |
| 86 | +new_pid_c=$(pidof svc_c) |
| 87 | +say "PIDs after: A=$new_pid_a B=$new_pid_b C=$new_pid_c" |
| 88 | + |
| 89 | +# shellcheck disable=SC2086 |
| 90 | +assert "A was not restarted" $new_pid_a -eq $pid_a |
| 91 | +# shellcheck disable=SC2086 |
| 92 | +assert "B was restarted (touched)" $new_pid_b -ne $pid_b |
| 93 | +# shellcheck disable=SC2086 |
| 94 | +assert "C was restarted (transitive dep)" $new_pid_c -ne $pid_c |
| 95 | + |
| 96 | +# ―――――――――――――――――――――――――――――――――――――――――――――――――――――― |
| 97 | +# Test 2: crash (kill -9), bypasses STOPPING |
| 98 | +# ―――――――――――――――――――――――――――――――――――――――――――――――――――――― |
| 99 | +sep "Test 2: Kill B with SIGKILL (bypasses STOPPING)" |
| 100 | + |
| 101 | +pid_b=$(pidof svc_b) |
| 102 | +pid_c=$(pidof svc_c) |
| 103 | +say "PIDs before: B=$pid_b C=$pid_c" |
| 104 | + |
| 105 | +run "kill -9 $pid_b" |
| 106 | + |
| 107 | +say "Wait for B to respawn and chain to settle" |
| 108 | +retry 'assert_status "svc_c" "running"' 15 1 |
| 109 | + |
| 110 | +run "initctl status" |
| 111 | +run "initctl cond dump" |
| 112 | + |
| 113 | +new_pid_b=$(pidof svc_b) |
| 114 | +new_pid_c=$(pidof svc_c) |
| 115 | +say "PIDs after: B=$new_pid_b C=$new_pid_c" |
| 116 | + |
| 117 | +# shellcheck disable=SC2086 |
| 118 | +assert "B was restarted (crashed+respawn)" $new_pid_b -ne $pid_b |
| 119 | +# shellcheck disable=SC2086 |
| 120 | +assert "C was restarted (transitive dep)" $new_pid_c -ne $pid_c |
| 121 | + |
| 122 | +return 0 |
0 commit comments