]>
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 "utmp-wtmp.h"
38 int utmp_get_runlevel(int *runlevel
, int *previous
) {
39 struct utmpx
*found
, lookup
= { .ut_type
= RUN_LVL
};
45 /* If these values are set in the environment this takes
46 * precedence. Presumably, sysvinit does this to work around a
47 * race condition that would otherwise exist where we'd always
48 * go to disk and hence might read runlevel data that might be
49 * very new and does not apply to the current script being
52 e
= getenv("RUNLEVEL");
57 /* $PREVLEVEL seems to be an Upstart thing */
59 e
= getenv("PREVLEVEL");
69 if (utmpxname(_PATH_UTMPX
) < 0)
74 found
= getutxid(&lookup
);
80 a
= found
->ut_pid
& 0xFF;
81 b
= (found
->ut_pid
>> 8) & 0xFF;
95 static void init_timestamp(struct utmpx
*store
, usec_t t
) {
99 t
= now(CLOCK_REALTIME
);
101 store
->ut_tv
.tv_sec
= t
/ USEC_PER_SEC
;
102 store
->ut_tv
.tv_usec
= t
% USEC_PER_SEC
;
105 static void init_entry(struct utmpx
*store
, usec_t t
) {
106 struct utsname uts
= {};
110 init_timestamp(store
, t
);
112 if (uname(&uts
) >= 0)
113 strncpy(store
->ut_host
, uts
.release
, sizeof(store
->ut_host
));
115 strncpy(store
->ut_line
, "~", sizeof(store
->ut_line
)); /* or ~~ ? */
116 strncpy(store
->ut_id
, "~~", sizeof(store
->ut_id
));
119 static int write_entry_utmp(const struct utmpx
*store
) {
124 /* utmp is similar to wtmp, but there is only one entry for
125 * each entry type resp. user; i.e. basically a key/value
128 if (utmpxname(_PATH_UTMPX
) < 0)
133 if (!pututxline(store
))
143 static int write_entry_wtmp(const struct utmpx
*store
) {
146 /* wtmp is a simple append-only file where each entry is
147 simply appended to the end; i.e. basically a log. */
150 updwtmpx(_PATH_WTMPX
, store
);
154 static int write_utmp_wtmp(const struct utmpx
*store_utmp
, const struct utmpx
*store_wtmp
) {
157 r
= write_entry_utmp(store_utmp
);
158 s
= write_entry_wtmp(store_wtmp
);
163 /* If utmp/wtmp have been disabled, that's a good thing, hence
164 * ignore the errors */
171 static int write_entry_both(const struct utmpx
*store
) {
172 return write_utmp_wtmp(store
, store
);
175 int utmp_put_shutdown(void) {
176 struct utmpx store
= {};
178 init_entry(&store
, 0);
180 store
.ut_type
= RUN_LVL
;
181 strncpy(store
.ut_user
, "shutdown", sizeof(store
.ut_user
));
183 return write_entry_both(&store
);
186 int utmp_put_reboot(usec_t t
) {
187 struct utmpx store
= {};
189 init_entry(&store
, t
);
191 store
.ut_type
= BOOT_TIME
;
192 strncpy(store
.ut_user
, "reboot", sizeof(store
.ut_user
));
194 return write_entry_both(&store
);
197 _pure_
static const char *sanitize_id(const char *id
) {
203 if (l
<= sizeof(((struct utmpx
*) NULL
)->ut_id
))
206 return id
+ l
- sizeof(((struct utmpx
*) NULL
)->ut_id
);
209 int utmp_put_init_process(const char *id
, pid_t pid
, pid_t sid
, const char *line
, int ut_type
, const char *user
) {
210 struct utmpx store
= {
211 .ut_type
= INIT_PROCESS
,
219 init_timestamp(&store
, 0);
221 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
222 strncpy(store
.ut_id
, sanitize_id(id
), sizeof(store
.ut_id
));
225 strncpy(store
.ut_line
, basename(line
), sizeof(store
.ut_line
));
227 r
= write_entry_both(&store
);
231 if (ut_type
== LOGIN_PROCESS
|| ut_type
== USER_PROCESS
) {
232 store
.ut_type
= LOGIN_PROCESS
;
233 r
= write_entry_both(&store
);
238 if (ut_type
== USER_PROCESS
) {
239 store
.ut_type
= USER_PROCESS
;
240 strncpy(store
.ut_user
, user
, sizeof(store
.ut_user
)-1);
241 r
= write_entry_both(&store
);
249 int utmp_put_dead_process(const char *id
, pid_t pid
, int code
, int status
) {
250 struct utmpx lookup
= {
251 .ut_type
= INIT_PROCESS
/* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
252 }, store
, store_wtmp
, *found
;
258 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
259 strncpy(lookup
.ut_id
, sanitize_id(id
), sizeof(lookup
.ut_id
));
261 found
= getutxid(&lookup
);
265 if (found
->ut_pid
!= pid
)
268 memcpy(&store
, found
, sizeof(store
));
269 store
.ut_type
= DEAD_PROCESS
;
270 store
.ut_exit
.e_termination
= code
;
271 store
.ut_exit
.e_exit
= status
;
277 memcpy(&store_wtmp
, &store
, sizeof(store_wtmp
));
278 /* wtmp wants the current time */
279 init_timestamp(&store_wtmp
, 0);
281 return write_utmp_wtmp(&store
, &store_wtmp
);
285 int utmp_put_runlevel(int runlevel
, int previous
) {
286 struct utmpx store
= {};
289 assert(runlevel
> 0);
292 /* Find the old runlevel automatically */
294 r
= utmp_get_runlevel(&previous
, NULL
);
303 if (previous
== runlevel
)
306 init_entry(&store
, 0);
308 store
.ut_type
= RUN_LVL
;
309 store
.ut_pid
= (runlevel
& 0xFF) | ((previous
& 0xFF) << 8);
310 strncpy(store
.ut_user
, "runlevel", sizeof(store
.ut_user
));
312 return write_entry_both(&store
);
315 #define TIMEOUT_MSEC 50
317 static int write_to_terminal(const char *tty
, const char *message
) {
318 _cleanup_close_
int fd
= -1;
326 fd
= open(tty
, O_WRONLY
|O_NDELAY
|O_NOCTTY
|O_CLOEXEC
);
327 if (fd
< 0 || !isatty(fd
))
331 left
= strlen(message
);
333 end
= now(CLOCK_MONOTONIC
) + TIMEOUT_MSEC
*USEC_PER_MSEC
;
337 struct pollfd pollfd
= {
344 t
= now(CLOCK_MONOTONIC
);
349 k
= poll(&pollfd
, 1, (end
- t
) / USEC_PER_MSEC
);
356 n
= write(fd
, p
, left
);
364 assert((size_t) n
<= left
);
375 const char *username
,
376 const char *origin_tty
,
377 bool (*match_tty
)(const char *tty
, void *userdata
),
380 _cleanup_free_
char *text
= NULL
, *hn
= NULL
, *un
= NULL
, *stdin_tty
= NULL
;
381 char date
[FORMAT_TIMESTAMP_MAX
];
385 hn
= gethostname_malloc();
389 un
= getlogname_malloc();
395 getttyname_harder(STDIN_FILENO
, &stdin_tty
);
396 origin_tty
= stdin_tty
;
401 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
404 origin_tty
? " on " : "", strempty(origin_tty
),
405 format_timestamp(date
, sizeof(date
), now(CLOCK_REALTIME
)),
413 while ((u
= getutxent())) {
414 _cleanup_free_
char *buf
= NULL
;
418 if (u
->ut_type
!= USER_PROCESS
|| u
->ut_user
[0] == 0)
421 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
422 if (path_startswith(u
->ut_line
, "/dev/"))
425 if (asprintf(&buf
, "/dev/%.*s", (int) sizeof(u
->ut_line
), u
->ut_line
) < 0)
431 if (!match_tty
|| match_tty(path
, userdata
)) {
432 q
= write_to_terminal(path
, text
);