]>
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/>.
25 #include <sys/utsname.h>
31 #include "path-util.h"
32 #include "terminal-util.h"
33 #include "hostname-util.h"
34 #include "utmp-wtmp.h"
36 int utmp_get_runlevel(int *runlevel
, int *previous
) {
37 struct utmpx
*found
, lookup
= { .ut_type
= RUN_LVL
};
43 /* If these values are set in the environment this takes
44 * precedence. Presumably, sysvinit does this to work around a
45 * race condition that would otherwise exist where we'd always
46 * go to disk and hence might read runlevel data that might be
47 * very new and does not apply to the current script being
50 e
= getenv("RUNLEVEL");
55 /* $PREVLEVEL seems to be an Upstart thing */
57 e
= getenv("PREVLEVEL");
67 if (utmpxname(_PATH_UTMPX
) < 0)
72 found
= getutxid(&lookup
);
78 a
= found
->ut_pid
& 0xFF;
79 b
= (found
->ut_pid
>> 8) & 0xFF;
93 static void init_timestamp(struct utmpx
*store
, usec_t t
) {
97 t
= now(CLOCK_REALTIME
);
99 store
->ut_tv
.tv_sec
= t
/ USEC_PER_SEC
;
100 store
->ut_tv
.tv_usec
= t
% USEC_PER_SEC
;
103 static void init_entry(struct utmpx
*store
, usec_t t
) {
104 struct utsname uts
= {};
108 init_timestamp(store
, t
);
110 if (uname(&uts
) >= 0)
111 strncpy(store
->ut_host
, uts
.release
, sizeof(store
->ut_host
));
113 strncpy(store
->ut_line
, "~", sizeof(store
->ut_line
)); /* or ~~ ? */
114 strncpy(store
->ut_id
, "~~", sizeof(store
->ut_id
));
117 static int write_entry_utmp(const struct utmpx
*store
) {
122 /* utmp is similar to wtmp, but there is only one entry for
123 * each entry type resp. user; i.e. basically a key/value
126 if (utmpxname(_PATH_UTMPX
) < 0)
131 if (!pututxline(store
))
141 static int write_entry_wtmp(const struct utmpx
*store
) {
144 /* wtmp is a simple append-only file where each entry is
145 simply appended to the end; i.e. basically a log. */
148 updwtmpx(_PATH_WTMPX
, store
);
152 static int write_utmp_wtmp(const struct utmpx
*store_utmp
, const struct utmpx
*store_wtmp
) {
155 r
= write_entry_utmp(store_utmp
);
156 s
= write_entry_wtmp(store_wtmp
);
161 /* If utmp/wtmp have been disabled, that's a good thing, hence
162 * ignore the errors */
169 static int write_entry_both(const struct utmpx
*store
) {
170 return write_utmp_wtmp(store
, store
);
173 int utmp_put_shutdown(void) {
174 struct utmpx store
= {};
176 init_entry(&store
, 0);
178 store
.ut_type
= RUN_LVL
;
179 strncpy(store
.ut_user
, "shutdown", sizeof(store
.ut_user
));
181 return write_entry_both(&store
);
184 int utmp_put_reboot(usec_t t
) {
185 struct utmpx store
= {};
187 init_entry(&store
, t
);
189 store
.ut_type
= BOOT_TIME
;
190 strncpy(store
.ut_user
, "reboot", sizeof(store
.ut_user
));
192 return write_entry_both(&store
);
195 _pure_
static const char *sanitize_id(const char *id
) {
201 if (l
<= sizeof(((struct utmpx
*) NULL
)->ut_id
))
204 return id
+ l
- sizeof(((struct utmpx
*) NULL
)->ut_id
);
207 int utmp_put_init_process(const char *id
, pid_t pid
, pid_t sid
, const char *line
, int ut_type
, const char *user
) {
208 struct utmpx store
= {
209 .ut_type
= INIT_PROCESS
,
217 init_timestamp(&store
, 0);
219 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
220 strncpy(store
.ut_id
, sanitize_id(id
), sizeof(store
.ut_id
));
223 strncpy(store
.ut_line
, basename(line
), sizeof(store
.ut_line
));
225 r
= write_entry_both(&store
);
229 if (ut_type
== LOGIN_PROCESS
|| ut_type
== USER_PROCESS
) {
230 store
.ut_type
= LOGIN_PROCESS
;
231 r
= write_entry_both(&store
);
236 if (ut_type
== USER_PROCESS
) {
237 store
.ut_type
= USER_PROCESS
;
238 strncpy(store
.ut_user
, user
, sizeof(store
.ut_user
)-1);
239 r
= write_entry_both(&store
);
247 int utmp_put_dead_process(const char *id
, pid_t pid
, int code
, int status
) {
248 struct utmpx lookup
= {
249 .ut_type
= INIT_PROCESS
/* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
250 }, store
, store_wtmp
, *found
;
256 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
257 strncpy(lookup
.ut_id
, sanitize_id(id
), sizeof(lookup
.ut_id
));
259 found
= getutxid(&lookup
);
263 if (found
->ut_pid
!= pid
)
266 memcpy(&store
, found
, sizeof(store
));
267 store
.ut_type
= DEAD_PROCESS
;
268 store
.ut_exit
.e_termination
= code
;
269 store
.ut_exit
.e_exit
= status
;
275 memcpy(&store_wtmp
, &store
, sizeof(store_wtmp
));
276 /* wtmp wants the current time */
277 init_timestamp(&store_wtmp
, 0);
279 return write_utmp_wtmp(&store
, &store_wtmp
);
283 int utmp_put_runlevel(int runlevel
, int previous
) {
284 struct utmpx store
= {};
287 assert(runlevel
> 0);
290 /* Find the old runlevel automatically */
292 r
= utmp_get_runlevel(&previous
, NULL
);
301 if (previous
== runlevel
)
304 init_entry(&store
, 0);
306 store
.ut_type
= RUN_LVL
;
307 store
.ut_pid
= (runlevel
& 0xFF) | ((previous
& 0xFF) << 8);
308 strncpy(store
.ut_user
, "runlevel", sizeof(store
.ut_user
));
310 return write_entry_both(&store
);
313 #define TIMEOUT_MSEC 50
315 static int write_to_terminal(const char *tty
, const char *message
) {
316 _cleanup_close_
int fd
= -1;
324 fd
= open(tty
, O_WRONLY
|O_NDELAY
|O_NOCTTY
|O_CLOEXEC
);
325 if (fd
< 0 || !isatty(fd
))
329 left
= strlen(message
);
331 end
= now(CLOCK_MONOTONIC
) + TIMEOUT_MSEC
*USEC_PER_MSEC
;
335 struct pollfd pollfd
= {
342 t
= now(CLOCK_MONOTONIC
);
347 k
= poll(&pollfd
, 1, (end
- t
) / USEC_PER_MSEC
);
354 n
= write(fd
, p
, left
);
362 assert((size_t) n
<= left
);
373 const char *username
,
374 const char *origin_tty
,
375 bool (*match_tty
)(const char *tty
, void *userdata
),
378 _cleanup_free_
char *text
= NULL
, *hn
= NULL
, *un
= NULL
, *stdin_tty
= NULL
;
379 char date
[FORMAT_TIMESTAMP_MAX
];
383 hn
= gethostname_malloc();
387 un
= getlogname_malloc();
393 getttyname_harder(STDIN_FILENO
, &stdin_tty
);
394 origin_tty
= stdin_tty
;
399 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
402 origin_tty
? " on " : "", strempty(origin_tty
),
403 format_timestamp(date
, sizeof(date
), now(CLOCK_REALTIME
)),
411 while ((u
= getutxent())) {
412 _cleanup_free_
char *buf
= NULL
;
416 if (u
->ut_type
!= USER_PROCESS
|| u
->ut_user
[0] == 0)
419 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
420 if (path_startswith(u
->ut_line
, "/dev/"))
423 if (asprintf(&buf
, "/dev/%.*s", (int) sizeof(u
->ut_line
), u
->ut_line
) < 0)
429 if (!match_tty
|| match_tty(path
, userdata
)) {
430 q
= write_to_terminal(path
, text
);