]>
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>
31 #include "hostname-util.h"
33 #include "path-util.h"
34 #include "string-util.h"
35 #include "terminal-util.h"
36 #include "user-util.h"
37 #include "utmp-wtmp.h"
39 int utmp_get_runlevel(int *runlevel
, int *previous
) {
40 struct utmpx
*found
, lookup
= { .ut_type
= RUN_LVL
};
46 /* If these values are set in the environment this takes
47 * precedence. Presumably, sysvinit does this to work around a
48 * race condition that would otherwise exist where we'd always
49 * go to disk and hence might read runlevel data that might be
50 * very new and does not apply to the current script being
53 e
= getenv("RUNLEVEL");
58 /* $PREVLEVEL seems to be an Upstart thing */
60 e
= getenv("PREVLEVEL");
70 if (utmpxname(_PATH_UTMPX
) < 0)
75 found
= getutxid(&lookup
);
81 a
= found
->ut_pid
& 0xFF;
82 b
= (found
->ut_pid
>> 8) & 0xFF;
96 static void init_timestamp(struct utmpx
*store
, usec_t t
) {
100 t
= now(CLOCK_REALTIME
);
102 store
->ut_tv
.tv_sec
= t
/ USEC_PER_SEC
;
103 store
->ut_tv
.tv_usec
= t
% USEC_PER_SEC
;
106 static void init_entry(struct utmpx
*store
, usec_t t
) {
107 struct utsname uts
= {};
111 init_timestamp(store
, t
);
113 if (uname(&uts
) >= 0)
114 strncpy(store
->ut_host
, uts
.release
, sizeof(store
->ut_host
));
116 strncpy(store
->ut_line
, "~", sizeof(store
->ut_line
)); /* or ~~ ? */
117 strncpy(store
->ut_id
, "~~", sizeof(store
->ut_id
));
120 static int write_entry_utmp(const struct utmpx
*store
) {
125 /* utmp is similar to wtmp, but there is only one entry for
126 * each entry type resp. user; i.e. basically a key/value
129 if (utmpxname(_PATH_UTMPX
) < 0)
134 if (!pututxline(store
))
144 static int write_entry_wtmp(const struct utmpx
*store
) {
147 /* wtmp is a simple append-only file where each entry is
148 simply appended to the end; i.e. basically a log. */
151 updwtmpx(_PATH_WTMPX
, store
);
155 static int write_utmp_wtmp(const struct utmpx
*store_utmp
, const struct utmpx
*store_wtmp
) {
158 r
= write_entry_utmp(store_utmp
);
159 s
= write_entry_wtmp(store_wtmp
);
164 /* If utmp/wtmp have been disabled, that's a good thing, hence
165 * ignore the errors */
172 static int write_entry_both(const struct utmpx
*store
) {
173 return write_utmp_wtmp(store
, store
);
176 int utmp_put_shutdown(void) {
177 struct utmpx store
= {};
179 init_entry(&store
, 0);
181 store
.ut_type
= RUN_LVL
;
182 strncpy(store
.ut_user
, "shutdown", sizeof(store
.ut_user
));
184 return write_entry_both(&store
);
187 int utmp_put_reboot(usec_t t
) {
188 struct utmpx store
= {};
190 init_entry(&store
, t
);
192 store
.ut_type
= BOOT_TIME
;
193 strncpy(store
.ut_user
, "reboot", sizeof(store
.ut_user
));
195 return write_entry_both(&store
);
198 _pure_
static const char *sanitize_id(const char *id
) {
204 if (l
<= sizeof(((struct utmpx
*) NULL
)->ut_id
))
207 return id
+ l
- sizeof(((struct utmpx
*) NULL
)->ut_id
);
210 int utmp_put_init_process(const char *id
, pid_t pid
, pid_t sid
, const char *line
, int ut_type
, const char *user
) {
211 struct utmpx store
= {
212 .ut_type
= INIT_PROCESS
,
220 init_timestamp(&store
, 0);
222 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
223 strncpy(store
.ut_id
, sanitize_id(id
), sizeof(store
.ut_id
));
226 strncpy(store
.ut_line
, basename(line
), sizeof(store
.ut_line
));
228 r
= write_entry_both(&store
);
232 if (ut_type
== LOGIN_PROCESS
|| ut_type
== USER_PROCESS
) {
233 store
.ut_type
= LOGIN_PROCESS
;
234 r
= write_entry_both(&store
);
239 if (ut_type
== USER_PROCESS
) {
240 store
.ut_type
= USER_PROCESS
;
241 strncpy(store
.ut_user
, user
, sizeof(store
.ut_user
)-1);
242 r
= write_entry_both(&store
);
250 int utmp_put_dead_process(const char *id
, pid_t pid
, int code
, int status
) {
251 struct utmpx lookup
= {
252 .ut_type
= INIT_PROCESS
/* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
253 }, store
, store_wtmp
, *found
;
259 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
260 strncpy(lookup
.ut_id
, sanitize_id(id
), sizeof(lookup
.ut_id
));
262 found
= getutxid(&lookup
);
266 if (found
->ut_pid
!= pid
)
269 memcpy(&store
, found
, sizeof(store
));
270 store
.ut_type
= DEAD_PROCESS
;
271 store
.ut_exit
.e_termination
= code
;
272 store
.ut_exit
.e_exit
= status
;
278 memcpy(&store_wtmp
, &store
, sizeof(store_wtmp
));
279 /* wtmp wants the current time */
280 init_timestamp(&store_wtmp
, 0);
282 return write_utmp_wtmp(&store
, &store_wtmp
);
286 int utmp_put_runlevel(int runlevel
, int previous
) {
287 struct utmpx store
= {};
290 assert(runlevel
> 0);
293 /* Find the old runlevel automatically */
295 r
= utmp_get_runlevel(&previous
, NULL
);
304 if (previous
== runlevel
)
307 init_entry(&store
, 0);
309 store
.ut_type
= RUN_LVL
;
310 store
.ut_pid
= (runlevel
& 0xFF) | ((previous
& 0xFF) << 8);
311 strncpy(store
.ut_user
, "runlevel", sizeof(store
.ut_user
));
313 return write_entry_both(&store
);
316 #define TIMEOUT_MSEC 50
318 static int write_to_terminal(const char *tty
, const char *message
) {
319 _cleanup_close_
int fd
= -1;
327 fd
= open(tty
, O_WRONLY
|O_NDELAY
|O_NOCTTY
|O_CLOEXEC
);
328 if (fd
< 0 || !isatty(fd
))
332 left
= strlen(message
);
334 end
= now(CLOCK_MONOTONIC
) + TIMEOUT_MSEC
*USEC_PER_MSEC
;
338 struct pollfd pollfd
= {
345 t
= now(CLOCK_MONOTONIC
);
350 k
= poll(&pollfd
, 1, (end
- t
) / USEC_PER_MSEC
);
357 n
= write(fd
, p
, left
);
365 assert((size_t) n
<= left
);
376 const char *username
,
377 const char *origin_tty
,
378 bool (*match_tty
)(const char *tty
, void *userdata
),
381 _cleanup_free_
char *text
= NULL
, *hn
= NULL
, *un
= NULL
, *stdin_tty
= NULL
;
382 char date
[FORMAT_TIMESTAMP_MAX
];
386 hn
= gethostname_malloc();
390 un
= getlogname_malloc();
396 getttyname_harder(STDIN_FILENO
, &stdin_tty
);
397 origin_tty
= stdin_tty
;
402 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
405 origin_tty
? " on " : "", strempty(origin_tty
),
406 format_timestamp(date
, sizeof(date
), now(CLOCK_REALTIME
)),
414 while ((u
= getutxent())) {
415 _cleanup_free_
char *buf
= NULL
;
419 if (u
->ut_type
!= USER_PROCESS
|| u
->ut_user
[0] == 0)
422 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
423 if (path_startswith(u
->ut_line
, "/dev/"))
426 if (asprintf(&buf
, "/dev/%.*s", (int) sizeof(u
->ut_line
), u
->ut_line
) < 0)
432 if (!match_tty
|| match_tty(path
, userdata
)) {
433 q
= write_to_terminal(path
, text
);