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