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