chiark / gitweb /
Move bus_manager_shutdown_or_sleep_now_or_later() back to logind-dbus.c
[elogind.git] / src / login / elogind-dbus.c
1 /***
2   This file is part of elogind.
3
4   Copyright 2017 Sven Eden
5
6   elogind 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   elogind 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 elogind; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20
21 #include "bus-common-errors.h"
22 #include "bus-error.h"
23 #include "bus-util.h"
24 #include "elogind-dbus.h"
25 #include "fd-util.h"
26 #include "process-util.h"
27 #include "sd-messages.h"
28 #include "sleep.h"
29 #include "sleep-config.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "update-utmp.h"
33 #include "user-util.h"
34
35
36 static int bus_manager_log_shutdown(
37                 Manager *m,
38                 InhibitWhat w,
39                 HandleAction action) {
40
41          const char *p, *q;
42
43          assert(m);
44
45          if (w != INHIBIT_SHUTDOWN)
46                  return 0;
47
48          switch (action) {
49          case HANDLE_POWEROFF:
50                  p = "MESSAGE=System is powering down.";
51                  q = "SHUTDOWN=power-off";
52                  break;
53          case HANDLE_HALT:
54                  p = "MESSAGE=System is halting.";
55                  q = "SHUTDOWN=halt";
56                  break;
57          case HANDLE_REBOOT:
58                  p = "MESSAGE=System is rebooting.";
59                  q = "SHUTDOWN=reboot";
60                  break;
61          case HANDLE_KEXEC:
62                  p = "MESSAGE=System is rebooting with kexec.";
63                  q = "SHUTDOWN=kexec";
64                  break;
65          default:
66                  p = "MESSAGE=System is shutting down.";
67                  q = NULL;
68          }
69
70         if (isempty(m->wall_message))
71                 p = strjoina(p, ".");
72         else
73                 p = strjoina(p, " (", m->wall_message, ").");
74
75         return log_struct(LOG_NOTICE,
76                           "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
77                           p,
78                           q,
79                           NULL);
80 }
81
82 /* elogind specific helper to make HALT and REBOOT possible. */
83 static int run_helper(const char *helper) {
84         pid_t pid = 0;
85         int   r   = 0;
86
87         r = safe_fork_full(helper, NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_WAIT, &pid);
88
89         if (r < 0)
90                 return log_error_errno(errno, "Failed to fork run %s: %m", helper);
91
92         if (pid == 0) {
93                 /* Child */
94
95                 execlp(helper, helper, NULL);
96                 log_error_errno(errno, "Failed to execute %s: %m", helper);
97                 _exit(EXIT_FAILURE);
98         }
99
100         return r;
101 }
102
103 /* elogind specific executor */
104 static int shutdown_or_sleep(Manager *m, HandleAction action) {
105
106         assert(m);
107
108         switch (action) {
109         case HANDLE_POWEROFF:
110                 return run_helper(POWEROFF);
111         case HANDLE_REBOOT:
112                 return run_helper(REBOOT);
113         case HANDLE_HALT:
114                 return run_helper(HALT);
115         case HANDLE_KEXEC:
116                 return run_helper(KEXEC);
117         case HANDLE_SUSPEND:
118                 return do_sleep("suspend", m->suspend_mode, m->suspend_state);
119         case HANDLE_HIBERNATE:
120                 return do_sleep("hibernate", m->hibernate_mode, m->hibernate_state);
121         case HANDLE_HYBRID_SLEEP:
122                 return do_sleep("hybrid-sleep", m->hybrid_sleep_mode, m->hybrid_sleep_state);
123         default:
124                 return -EINVAL;
125         }
126 }
127
128 int execute_shutdown_or_sleep(
129                 Manager *m,
130                 InhibitWhat w,
131                 HandleAction action,
132                 sd_bus_error *error) {
133
134         char** argv_utmp = NULL;
135         int r;
136
137         assert(m);
138         assert(w >= 0);
139         assert(w < _INHIBIT_WHAT_MAX);
140
141         bus_manager_log_shutdown(m, w, action);
142
143         if (IN_SET(action, HANDLE_HALT, HANDLE_POWEROFF, HANDLE_REBOOT)) {
144
145                 /* As we have no systemd update-utmp daemon running, we have to
146                  * set the relevant utmp/wtmp entries ourselves.
147                  */
148
149                 if (strv_extend(&argv_utmp, "elogind") < 0)
150                         return log_oom();
151
152                 if (HANDLE_REBOOT == action) {
153                         if (strv_extend(&argv_utmp, "reboot") < 0)
154                                 return log_oom();
155                 } else {
156                         if (strv_extend(&argv_utmp, "shutdown") < 0)
157                                 return log_oom();
158                 }
159
160                  /* This comes from our patched update-utmp/update-utmp.c */
161                 update_utmp(2, argv_utmp, m->bus);
162                 strv_free(argv_utmp);
163         }
164
165         /* Now perform the requested action */
166         r = shutdown_or_sleep(m, action);
167
168         /* no more pending actions, whether this failed or not */
169         m->pending_action = HANDLE_IGNORE;
170
171         /* As elogind can not rely on a systemd manager to call all
172          * sleeping processes to wake up, we have to tell them all
173          * by ourselves. */
174         if (w == INHIBIT_SLEEP) {
175                 (void) send_prepare_for(m, w, false);
176                 m->action_what = 0;
177         } else
178                 m->action_what = w;
179
180         if (r < 0)
181                 return r;
182
183         /* Make sure the lid switch is ignored for a while */
184         manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec);
185
186         return 0;
187 }
188
189 static int method_do_shutdown_or_sleep(
190                 Manager *m,
191                 sd_bus_message *message,
192                 HandleAction sleep_action,
193                 InhibitWhat w,
194                 const char *action,
195                 const char *action_multiple_sessions,
196                 const char *action_ignore_inhibit,
197                 const char *sleep_verb,
198                 sd_bus_error *error) {
199
200         int interactive, r;
201
202         assert(m);
203         assert(message);
204         assert(w >= 0);
205         assert(w <= _INHIBIT_WHAT_MAX);
206
207         r = sd_bus_message_read(message, "b", &interactive);
208         if (r < 0)
209                 return r;
210
211         log_debug_elogind("%s called with action '%s', sleep '%s' (%sinteractive)",
212                           __FUNCTION__, action, sleep_verb,
213                           interactive ? "" : "NOT ");
214
215         /* Don't allow multiple jobs being executed at the same time */
216         if (m->action_what)
217                 return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
218
219         if (sleep_verb) {
220                 r = can_sleep(m, sleep_verb);
221                 if (r < 0)
222                         return r;
223
224                 if (r == 0)
225                         return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
226         }
227
228         if (IN_SET(sleep_action, HANDLE_HALT, HANDLE_POWEROFF, HANDLE_REBOOT)) {
229                 r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions,
230                                           action_ignore_inhibit, error);
231                 log_debug_elogind("verify_shutdown_creds() returned %d", r);
232                 if (r != 0)
233                         return r;
234         }
235
236         r = bus_manager_shutdown_or_sleep_now_or_later(m, sleep_action, w, error);
237         if (r < 0)
238                 return r;
239
240         return sd_bus_reply_method_return(message, NULL);
241 }
242
243 int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
244         Manager *m = userdata;
245
246         log_debug_elogind("%s called", __FUNCTION__);
247
248         return method_do_shutdown_or_sleep(
249                         m, message,
250                         HANDLE_POWEROFF,
251                         INHIBIT_SHUTDOWN,
252                         "org.freedesktop.login1.power-off",
253                         "org.freedesktop.login1.power-off-multiple-sessions",
254                         "org.freedesktop.login1.power-off-ignore-inhibit",
255                         NULL,
256                         error);
257 }
258
259 int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
260         Manager *m = userdata;
261
262         log_debug_elogind("%s called", __FUNCTION__);
263
264         return method_do_shutdown_or_sleep(
265                         m, message,
266                         HANDLE_REBOOT,
267                         INHIBIT_SHUTDOWN,
268                         "org.freedesktop.login1.reboot",
269                         "org.freedesktop.login1.reboot-multiple-sessions",
270                         "org.freedesktop.login1.reboot-ignore-inhibit",
271                         NULL,
272                         error);
273 }
274
275 int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) {
276         Manager *m = userdata;
277
278         log_debug_elogind("%s called", __FUNCTION__);
279
280         return method_do_shutdown_or_sleep(
281                         m, message,
282                         HANDLE_HALT,
283                         INHIBIT_SHUTDOWN,
284                         "org.freedesktop.login1.halt",
285                         "org.freedesktop.login1.halt-multiple-sessions",
286                         "org.freedesktop.login1.halt-ignore-inhibit",
287                         NULL,
288                         error);
289 }
290
291 int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) {
292         Manager *m = userdata;
293
294         log_debug_elogind("%s called", __FUNCTION__);
295
296         return method_do_shutdown_or_sleep(
297                         m, message,
298                         HANDLE_SUSPEND,
299                         INHIBIT_SLEEP,
300                         "org.freedesktop.login1.suspend",
301                         "org.freedesktop.login1.suspend-multiple-sessions",
302                         "org.freedesktop.login1.suspend-ignore-inhibit",
303                         "suspend",
304                         error);
305 }
306
307 int manager_scheduled_shutdown_handler(
308                         sd_event_source *s,
309                         uint64_t usec,
310                         void *userdata) {
311
312         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
313         Manager *m = userdata;
314         HandleAction action;
315         int r;
316
317         assert(m);
318
319         if (isempty(m->scheduled_shutdown_type))
320                 return 0;
321
322         if (streq(m->scheduled_shutdown_type, "halt"))
323                 action = HANDLE_HALT;
324         else if (streq(m->scheduled_shutdown_type, "poweroff"))
325                 action = HANDLE_POWEROFF;
326         else
327                 action = HANDLE_REBOOT;
328
329         /* Don't allow multiple jobs being executed at the same time */
330         if (m->action_what) {
331                 r = -EALREADY;
332                 log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress",
333                           m->scheduled_shutdown_type);
334                 goto error;
335         }
336
337         if (m->shutdown_dry_run) {
338                 /* We do not process delay inhibitors here.  Otherwise, we
339                  * would have to be considered "in progress" (like the check
340                  * above) for some seconds after our admin has seen the final
341                  * wall message. */
342
343                 bus_manager_log_shutdown(m, INHIBIT_SHUTDOWN, action);
344                 log_info("Running in dry run, suppressing action.");
345                 reset_scheduled_shutdown(m);
346
347                 return 0;
348         }
349
350         r = execute_shutdown_or_sleep(m, 0, action, &error);
351         if (r < 0) {
352                 log_error_errno(r, "Scheduled shutdown to %s failed: %m",
353                                        m->scheduled_shutdown_type);
354                 goto error;
355         }
356
357         return 0;
358
359 error:
360         reset_scheduled_shutdown(m);
361         return r;
362 }
363
364 int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
365         Manager *m = userdata;
366
367         log_debug_elogind("%s called", __FUNCTION__);
368
369         return method_do_shutdown_or_sleep(
370                         m, message,
371                         HANDLE_HIBERNATE,
372                         INHIBIT_SLEEP,
373                         "org.freedesktop.login1.hibernate",
374                         "org.freedesktop.login1.hibernate-multiple-sessions",
375                         "org.freedesktop.login1.hibernate-ignore-inhibit",
376                         "hibernate",
377                         error);
378 }
379
380 int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) {
381         Manager *m = userdata;
382
383         log_debug_elogind("%s called", __FUNCTION__);
384
385         return method_do_shutdown_or_sleep(
386                         m, message,
387                         HANDLE_HYBRID_SLEEP,
388                         INHIBIT_SLEEP,
389                         "org.freedesktop.login1.hibernate",
390                         "org.freedesktop.login1.hibernate-multiple-sessions",
391                         "org.freedesktop.login1.hibernate-ignore-inhibit",
392                         "hybrid-sleep",
393                         error);
394 }