chiark / gitweb /
b1384874a066795b8a5401d02d3a7bfd951877de
[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 int manager_scheduled_shutdown_handler(
190                         sd_event_source *s,
191                         uint64_t usec,
192                         void *userdata) {
193
194         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
195         Manager *m = userdata;
196         HandleAction action;
197         int r;
198
199         assert(m);
200
201         if (isempty(m->scheduled_shutdown_type))
202                 return 0;
203
204         if (streq(m->scheduled_shutdown_type, "halt"))
205                 action = HANDLE_HALT;
206         else if (streq(m->scheduled_shutdown_type, "poweroff"))
207                 action = HANDLE_POWEROFF;
208         else
209                 action = HANDLE_REBOOT;
210
211         /* Don't allow multiple jobs being executed at the same time */
212         if (m->action_what) {
213                 r = -EALREADY;
214                 log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress",
215                           m->scheduled_shutdown_type);
216                 goto error;
217         }
218
219         if (m->shutdown_dry_run) {
220                 /* We do not process delay inhibitors here.  Otherwise, we
221                  * would have to be considered "in progress" (like the check
222                  * above) for some seconds after our admin has seen the final
223                  * wall message. */
224
225                 bus_manager_log_shutdown(m, INHIBIT_SHUTDOWN, action);
226                 log_info("Running in dry run, suppressing action.");
227                 reset_scheduled_shutdown(m);
228
229                 return 0;
230         }
231
232         r = execute_shutdown_or_sleep(m, 0, action, &error);
233         if (r < 0) {
234                 log_error_errno(r, "Scheduled shutdown to %s failed: %m",
235                                        m->scheduled_shutdown_type);
236                 goto error;
237         }
238
239         return 0;
240
241 error:
242         reset_scheduled_shutdown(m);
243         return r;
244 }