]>
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 "alloc-util.h"
32 #include "hostname-util.h"
34 #include "path-util.h"
35 #include "string-util.h"
36 #include "terminal-util.h"
37 #include "user-util.h"
38 #include "utmp-wtmp.h"
40 int utmp_get_runlevel(int *runlevel
, int *previous
) {
41 struct utmpx
*found
, lookup
= { .ut_type
= RUN_LVL
};
47 /* If these values are set in the environment this takes
48 * precedence. Presumably, sysvinit does this to work around a
49 * race condition that would otherwise exist where we'd always
50 * go to disk and hence might read runlevel data that might be
51 * very new and does not apply to the current script being
54 e
= getenv("RUNLEVEL");
59 /* $PREVLEVEL seems to be an Upstart thing */
61 e
= getenv("PREVLEVEL");
71 if (utmpxname(_PATH_UTMPX
) < 0)
76 found
= getutxid(&lookup
);
82 a
= found
->ut_pid
& 0xFF;
83 b
= (found
->ut_pid
>> 8) & 0xFF;
97 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
) {
108 struct utsname uts
= {};
112 init_timestamp(store
, t
);
114 if (uname(&uts
) >= 0)
115 strncpy(store
->ut_host
, uts
.release
, sizeof(store
->ut_host
));
117 strncpy(store
->ut_line
, "~", sizeof(store
->ut_line
)); /* or ~~ ? */
118 strncpy(store
->ut_id
, "~~", sizeof(store
->ut_id
));
121 static int write_entry_utmp(const struct utmpx
*store
) {
126 /* utmp is similar to wtmp, but there is only one entry for
127 * each entry type resp. user; i.e. basically a key/value
130 if (utmpxname(_PATH_UTMPX
) < 0)
135 if (!pututxline(store
))
145 static int write_entry_wtmp(const struct utmpx
*store
) {
148 /* wtmp is a simple append-only file where each entry is
149 simply appended to the end; i.e. basically a log. */
152 updwtmpx(_PATH_WTMPX
, store
);
156 static int write_utmp_wtmp(const struct utmpx
*store_utmp
, const struct utmpx
*store_wtmp
) {
159 r
= write_entry_utmp(store_utmp
);
160 s
= write_entry_wtmp(store_wtmp
);
165 /* If utmp/wtmp have been disabled, that's a good thing, hence
166 * ignore the errors */
173 static int write_entry_both(const struct utmpx
*store
) {
174 return write_utmp_wtmp(store
, store
);
177 int utmp_put_shutdown(void) {
178 struct utmpx store
= {};
180 init_entry(&store
, 0);
182 store
.ut_type
= RUN_LVL
;
183 strncpy(store
.ut_user
, "shutdown", sizeof(store
.ut_user
));
185 return write_entry_both(&store
);
188 int utmp_put_reboot(usec_t t
) {
189 struct utmpx store
= {};
191 init_entry(&store
, t
);
193 store
.ut_type
= BOOT_TIME
;
194 strncpy(store
.ut_user
, "reboot", sizeof(store
.ut_user
));
196 return write_entry_both(&store
);
199 _pure_
static const char *sanitize_id(const char *id
) {
205 if (l
<= sizeof(((struct utmpx
*) NULL
)->ut_id
))
208 return id
+ l
- sizeof(((struct utmpx
*) NULL
)->ut_id
);
211 int utmp_put_init_process(const char *id
, pid_t pid
, pid_t sid
, const char *line
, int ut_type
, const char *user
) {
212 struct utmpx store
= {
213 .ut_type
= INIT_PROCESS
,
221 init_timestamp(&store
, 0);
223 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
224 strncpy(store
.ut_id
, sanitize_id(id
), sizeof(store
.ut_id
));
227 strncpy(store
.ut_line
, basename(line
), sizeof(store
.ut_line
));
229 r
= write_entry_both(&store
);
233 if (ut_type
== LOGIN_PROCESS
|| ut_type
== USER_PROCESS
) {
234 store
.ut_type
= LOGIN_PROCESS
;
235 r
= write_entry_both(&store
);
240 if (ut_type
== USER_PROCESS
) {
241 store
.ut_type
= USER_PROCESS
;
242 strncpy(store
.ut_user
, user
, sizeof(store
.ut_user
)-1);
243 r
= write_entry_both(&store
);
251 int utmp_put_dead_process(const char *id
, pid_t pid
, int code
, int status
) {
252 struct utmpx lookup
= {
253 .ut_type
= INIT_PROCESS
/* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
254 }, store
, store_wtmp
, *found
;
260 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
261 strncpy(lookup
.ut_id
, sanitize_id(id
), sizeof(lookup
.ut_id
));
263 found
= getutxid(&lookup
);
267 if (found
->ut_pid
!= pid
)
270 memcpy(&store
, found
, sizeof(store
));
271 store
.ut_type
= DEAD_PROCESS
;
272 store
.ut_exit
.e_termination
= code
;
273 store
.ut_exit
.e_exit
= status
;
279 memcpy(&store_wtmp
, &store
, sizeof(store_wtmp
));
280 /* wtmp wants the current time */
281 init_timestamp(&store_wtmp
, 0);
283 return write_utmp_wtmp(&store
, &store_wtmp
);
287 int utmp_put_runlevel(int runlevel
, int previous
) {
288 struct utmpx store
= {};
291 assert(runlevel
> 0);
294 /* Find the old runlevel automatically */
296 r
= utmp_get_runlevel(&previous
, NULL
);
305 if (previous
== runlevel
)
308 init_entry(&store
, 0);
310 store
.ut_type
= RUN_LVL
;
311 store
.ut_pid
= (runlevel
& 0xFF) | ((previous
& 0xFF) << 8);
312 strncpy(store
.ut_user
, "runlevel", sizeof(store
.ut_user
));
314 return write_entry_both(&store
);
317 #define TIMEOUT_MSEC 50
319 static int write_to_terminal(const char *tty
, const char *message
) {
320 _cleanup_close_
int fd
= -1;
328 fd
= open(tty
, O_WRONLY
|O_NDELAY
|O_NOCTTY
|O_CLOEXEC
);
329 if (fd
< 0 || !isatty(fd
))
333 left
= strlen(message
);
335 end
= now(CLOCK_MONOTONIC
) + TIMEOUT_MSEC
*USEC_PER_MSEC
;
339 struct pollfd pollfd
= {
346 t
= now(CLOCK_MONOTONIC
);
351 k
= poll(&pollfd
, 1, (end
- t
) / USEC_PER_MSEC
);
358 n
= write(fd
, p
, left
);
366 assert((size_t) n
<= left
);
377 const char *username
,
378 const char *origin_tty
,
379 bool (*match_tty
)(const char *tty
, void *userdata
),
382 _cleanup_free_
char *text
= NULL
, *hn
= NULL
, *un
= NULL
, *stdin_tty
= NULL
;
383 char date
[FORMAT_TIMESTAMP_MAX
];
387 hn
= gethostname_malloc();
391 un
= getlogname_malloc();
397 getttyname_harder(STDIN_FILENO
, &stdin_tty
);
398 origin_tty
= stdin_tty
;
403 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
406 origin_tty
? " on " : "", strempty(origin_tty
),
407 format_timestamp(date
, sizeof(date
), now(CLOCK_REALTIME
)),
415 while ((u
= getutxent())) {
416 _cleanup_free_
char *buf
= NULL
;
420 if (u
->ut_type
!= USER_PROCESS
|| u
->ut_user
[0] == 0)
423 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
424 if (path_startswith(u
->ut_line
, "/dev/"))
427 if (asprintf(&buf
, "/dev/%.*s", (int) sizeof(u
->ut_line
), u
->ut_line
) < 0)
433 if (!match_tty
|| match_tty(path
, userdata
)) {
434 q
= write_to_terminal(path
, text
);