1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <unistd.h>
5 
6 #include "sd-messages.h"
7 
8 #include "alloc-util.h"
9 #include "audit-util.h"
10 #include "bus-common-errors.h"
11 #include "bus-error.h"
12 #include "bus-util.h"
13 #include "event-util.h"
14 #include "format-util.h"
15 #include "logind.h"
16 #include "path-util.h"
17 #include "special.h"
18 #include "strv.h"
19 #include "unit-name.h"
20 #include "user-util.h"
21 #include "utmp-wtmp.h"
22 
when_wall(usec_t n,usec_t elapse)23 _const_ static usec_t when_wall(usec_t n, usec_t elapse) {
24 
25         usec_t left;
26         unsigned i;
27         static const int wall_timers[] = {
28                 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
29                 25, 40, 55, 70, 100, 130, 150, 180,
30         };
31 
32         /* If the time is already passed, then don't announce */
33         if (n >= elapse)
34                 return 0;
35 
36         left = elapse - n;
37 
38         for (i = 1; i < ELEMENTSOF(wall_timers); i++)
39                 if (wall_timers[i] * USEC_PER_MINUTE >= left)
40                         return left - wall_timers[i-1] * USEC_PER_MINUTE;
41 
42         return left % USEC_PER_HOUR;
43 }
44 
logind_wall_tty_filter(const char * tty,void * userdata)45 bool logind_wall_tty_filter(const char *tty, void *userdata) {
46         Manager *m = userdata;
47         const char *p;
48 
49         assert(m);
50 
51         if (!m->scheduled_shutdown_tty)
52                 return true;
53 
54         p = path_startswith(tty, "/dev/");
55         if (!p)
56                 return true;
57 
58         return !streq(p, m->scheduled_shutdown_tty);
59 }
60 
warn_wall(Manager * m,usec_t n)61 static int warn_wall(Manager *m, usec_t n) {
62         _cleanup_free_ char *l = NULL, *username = NULL;
63         usec_t left;
64         int r;
65 
66         assert(m);
67 
68         if (!m->enable_wall_messages || !m->scheduled_shutdown_action)
69                 return 0;
70 
71         left = m->scheduled_shutdown_timeout > n;
72 
73         r = asprintf(&l, "%s%sThe system is going down for %s %s%s!",
74                      strempty(m->wall_message),
75                      isempty(m->wall_message) ? "" : "\n",
76                      handle_action_to_string(m->scheduled_shutdown_action->handle),
77                      left ? "at " : "NOW",
78                      left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : "");
79         if (r < 0) {
80                 log_oom();
81                 return 0;
82         }
83 
84         username = uid_to_name(m->scheduled_shutdown_uid);
85         utmp_wall(l, username, m->scheduled_shutdown_tty, logind_wall_tty_filter, m);
86 
87         return 1;
88 }
89 
wall_message_timeout_handler(sd_event_source * s,uint64_t usec,void * userdata)90 static int wall_message_timeout_handler(
91                         sd_event_source *s,
92                         uint64_t usec,
93                         void *userdata) {
94 
95         Manager *m = userdata;
96         usec_t n, next;
97         int r;
98 
99         assert(m);
100         assert(s == m->wall_message_timeout_source);
101 
102         n = now(CLOCK_REALTIME);
103 
104         r = warn_wall(m, n);
105         if (r == 0)
106                 return 0;
107 
108         next = when_wall(n, m->scheduled_shutdown_timeout);
109         if (next > 0) {
110                 r = sd_event_source_set_time(s, n + next);
111                 if (r < 0)
112                         return log_error_errno(r, "sd_event_source_set_time() failed. %m");
113 
114                 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
115                 if (r < 0)
116                         return log_error_errno(r, "sd_event_source_set_enabled() failed. %m");
117         }
118 
119         return 0;
120 }
121 
manager_setup_wall_message_timer(Manager * m)122 int manager_setup_wall_message_timer(Manager *m) {
123 
124         usec_t n, elapse;
125         int r;
126 
127         assert(m);
128 
129         n = now(CLOCK_REALTIME);
130         elapse = m->scheduled_shutdown_timeout;
131 
132         /* wall message handling */
133 
134         if (!m->scheduled_shutdown_action)
135                 return 0;
136 
137         if (elapse > 0 && elapse < n)
138                 return 0;
139 
140         /* Warn immediately if less than 15 minutes are left */
141         if (elapse == 0 || elapse - n < 15 * USEC_PER_MINUTE) {
142                 r = warn_wall(m, n);
143                 if (r == 0)
144                         return 0;
145         }
146 
147         elapse = when_wall(n, elapse);
148         if (elapse == 0)
149                 return 0;
150 
151         r = event_reset_time(m->event, &m->wall_message_timeout_source,
152                              CLOCK_REALTIME,
153                              n + elapse, 0,
154                              wall_message_timeout_handler, m,
155                              0, "wall-message-timer", true);
156 
157         if (r < 0) {
158                 m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
159                 return log_error_errno(r, "Failed to set up wall message timer: %m");
160         }
161 
162         return 0;
163 }
164