]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/inhibit.c
Merge pull request #1934 from martinpitt/master
[thirdparty/systemd.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 static const char* arg_what = "idle:sleep:shutdown";
42 static const char* arg_who = NULL;
43 static const char* arg_why = "Unknown reason";
44 static const char* arg_mode = NULL;
45
46 static enum {
47 ACTION_INHIBIT,
48 ACTION_LIST
49 } arg_action = ACTION_INHIBIT;
50
51 static int inhibit(sd_bus *bus, sd_bus_error *error) {
52 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
53 int r;
54 int fd;
55
56 r = sd_bus_call_method(
57 bus,
58 "org.freedesktop.login1",
59 "/org/freedesktop/login1",
60 "org.freedesktop.login1.Manager",
61 "Inhibit",
62 error,
63 &reply,
64 "ssss", arg_what, arg_who, arg_why, arg_mode);
65 if (r < 0)
66 return r;
67
68 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd);
69 if (r < 0)
70 return r;
71
72 r = fcntl(fd, F_DUPFD_CLOEXEC, 3);
73 if (r < 0)
74 return -errno;
75
76 return r;
77 }
78
79 static int print_inhibitors(sd_bus *bus, sd_bus_error *error) {
80 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
81 const char *what, *who, *why, *mode;
82 unsigned int uid, pid;
83 unsigned n = 0;
84 int r;
85
86 r = sd_bus_call_method(
87 bus,
88 "org.freedesktop.login1",
89 "/org/freedesktop/login1",
90 "org.freedesktop.login1.Manager",
91 "ListInhibitors",
92 error,
93 &reply,
94 "");
95 if (r < 0)
96 return r;
97
98 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
99 if (r < 0)
100 return bus_log_parse_error(r);
101
102 while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
103 _cleanup_free_ char *comm = NULL, *u = NULL;
104
105 if (arg_mode && !streq(mode, arg_mode))
106 continue;
107
108 get_process_comm(pid, &comm);
109 u = uid_to_name(uid);
110
111 printf(" Who: %s (UID "UID_FMT"/%s, PID "PID_FMT"/%s)\n"
112 " What: %s\n"
113 " Why: %s\n"
114 " Mode: %s\n\n",
115 who, uid, strna(u), pid, strna(comm),
116 what,
117 why,
118 mode);
119
120 n++;
121 }
122 if (r < 0)
123 return bus_log_parse_error(r);
124
125 r = sd_bus_message_exit_container(reply);
126 if (r < 0)
127 return bus_log_parse_error(r);
128
129 printf("%u inhibitors listed.\n", n);
130 return 0;
131 }
132
133 static void help(void) {
134 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
135 "Execute a process while inhibiting shutdown/sleep/idle.\n\n"
136 " -h --help Show this help\n"
137 " --version Show package version\n"
138 " --what=WHAT Operations to inhibit, colon separated list of:\n"
139 " shutdown, sleep, idle, handle-power-key,\n"
140 " handle-suspend-key, handle-hibernate-key,\n"
141 " handle-lid-switch\n"
142 " --who=STRING A descriptive string who is inhibiting\n"
143 " --why=STRING A descriptive string why is being inhibited\n"
144 " --mode=MODE One of block or delay\n"
145 " --list List active inhibitors\n"
146 , program_invocation_short_name);
147 }
148
149 static int parse_argv(int argc, char *argv[]) {
150
151 enum {
152 ARG_VERSION = 0x100,
153 ARG_WHAT,
154 ARG_WHO,
155 ARG_WHY,
156 ARG_MODE,
157 ARG_LIST,
158 };
159
160 static const struct option options[] = {
161 { "help", no_argument, NULL, 'h' },
162 { "version", no_argument, NULL, ARG_VERSION },
163 { "what", required_argument, NULL, ARG_WHAT },
164 { "who", required_argument, NULL, ARG_WHO },
165 { "why", required_argument, NULL, ARG_WHY },
166 { "mode", required_argument, NULL, ARG_MODE },
167 { "list", no_argument, NULL, ARG_LIST },
168 {}
169 };
170
171 int c;
172
173 assert(argc >= 0);
174 assert(argv);
175
176 while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0)
177
178 switch (c) {
179
180 case 'h':
181 help();
182 return 0;
183
184 case ARG_VERSION:
185 return version();
186
187 case ARG_WHAT:
188 arg_what = optarg;
189 break;
190
191 case ARG_WHO:
192 arg_who = optarg;
193 break;
194
195 case ARG_WHY:
196 arg_why = optarg;
197 break;
198
199 case ARG_MODE:
200 arg_mode = optarg;
201 break;
202
203 case ARG_LIST:
204 arg_action = ACTION_LIST;
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 log_parse_environment();
231 log_open();
232
233 r = parse_argv(argc, argv);
234 if (r < 0)
235 return EXIT_FAILURE;
236 if (r == 0)
237 return EXIT_SUCCESS;
238
239 r = sd_bus_default_system(&bus);
240 if (r < 0) {
241 log_error_errno(r, "Failed to connect to bus: %m");
242 return EXIT_FAILURE;
243 }
244
245 if (arg_action == ACTION_LIST) {
246
247 r = print_inhibitors(bus, &error);
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 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 pid = fork();
271 if (pid < 0) {
272 log_error_errno(errno, "Failed to fork: %m");
273 return EXIT_FAILURE;
274 }
275
276 if (pid == 0) {
277 /* Child */
278
279 (void) reset_all_signal_handlers();
280 (void) reset_signal_mask();
281
282 close_all_fds(NULL, 0);
283
284 execvp(argv[optind], argv + optind);
285 log_error_errno(errno, "Failed to execute %s: %m", argv[optind]);
286 _exit(EXIT_FAILURE);
287 }
288
289 r = wait_for_terminate_and_warn(argv[optind], pid, true);
290 return r < 0 ? EXIT_FAILURE : r;
291 }
292
293 return 0;
294 }