e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack/***
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack This file is part of systemd.
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack Copyright 2015 Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack systemd is free software; you can redistribute it and/or modify it
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack under the terms of the GNU Lesser General Public License as published by
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack the Free Software Foundation; either version 2.1 of the License, or
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack (at your option) any later version.
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack systemd is distributed in the hope that it will be useful, but
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack WITHOUT ANY WARRANTY; without even the implied warranty of
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack Lesser General Public License for more details.
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack You should have received a copy of the GNU Lesser General Public License
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack along with systemd; If not, see <http://www.gnu.org/licenses/>.
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack***/
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include <errno.h>
cf0fbc49e67b55f8d346fc94de28c90113505297Thomas Hindoe Paaboel Andersen#include <pwd.h>
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include <string.h>
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include <unistd.h>
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "sd-messages.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include "alloc-util.h"
430f0182b72373145c839dbfe99d2382855cb8f8Lennart Poettering#include "audit-util.h"
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "bus-common-errors.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "bus-error.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "bus-util.h"
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "formats-util.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "logind.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "special.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "strv.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "unit-name.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "user-util.h"
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "utmp-wtmp.h"
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack usec_t left;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack unsigned int i;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack static const int wall_timers[] = {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack 25, 40, 55, 70, 100, 130, 150, 180,
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack };
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* If the time is already passed, then don't announce */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (n >= elapse)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack left = elapse - n;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack for (i = 1; i < ELEMENTSOF(wall_timers); i++)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (wall_timers[i] * USEC_PER_MINUTE >= left)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return left - wall_timers[i-1] * USEC_PER_MINUTE;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return left % USEC_PER_HOUR;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack}
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mackbool logind_wall_tty_filter(const char *tty, void *userdata) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack Manager *m = userdata;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack assert(m);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!startswith(tty, "/dev/"))
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return true;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return !streq(tty + 5, m->scheduled_shutdown_tty);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack}
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mackstatic int warn_wall(Manager *m, usec_t n) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack char date[FORMAT_TIMESTAMP_MAX] = {};
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack _cleanup_free_ char *l = NULL;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack usec_t left;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack int r;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack assert(m);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!m->enable_wall_messages)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack left = m->scheduled_shutdown_timeout > n;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = asprintf(&l, "%s%sThe system is going down for %s %s%s!",
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack strempty(m->wall_message),
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack isempty(m->wall_message) ? "" : "\n",
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack m->scheduled_shutdown_type,
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack left ? "at " : "NOW",
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack left ? format_timestamp(date, sizeof(date), m->scheduled_shutdown_timeout) : "");
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack log_oom();
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
d02608170e599b1ffbc7c9a22062bae2579d6e36Lennart Poettering utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid),
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack m->scheduled_shutdown_tty, logind_wall_tty_filter, m);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 1;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack}
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mackstatic int wall_message_timeout_handler(
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack sd_event_source *s,
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint64_t usec,
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack void *userdata) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack Manager *m = userdata;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack usec_t n, next;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack int r;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack assert(m);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack assert(s == m->wall_message_timeout_source);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack n = now(CLOCK_REALTIME);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = warn_wall(m, n);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r == 0)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack next = when_wall(n, m->scheduled_shutdown_timeout);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (next > 0) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = sd_event_source_set_time(s, n + next);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0)
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack return log_error_errno(r, "sd_event_source_set_time() failed. %m");
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0)
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack return log_error_errno(r, "sd_event_source_set_enabled() failed. %m");
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack}
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mackint manager_setup_wall_message_timer(Manager *m) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack usec_t n, elapse;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack int r;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack assert(m);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack n = now(CLOCK_REALTIME);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack elapse = m->scheduled_shutdown_timeout;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* wall message handling */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (isempty(m->scheduled_shutdown_type)) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack warn_wall(m, n);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (elapse < n)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* Warn immediately if less than 15 minutes are left */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (elapse - n < 15 * USEC_PER_MINUTE) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = warn_wall(m, n);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r == 0)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack elapse = when_wall(n, elapse);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (elapse == 0)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (m->wall_message_timeout_source) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0)
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack return log_error_errno(r, "sd_event_source_set_time() failed. %m");
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0)
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack return log_error_errno(r, "sd_event_source_set_enabled() failed. %m");
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack } else {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = sd_event_add_time(m->event, &m->wall_message_timeout_source,
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0)
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack return log_error_errno(r, "sd_event_add_time() failed. %m");
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack}