chiark / gitweb /
Prep v239: Fix main() to call manager_new() again.
[elogind.git] / src / login / inhibit.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <fcntl.h>
4 #include <getopt.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8
9 #include "sd-bus.h"
10
11 #include "alloc-util.h"
12 #include "bus-error.h"
13 #include "bus-util.h"
14 #include "fd-util.h"
15 #include "format-util.h"
16 #include "pager.h"
17 #include "process-util.h"
18 #include "signal-util.h"
19 #include "strv.h"
20 #include "user-util.h"
21 #include "util.h"
22
23 /// Additional includes needed by elogind
24 #include "musl_missing.h"
25
26 static const char* arg_what = "idle:sleep:shutdown";
27 static const char* arg_who = NULL;
28 static const char* arg_why = "Unknown reason";
29 static const char* arg_mode = NULL;
30 static bool arg_no_pager = false;
31
32 static enum {
33         ACTION_INHIBIT,
34         ACTION_LIST
35 } arg_action = ACTION_INHIBIT;
36
37 static int inhibit(sd_bus *bus, sd_bus_error *error) {
38         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
39         int r;
40         int fd;
41
42         r = sd_bus_call_method(
43                         bus,
44                         "org.freedesktop.login1",
45                         "/org/freedesktop/login1",
46                         "org.freedesktop.login1.Manager",
47                         "Inhibit",
48                         error,
49                         &reply,
50                         "ssss", arg_what, arg_who, arg_why, arg_mode);
51         if (r < 0)
52                 return r;
53
54         r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd);
55         if (r < 0)
56                 return r;
57
58         r = fcntl(fd, F_DUPFD_CLOEXEC, 3);
59         if (r < 0)
60                 return -errno;
61
62         return r;
63 }
64
65 static int print_inhibitors(sd_bus *bus, sd_bus_error *error) {
66         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
67         const char *what, *who, *why, *mode;
68         unsigned int uid, pid;
69         unsigned n = 0;
70         int r;
71
72         (void) pager_open(arg_no_pager, false);
73
74         r = sd_bus_call_method(
75                         bus,
76                         "org.freedesktop.login1",
77                         "/org/freedesktop/login1",
78                         "org.freedesktop.login1.Manager",
79                         "ListInhibitors",
80                         error,
81                         &reply,
82                         "");
83         if (r < 0)
84                 return r;
85
86         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
87         if (r < 0)
88                 return bus_log_parse_error(r);
89
90         while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
91                 _cleanup_free_ char *comm = NULL, *u = NULL;
92
93                 if (arg_mode && !streq(mode, arg_mode))
94                         continue;
95
96                 get_process_comm(pid, &comm);
97                 u = uid_to_name(uid);
98
99                 printf("     Who: %s (UID "UID_FMT"/%s, PID "PID_FMT"/%s)\n"
100                        "    What: %s\n"
101                        "     Why: %s\n"
102                        "    Mode: %s\n\n",
103                        who, uid, strna(u), pid, strna(comm),
104                        what,
105                        why,
106                        mode);
107
108                 n++;
109         }
110         if (r < 0)
111                 return bus_log_parse_error(r);
112
113         r = sd_bus_message_exit_container(reply);
114         if (r < 0)
115                 return bus_log_parse_error(r);
116
117         printf("%u inhibitors listed.\n", n);
118         return 0;
119 }
120
121 static void help(void) {
122         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
123                "Execute a process while inhibiting shutdown/sleep/idle.\n\n"
124                "  -h --help               Show this help\n"
125                "     --version            Show package version\n"
126                "     --no-pager           Do not pipe output into a pager\n"
127                "     --what=WHAT          Operations to inhibit, colon separated list of:\n"
128                "                          shutdown, sleep, idle, handle-power-key,\n"
129                "                          handle-suspend-key, handle-hibernate-key,\n"
130                "                          handle-lid-switch\n"
131                "     --who=STRING         A descriptive string who is inhibiting\n"
132                "     --why=STRING         A descriptive string why is being inhibited\n"
133                "     --mode=MODE          One of block or delay\n"
134                "     --list               List active inhibitors\n"
135                , program_invocation_short_name);
136 }
137
138 static int parse_argv(int argc, char *argv[]) {
139
140         enum {
141                 ARG_VERSION = 0x100,
142                 ARG_WHAT,
143                 ARG_WHO,
144                 ARG_WHY,
145                 ARG_MODE,
146                 ARG_LIST,
147                 ARG_NO_PAGER,
148         };
149
150         static const struct option options[] = {
151                 { "help",         no_argument,       NULL, 'h'              },
152                 { "version",      no_argument,       NULL, ARG_VERSION      },
153                 { "what",         required_argument, NULL, ARG_WHAT         },
154                 { "who",          required_argument, NULL, ARG_WHO          },
155                 { "why",          required_argument, NULL, ARG_WHY          },
156                 { "mode",         required_argument, NULL, ARG_MODE         },
157                 { "list",         no_argument,       NULL, ARG_LIST         },
158                 { "no-pager",     no_argument,       NULL, ARG_NO_PAGER     },
159                 {}
160         };
161
162         int c;
163
164         assert(argc >= 0);
165         assert(argv);
166
167         while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0)
168
169                 switch (c) {
170
171                 case 'h':
172                         help();
173                         return 0;
174
175                 case ARG_VERSION:
176                         return version();
177
178                 case ARG_WHAT:
179                         arg_what = optarg;
180                         break;
181
182                 case ARG_WHO:
183                         arg_who = optarg;
184                         break;
185
186                 case ARG_WHY:
187                         arg_why = optarg;
188                         break;
189
190                 case ARG_MODE:
191                         arg_mode = optarg;
192                         break;
193
194                 case ARG_LIST:
195                         arg_action = ACTION_LIST;
196                         break;
197
198                 case ARG_NO_PAGER:
199                         arg_no_pager = true;
200                         break;
201
202                 case '?':
203                         return -EINVAL;
204
205                 default:
206                         assert_not_reached("Unhandled option");
207                 }
208
209         if (arg_action == ACTION_INHIBIT && optind == argc)
210                 arg_action = ACTION_LIST;
211
212         else if (arg_action == ACTION_INHIBIT && optind >= argc) {
213                 log_error("Missing command line to execute.");
214                 return -EINVAL;
215         }
216
217         return 1;
218 }
219
220 int main(int argc, char *argv[]) {
221         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
222         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
223         int r;
224
225         elogind_set_program_name(argv[0]);
226         log_parse_environment();
227         log_open();
228
229         r = parse_argv(argc, argv);
230         if (r < 0)
231                 return EXIT_FAILURE;
232         if (r == 0)
233                 return EXIT_SUCCESS;
234
235         r = sd_bus_default_system(&bus);
236         if (r < 0) {
237                 log_error_errno(r, "Failed to connect to bus: %m");
238                 return EXIT_FAILURE;
239         }
240
241         if (arg_action == ACTION_LIST) {
242
243                 r = print_inhibitors(bus, &error);
244                 pager_close();
245                 if (r < 0) {
246                         log_error("Failed to list inhibitors: %s", bus_error_message(&error, -r));
247                         return EXIT_FAILURE;
248                 }
249
250         } else {
251                 _cleanup_close_ int fd = -1;
252                 _cleanup_free_ char *w = NULL;
253                 pid_t pid;
254
255                 /* Ignore SIGINT and allow the forked process to receive it */
256                 (void) ignore_signals(SIGINT, -1);
257
258                 if (!arg_who)
259                         arg_who = w = strv_join(argv + optind, " ");
260
261                 if (!arg_mode)
262                         arg_mode = "block";
263
264                 fd = inhibit(bus, &error);
265                 if (fd < 0) {
266                         log_error("Failed to inhibit: %s", bus_error_message(&error, fd));
267                         return EXIT_FAILURE;
268                 }
269
270                 r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
271                 if (r < 0)
272                         return EXIT_FAILURE;
273                 if (r == 0) {
274                         /* Child */
275                         execvp(argv[optind], argv + optind);
276                         log_open();
277                         log_error_errno(errno, "Failed to execute %s: %m", argv[optind]);
278                         _exit(EXIT_FAILURE);
279                 }
280
281                 r = wait_for_terminate_and_check(argv[optind], pid, WAIT_LOG);
282                 return r < 0 ? EXIT_FAILURE : r;
283         }
284
285         return EXIT_SUCCESS;
286 }