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