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