chiark / gitweb /
general: various cleanups
[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
31 #include "bus-util.h"
32 #include "bus-error.h"
33 #include "util.h"
34 #include "build.h"
35 #include "strv.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 = "block";
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 -EIO;
67
68         r = dup(fd);
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 -EIO;
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                 get_process_comm(pid, &comm);
102                 u = uid_to_name(uid);
103
104                 printf("     Who: %s (UID %lu/%s, PID %lu/%s)\n"
105                        "    What: %s\n"
106                        "     Why: %s\n"
107                        "    Mode: %s\n\n",
108                        who, (unsigned long) uid, strna(u), (unsigned long) pid, strna(comm),
109                        what,
110                        why,
111                        mode);
112
113                 n++;
114         }
115         if (r < 0)
116                 return -EIO;
117
118         r = sd_bus_message_exit_container(reply);
119         if (r < 0)
120                 return -EIO;
121
122         printf("%u inhibitors listed.\n", n);
123         return 0;
124 }
125
126 static int help(void) {
127
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         return 0;
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                 { NULL,           0,                 NULL, 0                }
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                 default:
206                         log_error("Unknown option code %c", c);
207                         return -EINVAL;
208                 }
209         }
210
211         if (arg_action == ACTION_INHIBIT && argc == 1)
212                 arg_action = ACTION_LIST;
213
214         else if (arg_action == ACTION_INHIBIT && optind >= argc) {
215                 log_error("Missing command line to execute.");
216                 return -EINVAL;
217         }
218
219         return 1;
220 }
221
222 int main(int argc, char *argv[]) {
223         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
224         _cleanup_bus_unref_ sd_bus *bus = NULL;
225         int r;
226
227         log_parse_environment();
228         log_open();
229
230         r = parse_argv(argc, argv);
231         if (r <= 0)
232                 return EXIT_FAILURE;
233
234         r = sd_bus_open_system(&bus);
235         if (r < 0) {
236                 log_error("Failed to connect to bus: %s", strerror(-r));
237                 return EXIT_FAILURE;
238         }
239
240         if (arg_action == ACTION_LIST) {
241
242                 r = print_inhibitors(bus, &error);
243                 if (r < 0) {
244                         log_error("Failed to list inhibitors: %s", bus_error_message(&error, -r));
245                         return EXIT_FAILURE;
246                 }
247
248         } else {
249                 _cleanup_close_ int fd = -1;
250                 _cleanup_free_ char *w = NULL;
251                 pid_t pid;
252
253                 if (!arg_who)
254                         arg_who = w = strv_join(argv + optind, " ");
255
256                 fd = inhibit(bus, &error);
257                 if (fd < 0) {
258                         log_error("Failed to inhibit: %s", bus_error_message(&error, -r));
259                         return EXIT_FAILURE;
260                 }
261
262                 pid = fork();
263                 if (pid < 0) {
264                         log_error("Failed to fork: %m");
265                         return EXIT_FAILURE;
266                 }
267
268                 if (pid == 0) {
269                         /* Child */
270
271                         close_all_fds(NULL, 0);
272
273                         execvp(argv[optind], argv + optind);
274                         log_error("Failed to execute %s: %m", argv[optind]);
275                         _exit(EXIT_FAILURE);
276                 }
277
278                 r = wait_for_terminate_and_warn(argv[optind], pid);
279                 return r < 0 ? EXIT_FAILURE : r;
280         }
281
282         return 0;
283 }