]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/utmp-wtmp.c
Merge pull request #1679 from evverx/refuse-manual-start-by-reload-or-restart
[thirdparty/systemd.git] / src / shared / utmp-wtmp.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <poll.h>
25 #include <string.h>
26 #include <sys/utsname.h>
27 #include <unistd.h>
28 #include <utmpx.h>
29
30 #include "fd-util.h"
31 #include "hostname-util.h"
32 #include "macro.h"
33 #include "path-util.h"
34 #include "string-util.h"
35 #include "terminal-util.h"
36 #include "user-util.h"
37 #include "utmp-wtmp.h"
38
39 int utmp_get_runlevel(int *runlevel, int *previous) {
40 struct utmpx *found, lookup = { .ut_type = RUN_LVL };
41 int r;
42 const char *e;
43
44 assert(runlevel);
45
46 /* If these values are set in the environment this takes
47 * precedence. Presumably, sysvinit does this to work around a
48 * race condition that would otherwise exist where we'd always
49 * go to disk and hence might read runlevel data that might be
50 * very new and does not apply to the current script being
51 * executed. */
52
53 e = getenv("RUNLEVEL");
54 if (e && e[0] > 0) {
55 *runlevel = e[0];
56
57 if (previous) {
58 /* $PREVLEVEL seems to be an Upstart thing */
59
60 e = getenv("PREVLEVEL");
61 if (e && e[0] > 0)
62 *previous = e[0];
63 else
64 *previous = 0;
65 }
66
67 return 0;
68 }
69
70 if (utmpxname(_PATH_UTMPX) < 0)
71 return -errno;
72
73 setutxent();
74
75 found = getutxid(&lookup);
76 if (!found)
77 r = -errno;
78 else {
79 int a, b;
80
81 a = found->ut_pid & 0xFF;
82 b = (found->ut_pid >> 8) & 0xFF;
83
84 *runlevel = a;
85 if (previous)
86 *previous = b;
87
88 r = 0;
89 }
90
91 endutxent();
92
93 return r;
94 }
95
96 static void init_timestamp(struct utmpx *store, usec_t t) {
97 assert(store);
98
99 if (t <= 0)
100 t = now(CLOCK_REALTIME);
101
102 store->ut_tv.tv_sec = t / USEC_PER_SEC;
103 store->ut_tv.tv_usec = t % USEC_PER_SEC;
104 }
105
106 static void init_entry(struct utmpx *store, usec_t t) {
107 struct utsname uts = {};
108
109 assert(store);
110
111 init_timestamp(store, t);
112
113 if (uname(&uts) >= 0)
114 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
115
116 strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
117 strncpy(store->ut_id, "~~", sizeof(store->ut_id));
118 }
119
120 static int write_entry_utmp(const struct utmpx *store) {
121 int r;
122
123 assert(store);
124
125 /* utmp is similar to wtmp, but there is only one entry for
126 * each entry type resp. user; i.e. basically a key/value
127 * table. */
128
129 if (utmpxname(_PATH_UTMPX) < 0)
130 return -errno;
131
132 setutxent();
133
134 if (!pututxline(store))
135 r = -errno;
136 else
137 r = 0;
138
139 endutxent();
140
141 return r;
142 }
143
144 static int write_entry_wtmp(const struct utmpx *store) {
145 assert(store);
146
147 /* wtmp is a simple append-only file where each entry is
148 simply appended to the end; i.e. basically a log. */
149
150 errno = 0;
151 updwtmpx(_PATH_WTMPX, store);
152 return -errno;
153 }
154
155 static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
156 int r, s;
157
158 r = write_entry_utmp(store_utmp);
159 s = write_entry_wtmp(store_wtmp);
160
161 if (r >= 0)
162 r = s;
163
164 /* If utmp/wtmp have been disabled, that's a good thing, hence
165 * ignore the errors */
166 if (r == -ENOENT)
167 r = 0;
168
169 return r;
170 }
171
172 static int write_entry_both(const struct utmpx *store) {
173 return write_utmp_wtmp(store, store);
174 }
175
176 int utmp_put_shutdown(void) {
177 struct utmpx store = {};
178
179 init_entry(&store, 0);
180
181 store.ut_type = RUN_LVL;
182 strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
183
184 return write_entry_both(&store);
185 }
186
187 int utmp_put_reboot(usec_t t) {
188 struct utmpx store = {};
189
190 init_entry(&store, t);
191
192 store.ut_type = BOOT_TIME;
193 strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
194
195 return write_entry_both(&store);
196 }
197
198 _pure_ static const char *sanitize_id(const char *id) {
199 size_t l;
200
201 assert(id);
202 l = strlen(id);
203
204 if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
205 return id;
206
207 return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
208 }
209
210 int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) {
211 struct utmpx store = {
212 .ut_type = INIT_PROCESS,
213 .ut_pid = pid,
214 .ut_session = sid,
215 };
216 int r;
217
218 assert(id);
219
220 init_timestamp(&store, 0);
221
222 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
223 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
224
225 if (line)
226 strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
227
228 r = write_entry_both(&store);
229 if (r < 0)
230 return r;
231
232 if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) {
233 store.ut_type = LOGIN_PROCESS;
234 r = write_entry_both(&store);
235 if (r < 0)
236 return r;
237 }
238
239 if (ut_type == USER_PROCESS) {
240 store.ut_type = USER_PROCESS;
241 strncpy(store.ut_user, user, sizeof(store.ut_user)-1);
242 r = write_entry_both(&store);
243 if (r < 0)
244 return r;
245 }
246
247 return 0;
248 }
249
250 int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
251 struct utmpx lookup = {
252 .ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
253 }, store, store_wtmp, *found;
254
255 assert(id);
256
257 setutxent();
258
259 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
260 strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
261
262 found = getutxid(&lookup);
263 if (!found)
264 return 0;
265
266 if (found->ut_pid != pid)
267 return 0;
268
269 memcpy(&store, found, sizeof(store));
270 store.ut_type = DEAD_PROCESS;
271 store.ut_exit.e_termination = code;
272 store.ut_exit.e_exit = status;
273
274 zero(store.ut_user);
275 zero(store.ut_host);
276 zero(store.ut_tv);
277
278 memcpy(&store_wtmp, &store, sizeof(store_wtmp));
279 /* wtmp wants the current time */
280 init_timestamp(&store_wtmp, 0);
281
282 return write_utmp_wtmp(&store, &store_wtmp);
283 }
284
285
286 int utmp_put_runlevel(int runlevel, int previous) {
287 struct utmpx store = {};
288 int r;
289
290 assert(runlevel > 0);
291
292 if (previous <= 0) {
293 /* Find the old runlevel automatically */
294
295 r = utmp_get_runlevel(&previous, NULL);
296 if (r < 0) {
297 if (r != -ESRCH)
298 return r;
299
300 previous = 0;
301 }
302 }
303
304 if (previous == runlevel)
305 return 0;
306
307 init_entry(&store, 0);
308
309 store.ut_type = RUN_LVL;
310 store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
311 strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
312
313 return write_entry_both(&store);
314 }
315
316 #define TIMEOUT_MSEC 50
317
318 static int write_to_terminal(const char *tty, const char *message) {
319 _cleanup_close_ int fd = -1;
320 const char *p;
321 size_t left;
322 usec_t end;
323
324 assert(tty);
325 assert(message);
326
327 fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC);
328 if (fd < 0 || !isatty(fd))
329 return -errno;
330
331 p = message;
332 left = strlen(message);
333
334 end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
335
336 while (left > 0) {
337 ssize_t n;
338 struct pollfd pollfd = {
339 .fd = fd,
340 .events = POLLOUT,
341 };
342 usec_t t;
343 int k;
344
345 t = now(CLOCK_MONOTONIC);
346
347 if (t >= end)
348 return -ETIME;
349
350 k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
351 if (k < 0)
352 return -errno;
353
354 if (k == 0)
355 return -ETIME;
356
357 n = write(fd, p, left);
358 if (n < 0) {
359 if (errno == EAGAIN)
360 continue;
361
362 return -errno;
363 }
364
365 assert((size_t) n <= left);
366
367 p += n;
368 left -= n;
369 }
370
371 return 0;
372 }
373
374 int utmp_wall(
375 const char *message,
376 const char *username,
377 const char *origin_tty,
378 bool (*match_tty)(const char *tty, void *userdata),
379 void *userdata) {
380
381 _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL;
382 char date[FORMAT_TIMESTAMP_MAX];
383 struct utmpx *u;
384 int r;
385
386 hn = gethostname_malloc();
387 if (!hn)
388 return -ENOMEM;
389 if (!username) {
390 un = getlogname_malloc();
391 if (!un)
392 return -ENOMEM;
393 }
394
395 if (!origin_tty) {
396 getttyname_harder(STDIN_FILENO, &stdin_tty);
397 origin_tty = stdin_tty;
398 }
399
400 if (asprintf(&text,
401 "\a\r\n"
402 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
403 "%s\r\n\r\n",
404 un ?: username, hn,
405 origin_tty ? " on " : "", strempty(origin_tty),
406 format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
407 message) < 0)
408 return -ENOMEM;
409
410 setutxent();
411
412 r = 0;
413
414 while ((u = getutxent())) {
415 _cleanup_free_ char *buf = NULL;
416 const char *path;
417 int q;
418
419 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
420 continue;
421
422 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
423 if (path_startswith(u->ut_line, "/dev/"))
424 path = u->ut_line;
425 else {
426 if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0)
427 return -ENOMEM;
428
429 path = buf;
430 }
431
432 if (!match_tty || match_tty(path, userdata)) {
433 q = write_to_terminal(path, text);
434 if (q < 0)
435 r = q;
436 }
437 }
438
439 return r;
440 }