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