Coverage Report

Created: 2026-04-29 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/core/emergency-action.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <unistd.h>
4
5
#include "ansi-color.h"
6
#include "emergency-action.h"
7
#include "manager.h"
8
#include "reboot-util.h"
9
#include "special.h"
10
#include "string-table.h"
11
#include "string-util.h"
12
#include "virt.h"
13
14
static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
15
        [EMERGENCY_ACTION_NONE]               = "none",
16
        [EMERGENCY_ACTION_EXIT]               = "exit",
17
        [EMERGENCY_ACTION_EXIT_FORCE]         = "exit-force",
18
        [EMERGENCY_ACTION_REBOOT]             = "reboot",
19
        [EMERGENCY_ACTION_REBOOT_FORCE]       = "reboot-force",
20
        [EMERGENCY_ACTION_REBOOT_IMMEDIATE]   = "reboot-immediate",
21
        [EMERGENCY_ACTION_POWEROFF]           = "poweroff",
22
        [EMERGENCY_ACTION_POWEROFF_FORCE]     = "poweroff-force",
23
        [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
24
        [EMERGENCY_ACTION_SOFT_REBOOT]        = "soft-reboot",
25
        [EMERGENCY_ACTION_SOFT_REBOOT_FORCE]  = "soft-reboot-force",
26
        [EMERGENCY_ACTION_KEXEC]              = "kexec",
27
        [EMERGENCY_ACTION_KEXEC_FORCE]        = "kexec-force",
28
        [EMERGENCY_ACTION_HALT]               = "halt",
29
        [EMERGENCY_ACTION_HALT_FORCE]         = "halt-force",
30
        [EMERGENCY_ACTION_HALT_IMMEDIATE]     = "halt-immediate",
31
};
32
33
static void log_and_status(
34
                Manager *m,
35
                EmergencyAction action,
36
                EmergencyActionFlags flags,
37
                const char *message,
38
0
                const char *reason) {
39
40
0
        assert(m);
41
0
        assert(message);
42
0
        assert(reason);
43
44
0
        log_full(FLAGS_SET(flags, EMERGENCY_ACTION_WARN) ? LOG_WARNING : LOG_DEBUG,
45
0
                 "%s: %s", message, reason);
46
47
0
        bool do_sleep = FLAGS_SET(flags, EMERGENCY_ACTION_WARN|EMERGENCY_ACTION_SLEEP_5S) &&
48
0
                IN_SET(action,
49
0
                       EMERGENCY_ACTION_EXIT_FORCE,
50
0
                       EMERGENCY_ACTION_REBOOT_FORCE, EMERGENCY_ACTION_REBOOT_IMMEDIATE,
51
0
                       EMERGENCY_ACTION_POWEROFF_FORCE, EMERGENCY_ACTION_POWEROFF_IMMEDIATE,
52
0
                       EMERGENCY_ACTION_SOFT_REBOOT_FORCE,
53
0
                       EMERGENCY_ACTION_KEXEC_FORCE);
54
55
0
        if (FLAGS_SET(flags, EMERGENCY_ACTION_WARN))
56
0
                manager_status_printf(
57
0
                                m,
58
0
                                STATUS_TYPE_EMERGENCY,
59
0
                                ANSI_HIGHLIGHT_RED "  !!  " ANSI_NORMAL,
60
0
                                "%s: %s%s", message, reason,
61
0
                                do_sleep ? ", proceeding in 5s" : "");
62
63
        /* Optionally sleep for 5s so that the user can see this output, before we actually execute the
64
         * operation. Do this only if we immediately execute an operation, i.e. when there's no event loop to
65
         * feed anymore. */
66
0
        if (do_sleep)
67
0
                (void) sleep(5);
68
0
}
69
70
void emergency_action(
71
                Manager *m,
72
                EmergencyAction action,
73
                EmergencyActionFlags flags,
74
                const char *reboot_arg,
75
                int exit_status,
76
0
                const char *reason) {
77
78
0
        Unit *u;
79
80
0
        assert(m);
81
0
        assert(action >= 0);
82
0
        assert(action < _EMERGENCY_ACTION_MAX);
83
0
        assert((flags & ~_EMERGENCY_ACTION_FLAGS_MAX) == 0);
84
0
        assert(reason);
85
86
0
        if (action == EMERGENCY_ACTION_NONE)
87
0
                return;
88
89
        /* Is the special shutdown target active or queued? If so, we are in shutdown state */
90
0
        if (IN_SET(action,
91
0
                   EMERGENCY_ACTION_REBOOT,
92
0
                   EMERGENCY_ACTION_SOFT_REBOOT,
93
0
                   EMERGENCY_ACTION_POWEROFF,
94
0
                   EMERGENCY_ACTION_EXIT,
95
0
                   EMERGENCY_ACTION_KEXEC,
96
0
                   EMERGENCY_ACTION_HALT)) {
97
0
                u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
98
0
                if (u && unit_active_or_pending(u)) {
99
0
                        log_notice("Shutdown is already active. Skipping emergency action request %s.",
100
0
                                   emergency_action_table[action]);
101
0
                        return;
102
0
                }
103
0
        }
104
105
0
        if (FLAGS_SET(flags, EMERGENCY_ACTION_IS_WATCHDOG) && !m->service_watchdogs) {
106
0
                log_warning("Watchdog disabled! Not acting on: %s", reason);
107
0
                return;
108
0
        }
109
110
0
        switch (action) {
111
112
0
        case EMERGENCY_ACTION_REBOOT:
113
0
                log_and_status(m, action, flags, "Rebooting", reason);
114
115
0
                (void) update_reboot_parameter_and_warn(reboot_arg, true);
116
0
                (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
117
0
                break;
118
119
0
        case EMERGENCY_ACTION_REBOOT_FORCE:
120
0
                log_and_status(m, action, flags, "Forcibly rebooting", reason);
121
122
0
                (void) update_reboot_parameter_and_warn(reboot_arg, true);
123
0
                m->objective = MANAGER_REBOOT;
124
0
                break;
125
126
0
        case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
127
0
                log_and_status(m, action, flags, "Rebooting immediately", reason);
128
129
0
                sync();
130
131
0
                if (!isempty(reboot_arg)) {
132
0
                        log_info("Rebooting with argument '%s'.", reboot_arg);
133
0
                        (void) raw_reboot(LINUX_REBOOT_CMD_RESTART2, reboot_arg);
134
0
                        log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
135
0
                }
136
137
0
                log_info("Rebooting.");
138
0
                (void) reboot(RB_AUTOBOOT);
139
0
                break;
140
141
0
        case EMERGENCY_ACTION_SOFT_REBOOT:
142
0
                log_and_status(m, action, flags, "Soft-rebooting", reason);
143
144
0
                (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_SOFT_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
145
0
                break;
146
147
0
        case EMERGENCY_ACTION_SOFT_REBOOT_FORCE:
148
0
                log_and_status(m, action, flags, "Forcibly soft-rebooting", reason);
149
150
0
                m->objective = MANAGER_SOFT_REBOOT;
151
0
                break;
152
153
0
        case EMERGENCY_ACTION_EXIT:
154
155
0
                if (exit_status >= 0)
156
0
                        m->return_value = exit_status;
157
158
0
                if (MANAGER_IS_USER(m) || detect_container() > 0) {
159
0
                        log_and_status(m, action, flags, "Exiting", reason);
160
0
                        (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_EXIT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
161
0
                        break;
162
0
                }
163
164
0
                log_notice("Doing \"poweroff\" action instead of an \"exit\" emergency action.");
165
0
                _fallthrough_;
166
167
0
        case EMERGENCY_ACTION_POWEROFF:
168
0
                log_and_status(m, action, flags, "Powering off", reason);
169
0
                (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
170
0
                break;
171
172
0
        case EMERGENCY_ACTION_EXIT_FORCE:
173
174
0
                if (exit_status >= 0)
175
0
                        m->return_value = exit_status;
176
177
0
                if (MANAGER_IS_USER(m) || detect_container() > 0) {
178
0
                        log_and_status(m, action, flags, "Exiting immediately", reason);
179
0
                        m->objective = MANAGER_EXIT;
180
0
                        break;
181
0
                }
182
183
0
                log_notice("Doing \"poweroff-force\" action instead of an \"exit-force\" emergency action.");
184
0
                _fallthrough_;
185
186
0
        case EMERGENCY_ACTION_POWEROFF_FORCE:
187
0
                log_and_status(m, action, flags, "Forcibly powering off", reason);
188
0
                m->objective = MANAGER_POWEROFF;
189
0
                break;
190
191
0
        case EMERGENCY_ACTION_POWEROFF_IMMEDIATE:
192
0
                log_and_status(m, action, flags, "Powering off immediately", reason);
193
194
0
                sync();
195
196
0
                log_info("Powering off.");
197
0
                (void) reboot(RB_POWER_OFF);
198
0
                break;
199
200
0
        case EMERGENCY_ACTION_KEXEC:
201
0
                log_and_status(m, action, flags, "Executing kexec", reason);
202
0
                (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
203
0
                break;
204
205
0
        case EMERGENCY_ACTION_KEXEC_FORCE:
206
0
                log_and_status(m, action, flags, "Forcibly executing kexec", reason);
207
0
                m->objective = MANAGER_KEXEC;
208
0
                break;
209
210
0
        case EMERGENCY_ACTION_HALT:
211
0
                log_and_status(m, action, flags, "Halting", reason);
212
0
                (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
213
0
                break;
214
215
0
        case EMERGENCY_ACTION_HALT_FORCE:
216
0
                log_and_status(m, action, flags, "Forcibly halting", reason);
217
0
                m->objective = MANAGER_HALT;
218
0
                break;
219
220
0
        case EMERGENCY_ACTION_HALT_IMMEDIATE:
221
0
                log_and_status(m, action, flags, "Halting immediately", reason);
222
223
0
                sync();
224
225
0
                log_info("Halting.");
226
0
                (void) reboot(RB_HALT_SYSTEM);
227
0
                break;
228
229
0
        default:
230
0
                assert_not_reached();
231
0
        }
232
0
}
233
234
DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction);
235
236
int parse_emergency_action(
237
                const char *value,
238
                RuntimeScope runtime_scope,
239
552
                EmergencyAction *ret) {
240
241
552
        EmergencyAction x;
242
243
552
        assert(ret);
244
245
552
        x = emergency_action_from_string(value);
246
552
        if (x < 0)
247
326
                return -EINVAL;
248
249
226
        if (runtime_scope != RUNTIME_SCOPE_SYSTEM && x > _EMERGENCY_ACTION_LAST_USER_ACTION)
250
0
                return -EOPNOTSUPP;
251
252
226
        *ret = x;
253
226
        return 0;
254
226
}