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