chiark / gitweb /
journald: check session owner UID rather then audit ID when splitting up journal...
[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 && optind >= argc) {
234                 log_error("Missing command line to execute.");
235                 return -EINVAL;
236         }
237
238         return 1;
239 }
240
241 int main(int argc, char *argv[]) {
242         int r, exit_code = 0;
243         DBusConnection *bus = NULL;
244         DBusError error;
245         int fd = -1;
246
247         dbus_error_init(&error);
248
249         log_parse_environment();
250         log_open();
251
252         r = parse_argv(argc, argv);
253         if (r <= 0)
254                 goto finish;
255
256         bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
257         if (!bus) {
258                 log_error("Failed to connect to bus: %s", bus_error_message(&error));
259                 r = -EIO;
260                 goto finish;
261         }
262
263         if (arg_action == ACTION_LIST) {
264
265                 r = print_inhibitors(bus, &error);
266                 if (r < 0) {
267                         log_error("Failed to list inhibitors: %s", bus_error_message_or_strerror(&error, -r));
268                         goto finish;
269                 }
270
271         } else {
272                 char *w = NULL;
273                 pid_t pid;
274
275                 if (!arg_who)
276                         arg_who = w = strv_join(argv + optind, " ");
277
278                 fd = inhibit(bus, &error);
279                 free(w);
280
281                 if (fd < 0) {
282                         log_error("Failed to inhibit: %s", bus_error_message_or_strerror(&error, -r));
283                         r = fd;
284                         goto finish;
285                 }
286
287                 pid = fork();
288                 if (pid < 0) {
289                         log_error("Failed to fork: %m");
290                         r = -errno;
291                         goto finish;
292                 }
293
294                 if (pid == 0) {
295                         /* Child */
296
297                         close_nointr_nofail(fd);
298                         close_all_fds(NULL, 0);
299
300                         execvp(argv[optind], argv + optind);
301                         log_error("Failed to execute %s: %m", argv[optind]);
302                         _exit(EXIT_FAILURE);
303                 }
304
305                 r = wait_for_terminate_and_warn(argv[optind], pid);
306                 if (r >= 0)
307                         exit_code = r;
308         }
309
310 finish:
311         if (bus) {
312                 dbus_connection_close(bus);
313                 dbus_connection_unref(bus);
314         }
315
316         dbus_error_free(&error);
317
318         if (fd >= 0)
319                 close_nointr_nofail(fd);
320
321         return r < 0 ? EXIT_FAILURE : exit_code;
322 }