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