]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/inhibit.c
core: failed scope units may not be restarted
[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 <getopt.h>
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <dbus.h>
27 #include <unistd.h>
28
29 #include "dbus-common.h"
30 #include "util.h"
31 #include "build.h"
32 #include "strv.h"
33
34 static const char* arg_what = "idle:sleep:shutdown";
35 static const char* arg_who = NULL;
36 static const char* arg_why = "Unknown reason";
37 static const char* arg_mode = "block";
38
39 static enum {
40 ACTION_INHIBIT,
41 ACTION_LIST
42 } arg_action = ACTION_INHIBIT;
43
44 static int inhibit(DBusConnection *bus, DBusError *error) {
45 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
46 int r;
47
48 r = bus_method_call_with_reply(
49 bus,
50 "org.freedesktop.login1",
51 "/org/freedesktop/login1",
52 "org.freedesktop.login1.Manager",
53 "Inhibit",
54 &reply,
55 NULL,
56 DBUS_TYPE_STRING, &arg_what,
57 DBUS_TYPE_STRING, &arg_who,
58 DBUS_TYPE_STRING, &arg_why,
59 DBUS_TYPE_STRING, &arg_mode,
60 DBUS_TYPE_INVALID);
61 if (r < 0)
62 return r;
63
64 if (!dbus_message_get_args(reply, error,
65 DBUS_TYPE_UNIX_FD, &r,
66 DBUS_TYPE_INVALID))
67 return -EIO;
68
69 return r;
70 }
71
72 static int print_inhibitors(DBusConnection *bus, DBusError *error) {
73 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
74 unsigned n = 0;
75 DBusMessageIter iter, sub, sub2;
76 int r;
77
78 r = bus_method_call_with_reply(
79 bus,
80 "org.freedesktop.login1",
81 "/org/freedesktop/login1",
82 "org.freedesktop.login1.Manager",
83 "ListInhibitors",
84 &reply,
85 NULL,
86 DBUS_TYPE_INVALID);
87 if (r < 0)
88 return r;
89
90 if (!dbus_message_iter_init(reply, &iter))
91 return -ENOMEM;
92
93 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
94 return -EIO;
95
96 dbus_message_iter_recurse(&iter, &sub);
97 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
98 const char *what, *who, *why, *mode;
99 _cleanup_free_ char *comm = NULL, *u = NULL;
100 dbus_uint32_t uid, pid;
101
102 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
103 return -EIO;
104
105 dbus_message_iter_recurse(&sub, &sub2);
106
107 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 ||
108 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 ||
109 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 ||
110 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 ||
111 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
112 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0)
113 return -EIO;
114
115 get_process_comm(pid, &comm);
116 u = uid_to_name(uid);
117
118 printf(" Who: %s (UID %lu/%s, PID %lu/%s)\n"
119 " What: %s\n"
120 " Why: %s\n"
121 " Mode: %s\n\n",
122 who, (unsigned long) uid, strna(u), (unsigned long) pid, strna(comm),
123 what,
124 why,
125 mode);
126
127 dbus_message_iter_next(&sub);
128
129 n++;
130 }
131
132 printf("%u inhibitors listed.\n", n);
133 return 0;
134 }
135
136 static int help(void) {
137
138 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
139 "Execute a process while inhibiting shutdown/sleep/idle.\n\n"
140 " -h --help Show this help\n"
141 " --version Show package version\n"
142 " --what=WHAT Operations to inhibit, colon separated list of:\n"
143 " shutdown, sleep, idle, handle-power-key,\n"
144 " handle-suspend-key, handle-hibernate-key,\n"
145 " handle-lid-switch\n"
146 " --who=STRING A descriptive string who is inhibiting\n"
147 " --why=STRING A descriptive string why is being inhibited\n"
148 " --mode=MODE One of block or delay\n"
149 " --list List active inhibitors\n",
150 program_invocation_short_name);
151
152 return 0;
153 }
154
155 static int parse_argv(int argc, char *argv[]) {
156
157 enum {
158 ARG_VERSION = 0x100,
159 ARG_WHAT,
160 ARG_WHO,
161 ARG_WHY,
162 ARG_MODE,
163 ARG_LIST,
164 };
165
166 static const struct option options[] = {
167 { "help", no_argument, NULL, 'h' },
168 { "version", no_argument, NULL, ARG_VERSION },
169 { "what", required_argument, NULL, ARG_WHAT },
170 { "who", required_argument, NULL, ARG_WHO },
171 { "why", required_argument, NULL, ARG_WHY },
172 { "mode", required_argument, NULL, ARG_MODE },
173 { "list", no_argument, NULL, ARG_LIST },
174 { NULL, 0, NULL, 0 }
175 };
176
177 int c;
178
179 assert(argc >= 0);
180 assert(argv);
181
182 while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) {
183
184 switch (c) {
185
186 case 'h':
187 help();
188 return 0;
189
190 case ARG_VERSION:
191 puts(PACKAGE_STRING);
192 puts(SYSTEMD_FEATURES);
193 return 0;
194
195 case ARG_WHAT:
196 arg_what = optarg;
197 break;
198
199 case ARG_WHO:
200 arg_who = optarg;
201 break;
202
203 case ARG_WHY:
204 arg_why = optarg;
205 break;
206
207 case ARG_MODE:
208 arg_mode = optarg;
209 break;
210
211 case ARG_LIST:
212 arg_action = ACTION_LIST;
213 break;
214
215 default:
216 log_error("Unknown option code %c", c);
217 return -EINVAL;
218 }
219 }
220
221 if (arg_action == ACTION_INHIBIT && argc == 1)
222 arg_action = ACTION_LIST;
223
224 else if (arg_action == ACTION_INHIBIT && optind >= argc) {
225 log_error("Missing command line to execute.");
226 return -EINVAL;
227 }
228
229 return 1;
230 }
231
232 int main(int argc, char *argv[]) {
233 int r, exit_code = 0;
234 DBusConnection *bus = NULL;
235 DBusError error;
236 _cleanup_close_ int fd = -1;
237
238 dbus_error_init(&error);
239
240 log_parse_environment();
241 log_open();
242
243 r = parse_argv(argc, argv);
244 if (r <= 0)
245 goto finish;
246
247 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
248 if (!bus) {
249 log_error("Failed to connect to bus: %s", bus_error_message(&error));
250 r = -EIO;
251 goto finish;
252 }
253
254 if (arg_action == ACTION_LIST) {
255
256 r = print_inhibitors(bus, &error);
257 if (r < 0) {
258 log_error("Failed to list inhibitors: %s", bus_error(&error, r));
259 goto finish;
260 }
261
262 } else {
263 char *w = NULL;
264 pid_t pid;
265
266 if (!arg_who)
267 arg_who = w = strv_join(argv + optind, " ");
268
269 fd = inhibit(bus, &error);
270 free(w);
271
272 if (fd < 0) {
273 log_error("Failed to inhibit: %s", bus_error(&error, r));
274 r = fd;
275 goto finish;
276 }
277
278 pid = fork();
279 if (pid < 0) {
280 log_error("Failed to fork: %m");
281 r = -errno;
282 goto finish;
283 }
284
285 if (pid == 0) {
286 /* Child */
287
288 close_nointr_nofail(fd);
289 close_all_fds(NULL, 0);
290
291 execvp(argv[optind], argv + optind);
292 log_error("Failed to execute %s: %m", argv[optind]);
293 _exit(EXIT_FAILURE);
294 }
295
296 r = wait_for_terminate_and_warn(argv[optind], pid);
297 if (r >= 0)
298 exit_code = r;
299 }
300
301 finish:
302 if (bus) {
303 dbus_connection_close(bus);
304 dbus_connection_unref(bus);
305 }
306
307 dbus_error_free(&error);
308
309 return r < 0 ? EXIT_FAILURE : exit_code;
310 }