chiark / gitweb /
logind: minor cleanup and use IN_SET() in manager_handle_action()
[elogind.git] / src / login / inhibit.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2012 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #include "sd-bus.h"
27
28 #include "alloc-util.h"
29 #include "bus-error.h"
30 #include "bus-util.h"
31 #include "fd-util.h"
32 #include "formats-util.h"
33 #include "process-util.h"
34 #include "signal-util.h"
35 #include "strv.h"
36 #include "user-util.h"
37 #include "util.h"
38
39 /// Additional includes needed by elogind
40 #include "musl_missing.h"
41
42 static const char* arg_what = "idle:sleep:shutdown";
43 static const char* arg_who = NULL;
44 static const char* arg_why = "Unknown reason";
45 static const char* arg_mode = NULL;
46
47 static enum {
48         ACTION_INHIBIT,
49         ACTION_LIST
50 } arg_action = ACTION_INHIBIT;
51
52 static int inhibit(sd_bus *bus, sd_bus_error *error) {
53         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
54         int r;
55         int fd;
56
57         r = sd_bus_call_method(
58                         bus,
59                         "org.freedesktop.login1",
60                         "/org/freedesktop/login1",
61                         "org.freedesktop.login1.Manager",
62                         "Inhibit",
63                         error,
64                         &reply,
65                         "ssss", arg_what, arg_who, arg_why, arg_mode);
66         if (r < 0)
67                 return r;
68
69         r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd);
70         if (r < 0)
71                 return r;
72
73         r = fcntl(fd, F_DUPFD_CLOEXEC, 3);
74         if (r < 0)
75                 return -errno;
76
77         return r;
78 }
79
80 static int print_inhibitors(sd_bus *bus, sd_bus_error *error) {
81         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
82         const char *what, *who, *why, *mode;
83         unsigned int uid, pid;
84         unsigned n = 0;
85         int r;
86
87         r = sd_bus_call_method(
88                         bus,
89                         "org.freedesktop.login1",
90                         "/org/freedesktop/login1",
91                         "org.freedesktop.login1.Manager",
92                         "ListInhibitors",
93                         error,
94                         &reply,
95                         "");
96         if (r < 0)
97                 return r;
98
99         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
100         if (r < 0)
101                 return bus_log_parse_error(r);
102
103         while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
104                 _cleanup_free_ char *comm = NULL, *u = NULL;
105
106                 if (arg_mode && !streq(mode, arg_mode))
107                         continue;
108
109                 get_process_comm(pid, &comm);
110                 u = uid_to_name(uid);
111
112                 printf("     Who: %s (UID "UID_FMT"/%s, PID "PID_FMT"/%s)\n"
113                        "    What: %s\n"
114                        "     Why: %s\n"
115                        "    Mode: %s\n\n",
116                        who, uid, strna(u), pid, strna(comm),
117                        what,
118                        why,
119                        mode);
120
121                 n++;
122         }
123         if (r < 0)
124                 return bus_log_parse_error(r);
125
126         r = sd_bus_message_exit_container(reply);
127         if (r < 0)
128                 return bus_log_parse_error(r);
129
130         printf("%u inhibitors listed.\n", n);
131         return 0;
132 }
133
134 static void help(void) {
135         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
136                "Execute a process while inhibiting shutdown/sleep/idle.\n\n"
137                "  -h --help               Show this help\n"
138                "     --version            Show package version\n"
139                "     --what=WHAT          Operations to inhibit, colon separated list of:\n"
140                "                          shutdown, sleep, idle, handle-power-key,\n"
141                "                          handle-suspend-key, handle-hibernate-key,\n"
142                "                          handle-lid-switch\n"
143                "     --who=STRING         A descriptive string who is inhibiting\n"
144                "     --why=STRING         A descriptive string why is being inhibited\n"
145                "     --mode=MODE          One of block or delay\n"
146                "     --list               List active inhibitors\n"
147                , program_invocation_short_name);
148 }
149
150 static int parse_argv(int argc, char *argv[]) {
151
152         enum {
153                 ARG_VERSION = 0x100,
154                 ARG_WHAT,
155                 ARG_WHO,
156                 ARG_WHY,
157                 ARG_MODE,
158                 ARG_LIST,
159         };
160
161         static const struct option options[] = {
162                 { "help",         no_argument,       NULL, 'h'              },
163                 { "version",      no_argument,       NULL, ARG_VERSION      },
164                 { "what",         required_argument, NULL, ARG_WHAT         },
165                 { "who",          required_argument, NULL, ARG_WHO          },
166                 { "why",          required_argument, NULL, ARG_WHY          },
167                 { "mode",         required_argument, NULL, ARG_MODE         },
168                 { "list",         no_argument,       NULL, ARG_LIST         },
169                 {}
170         };
171
172         int c;
173
174         assert(argc >= 0);
175         assert(argv);
176
177         while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0)
178
179                 switch (c) {
180
181                 case 'h':
182                         help();
183                         return 0;
184
185                 case ARG_VERSION:
186                         return version();
187
188                 case ARG_WHAT:
189                         arg_what = optarg;
190                         break;
191
192                 case ARG_WHO:
193                         arg_who = optarg;
194                         break;
195
196                 case ARG_WHY:
197                         arg_why = optarg;
198                         break;
199
200                 case ARG_MODE:
201                         arg_mode = optarg;
202                         break;
203
204                 case ARG_LIST:
205                         arg_action = ACTION_LIST;
206                         break;
207
208                 case '?':
209                         return -EINVAL;
210
211                 default:
212                         assert_not_reached("Unhandled option");
213                 }
214
215         if (arg_action == ACTION_INHIBIT && optind == argc)
216                 arg_action = ACTION_LIST;
217
218         else if (arg_action == ACTION_INHIBIT && optind >= argc) {
219                 log_error("Missing command line to execute.");
220                 return -EINVAL;
221         }
222
223         return 1;
224 }
225
226 int main(int argc, char *argv[]) {
227         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
228         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
229         int r;
230
231         elogind_set_program_name(argv[0]);
232         log_parse_environment();
233         log_open();
234
235         r = parse_argv(argc, argv);
236         if (r < 0)
237                 return EXIT_FAILURE;
238         if (r == 0)
239                 return EXIT_SUCCESS;
240
241         r = sd_bus_default_system(&bus);
242         if (r < 0) {
243                 log_error_errno(r, "Failed to connect to bus: %m");
244                 return EXIT_FAILURE;
245         }
246
247         if (arg_action == ACTION_LIST) {
248
249                 r = print_inhibitors(bus, &error);
250                 if (r < 0) {
251                         log_error("Failed to list inhibitors: %s", bus_error_message(&error, -r));
252                         return EXIT_FAILURE;
253                 }
254
255         } else {
256                 _cleanup_close_ int fd = -1;
257                 _cleanup_free_ char *w = NULL;
258                 pid_t pid;
259
260                 if (!arg_who)
261                         arg_who = w = strv_join(argv + optind, " ");
262
263                 if (!arg_mode)
264                         arg_mode = "block";
265
266                 fd = inhibit(bus, &error);
267                 if (fd < 0) {
268                         log_error("Failed to inhibit: %s", bus_error_message(&error, fd));
269                         return EXIT_FAILURE;
270                 }
271
272                 pid = fork();
273                 if (pid < 0) {
274                         log_error_errno(errno, "Failed to fork: %m");
275                         return EXIT_FAILURE;
276                 }
277
278                 if (pid == 0) {
279                         /* Child */
280
281                         (void) reset_all_signal_handlers();
282                         (void) reset_signal_mask();
283
284                         close_all_fds(NULL, 0);
285
286                         execvp(argv[optind], argv + optind);
287                         log_error_errno(errno, "Failed to execute %s: %m", argv[optind]);
288                         _exit(EXIT_FAILURE);
289                 }
290
291                 r = wait_for_terminate_and_warn(argv[optind], pid, true);
292                 return r < 0 ? EXIT_FAILURE : r;
293         }
294
295         return 0;
296 }