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