]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/inhibit.c
move _cleanup_ attribute in front of the type
[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 dbus_uint32_t uid, pid;
100
101 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
102 return -EIO;
103
104 dbus_message_iter_recurse(&sub, &sub2);
105
106 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 ||
107 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 ||
108 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 ||
109 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 ||
110 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
111 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0)
112 return -EIO;
113
114 printf(" Who: %s (UID %lu, PID %lu)\n"
115 " What: %s\n"
116 " Why: %s\n"
117 " Mode: %s\n\n",
118 who, (unsigned long) uid, (unsigned long) pid,
119 what,
120 why,
121 mode);
122
123 dbus_message_iter_next(&sub);
124
125 n++;
126 }
127
128 printf("%u inhibitors listed.\n", n);
129 return 0;
130 }
131
132 static int help(void) {
133
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 return 0;
149 }
150
151 static int parse_argv(int argc, char *argv[]) {
152
153 enum {
154 ARG_VERSION = 0x100,
155 ARG_WHAT,
156 ARG_WHO,
157 ARG_WHY,
158 ARG_MODE,
159 ARG_LIST,
160 };
161
162 static const struct option options[] = {
163 { "help", no_argument, NULL, 'h' },
164 { "version", no_argument, NULL, ARG_VERSION },
165 { "what", required_argument, NULL, ARG_WHAT },
166 { "who", required_argument, NULL, ARG_WHO },
167 { "why", required_argument, NULL, ARG_WHY },
168 { "mode", required_argument, NULL, ARG_MODE },
169 { "list", no_argument, NULL, ARG_LIST },
170 { NULL, 0, NULL, 0 }
171 };
172
173 int c;
174
175 assert(argc >= 0);
176 assert(argv);
177
178 while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) {
179
180 switch (c) {
181
182 case 'h':
183 help();
184 return 0;
185
186 case ARG_VERSION:
187 puts(PACKAGE_STRING);
188 puts(SYSTEMD_FEATURES);
189 return 0;
190
191 case ARG_WHAT:
192 arg_what = optarg;
193 break;
194
195 case ARG_WHO:
196 arg_who = optarg;
197 break;
198
199 case ARG_WHY:
200 arg_why = optarg;
201 break;
202
203 case ARG_MODE:
204 arg_mode = optarg;
205 break;
206
207 case ARG_LIST:
208 arg_action = ACTION_LIST;
209 break;
210
211 default:
212 log_error("Unknown option code %c", c);
213 return -EINVAL;
214 }
215 }
216
217 if (arg_action == ACTION_INHIBIT && argc == 1)
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 int r, exit_code = 0;
230 DBusConnection *bus = NULL;
231 DBusError error;
232 _cleanup_close_ int fd = -1;
233
234 dbus_error_init(&error);
235
236 log_parse_environment();
237 log_open();
238
239 r = parse_argv(argc, argv);
240 if (r <= 0)
241 goto finish;
242
243 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
244 if (!bus) {
245 log_error("Failed to connect to bus: %s", bus_error_message(&error));
246 r = -EIO;
247 goto finish;
248 }
249
250 if (arg_action == ACTION_LIST) {
251
252 r = print_inhibitors(bus, &error);
253 if (r < 0) {
254 log_error("Failed to list inhibitors: %s", bus_error(&error, r));
255 goto finish;
256 }
257
258 } else {
259 char *w = NULL;
260 pid_t pid;
261
262 if (!arg_who)
263 arg_who = w = strv_join(argv + optind, " ");
264
265 fd = inhibit(bus, &error);
266 free(w);
267
268 if (fd < 0) {
269 log_error("Failed to inhibit: %s", bus_error(&error, r));
270 r = fd;
271 goto finish;
272 }
273
274 pid = fork();
275 if (pid < 0) {
276 log_error("Failed to fork: %m");
277 r = -errno;
278 goto finish;
279 }
280
281 if (pid == 0) {
282 /* Child */
283
284 close_nointr_nofail(fd);
285 close_all_fds(NULL, 0);
286
287 execvp(argv[optind], argv + optind);
288 log_error("Failed to execute %s: %m", argv[optind]);
289 _exit(EXIT_FAILURE);
290 }
291
292 r = wait_for_terminate_and_warn(argv[optind], pid);
293 if (r >= 0)
294 exit_code = r;
295 }
296
297 finish:
298 if (bus) {
299 dbus_connection_close(bus);
300 dbus_connection_unref(bus);
301 }
302
303 dbus_error_free(&error);
304
305 return r < 0 ? EXIT_FAILURE : exit_code;
306 }