]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/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>
30 #include "hostname-util.h"
32 #include "path-util.h"
33 #include "string-util.h"
34 #include "terminal-util.h"
35 #include "utmp-wtmp.h"
37 int utmp_get_runlevel(int *runlevel
, int *previous
) {
38 struct utmpx
*found
, lookup
= { .ut_type
= RUN_LVL
};
44 /* If these values are set in the environment this takes
45 * precedence. Presumably, sysvinit does this to work around a
46 * race condition that would otherwise exist where we'd always
47 * go to disk and hence might read runlevel data that might be
48 * very new and does not apply to the current script being
51 e
= getenv("RUNLEVEL");
56 /* $PREVLEVEL seems to be an Upstart thing */
58 e
= getenv("PREVLEVEL");
68 if (utmpxname(_PATH_UTMPX
) < 0)
73 found
= getutxid(&lookup
);
79 a
= found
->ut_pid
& 0xFF;
80 b
= (found
->ut_pid
>> 8) & 0xFF;
94 static void init_timestamp(struct utmpx
*store
, usec_t t
) {
98 t
= now(CLOCK_REALTIME
);
100 store
->ut_tv
.tv_sec
= t
/ USEC_PER_SEC
;
101 store
->ut_tv
.tv_usec
= t
% USEC_PER_SEC
;
104 static void init_entry(struct utmpx
*store
, usec_t t
) {
105 struct utsname uts
= {};
109 init_timestamp(store
, t
);
111 if (uname(&uts
) >= 0)
112 strncpy(store
->ut_host
, uts
.release
, sizeof(store
->ut_host
));
114 strncpy(store
->ut_line
, "~", sizeof(store
->ut_line
)); /* or ~~ ? */
115 strncpy(store
->ut_id
, "~~", sizeof(store
->ut_id
));
118 static int write_entry_utmp(const struct utmpx
*store
) {
123 /* utmp is similar to wtmp, but there is only one entry for
124 * each entry type resp. user; i.e. basically a key/value
127 if (utmpxname(_PATH_UTMPX
) < 0)
132 if (!pututxline(store
))
142 static int write_entry_wtmp(const struct utmpx
*store
) {
145 /* wtmp is a simple append-only file where each entry is
146 simply appended to the end; i.e. basically a log. */
149 updwtmpx(_PATH_WTMPX
, store
);
153 static int write_utmp_wtmp(const struct utmpx
*store_utmp
, const struct utmpx
*store_wtmp
) {
156 r
= write_entry_utmp(store_utmp
);
157 s
= write_entry_wtmp(store_wtmp
);
162 /* If utmp/wtmp have been disabled, that's a good thing, hence
163 * ignore the errors */
170 static int write_entry_both(const struct utmpx
*store
) {
171 return write_utmp_wtmp(store
, store
);
174 int utmp_put_shutdown(void) {
175 struct utmpx store
= {};
177 init_entry(&store
, 0);
179 store
.ut_type
= RUN_LVL
;
180 strncpy(store
.ut_user
, "shutdown", sizeof(store
.ut_user
));
182 return write_entry_both(&store
);
185 int utmp_put_reboot(usec_t t
) {
186 struct utmpx store
= {};
188 init_entry(&store
, t
);
190 store
.ut_type
= BOOT_TIME
;
191 strncpy(store
.ut_user
, "reboot", sizeof(store
.ut_user
));
193 return write_entry_both(&store
);
196 _pure_
static const char *sanitize_id(const char *id
) {
202 if (l
<= sizeof(((struct utmpx
*) NULL
)->ut_id
))
205 return id
+ l
- sizeof(((struct utmpx
*) NULL
)->ut_id
);
208 int utmp_put_init_process(const char *id
, pid_t pid
, pid_t sid
, const char *line
, int ut_type
, const char *user
) {
209 struct utmpx store
= {
210 .ut_type
= INIT_PROCESS
,
218 init_timestamp(&store
, 0);
220 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
221 strncpy(store
.ut_id
, sanitize_id(id
), sizeof(store
.ut_id
));
224 strncpy(store
.ut_line
, basename(line
), sizeof(store
.ut_line
));
226 r
= write_entry_both(&store
);
230 if (ut_type
== LOGIN_PROCESS
|| ut_type
== USER_PROCESS
) {
231 store
.ut_type
= LOGIN_PROCESS
;
232 r
= write_entry_both(&store
);
237 if (ut_type
== USER_PROCESS
) {
238 store
.ut_type
= USER_PROCESS
;
239 strncpy(store
.ut_user
, user
, sizeof(store
.ut_user
)-1);
240 r
= write_entry_both(&store
);
248 int utmp_put_dead_process(const char *id
, pid_t pid
, int code
, int status
) {
249 struct utmpx lookup
= {
250 .ut_type
= INIT_PROCESS
/* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
251 }, store
, store_wtmp
, *found
;
257 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
258 strncpy(lookup
.ut_id
, sanitize_id(id
), sizeof(lookup
.ut_id
));
260 found
= getutxid(&lookup
);
264 if (found
->ut_pid
!= pid
)
267 memcpy(&store
, found
, sizeof(store
));
268 store
.ut_type
= DEAD_PROCESS
;
269 store
.ut_exit
.e_termination
= code
;
270 store
.ut_exit
.e_exit
= status
;
276 memcpy(&store_wtmp
, &store
, sizeof(store_wtmp
));
277 /* wtmp wants the current time */
278 init_timestamp(&store_wtmp
, 0);
280 return write_utmp_wtmp(&store
, &store_wtmp
);
284 int utmp_put_runlevel(int runlevel
, int previous
) {
285 struct utmpx store
= {};
288 assert(runlevel
> 0);
291 /* Find the old runlevel automatically */
293 r
= utmp_get_runlevel(&previous
, NULL
);
302 if (previous
== runlevel
)
305 init_entry(&store
, 0);
307 store
.ut_type
= RUN_LVL
;
308 store
.ut_pid
= (runlevel
& 0xFF) | ((previous
& 0xFF) << 8);
309 strncpy(store
.ut_user
, "runlevel", sizeof(store
.ut_user
));
311 return write_entry_both(&store
);
314 #define TIMEOUT_MSEC 50
316 static int write_to_terminal(const char *tty
, const char *message
) {
317 _cleanup_close_
int fd
= -1;
325 fd
= open(tty
, O_WRONLY
|O_NDELAY
|O_NOCTTY
|O_CLOEXEC
);
326 if (fd
< 0 || !isatty(fd
))
330 left
= strlen(message
);
332 end
= now(CLOCK_MONOTONIC
) + TIMEOUT_MSEC
*USEC_PER_MSEC
;
336 struct pollfd pollfd
= {
343 t
= now(CLOCK_MONOTONIC
);
348 k
= poll(&pollfd
, 1, (end
- t
) / USEC_PER_MSEC
);
355 n
= write(fd
, p
, left
);
363 assert((size_t) n
<= left
);
374 const char *username
,
375 const char *origin_tty
,
376 bool (*match_tty
)(const char *tty
, void *userdata
),
379 _cleanup_free_
char *text
= NULL
, *hn
= NULL
, *un
= NULL
, *stdin_tty
= NULL
;
380 char date
[FORMAT_TIMESTAMP_MAX
];
384 hn
= gethostname_malloc();
388 un
= getlogname_malloc();
394 getttyname_harder(STDIN_FILENO
, &stdin_tty
);
395 origin_tty
= stdin_tty
;
400 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
403 origin_tty
? " on " : "", strempty(origin_tty
),
404 format_timestamp(date
, sizeof(date
), now(CLOCK_REALTIME
)),
412 while ((u
= getutxent())) {
413 _cleanup_free_
char *buf
= NULL
;
417 if (u
->ut_type
!= USER_PROCESS
|| u
->ut_user
[0] == 0)
420 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
421 if (path_startswith(u
->ut_line
, "/dev/"))
424 if (asprintf(&buf
, "/dev/%.*s", (int) sizeof(u
->ut_line
), u
->ut_line
) < 0)
430 if (!match_tty
|| match_tty(path
, userdata
)) {
431 q
= write_to_terminal(path
, text
);