chiark / gitweb /
Prep v228: Add remaining updates from upstream (3/3)
[elogind.git] / src / login / inhibit.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <fcntl.h>
23 #include <getopt.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27
28 #include "sd-bus.h"
29
30 #include "alloc-util.h"
31 #include "bus-error.h"
32 #include "bus-util.h"
33 #include "fd-util.h"
34 #include "formats-util.h"
35 #include "process-util.h"
36 #include "signal-util.h"
37 #include "strv.h"
38 #include "user-util.h"
39 #include "util.h"
40
41 /// Additional includes needed by elogind
42 #include "musl_missing.h"
43
44 static const char* arg_what = "idle:sleep:shutdown";
45 static const char* arg_who = NULL;
46 static const char* arg_why = "Unknown reason";
47 static const char* arg_mode = NULL;
48
49 static enum {
50         ACTION_INHIBIT,
51         ACTION_LIST
52 } arg_action = ACTION_INHIBIT;
53
54 static int inhibit(sd_bus *bus, sd_bus_error *error) {
55         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
56         int r;
57         int fd;
58
59         r = sd_bus_call_method(
60                         bus,
61                         "org.freedesktop.login1",
62                         "/org/freedesktop/login1",
63                         "org.freedesktop.login1.Manager",
64                         "Inhibit",
65                         error,
66                         &reply,
67                         "ssss", arg_what, arg_who, arg_why, arg_mode);
68         if (r < 0)
69                 return r;
70
71         r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd);
72         if (r < 0)
73                 return r;
74
75         r = fcntl(fd, F_DUPFD_CLOEXEC, 3);
76         if (r < 0)
77                 return -errno;
78
79         return r;
80 }
81
82 static int print_inhibitors(sd_bus *bus, sd_bus_error *error) {
83         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
84         const char *what, *who, *why, *mode;
85         unsigned int uid, pid;
86         unsigned n = 0;
87         int r;
88
89         r = sd_bus_call_method(
90                         bus,
91                         "org.freedesktop.login1",
92                         "/org/freedesktop/login1",
93                         "org.freedesktop.login1.Manager",
94                         "ListInhibitors",
95                         error,
96                         &reply,
97                         "");
98         if (r < 0)
99                 return r;
100
101         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
102         if (r < 0)
103                 return bus_log_parse_error(r);
104
105         while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
106                 _cleanup_free_ char *comm = NULL, *u = NULL;
107
108                 if (arg_mode && !streq(mode, arg_mode))
109                         continue;
110
111                 get_process_comm(pid, &comm);
112                 u = uid_to_name(uid);
113
114                 printf("     Who: %s (UID "UID_FMT"/%s, PID "PID_FMT"/%s)\n"
115                        "    What: %s\n"
116                        "     Why: %s\n"
117                        "    Mode: %s\n\n",
118                        who, uid, strna(u), pid, strna(comm),
119                        what,
120                        why,
121                        mode);
122
123                 n++;
124         }
125         if (r < 0)
126                 return bus_log_parse_error(r);
127
128         r = sd_bus_message_exit_container(reply);
129         if (r < 0)
130                 return bus_log_parse_error(r);
131
132         printf("%u inhibitors listed.\n", n);
133         return 0;
134 }
135
136 static void help(void) {
137         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
138                "Execute a process while inhibiting shutdown/sleep/idle.\n\n"
139                "  -h --help               Show this help\n"
140                "     --version            Show package version\n"
141                "     --what=WHAT          Operations to inhibit, colon separated list of:\n"
142                "                          shutdown, sleep, idle, handle-power-key,\n"
143                "                          handle-suspend-key, handle-hibernate-key,\n"
144                "                          handle-lid-switch\n"
145                "     --who=STRING         A descriptive string who is inhibiting\n"
146                "     --why=STRING         A descriptive string why is being inhibited\n"
147                "     --mode=MODE          One of block or delay\n"
148                "     --list               List active inhibitors\n"
149                , program_invocation_short_name);
150 }
151
152 static int parse_argv(int argc, char *argv[]) {
153
154         enum {
155                 ARG_VERSION = 0x100,
156                 ARG_WHAT,
157                 ARG_WHO,
158                 ARG_WHY,
159                 ARG_MODE,
160                 ARG_LIST,
161         };
162
163         static const struct option options[] = {
164                 { "help",         no_argument,       NULL, 'h'              },
165                 { "version",      no_argument,       NULL, ARG_VERSION      },
166                 { "what",         required_argument, NULL, ARG_WHAT         },
167                 { "who",          required_argument, NULL, ARG_WHO          },
168                 { "why",          required_argument, NULL, ARG_WHY          },
169                 { "mode",         required_argument, NULL, ARG_MODE         },
170                 { "list",         no_argument,       NULL, ARG_LIST         },
171                 {}
172         };
173
174         int c;
175
176         assert(argc >= 0);
177         assert(argv);
178
179         while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0)
180
181                 switch (c) {
182
183                 case 'h':
184                         help();
185                         return 0;
186
187                 case ARG_VERSION:
188                         return version();
189
190                 case ARG_WHAT:
191                         arg_what = optarg;
192                         break;
193
194                 case ARG_WHO:
195                         arg_who = optarg;
196                         break;
197
198                 case ARG_WHY:
199                         arg_why = optarg;
200                         break;
201
202                 case ARG_MODE:
203                         arg_mode = optarg;
204                         break;
205
206                 case ARG_LIST:
207                         arg_action = ACTION_LIST;
208                         break;
209
210                 case '?':
211                         return -EINVAL;
212
213                 default:
214                         assert_not_reached("Unhandled option");
215                 }
216
217         if (arg_action == ACTION_INHIBIT && optind == argc)
218                 arg_action = ACTION_LIST;
219
220         else if (arg_action == ACTION_INHIBIT && optind >= argc) {
221                 log_error("Missing command line to execute.");
222                 return -EINVAL;
223         }
224
225         return 1;
226 }
227
228 int main(int argc, char *argv[]) {
229         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
230         _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
231         int r;
232
233         elogind_set_program_name(argv[0]);
234         log_parse_environment();
235         log_open();
236
237         r = parse_argv(argc, argv);
238         if (r < 0)
239                 return EXIT_FAILURE;
240         if (r == 0)
241                 return EXIT_SUCCESS;
242
243         r = sd_bus_default_system(&bus);
244         if (r < 0) {
245                 log_error_errno(r, "Failed to connect to bus: %m");
246                 return EXIT_FAILURE;
247         }
248
249         if (arg_action == ACTION_LIST) {
250
251                 r = print_inhibitors(bus, &error);
252                 if (r < 0) {
253                         log_error("Failed to list inhibitors: %s", bus_error_message(&error, -r));
254                         return EXIT_FAILURE;
255                 }
256
257         } else {
258                 _cleanup_close_ int fd = -1;
259                 _cleanup_free_ char *w = NULL;
260                 pid_t pid;
261
262                 if (!arg_who)
263                         arg_who = w = strv_join(argv + optind, " ");
264
265                 if (!arg_mode)
266                         arg_mode = "block";
267
268                 fd = inhibit(bus, &error);
269                 if (fd < 0) {
270                         log_error("Failed to inhibit: %s", bus_error_message(&error, fd));
271                         return EXIT_FAILURE;
272                 }
273
274                 pid = fork();
275                 if (pid < 0) {
276                         log_error_errno(errno, "Failed to fork: %m");
277                         return EXIT_FAILURE;
278                 }
279
280                 if (pid == 0) {
281                         /* Child */
282
283                         (void) reset_all_signal_handlers();
284                         (void) reset_signal_mask();
285
286                         close_all_fds(NULL, 0);
287
288                         execvp(argv[optind], argv + optind);
289                         log_error_errno(errno, "Failed to execute %s: %m", argv[optind]);
290                         _exit(EXIT_FAILURE);
291                 }
292
293                 r = wait_for_terminate_and_warn(argv[optind], pid, true);
294                 return r < 0 ? EXIT_FAILURE : r;
295         }
296
297         return 0;
298 }