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