]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/utmp-wtmp.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include <sys/utsname.h>
33 #include "alloc-util.h"
35 #include "hostname-util.h"
37 #include "path-util.h"
38 #include "string-util.h"
39 #include "terminal-util.h"
40 #include "time-util.h"
41 #include "user-util.h"
43 #include "utmp-wtmp.h"
45 int utmp_get_runlevel(int *runlevel
, int *previous
) {
46 struct utmpx
*found
, lookup
= { .ut_type
= RUN_LVL
};
52 /* If these values are set in the environment this takes
53 * precedence. Presumably, sysvinit does this to work around a
54 * race condition that would otherwise exist where we'd always
55 * go to disk and hence might read runlevel data that might be
56 * very new and does not apply to the current script being
59 e
= getenv("RUNLEVEL");
64 /* $PREVLEVEL seems to be an Upstart thing */
66 e
= getenv("PREVLEVEL");
76 if (utmpxname(_PATH_UTMPX
) < 0)
81 found
= getutxid(&lookup
);
87 a
= found
->ut_pid
& 0xFF;
88 b
= (found
->ut_pid
>> 8) & 0xFF;
102 static void init_timestamp(struct utmpx
*store
, usec_t t
) {
106 t
= now(CLOCK_REALTIME
);
108 store
->ut_tv
.tv_sec
= t
/ USEC_PER_SEC
;
109 store
->ut_tv
.tv_usec
= t
% USEC_PER_SEC
;
112 static void init_entry(struct utmpx
*store
, usec_t t
) {
113 struct utsname uts
= {};
117 init_timestamp(store
, t
);
119 if (uname(&uts
) >= 0)
120 strncpy(store
->ut_host
, uts
.release
, sizeof(store
->ut_host
));
122 strncpy(store
->ut_line
, "~", sizeof(store
->ut_line
)); /* or ~~ ? */
123 strncpy(store
->ut_id
, "~~", sizeof(store
->ut_id
));
126 static int write_entry_utmp(const struct utmpx
*store
) {
131 /* utmp is similar to wtmp, but there is only one entry for
132 * each entry type resp. user; i.e. basically a key/value
135 if (utmpxname(_PATH_UTMPX
) < 0)
140 if (!pututxline(store
))
150 static int write_entry_wtmp(const struct utmpx
*store
) {
153 /* wtmp is a simple append-only file where each entry is
154 simply appended to the end; i.e. basically a log. */
157 updwtmpx(_PATH_WTMPX
, store
);
161 static int write_utmp_wtmp(const struct utmpx
*store_utmp
, const struct utmpx
*store_wtmp
) {
164 r
= write_entry_utmp(store_utmp
);
165 s
= write_entry_wtmp(store_wtmp
);
170 /* If utmp/wtmp have been disabled, that's a good thing, hence
171 * ignore the errors */
178 static int write_entry_both(const struct utmpx
*store
) {
179 return write_utmp_wtmp(store
, store
);
182 int utmp_put_shutdown(void) {
183 struct utmpx store
= {};
185 init_entry(&store
, 0);
187 store
.ut_type
= RUN_LVL
;
188 strncpy(store
.ut_user
, "shutdown", sizeof(store
.ut_user
));
190 return write_entry_both(&store
);
193 int utmp_put_reboot(usec_t t
) {
194 struct utmpx store
= {};
196 init_entry(&store
, t
);
198 store
.ut_type
= BOOT_TIME
;
199 strncpy(store
.ut_user
, "reboot", sizeof(store
.ut_user
));
201 return write_entry_both(&store
);
204 _pure_
static const char *sanitize_id(const char *id
) {
210 if (l
<= sizeof(((struct utmpx
*) NULL
)->ut_id
))
213 return id
+ l
- sizeof(((struct utmpx
*) NULL
)->ut_id
);
216 int utmp_put_init_process(const char *id
, pid_t pid
, pid_t sid
, const char *line
, int ut_type
, const char *user
) {
217 struct utmpx store
= {
218 .ut_type
= INIT_PROCESS
,
226 init_timestamp(&store
, 0);
228 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
229 strncpy(store
.ut_id
, sanitize_id(id
), sizeof(store
.ut_id
));
232 strncpy(store
.ut_line
, basename(line
), sizeof(store
.ut_line
));
234 r
= write_entry_both(&store
);
238 if (IN_SET(ut_type
, LOGIN_PROCESS
, USER_PROCESS
)) {
239 store
.ut_type
= LOGIN_PROCESS
;
240 r
= write_entry_both(&store
);
245 if (ut_type
== USER_PROCESS
) {
246 store
.ut_type
= USER_PROCESS
;
247 strncpy(store
.ut_user
, user
, sizeof(store
.ut_user
)-1);
248 r
= write_entry_both(&store
);
256 int utmp_put_dead_process(const char *id
, pid_t pid
, int code
, int status
) {
257 struct utmpx lookup
= {
258 .ut_type
= INIT_PROCESS
/* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
259 }, store
, store_wtmp
, *found
;
265 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
266 strncpy(lookup
.ut_id
, sanitize_id(id
), sizeof(lookup
.ut_id
));
268 found
= getutxid(&lookup
);
272 if (found
->ut_pid
!= pid
)
275 memcpy(&store
, found
, sizeof(store
));
276 store
.ut_type
= DEAD_PROCESS
;
277 store
.ut_exit
.e_termination
= code
;
278 store
.ut_exit
.e_exit
= status
;
284 memcpy(&store_wtmp
, &store
, sizeof(store_wtmp
));
285 /* wtmp wants the current time */
286 init_timestamp(&store_wtmp
, 0);
288 return write_utmp_wtmp(&store
, &store_wtmp
);
292 int utmp_put_runlevel(int runlevel
, int previous
) {
293 struct utmpx store
= {};
296 assert(runlevel
> 0);
299 /* Find the old runlevel automatically */
301 r
= utmp_get_runlevel(&previous
, NULL
);
310 if (previous
== runlevel
)
313 init_entry(&store
, 0);
315 store
.ut_type
= RUN_LVL
;
316 store
.ut_pid
= (runlevel
& 0xFF) | ((previous
& 0xFF) << 8);
317 strncpy(store
.ut_user
, "runlevel", sizeof(store
.ut_user
));
319 return write_entry_both(&store
);
322 #define TIMEOUT_MSEC 50
324 static int write_to_terminal(const char *tty
, const char *message
) {
325 _cleanup_close_
int fd
= -1;
333 fd
= open(tty
, O_WRONLY
|O_NDELAY
|O_NOCTTY
|O_CLOEXEC
);
334 if (fd
< 0 || !isatty(fd
))
338 left
= strlen(message
);
340 end
= now(CLOCK_MONOTONIC
) + TIMEOUT_MSEC
*USEC_PER_MSEC
;
344 struct pollfd pollfd
= {
351 t
= now(CLOCK_MONOTONIC
);
356 k
= poll(&pollfd
, 1, (end
- t
) / USEC_PER_MSEC
);
363 n
= write(fd
, p
, left
);
371 assert((size_t) n
<= left
);
382 const char *username
,
383 const char *origin_tty
,
384 bool (*match_tty
)(const char *tty
, void *userdata
),
387 _cleanup_free_
char *text
= NULL
, *hn
= NULL
, *un
= NULL
, *stdin_tty
= NULL
;
388 char date
[FORMAT_TIMESTAMP_MAX
];
392 hn
= gethostname_malloc();
396 un
= getlogname_malloc();
402 getttyname_harder(STDIN_FILENO
, &stdin_tty
);
403 origin_tty
= stdin_tty
;
408 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
411 origin_tty
? " on " : "", strempty(origin_tty
),
412 format_timestamp(date
, sizeof(date
), now(CLOCK_REALTIME
)),
420 while ((u
= getutxent())) {
421 _cleanup_free_
char *buf
= NULL
;
425 if (u
->ut_type
!= USER_PROCESS
|| u
->ut_user
[0] == 0)
428 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
429 if (path_startswith(u
->ut_line
, "/dev/"))
432 if (asprintf(&buf
, "/dev/%.*s", (int) sizeof(u
->ut_line
), u
->ut_line
) < 0)
438 if (!match_tty
|| match_tty(path
, userdata
)) {
439 q
= write_to_terminal(path
, text
);