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