1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <getopt.h>
4 #include <unistd.h>
5 
6 #include "sd-daemon.h"
7 
8 #include "alloc-util.h"
9 #include "pretty-print.h"
10 #include "process-util.h"
11 #include "reboot-util.h"
12 #include "systemctl-compat-halt.h"
13 #include "systemctl-compat-telinit.h"
14 #include "systemctl-logind.h"
15 #include "systemctl-start-unit.h"
16 #include "systemctl-util.h"
17 #include "systemctl.h"
18 #include "terminal-util.h"
19 #include "utmp-wtmp.h"
20 
halt_help(void)21 static int halt_help(void) {
22         _cleanup_free_ char *link = NULL;
23         int r;
24 
25         r = terminal_urlify_man("halt", "8", &link);
26         if (r < 0)
27                 return log_oom();
28 
29         printf("%s [OPTIONS...]%s\n"
30                "\n%s%s the system.%s\n"
31                "\nOptions:\n"
32                "     --help      Show this help\n"
33                "     --halt      Halt the machine\n"
34                "  -p --poweroff  Switch off the machine\n"
35                "     --reboot    Reboot the machine\n"
36                "  -f --force     Force immediate halt/power-off/reboot\n"
37                "  -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
38                "  -d --no-wtmp   Don't write wtmp record\n"
39                "     --no-wall   Don't send wall message before halt/power-off/reboot\n"
40                "\nSee the %s for details.\n",
41                program_invocation_short_name,
42                arg_action == ACTION_REBOOT ? " [ARG]" : "",
43                ansi_highlight(),
44                arg_action == ACTION_REBOOT           ? "Reboot" :
45                        arg_action == ACTION_POWEROFF ? "Power off" :
46                                                        "Halt",
47                ansi_normal(),
48                link);
49 
50         return 0;
51 }
52 
halt_parse_argv(int argc,char * argv[])53 int halt_parse_argv(int argc, char *argv[]) {
54         enum {
55                 ARG_HELP = 0x100,
56                 ARG_HALT,
57                 ARG_REBOOT,
58                 ARG_NO_WALL
59         };
60 
61         static const struct option options[] = {
62                 { "help",      no_argument,       NULL, ARG_HELP    },
63                 { "halt",      no_argument,       NULL, ARG_HALT    },
64                 { "poweroff",  no_argument,       NULL, 'p'         },
65                 { "reboot",    no_argument,       NULL, ARG_REBOOT  },
66                 { "force",     no_argument,       NULL, 'f'         },
67                 { "wtmp-only", no_argument,       NULL, 'w'         },
68                 { "no-wtmp",   no_argument,       NULL, 'd'         },
69                 { "no-sync",   no_argument,       NULL, 'n'         },
70                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
71                 {}
72         };
73 
74         int c, r, runlevel;
75 
76         assert(argc >= 0);
77         assert(argv);
78 
79         /* called in sysvinit system as last command in shutdown/reboot so this is always forceful */
80         if (utmp_get_runlevel(&runlevel, NULL) >= 0)
81                 if (IN_SET(runlevel, '0', '6'))
82                         arg_force = 2;
83 
84         while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0)
85                 switch (c) {
86 
87                 case ARG_HELP:
88                         return halt_help();
89 
90                 case ARG_HALT:
91                         arg_action = ACTION_HALT;
92                         break;
93 
94                 case 'p':
95                         if (arg_action != ACTION_REBOOT)
96                                 arg_action = ACTION_POWEROFF;
97                         break;
98 
99                 case ARG_REBOOT:
100                         arg_action = ACTION_REBOOT;
101                         break;
102 
103                 case 'f':
104                         arg_force = 2;
105                         break;
106 
107                 case 'w':
108                         arg_dry_run = true;
109                         break;
110 
111                 case 'd':
112                         arg_no_wtmp = true;
113                         break;
114 
115                 case 'n':
116                         arg_no_sync = true;
117                         break;
118 
119                 case ARG_NO_WALL:
120                         arg_no_wall = true;
121                         break;
122 
123                 case 'i':
124                 case 'h':
125                         /* Compatibility nops */
126                         break;
127 
128                 case '?':
129                         return -EINVAL;
130 
131                 default:
132                         assert_not_reached();
133                 }
134 
135         if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
136                 r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL, false);
137                 if (r < 0)
138                         return r;
139         } else if (optind < argc)
140                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
141                                        "Too many arguments.");
142 
143         return 1;
144 }
145 
halt_main(void)146 int halt_main(void) {
147         int r;
148 
149         if (arg_force == 0) {
150                 /* always try logind first */
151                 if (arg_when > 0)
152                         r = logind_schedule_shutdown();
153                 else {
154                         r = logind_check_inhibitors(arg_action);
155                         if (r < 0)
156                                 return r;
157 
158                         r = logind_reboot(arg_action);
159                 }
160                 if (r >= 0)
161                         return r;
162                 if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
163                         /* Requested operation requires auth, is not supported on the local system or already in
164                          * progress */
165                         return r;
166                 /* on all other errors, try low-level operation */
167 
168                 /* In order to minimize the difference between operation with and without logind, we explicitly
169                  * enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */
170                 arg_no_block = true;
171 
172                 if (!arg_dry_run)
173                         return start_with_fallback();
174         }
175 
176         if (geteuid() != 0) {
177                 (void) must_be_root();
178                 return -EPERM;
179         }
180 
181         if (!arg_no_wtmp) {
182                 if (sd_booted() > 0)
183                         log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
184                 else {
185                         r = utmp_put_shutdown();
186                         if (r < 0)
187                                 log_warning_errno(r, "Failed to write utmp record: %m");
188                 }
189         }
190 
191         if (arg_dry_run)
192                 return 0;
193 
194         r = halt_now(arg_action);
195         return log_error_errno(r, "Failed to %s: %m", action_table[arg_action].verb);
196 }
197