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