1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <getopt.h>
4 #include <unistd.h>
5 
6 #include "alloc-util.h"
7 #include "pretty-print.h"
8 #include "rlimit-util.h"
9 #include "systemctl-compat-telinit.h"
10 #include "systemctl-daemon-reload.h"
11 #include "systemctl-start-unit.h"
12 #include "systemctl-sysv-compat.h"
13 #include "systemctl.h"
14 #include "terminal-util.h"
15 
telinit_help(void)16 static int telinit_help(void) {
17         _cleanup_free_ char *link = NULL;
18         int r;
19 
20         r = terminal_urlify_man("telinit", "8", &link);
21         if (r < 0)
22                 return log_oom();
23 
24         printf("%s [OPTIONS...] COMMAND\n\n"
25                "%sSend control commands to the init daemon.%s\n"
26                "\nCommands:\n"
27                "  0              Power-off the machine\n"
28                "  6              Reboot the machine\n"
29                "  2, 3, 4, 5     Start runlevelX.target unit\n"
30                "  1, s, S        Enter rescue mode\n"
31                "  q, Q           Reload init daemon configuration\n"
32                "  u, U           Reexecute init daemon\n"
33                "\nOptions:\n"
34                "     --help      Show this help\n"
35                "     --no-wall   Don't send wall message before halt/power-off/reboot\n"
36                "\nSee the %s for details.\n",
37                program_invocation_short_name,
38                ansi_highlight(),
39                ansi_normal(),
40                link);
41 
42         return 0;
43 }
44 
telinit_parse_argv(int argc,char * argv[])45 int telinit_parse_argv(int argc, char *argv[]) {
46         enum {
47                 ARG_HELP = 0x100,
48                 ARG_NO_WALL
49         };
50 
51         static const struct option options[] = {
52                 { "help",      no_argument,       NULL, ARG_HELP    },
53                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
54                 {}
55         };
56 
57         static const struct {
58                 char from;
59                 enum action to;
60         } table[] = {
61                 { '0', ACTION_POWEROFF },
62                 { '6', ACTION_REBOOT },
63                 { '1', ACTION_RESCUE },
64                 { '2', ACTION_RUNLEVEL2 },
65                 { '3', ACTION_RUNLEVEL3 },
66                 { '4', ACTION_RUNLEVEL4 },
67                 { '5', ACTION_RUNLEVEL5 },
68                 { 's', ACTION_RESCUE },
69                 { 'S', ACTION_RESCUE },
70                 { 'q', ACTION_RELOAD },
71                 { 'Q', ACTION_RELOAD },
72                 { 'u', ACTION_REEXEC },
73                 { 'U', ACTION_REEXEC }
74         };
75 
76         unsigned i;
77         int c;
78 
79         assert(argc >= 0);
80         assert(argv);
81 
82         while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
83                 switch (c) {
84 
85                 case ARG_HELP:
86                         return telinit_help();
87 
88                 case ARG_NO_WALL:
89                         arg_no_wall = true;
90                         break;
91 
92                 case '?':
93                         return -EINVAL;
94 
95                 default:
96                         assert_not_reached();
97                 }
98 
99         if (optind >= argc)
100                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
101                                        "%s: required argument missing.",
102                                        program_invocation_short_name);
103 
104         if (optind + 1 < argc)
105                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
106                                        "Too many arguments.");
107 
108         if (strlen(argv[optind]) != 1)
109                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
110                                        "Expected single character argument.");
111 
112         for (i = 0; i < ELEMENTSOF(table); i++)
113                 if (table[i].from == argv[optind][0])
114                         break;
115 
116         if (i >= ELEMENTSOF(table))
117                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
118                                        "Unknown command '%s'.", argv[optind]);
119 
120         arg_action = table[i].to;
121 
122         optind++;
123 
124         return 1;
125 }
126 
start_with_fallback(void)127 int start_with_fallback(void) {
128         int r;
129 
130         /* First, try systemd via D-Bus. */
131         r = verb_start(0, NULL, NULL);
132         if (r == 0)
133                 return 0;
134 
135 #if HAVE_SYSV_COMPAT
136         /* Nothing else worked, so let's try /dev/initctl */
137         if (talk_initctl(action_to_runlevel()) > 0)
138                 return 0;
139 #endif
140 
141         return log_error_errno(r, "Failed to talk to init daemon: %m");
142 }
143 
reload_with_fallback(void)144 int reload_with_fallback(void) {
145 
146         assert(IN_SET(arg_action, ACTION_RELOAD, ACTION_REEXEC));
147 
148         /* First, try systemd via D-Bus */
149         if (daemon_reload(arg_action, /* graceful= */ true) > 0)
150                 return 0;
151 
152         /* That didn't work, so let's try signals */
153         if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0)
154                 return log_error_errno(errno, "kill() failed: %m");
155 
156         return 0;
157 }
158 
exec_telinit(char * argv[])159 int exec_telinit(char *argv[]) {
160         (void) rlimit_nofile_safe();
161         (void) execv(TELINIT, argv);
162 
163         return log_error_errno(SYNTHETIC_ERRNO(EIO),
164                                "Couldn't find an alternative telinit implementation to spawn.");
165 }
166