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