]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/utmp-wtmp.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
26 #include <sys/utsname.h>
32 #include "utmp-wtmp.h"
34 int utmp_get_runlevel(int *runlevel
, int *previous
) {
35 struct utmpx lookup
, *found
;
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
48 if ((e
= getenv("RUNLEVEL")) && e
[0] > 0) {
52 /* $PREVLEVEL seems to be an Upstart thing */
54 if ((e
= getenv("PREVLEVEL")) && e
[0] > 0)
63 if (utmpxname(_PATH_UTMPX
) < 0)
69 lookup
.ut_type
= RUN_LVL
;
71 if (!(found
= getutxid(&lookup
)))
76 a
= found
->ut_pid
& 0xFF;
77 b
= (found
->ut_pid
>> 8) & 0xFF;
95 static void init_timestamp(struct utmpx
*store
, usec_t t
) {
101 t
= now(CLOCK_REALTIME
);
103 store
->ut_tv
.tv_sec
= t
/ USEC_PER_SEC
;
104 store
->ut_tv
.tv_usec
= t
% USEC_PER_SEC
;
107 static void init_entry(struct utmpx
*store
, usec_t t
) {
112 init_timestamp(store
, t
);
116 if (uname(&uts
) >= 0)
117 strncpy(store
->ut_host
, uts
.release
, sizeof(store
->ut_host
));
119 strncpy(store
->ut_line
, "~", sizeof(store
->ut_line
)); /* or ~~ ? */
120 strncpy(store
->ut_id
, "~~", sizeof(store
->ut_id
));
123 static int write_entry_utmp(const struct utmpx
*store
) {
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
132 if (utmpxname(_PATH_UTMPX
) < 0)
137 if (!pututxline(store
))
147 static int write_entry_wtmp(const struct utmpx
*store
) {
150 /* wtmp is a simple append-only file where each entry is
151 simply appended to * the end; i.e. basically a log. */
154 updwtmpx(_PATH_WTMPX
, store
);
158 static int write_utmp_wtmp(const struct utmpx
*store_utmp
, const struct utmpx
*store_wtmp
) {
161 r
= write_entry_utmp(store_utmp
);
162 s
= write_entry_wtmp(store_wtmp
);
167 /* If utmp/wtmp have been disabled, that's a good thing, hence
168 * ignore the errors */
175 static int write_entry_both(const struct utmpx
*store
) {
176 return write_utmp_wtmp(store
, store
);
179 int utmp_put_shutdown(void) {
182 init_entry(&store
, 0);
184 store
.ut_type
= RUN_LVL
;
185 strncpy(store
.ut_user
, "shutdown", sizeof(store
.ut_user
));
187 return write_entry_both(&store
);
190 int utmp_put_reboot(usec_t t
) {
193 init_entry(&store
, t
);
195 store
.ut_type
= BOOT_TIME
;
196 strncpy(store
.ut_user
, "reboot", sizeof(store
.ut_user
));
198 return write_entry_both(&store
);
201 static const char *sanitize_id(const char *id
) {
207 if (l
<= sizeof(((struct utmpx
*) NULL
)->ut_id
))
210 return id
+ l
- sizeof(((struct utmpx
*) NULL
)->ut_id
);
213 int utmp_put_init_process(const char *id
, pid_t pid
, pid_t sid
, const char *line
) {
218 init_timestamp(&store
, 0);
220 store
.ut_type
= INIT_PROCESS
;
222 store
.ut_session
= sid
;
224 strncpy(store
.ut_id
, sanitize_id(id
), sizeof(store
.ut_id
));
227 strncpy(store
.ut_line
, file_name_from_path(line
), sizeof(store
.ut_line
));
229 return write_entry_both(&store
);
232 int utmp_put_dead_process(const char *id
, pid_t pid
, int code
, int status
) {
233 struct utmpx lookup
, store
, store_wtmp
, *found
;
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
));
243 if (!(found
= getutxid(&lookup
)))
246 if (found
->ut_pid
!= pid
)
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
;
258 memcpy(&store_wtmp
, &store
, sizeof(store_wtmp
));
259 /* wtmp wants the current time */
260 init_timestamp(&store_wtmp
, 0);
262 return write_utmp_wtmp(&store
, &store_wtmp
);
266 int utmp_put_runlevel(int runlevel
, int previous
) {
270 assert(runlevel
> 0);
273 /* Find the old runlevel automatically */
275 if ((r
= utmp_get_runlevel(&previous
, NULL
)) < 0) {
283 if (previous
== runlevel
)
286 init_entry(&store
, 0);
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
));
292 return write_entry_both(&store
);
295 #define TIMEOUT_MSEC 50
297 static int write_to_terminal(const char *tty
, const char *message
) {
306 if ((fd
= open(tty
, O_WRONLY
|O_NDELAY
|O_NOCTTY
|O_CLOEXEC
)) < 0)
315 left
= strlen(message
);
317 end
= now(CLOCK_MONOTONIC
) + TIMEOUT_MSEC
*USEC_PER_MSEC
;
321 struct pollfd pollfd
;
325 t
= now(CLOCK_MONOTONIC
);
334 pollfd
.events
= POLLOUT
;
336 if ((k
= poll(&pollfd
, 1, (end
- t
) / USEC_PER_MSEC
)) < 0)
344 if ((n
= write(fd
, p
, left
)) < 0) {
353 assert((size_t) n
<= left
);
362 close_nointr_nofail(fd
);
367 int utmp_wall(const char *message
, bool (*match_tty
)(const char *tty
)) {
369 char date
[FORMAT_TIMESTAMP_MAX
];
370 char *text
= NULL
, *hn
= NULL
, *un
= NULL
, *tty
= NULL
;
373 if (!(hn
= gethostname_malloc()) ||
374 !(un
= getlogname_malloc())) {
379 getttyname_harder(STDIN_FILENO
, &tty
);
383 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
386 tty
? " on " : "", strempty(tty
),
387 format_timestamp(date
, sizeof(date
), now(CLOCK_REALTIME
)),
397 while ((u
= getutxent())) {
402 if (u
->ut_type
!= USER_PROCESS
|| u
->ut_user
[0] == 0)
405 if (path_startswith(u
->ut_line
, "/dev/"))
408 if (asprintf(&buf
, "/dev/%s", u
->ut_line
) < 0) {
416 if (!match_tty
|| match_tty(path
))
417 if ((q
= write_to_terminal(path
, text
)) < 0)