/*** This file is part of elogind. Copyright 2017 Sven Eden elogind is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. elogind is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with elogind; If not, see . ***/ #include "elogind-dbus.h" #include "exec-util.h" #include "process-util.h" #include "sd-messages.h" #include "sleep.h" #include "string-util.h" #include "strv.h" #include "update-utmp.h" static int bus_manager_log_shutdown( Manager *m, InhibitWhat w, HandleAction action) { const char *p, *q; assert(m); if (w != INHIBIT_SHUTDOWN) return 0; switch (action) { case HANDLE_POWEROFF: p = "MESSAGE=System is powering down."; q = "SHUTDOWN=power-off"; break; case HANDLE_HALT: p = "MESSAGE=System is halting."; q = "SHUTDOWN=halt"; break; case HANDLE_REBOOT: p = "MESSAGE=System is rebooting."; q = "SHUTDOWN=reboot"; break; case HANDLE_KEXEC: p = "MESSAGE=System is rebooting with kexec."; q = "SHUTDOWN=kexec"; break; default: p = "MESSAGE=System is shutting down."; q = NULL; } if (isempty(m->wall_message)) p = strjoina(p, "."); else p = strjoina(p, " (", m->wall_message, ")."); return log_struct(LOG_NOTICE, "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR, p, q, NULL); } /* elogind specific helper to make HALT and REBOOT possible. */ static int run_helper(const char *helper, const char *arg_verb) { char *arguments[3]; static const char* const dirs[] = { SYSTEM_SHUTDOWN_PATH, NULL }; int r = 0; arguments[0] = NULL; arguments[1] = (char*)arg_verb; arguments[2] = NULL; execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); r = safe_fork_full(helper, NULL, 0, FORK_RESET_SIGNALS|FORK_REOPEN_LOG, NULL); if (r < 0) return log_error_errno(errno, "Failed to fork run %s: %m", helper); if (0 == r) { /* Child */ execlp(helper, helper, NULL); log_error_errno(errno, "Failed to execute %s: %m", helper); _exit(EXIT_FAILURE); } return 0; } /* elogind specific executor */ static int shutdown_or_sleep(Manager *m, HandleAction action) { assert(m); switch (action) { case HANDLE_POWEROFF: return run_helper(POWEROFF, "poweroff"); case HANDLE_REBOOT: return run_helper(REBOOT, "reboot"); case HANDLE_HALT: return run_helper(HALT, "halt"); case HANDLE_KEXEC: return run_helper(KEXEC, "kexec"); case HANDLE_SUSPEND: return do_sleep(m, "suspend"); case HANDLE_HIBERNATE: return do_sleep(m, "hibernate"); case HANDLE_HYBRID_SLEEP: return do_sleep(m, "hybrid-sleep"); case HANDLE_SUSPEND_THEN_HIBERNATE: return do_sleep(m, "suspend-then-hibernate"); default: return -EINVAL; } } int execute_shutdown_or_sleep( Manager *m, InhibitWhat w, HandleAction action, sd_bus_error *error) { char** argv_utmp = NULL; int r; assert(m); assert(w >= 0); assert(w < _INHIBIT_WHAT_MAX); bus_manager_log_shutdown(m, w, action); if (IN_SET(action, HANDLE_HALT, HANDLE_POWEROFF, HANDLE_REBOOT)) { /* As we have no systemd update-utmp daemon running, we have to * set the relevant utmp/wtmp entries ourselves. */ if (strv_extend(&argv_utmp, "elogind") < 0) return log_oom(); if (HANDLE_REBOOT == action) { if (strv_extend(&argv_utmp, "reboot") < 0) return log_oom(); } else { if (strv_extend(&argv_utmp, "shutdown") < 0) return log_oom(); } /* This comes from our patched update-utmp/update-utmp.c */ update_utmp(2, argv_utmp); strv_free(argv_utmp); } /* Now perform the requested action */ r = shutdown_or_sleep(m, action); /* no more pending actions, whether this failed or not */ m->pending_action = HANDLE_IGNORE; /* As elogind can not rely on a systemd manager to call all * sleeping processes to wake up, we have to tell them all * by ourselves. */ if (w == INHIBIT_SLEEP) { (void) send_prepare_for(m, w, false); m->action_what = 0; } else m->action_what = w; if (r < 0) return r; /* Make sure the lid switch is ignored for a while */ manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec); return 0; } int manager_scheduled_shutdown_handler( sd_event_source *s, uint64_t usec, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Manager *m = userdata; HandleAction action; int r; assert(m); if (isempty(m->scheduled_shutdown_type)) return 0; if (streq(m->scheduled_shutdown_type, "halt")) action = HANDLE_HALT; else if (streq(m->scheduled_shutdown_type, "poweroff")) action = HANDLE_POWEROFF; else action = HANDLE_REBOOT; /* Don't allow multiple jobs being executed at the same time */ if (m->action_what) { r = -EALREADY; log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", m->scheduled_shutdown_type); goto error; } if (m->shutdown_dry_run) { /* We do not process delay inhibitors here. Otherwise, we * would have to be considered "in progress" (like the check * above) for some seconds after our admin has seen the final * wall message. */ bus_manager_log_shutdown(m, INHIBIT_SHUTDOWN, action); log_info("Running in dry run, suppressing action."); reset_scheduled_shutdown(m); return 0; } r = execute_shutdown_or_sleep(m, 0, action, &error); if (r < 0) { log_error_errno(r, "Scheduled shutdown to %s failed: %m", m->scheduled_shutdown_type); goto error; } return 0; error: reset_scheduled_shutdown(m); return r; }