chiark / gitweb /
715b28b177ec1ced9a0d5a31e192ac1ca040d0f2
[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("suspend", m->suspend_mode, m->suspend_state);
111         case HANDLE_HIBERNATE:
112                 return do_sleep("hibernate", m->hibernate_mode, m->hibernate_state);
113         case HANDLE_HYBRID_SLEEP:
114                 return do_sleep("hybrid-sleep", m->hybrid_sleep_mode, m->hybrid_sleep_state);
115         default:
116                 return -EINVAL;
117         }
118 }
119
120 int execute_shutdown_or_sleep(
121                 Manager *m,
122                 InhibitWhat w,
123                 HandleAction action,
124                 sd_bus_error *error) {
125
126         char** argv_utmp = NULL;
127         int r;
128
129         assert(m);
130         assert(w >= 0);
131         assert(w < _INHIBIT_WHAT_MAX);
132
133         bus_manager_log_shutdown(m, w, action);
134
135         if (IN_SET(action, HANDLE_HALT, HANDLE_POWEROFF, HANDLE_REBOOT)) {
136
137                 /* As we have no systemd update-utmp daemon running, we have to
138                  * set the relevant utmp/wtmp entries ourselves.
139                  */
140
141                 if (strv_extend(&argv_utmp, "elogind") < 0)
142                         return log_oom();
143
144                 if (HANDLE_REBOOT == action) {
145                         if (strv_extend(&argv_utmp, "reboot") < 0)
146                                 return log_oom();
147                 } else {
148                         if (strv_extend(&argv_utmp, "shutdown") < 0)
149                                 return log_oom();
150                 }
151
152                  /* This comes from our patched update-utmp/update-utmp.c */
153                 update_utmp(2, argv_utmp);
154                 strv_free(argv_utmp);
155         }
156
157         /* Now perform the requested action */
158         r = shutdown_or_sleep(m, action);
159
160         /* no more pending actions, whether this failed or not */
161         m->pending_action = HANDLE_IGNORE;
162
163         /* As elogind can not rely on a systemd manager to call all
164          * sleeping processes to wake up, we have to tell them all
165          * by ourselves. */
166         if (w == INHIBIT_SLEEP) {
167                 (void) send_prepare_for(m, w, false);
168                 m->action_what = 0;
169         } else
170                 m->action_what = w;
171
172         if (r < 0)
173                 return r;
174
175         /* Make sure the lid switch is ignored for a while */
176         manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec);
177
178         return 0;
179 }
180
181 int manager_scheduled_shutdown_handler(
182                         sd_event_source *s,
183                         uint64_t usec,
184                         void *userdata) {
185
186         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
187         Manager *m = userdata;
188         HandleAction action;
189         int r;
190
191         assert(m);
192
193         if (isempty(m->scheduled_shutdown_type))
194                 return 0;
195
196         if (streq(m->scheduled_shutdown_type, "halt"))
197                 action = HANDLE_HALT;
198         else if (streq(m->scheduled_shutdown_type, "poweroff"))
199                 action = HANDLE_POWEROFF;
200         else
201                 action = HANDLE_REBOOT;
202
203         /* Don't allow multiple jobs being executed at the same time */
204         if (m->action_what) {
205                 r = -EALREADY;
206                 log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress",
207                           m->scheduled_shutdown_type);
208                 goto error;
209         }
210
211         if (m->shutdown_dry_run) {
212                 /* We do not process delay inhibitors here.  Otherwise, we
213                  * would have to be considered "in progress" (like the check
214                  * above) for some seconds after our admin has seen the final
215                  * wall message. */
216
217                 bus_manager_log_shutdown(m, INHIBIT_SHUTDOWN, action);
218                 log_info("Running in dry run, suppressing action.");
219                 reset_scheduled_shutdown(m);
220
221                 return 0;
222         }
223
224         r = execute_shutdown_or_sleep(m, 0, action, &error);
225         if (r < 0) {
226                 log_error_errno(r, "Scheduled shutdown to %s failed: %m",
227                                        m->scheduled_shutdown_type);
228                 goto error;
229         }
230
231         return 0;
232
233 error:
234         reset_scheduled_shutdown(m);
235         return r;
236 }