chiark / gitweb /
Prep v239: Add support for the new 'suspend-then-hibernate' method.
[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 "elogind-dbus.h"
22 #include "process-util.h"
23 #include "sd-messages.h"
24 #include "sleep.h"
25 #include "string-util.h"
26 #include "strv.h"
27 #include "update-utmp.h"
28
29
30 static int bus_manager_log_shutdown(
31                 Manager *m,
32                 InhibitWhat w,
33                 HandleAction action) {
34
35          const char *p, *q;
36
37          assert(m);
38
39          if (w != INHIBIT_SHUTDOWN)
40                  return 0;
41
42          switch (action) {
43          case HANDLE_POWEROFF:
44                  p = "MESSAGE=System is powering down.";
45                  q = "SHUTDOWN=power-off";
46                  break;
47          case HANDLE_HALT:
48                  p = "MESSAGE=System is halting.";
49                  q = "SHUTDOWN=halt";
50                  break;
51          case HANDLE_REBOOT:
52                  p = "MESSAGE=System is rebooting.";
53                  q = "SHUTDOWN=reboot";
54                  break;
55          case HANDLE_KEXEC:
56                  p = "MESSAGE=System is rebooting with kexec.";
57                  q = "SHUTDOWN=kexec";
58                  break;
59          default:
60                  p = "MESSAGE=System is shutting down.";
61                  q = NULL;
62          }
63
64         if (isempty(m->wall_message))
65                 p = strjoina(p, ".");
66         else
67                 p = strjoina(p, " (", m->wall_message, ").");
68
69         return log_struct(LOG_NOTICE,
70                           "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
71                           p,
72                           q,
73                           NULL);
74 }
75
76 /* elogind specific helper to make HALT and REBOOT possible. */
77 static int run_helper(const char *helper) {
78         int   r   = 0;
79
80         r = safe_fork_full(helper, NULL, 0, FORK_RESET_SIGNALS|FORK_REOPEN_LOG, NULL);
81
82         if (r < 0)
83                 return log_error_errno(errno, "Failed to fork run %s: %m", helper);
84
85         if (0 == r) {
86                 /* Child */
87                 execlp(helper, helper, NULL);
88                 log_error_errno(errno, "Failed to execute %s: %m", helper);
89                 _exit(EXIT_FAILURE);
90         }
91
92         return 0;
93 }
94
95 /* elogind specific executor */
96 static int shutdown_or_sleep(Manager *m, HandleAction action) {
97
98         assert(m);
99
100         switch (action) {
101         case HANDLE_POWEROFF:
102                 return run_helper(POWEROFF);
103         case HANDLE_REBOOT:
104                 return run_helper(REBOOT);
105         case HANDLE_HALT:
106                 return run_helper(HALT);
107         case HANDLE_KEXEC:
108                 return run_helper(KEXEC);
109         case HANDLE_SUSPEND:
110                 return do_sleep(m, "suspend");
111         case HANDLE_HIBERNATE:
112                 return do_sleep(m, "hibernate");
113         case HANDLE_HYBRID_SLEEP:
114                 return do_sleep(m, "hybrid-sleep");
115         case HANDLE_SUSPEND_THEN_HIBERNATE:
116                 return do_sleep(m, "suspend-then-hibernate");
117         default:
118                 return -EINVAL;
119         }
120 }
121
122 int execute_shutdown_or_sleep(
123                 Manager *m,
124                 InhibitWhat w,
125                 HandleAction action,
126                 sd_bus_error *error) {
127
128         char** argv_utmp = NULL;
129         int r;
130
131         assert(m);
132         assert(w >= 0);
133         assert(w < _INHIBIT_WHAT_MAX);
134
135         bus_manager_log_shutdown(m, w, action);
136
137         if (IN_SET(action, HANDLE_HALT, HANDLE_POWEROFF, HANDLE_REBOOT)) {
138
139                 /* As we have no systemd update-utmp daemon running, we have to
140                  * set the relevant utmp/wtmp entries ourselves.
141                  */
142
143                 if (strv_extend(&argv_utmp, "elogind") < 0)
144                         return log_oom();
145
146                 if (HANDLE_REBOOT == action) {
147                         if (strv_extend(&argv_utmp, "reboot") < 0)
148                                 return log_oom();
149                 } else {
150                         if (strv_extend(&argv_utmp, "shutdown") < 0)
151                                 return log_oom();
152                 }
153
154                  /* This comes from our patched update-utmp/update-utmp.c */
155                 update_utmp(2, argv_utmp);
156                 strv_free(argv_utmp);
157         }
158
159         /* Now perform the requested action */
160         r = shutdown_or_sleep(m, action);
161
162         /* no more pending actions, whether this failed or not */
163         m->pending_action = HANDLE_IGNORE;
164
165         /* As elogind can not rely on a systemd manager to call all
166          * sleeping processes to wake up, we have to tell them all
167          * by ourselves. */
168         if (w == INHIBIT_SLEEP) {
169                 (void) send_prepare_for(m, w, false);
170                 m->action_what = 0;
171         } else
172                 m->action_what = w;
173
174         if (r < 0)
175                 return r;
176
177         /* Make sure the lid switch is ignored for a while */
178         manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec);
179
180         return 0;
181 }
182
183 int manager_scheduled_shutdown_handler(
184                         sd_event_source *s,
185                         uint64_t usec,
186                         void *userdata) {
187
188         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
189         Manager *m = userdata;
190         HandleAction action;
191         int r;
192
193         assert(m);
194
195         if (isempty(m->scheduled_shutdown_type))
196                 return 0;
197
198         if (streq(m->scheduled_shutdown_type, "halt"))
199                 action = HANDLE_HALT;
200         else if (streq(m->scheduled_shutdown_type, "poweroff"))
201                 action = HANDLE_POWEROFF;
202         else
203                 action = HANDLE_REBOOT;
204
205         /* Don't allow multiple jobs being executed at the same time */
206         if (m->action_what) {
207                 r = -EALREADY;
208                 log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress",
209                           m->scheduled_shutdown_type);
210                 goto error;
211         }
212
213         if (m->shutdown_dry_run) {
214                 /* We do not process delay inhibitors here.  Otherwise, we
215                  * would have to be considered "in progress" (like the check
216                  * above) for some seconds after our admin has seen the final
217                  * wall message. */
218
219                 bus_manager_log_shutdown(m, INHIBIT_SHUTDOWN, action);
220                 log_info("Running in dry run, suppressing action.");
221                 reset_scheduled_shutdown(m);
222
223                 return 0;
224         }
225
226         r = execute_shutdown_or_sleep(m, 0, action, &error);
227         if (r < 0) {
228                 log_error_errno(r, "Scheduled shutdown to %s failed: %m",
229                                        m->scheduled_shutdown_type);
230                 goto error;
231         }
232
233         return 0;
234
235 error:
236         reset_scheduled_shutdown(m);
237         return r;
238 }