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