chiark / gitweb /
logind: rename "seat-master" tag to "master-of-seat"
[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(SYSTEMD_FEATURES);
214                         return 0;
215
216                 case ARG_WHAT:
217                         arg_what = optarg;
218                         break;
219
220                 case ARG_WHO:
221                         arg_who = optarg;
222                         break;
223
224                 case ARG_WHY:
225                         arg_why = optarg;
226                         break;
227
228                 case ARG_MODE:
229                         arg_mode = optarg;
230                         break;
231
232                 case ARG_LIST:
233                         arg_action = ACTION_LIST;
234                         break;
235
236                 default:
237                         log_error("Unknown option code %c", c);
238                         return -EINVAL;
239                 }
240         }
241
242         if (arg_action == ACTION_INHIBIT && optind >= argc) {
243                 log_error("Missing command line to execute.");
244                 return -EINVAL;
245         }
246
247         return 1;
248 }
249
250 int main(int argc, char *argv[]) {
251         int r, exit_code = 0;
252         DBusConnection *bus = NULL;
253         DBusError error;
254         int fd = -1;
255
256         dbus_error_init(&error);
257
258         log_parse_environment();
259         log_open();
260
261         r = parse_argv(argc, argv);
262         if (r <= 0)
263                 goto finish;
264
265         bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
266         if (!bus) {
267                 log_error("Failed to connect to bus: %s", bus_error_message(&error));
268                 r = -EIO;
269                 goto finish;
270         }
271
272         if (arg_action == ACTION_LIST) {
273
274                 r = print_inhibitors(bus, &error);
275                 if (r < 0) {
276                         log_error("Failed to list inhibitors: %s", bus_error_message_or_strerror(&error, -r));
277                         goto finish;
278                 }
279
280         } else {
281                 char *w = NULL;
282                 pid_t pid;
283
284                 if (!arg_who)
285                         arg_who = w = strv_join(argv + optind, " ");
286
287                 fd = inhibit(bus, &error);
288                 free(w);
289
290                 if (fd < 0) {
291                         log_error("Failed to inhibit: %s", bus_error_message_or_strerror(&error, -r));
292                         r = fd;
293                         goto finish;
294                 }
295
296                 pid = fork();
297                 if (pid < 0) {
298                         log_error("Failed to fork: %m");
299                         r = -errno;
300                         goto finish;
301                 }
302
303                 if (pid == 0) {
304                         /* Child */
305
306                         close_nointr_nofail(fd);
307                         close_all_fds(NULL, 0);
308
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 }