]>
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/>.
30 #include <sys/utsname.h>
34 #include "alloc-util.h"
36 #include "hostname-util.h"
38 #include "path-util.h"
39 #include "string-util.h"
40 #include "terminal-util.h"
41 #include "time-util.h"
42 #include "user-util.h"
44 #include "utmp-wtmp.h"
46 int utmp_get_runlevel(int *runlevel
, int *previous
) {
47 struct utmpx
*found
, lookup
= { .ut_type
= RUN_LVL
};
53 /* If these values are set in the environment this takes
54 * precedence. Presumably, sysvinit does this to work around a
55 * race condition that would otherwise exist where we'd always
56 * go to disk and hence might read runlevel data that might be
57 * very new and does not apply to the current script being
60 e
= getenv("RUNLEVEL");
65 /* $PREVLEVEL seems to be an Upstart thing */
67 e
= getenv("PREVLEVEL");
77 if (utmpxname(_PATH_UTMPX
) < 0)
82 found
= getutxid(&lookup
);
88 a
= found
->ut_pid
& 0xFF;
89 b
= (found
->ut_pid
>> 8) & 0xFF;
103 static void init_timestamp(struct utmpx
*store
, usec_t t
) {
107 t
= now(CLOCK_REALTIME
);
109 store
->ut_tv
.tv_sec
= t
/ USEC_PER_SEC
;
110 store
->ut_tv
.tv_usec
= t
% USEC_PER_SEC
;
113 static void init_entry(struct utmpx
*store
, usec_t t
) {
114 struct utsname uts
= {};
118 init_timestamp(store
, t
);
120 if (uname(&uts
) >= 0)
121 strncpy(store
->ut_host
, uts
.release
, sizeof(store
->ut_host
));
123 strncpy(store
->ut_line
, "~", sizeof(store
->ut_line
)); /* or ~~ ? */
124 strncpy(store
->ut_id
, "~~", sizeof(store
->ut_id
));
127 static int write_entry_utmp(const struct utmpx
*store
) {
132 /* utmp is similar to wtmp, but there is only one entry for
133 * each entry type resp. user; i.e. basically a key/value
136 if (utmpxname(_PATH_UTMPX
) < 0)
141 if (!pututxline(store
))
151 static int write_entry_wtmp(const struct utmpx
*store
) {
154 /* wtmp is a simple append-only file where each entry is
155 simply appended to the end; i.e. basically a log. */
158 updwtmpx(_PATH_WTMPX
, store
);
162 static int write_utmp_wtmp(const struct utmpx
*store_utmp
, const struct utmpx
*store_wtmp
) {
165 r
= write_entry_utmp(store_utmp
);
166 s
= write_entry_wtmp(store_wtmp
);
171 /* If utmp/wtmp have been disabled, that's a good thing, hence
172 * ignore the errors */
179 static int write_entry_both(const struct utmpx
*store
) {
180 return write_utmp_wtmp(store
, store
);
183 int utmp_put_shutdown(void) {
184 struct utmpx store
= {};
186 init_entry(&store
, 0);
188 store
.ut_type
= RUN_LVL
;
189 strncpy(store
.ut_user
, "shutdown", sizeof(store
.ut_user
));
191 return write_entry_both(&store
);
194 int utmp_put_reboot(usec_t t
) {
195 struct utmpx store
= {};
197 init_entry(&store
, t
);
199 store
.ut_type
= BOOT_TIME
;
200 strncpy(store
.ut_user
, "reboot", sizeof(store
.ut_user
));
202 return write_entry_both(&store
);
205 _pure_
static const char *sanitize_id(const char *id
) {
211 if (l
<= sizeof(((struct utmpx
*) NULL
)->ut_id
))
214 return id
+ l
- sizeof(((struct utmpx
*) NULL
)->ut_id
);
217 int utmp_put_init_process(const char *id
, pid_t pid
, pid_t sid
, const char *line
, int ut_type
, const char *user
) {
218 struct utmpx store
= {
219 .ut_type
= INIT_PROCESS
,
227 init_timestamp(&store
, 0);
229 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
230 strncpy(store
.ut_id
, sanitize_id(id
), sizeof(store
.ut_id
));
233 strncpy(store
.ut_line
, basename(line
), sizeof(store
.ut_line
));
235 r
= write_entry_both(&store
);
239 if (ut_type
== LOGIN_PROCESS
|| ut_type
== USER_PROCESS
) {
240 store
.ut_type
= LOGIN_PROCESS
;
241 r
= write_entry_both(&store
);
246 if (ut_type
== USER_PROCESS
) {
247 store
.ut_type
= USER_PROCESS
;
248 strncpy(store
.ut_user
, user
, sizeof(store
.ut_user
)-1);
249 r
= write_entry_both(&store
);
257 int utmp_put_dead_process(const char *id
, pid_t pid
, int code
, int status
) {
258 struct utmpx lookup
= {
259 .ut_type
= INIT_PROCESS
/* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
260 }, store
, store_wtmp
, *found
;
266 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
267 strncpy(lookup
.ut_id
, sanitize_id(id
), sizeof(lookup
.ut_id
));
269 found
= getutxid(&lookup
);
273 if (found
->ut_pid
!= pid
)
276 memcpy(&store
, found
, sizeof(store
));
277 store
.ut_type
= DEAD_PROCESS
;
278 store
.ut_exit
.e_termination
= code
;
279 store
.ut_exit
.e_exit
= status
;
285 memcpy(&store_wtmp
, &store
, sizeof(store_wtmp
));
286 /* wtmp wants the current time */
287 init_timestamp(&store_wtmp
, 0);
289 return write_utmp_wtmp(&store
, &store_wtmp
);
293 int utmp_put_runlevel(int runlevel
, int previous
) {
294 struct utmpx store
= {};
297 assert(runlevel
> 0);
300 /* Find the old runlevel automatically */
302 r
= utmp_get_runlevel(&previous
, NULL
);
311 if (previous
== runlevel
)
314 init_entry(&store
, 0);
316 store
.ut_type
= RUN_LVL
;
317 store
.ut_pid
= (runlevel
& 0xFF) | ((previous
& 0xFF) << 8);
318 strncpy(store
.ut_user
, "runlevel", sizeof(store
.ut_user
));
320 return write_entry_both(&store
);
323 #define TIMEOUT_MSEC 50
325 static int write_to_terminal(const char *tty
, const char *message
) {
326 _cleanup_close_
int fd
= -1;
334 fd
= open(tty
, O_WRONLY
|O_NDELAY
|O_NOCTTY
|O_CLOEXEC
);
335 if (fd
< 0 || !isatty(fd
))
339 left
= strlen(message
);
341 end
= now(CLOCK_MONOTONIC
) + TIMEOUT_MSEC
*USEC_PER_MSEC
;
345 struct pollfd pollfd
= {
352 t
= now(CLOCK_MONOTONIC
);
357 k
= poll(&pollfd
, 1, (end
- t
) / USEC_PER_MSEC
);
364 n
= write(fd
, p
, left
);
372 assert((size_t) n
<= left
);
383 const char *username
,
384 const char *origin_tty
,
385 bool (*match_tty
)(const char *tty
, void *userdata
),
388 _cleanup_free_
char *text
= NULL
, *hn
= NULL
, *un
= NULL
, *stdin_tty
= NULL
;
389 char date
[FORMAT_TIMESTAMP_MAX
];
393 hn
= gethostname_malloc();
397 un
= getlogname_malloc();
403 getttyname_harder(STDIN_FILENO
, &stdin_tty
);
404 origin_tty
= stdin_tty
;
409 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
412 origin_tty
? " on " : "", strempty(origin_tty
),
413 format_timestamp(date
, sizeof(date
), now(CLOCK_REALTIME
)),
421 while ((u
= getutxent())) {
422 _cleanup_free_
char *buf
= NULL
;
426 if (u
->ut_type
!= USER_PROCESS
|| u
->ut_user
[0] == 0)
429 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
430 if (path_startswith(u
->ut_line
, "/dev/"))
433 if (asprintf(&buf
, "/dev/%.*s", (int) sizeof(u
->ut_line
), u
->ut_line
) < 0)
439 if (!match_tty
|| match_tty(path
, userdata
)) {
440 q
= write_to_terminal(path
, text
);