chiark / gitweb /
404881be6ef7aaa2d6b85552c2fd85838df948c7
[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 <unistd.h>
27 #include <fcntl.h>
28
29 #include "sd-bus.h"
30 #include "bus-util.h"
31 #include "bus-error.h"
32 #include "util.h"
33 #include "build.h"
34 #include "strv.h"
35
36 static const char* arg_what = "idle:sleep:shutdown";
37 static const char* arg_who = NULL;
38 static const char* arg_why = "Unknown reason";
39 static const char* arg_mode = "block";
40
41 static enum {
42         ACTION_INHIBIT,
43         ACTION_LIST
44 } arg_action = ACTION_INHIBIT;
45
46 static int inhibit(sd_bus *bus, sd_bus_error *error) {
47         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
48         int r;
49         int fd;
50
51         r = sd_bus_call_method(
52                         bus,
53                         "org.freedesktop.login1",
54                         "/org/freedesktop/login1",
55                         "org.freedesktop.login1.Manager",
56                         "Inhibit",
57                         error,
58                         &reply,
59                         "ssss", arg_what, arg_who, arg_why, arg_mode);
60         if (r < 0)
61                 return r;
62
63         r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd);
64         if (r < 0)
65                 return -EIO;
66
67         r = dup(fd);
68         if (r < 0)
69                 return -errno;
70
71         return r;
72 }
73
74 static int print_inhibitors(sd_bus *bus, sd_bus_error *error) {
75         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
76         const char *what, *who, *why, *mode;
77         unsigned int uid, pid;
78         unsigned n = 0;
79         int r;
80
81         r = sd_bus_call_method(
82                         bus,
83                         "org.freedesktop.login1",
84                         "/org/freedesktop/login1",
85                         "org.freedesktop.login1.Manager",
86                         "ListInhibitors",
87                         error,
88                         &reply,
89                         "");
90         if (r < 0)
91                 return r;
92
93         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
94         if (r < 0)
95                 return -EIO;
96
97         while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
98                 _cleanup_free_ char *comm = NULL, *u = NULL;
99
100                 get_process_comm(pid, &comm);
101                 u = uid_to_name(uid);
102
103                 printf("     Who: %s (UID %lu/%s, PID %lu/%s)\n"
104                        "    What: %s\n"
105                        "     Why: %s\n"
106                        "    Mode: %s\n\n",
107                        who, (unsigned long) uid, strna(u), (unsigned long) pid, strna(comm),
108                        what,
109                        why,
110                        mode);
111
112                 n++;
113         }
114         if (r < 0)
115                 return -EIO;
116
117         r = sd_bus_message_exit_container(reply);
118         if (r < 0)
119                 return -EIO;
120
121         printf("%u inhibitors listed.\n", n);
122         return 0;
123 }
124
125 static int help(void) {
126
127         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
128                "Execute a process while inhibiting shutdown/sleep/idle.\n\n"
129                "  -h --help               Show this help\n"
130                "     --version            Show package version\n"
131                "     --what=WHAT          Operations to inhibit, colon separated list of:\n"
132                "                          shutdown, sleep, idle, handle-power-key,\n"
133                "                          handle-suspend-key, handle-hibernate-key,\n"
134                "                          handle-lid-switch\n"
135                "     --who=STRING         A descriptive string who is inhibiting\n"
136                "     --why=STRING         A descriptive string why is being inhibited\n"
137                "     --mode=MODE          One of block or delay\n"
138                "     --list               List active inhibitors\n",
139                program_invocation_short_name);
140
141         return 0;
142 }
143
144 static int parse_argv(int argc, char *argv[]) {
145
146         enum {
147                 ARG_VERSION = 0x100,
148                 ARG_WHAT,
149                 ARG_WHO,
150                 ARG_WHY,
151                 ARG_MODE,
152                 ARG_LIST,
153         };
154
155         static const struct option options[] = {
156                 { "help",         no_argument,       NULL, 'h'              },
157                 { "version",      no_argument,       NULL, ARG_VERSION      },
158                 { "what",         required_argument, NULL, ARG_WHAT         },
159                 { "who",          required_argument, NULL, ARG_WHO          },
160                 { "why",          required_argument, NULL, ARG_WHY          },
161                 { "mode",         required_argument, NULL, ARG_MODE         },
162                 { "list",         no_argument,       NULL, ARG_LIST         },
163                 { NULL,           0,                 NULL, 0                }
164         };
165
166         int c;
167
168         assert(argc >= 0);
169         assert(argv);
170
171         while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) {
172
173                 switch (c) {
174
175                 case 'h':
176                         help();
177                         return 0;
178
179                 case ARG_VERSION:
180                         puts(PACKAGE_STRING);
181                         puts(SYSTEMD_FEATURES);
182                         return 0;
183
184                 case ARG_WHAT:
185                         arg_what = optarg;
186                         break;
187
188                 case ARG_WHO:
189                         arg_who = optarg;
190                         break;
191
192                 case ARG_WHY:
193                         arg_why = optarg;
194                         break;
195
196                 case ARG_MODE:
197                         arg_mode = optarg;
198                         break;
199
200                 case ARG_LIST:
201                         arg_action = ACTION_LIST;
202                         break;
203
204                 default:
205                         log_error("Unknown option code %c", c);
206                         return -EINVAL;
207                 }
208         }
209
210         if (arg_action == ACTION_INHIBIT && argc == 1)
211                 arg_action = ACTION_LIST;
212
213         else if (arg_action == ACTION_INHIBIT && optind >= argc) {
214                 log_error("Missing command line to execute.");
215                 return -EINVAL;
216         }
217
218         return 1;
219 }
220
221 int main(int argc, char *argv[]) {
222         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
223         _cleanup_bus_unref_ sd_bus *bus = NULL;
224         int r;
225
226         log_parse_environment();
227         log_open();
228
229         r = parse_argv(argc, argv);
230         if (r < 0)
231                 return EXIT_FAILURE;
232         if (r == 0)
233                 return EXIT_SUCCESS;
234
235         r = sd_bus_open_system(&bus);
236         if (r < 0) {
237                 log_error("Failed to connect to bus: %s", strerror(-r));
238                 return EXIT_FAILURE;
239         }
240
241         if (arg_action == ACTION_LIST) {
242
243                 r = print_inhibitors(bus, &error);
244                 if (r < 0) {
245                         log_error("Failed to list inhibitors: %s", bus_error_message(&error, -r));
246                         return EXIT_FAILURE;
247                 }
248
249         } else {
250                 _cleanup_close_ int fd = -1;
251                 _cleanup_free_ char *w = NULL;
252                 pid_t pid;
253
254                 if (!arg_who)
255                         arg_who = w = strv_join(argv + optind, " ");
256
257                 fd = inhibit(bus, &error);
258                 if (fd < 0) {
259                         log_error("Failed to inhibit: %s", bus_error_message(&error, -r));
260                         return EXIT_FAILURE;
261                 }
262
263                 pid = fork();
264                 if (pid < 0) {
265                         log_error("Failed to fork: %m");
266                         return EXIT_FAILURE;
267                 }
268
269                 if (pid == 0) {
270                         /* Child */
271
272                         close_all_fds(NULL, 0);
273
274                         execvp(argv[optind], argv + optind);
275                         log_error("Failed to execute %s: %m", argv[optind]);
276                         _exit(EXIT_FAILURE);
277                 }
278
279                 r = wait_for_terminate_and_warn(argv[optind], pid);
280                 return r < 0 ? EXIT_FAILURE : r;
281         }
282
283         return 0;
284 }