chiark / gitweb /
a817c84b3605b7e1d3a3379a0bbf03692a700310
[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 <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:suspend: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         DBusMessage *m = NULL, *reply = NULL;
46         int fd;
47
48         assert(bus);
49
50         m = dbus_message_new_method_call(
51                         "org.freedesktop.login1",
52                         "/org/freedesktop/login1",
53                         "org.freedesktop.login1.Manager",
54                         "Inhibit");
55         if (!m)
56                 return -ENOMEM;
57
58         if (!dbus_message_append_args(m,
59                                       DBUS_TYPE_STRING, &arg_what,
60                                       DBUS_TYPE_STRING, &arg_who,
61                                       DBUS_TYPE_STRING, &arg_why,
62                                       DBUS_TYPE_STRING, &arg_mode,
63                                       DBUS_TYPE_INVALID)) {
64                 fd = -ENOMEM;
65                 goto finish;
66         }
67
68         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
69         if (!reply) {
70                 fd = -EIO;
71                 goto finish;
72         }
73
74         if (!dbus_message_get_args(reply, error,
75                                    DBUS_TYPE_UNIX_FD, &fd,
76                                    DBUS_TYPE_INVALID)){
77                 fd = -EIO;
78                 goto finish;
79         }
80
81 finish:
82         if (m)
83                 dbus_message_unref(m);
84
85         if (reply)
86                 dbus_message_unref(reply);
87
88         return fd;
89 }
90
91 static int print_inhibitors(DBusConnection *bus, DBusError *error) {
92         DBusMessage *m, *reply;
93         unsigned n = 0;
94         DBusMessageIter iter, sub, sub2;
95         int r;
96
97         assert(bus);
98
99         m = dbus_message_new_method_call(
100                         "org.freedesktop.login1",
101                         "/org/freedesktop/login1",
102                         "org.freedesktop.login1.Manager",
103                         "ListInhibitors");
104         if (!m)
105                 return -ENOMEM;
106
107         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
108         if (!reply) {
109                 r = -EIO;
110                 goto finish;
111         }
112
113         if (!dbus_message_iter_init(reply, &iter)) {
114                 r = -ENOMEM;
115                 goto finish;
116         }
117
118         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
119                 r = -EIO;
120                 goto finish;
121         }
122         dbus_message_iter_recurse(&iter, &sub);
123
124         printf("%-21s %-20s %-20s %-5s %6s %6s\n",
125                "WHAT",
126                "WHO",
127                "WHY",
128                "MODE",
129                "UID",
130                "PID");
131
132
133         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
134                 const char *what, *who, *why, *mode;
135                 char *ewho, *ewhy;
136                 dbus_uint32_t uid, pid;
137
138                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
139                         r = -EIO;
140                         goto finish;
141                 }
142
143                 dbus_message_iter_recurse(&sub, &sub2);
144
145                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 ||
146                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 ||
147                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 ||
148                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 ||
149                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
150                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0) {
151                         r = -EIO;
152                         goto finish;
153                 }
154
155                 ewho = ellipsize(who, 20, 66);
156                 ewhy = ellipsize(why, 20, 66);
157
158                 printf("%-21s %-20s %-20s %-5s %6lu %6lu\n",
159                        what, ewho ? ewho : who, ewhy ? ewhy : why, mode, (unsigned long) uid, (unsigned long) pid);
160
161                 free(ewho);
162                 free(ewhy);
163
164                 dbus_message_iter_next(&sub);
165
166                 n++;
167         }
168
169         printf("\n%u inhibitors listed.\n", n);
170         r = 0;
171
172 finish:
173         if (m)
174                 dbus_message_unref(m);
175
176         if (reply)
177                 dbus_message_unref(reply);
178
179         return r;
180 }
181
182 static int help(void) {
183
184         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
185                "Execute a process while inhibiting shutdown/suspend/idle.\n\n"
186                "  -h --help               Show this help\n"
187                "     --version            Show package version\n"
188                "     --what=WHAT          Operations to inhibit, colon separated list of idle,\n"
189                "                          suspend, shutdown\n"
190                "     --who=STRING         A descriptive string who is inhibiting\n"
191                "     --why=STRING         A descriptive string why is being inhibited\n"
192                "     --mode=MODE          One of block or delay\n"
193                "     --list               List active inhibitors\n",
194                program_invocation_short_name);
195
196         return 0;
197 }
198
199 static int parse_argv(int argc, char *argv[]) {
200
201         enum {
202                 ARG_VERSION = 0x100,
203                 ARG_WHAT,
204                 ARG_WHO,
205                 ARG_WHY,
206                 ARG_MODE,
207                 ARG_LIST,
208         };
209
210         static const struct option options[] = {
211                 { "help",         no_argument,       NULL, 'h'              },
212                 { "version",      no_argument,       NULL, ARG_VERSION      },
213                 { "what",         required_argument, NULL, ARG_WHAT         },
214                 { "who",          required_argument, NULL, ARG_WHO          },
215                 { "why",          required_argument, NULL, ARG_WHY          },
216                 { "mode",         required_argument, NULL, ARG_MODE         },
217                 { "list",         no_argument,       NULL, ARG_LIST         },
218                 { NULL,           0,                 NULL, 0                }
219         };
220
221         int c;
222
223         assert(argc >= 0);
224         assert(argv);
225
226         while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) {
227
228                 switch (c) {
229
230                 case 'h':
231                         help();
232                         return 0;
233
234                 case ARG_VERSION:
235                         puts(PACKAGE_STRING);
236                         puts(DISTRIBUTION);
237                         puts(SYSTEMD_FEATURES);
238                         return 0;
239
240                 case ARG_WHAT:
241                         arg_what = optarg;
242                         break;
243
244                 case ARG_WHO:
245                         arg_who = optarg;
246                         break;
247
248                 case ARG_WHY:
249                         arg_why = optarg;
250                         break;
251
252                 case ARG_MODE:
253                         arg_mode = optarg;
254                         break;
255
256                 case ARG_LIST:
257                         arg_action = ACTION_LIST;
258                         break;
259
260                 default:
261                         log_error("Unknown option code %c", c);
262                         return -EINVAL;
263                 }
264         }
265
266         if (arg_action == ACTION_INHIBIT && optind >= argc) {
267                 log_error("Missing command line to execute.");
268                 return -EINVAL;
269         }
270
271         return 1;
272 }
273
274 int main(int argc, char *argv[]) {
275         int r, exit_code = 0;
276         DBusConnection *bus = NULL;
277         DBusError error;
278         int fd = -1;
279
280         dbus_error_init(&error);
281
282         log_parse_environment();
283         log_open();
284
285         r = parse_argv(argc, argv);
286         if (r <= 0)
287                 goto finish;
288
289         bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
290         if (!bus) {
291                 log_error("Failed to connect to bus: %s", bus_error_message(&error));
292                 r = -EIO;
293                 goto finish;
294         }
295
296         if (arg_action == ACTION_LIST) {
297
298                 r = print_inhibitors(bus, &error);
299                 if (r < 0) {
300                         log_error("Failed to list inhibitors: %s", bus_error_message_or_strerror(&error, -r));
301                         goto finish;
302                 }
303
304         } else {
305                 char *w = NULL;
306                 pid_t pid;
307
308                 if (!arg_who)
309                         arg_who = w = strv_join(argv + optind, " ");
310
311                 fd = inhibit(bus, &error);
312                 free(w);
313
314                 if (fd < 0) {
315                         log_error("Failed to inhibit: %s", bus_error_message_or_strerror(&error, -r));
316                         r = fd;
317                         goto finish;
318                 }
319
320                 pid = fork();
321                 if (pid < 0) {
322                         log_error("Failed to fork: %m");
323                         r = -errno;
324                         goto finish;
325                 }
326
327                 if (pid == 0) {
328                         /* Child */
329
330                         close_nointr_nofail(fd);
331                         execvp(argv[optind], argv + optind);
332                         log_error("Failed to execute %s: %m", argv[optind]);
333                         _exit(EXIT_FAILURE);
334                 }
335
336                 r = wait_for_terminate_and_warn(argv[optind], pid);
337                 if (r >= 0)
338                         exit_code = r;
339         }
340
341 finish:
342         if (bus) {
343                 dbus_connection_close(bus);
344                 dbus_connection_unref(bus);
345         }
346
347         dbus_error_free(&error);
348
349         if (fd >= 0)
350                 close_nointr_nofail(fd);
351
352         return r < 0 ? EXIT_FAILURE : exit_code;
353 }