1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/reboot.h>
4
5 #include "bus-error.h"
6 #include "bus-util.h"
7 #include "emergency-action.h"
8 #include "raw-reboot.h"
9 #include "reboot-util.h"
10 #include "special.h"
11 #include "string-table.h"
12 #include "terminal-util.h"
13 #include "virt.h"
14
15 static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
16 [EMERGENCY_ACTION_NONE] = "none",
17 [EMERGENCY_ACTION_REBOOT] = "reboot",
18 [EMERGENCY_ACTION_REBOOT_FORCE] = "reboot-force",
19 [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
20 [EMERGENCY_ACTION_POWEROFF] = "poweroff",
21 [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
22 [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
23 [EMERGENCY_ACTION_EXIT] = "exit",
24 [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
25 };
26
log_and_status(Manager * m,bool warn,const char * message,const char * reason)27 static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
28 log_full(warn ? LOG_WARNING : LOG_DEBUG, "%s: %s", message, reason);
29 if (warn)
30 manager_status_printf(m, STATUS_TYPE_EMERGENCY,
31 ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL,
32 "%s: %s", message, reason);
33 }
34
emergency_action(Manager * m,EmergencyAction action,EmergencyActionFlags options,const char * reboot_arg,int exit_status,const char * reason)35 void emergency_action(
36 Manager *m,
37 EmergencyAction action,
38 EmergencyActionFlags options,
39 const char *reboot_arg,
40 int exit_status,
41 const char *reason) {
42
43 Unit *u;
44
45 assert(m);
46 assert(action >= 0);
47 assert(action < _EMERGENCY_ACTION_MAX);
48
49 /* Is the special shutdown target active or queued? If so, we are in shutdown state */
50 if (IN_SET(action, EMERGENCY_ACTION_REBOOT, EMERGENCY_ACTION_POWEROFF, EMERGENCY_ACTION_EXIT)) {
51 u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
52 if (u && unit_active_or_pending(u)) {
53 log_notice("Shutdown is already active. Skipping emergency action request %s.",
54 emergency_action_table[action]);
55 return;
56 }
57 }
58
59 if (action == EMERGENCY_ACTION_NONE)
60 return;
61
62 if (FLAGS_SET(options, EMERGENCY_ACTION_IS_WATCHDOG) && !m->service_watchdogs) {
63 log_warning("Watchdog disabled! Not acting on: %s", reason);
64 return;
65 }
66
67 bool warn = FLAGS_SET(options, EMERGENCY_ACTION_WARN);
68
69 switch (action) {
70
71 case EMERGENCY_ACTION_REBOOT:
72 log_and_status(m, warn, "Rebooting", reason);
73
74 (void) update_reboot_parameter_and_warn(reboot_arg, true);
75 (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
76 break;
77
78 case EMERGENCY_ACTION_REBOOT_FORCE:
79 log_and_status(m, warn, "Forcibly rebooting", reason);
80
81 (void) update_reboot_parameter_and_warn(reboot_arg, true);
82 m->objective = MANAGER_REBOOT;
83
84 break;
85
86 case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
87 log_and_status(m, warn, "Rebooting immediately", reason);
88
89 sync();
90
91 if (!isempty(reboot_arg)) {
92 log_info("Rebooting with argument '%s'.", reboot_arg);
93 (void) raw_reboot(LINUX_REBOOT_CMD_RESTART2, reboot_arg);
94 log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
95 }
96
97 log_info("Rebooting.");
98 (void) reboot(RB_AUTOBOOT);
99 break;
100
101 case EMERGENCY_ACTION_EXIT:
102
103 if (exit_status >= 0)
104 m->return_value = exit_status;
105
106 if (MANAGER_IS_USER(m) || detect_container() > 0) {
107 log_and_status(m, warn, "Exiting", reason);
108 (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_EXIT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
109 break;
110 }
111
112 log_notice("Doing \"poweroff\" action instead of an \"exit\" emergency action.");
113 _fallthrough_;
114
115 case EMERGENCY_ACTION_POWEROFF:
116 log_and_status(m, warn, "Powering off", reason);
117 (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
118 break;
119
120 case EMERGENCY_ACTION_EXIT_FORCE:
121
122 if (exit_status >= 0)
123 m->return_value = exit_status;
124
125 if (MANAGER_IS_USER(m) || detect_container() > 0) {
126 log_and_status(m, warn, "Exiting immediately", reason);
127 m->objective = MANAGER_EXIT;
128 break;
129 }
130
131 log_notice("Doing \"poweroff-force\" action instead of an \"exit-force\" emergency action.");
132 _fallthrough_;
133
134 case EMERGENCY_ACTION_POWEROFF_FORCE:
135 log_and_status(m, warn, "Forcibly powering off", reason);
136 m->objective = MANAGER_POWEROFF;
137 break;
138
139 case EMERGENCY_ACTION_POWEROFF_IMMEDIATE:
140 log_and_status(m, warn, "Powering off immediately", reason);
141
142 sync();
143
144 log_info("Powering off.");
145 (void) reboot(RB_POWER_OFF);
146 break;
147
148 default:
149 assert_not_reached();
150 }
151 }
152
153 DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction);
154
parse_emergency_action(const char * value,bool system,EmergencyAction * ret)155 int parse_emergency_action(
156 const char *value,
157 bool system,
158 EmergencyAction *ret) {
159
160 EmergencyAction x;
161
162 x = emergency_action_from_string(value);
163 if (x < 0)
164 return -EINVAL;
165
166 if (!system && x != EMERGENCY_ACTION_NONE && x < _EMERGENCY_ACTION_FIRST_USER_ACTION)
167 return -EOPNOTSUPP;
168
169 *ret = x;
170 return 0;
171 }
172