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