chiark / gitweb /
journalctl: add --verify-seed= switch to specify seed value
[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                 return -ENOMEM;
91                 goto finish;
92
93         if (!dbus_message_iter_init(reply, &iter)) {
94                 r = -ENOMEM;
95                 goto finish;
96         }
97
98         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
99                 r = -EIO;
100                 goto finish;
101         }
102         dbus_message_iter_recurse(&iter, &sub);
103
104         printf("%-21s %-20s %-20s %-5s %6s %6s\n",
105                "WHAT",
106                "WHO",
107                "WHY",
108                "MODE",
109                "UID",
110                "PID");
111
112
113         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
114                 const char *what, *who, *why, *mode;
115                 char *ewho, *ewhy;
116                 dbus_uint32_t uid, pid;
117
118                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
119                         r = -EIO;
120                         goto finish;
121                 }
122
123                 dbus_message_iter_recurse(&sub, &sub2);
124
125                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 ||
126                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 ||
127                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 ||
128                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 ||
129                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
130                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0) {
131                         r = -EIO;
132                         goto finish;
133                 }
134
135                 ewho = ellipsize(who, 20, 66);
136                 ewhy = ellipsize(why, 20, 66);
137
138                 printf("%-21s %-20s %-20s %-5s %6lu %6lu\n",
139                        what, ewho ? ewho : who, ewhy ? ewhy : why, mode, (unsigned long) uid, (unsigned long) pid);
140
141                 free(ewho);
142                 free(ewhy);
143
144                 dbus_message_iter_next(&sub);
145
146                 n++;
147         }
148
149         printf("\n%u inhibitors listed.\n", n);
150         r = 0;
151
152 finish:
153         if (reply)
154                 dbus_message_unref(reply);
155
156         return r;
157 }
158
159 static int help(void) {
160
161         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
162                "Execute a process while inhibiting shutdown/sleep/idle.\n\n"
163                "  -h --help               Show this help\n"
164                "     --version            Show package version\n"
165                "     --what=WHAT          Operations to inhibit, colon separated list of idle,\n"
166                "                          sleep, shutdown\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                         execvp(argv[optind], argv + optind);
309                         log_error("Failed to execute %s: %m", argv[optind]);
310                         _exit(EXIT_FAILURE);
311                 }
312
313                 r = wait_for_terminate_and_warn(argv[optind], pid);
314                 if (r >= 0)
315                         exit_code = r;
316         }
317
318 finish:
319         if (bus) {
320                 dbus_connection_close(bus);
321                 dbus_connection_unref(bus);
322         }
323
324         dbus_error_free(&error);
325
326         if (fd >= 0)
327                 close_nointr_nofail(fd);
328
329         return r < 0 ? EXIT_FAILURE : exit_code;
330 }