1 /* SPDX-License-Identifier: LGPL-2.1+ */
13 #include "alloc-util.h"
14 #include "bus-error.h"
16 #include "format-util.h"
19 #include "process-util.h"
22 #include "unit-name.h"
24 #include "utmp-wtmp.h"
26 typedef struct Context
{
33 static void context_clear(Context
*c
) {
36 c
->bus
= sd_bus_flush_close_unref(c
->bus
);
39 audit_close(c
->audit_fd
);
44 static usec_t
get_startup_time(Context
*c
) {
45 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
51 r
= sd_bus_get_property_trivial(
53 "org.freedesktop.systemd1",
54 "/org/freedesktop/systemd1",
55 "org.freedesktop.systemd1.Manager",
60 log_error_errno(r
, "Failed to get timestamp: %s", bus_error_message(&error
, r
));
67 static int get_current_runlevel(Context
*c
) {
72 /* The first target of this list that is active or has
73 * a job scheduled wins. We prefer runlevels 5 and 3
74 * here over the others, since these are the main
75 * runlevels used on Fedora. It might make sense to
76 * change the order on some distributions. */
77 { '5', SPECIAL_GRAPHICAL_TARGET
},
78 { '3', SPECIAL_MULTI_USER_TARGET
},
79 { '1', SPECIAL_RESCUE_TARGET
},
82 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
88 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
89 _cleanup_free_
char *state
= NULL
, *path
= NULL
;
91 path
= unit_dbus_path_from_name(table
[i
].special
);
95 r
= sd_bus_get_property_string(
97 "org.freedesktop.systemd1",
99 "org.freedesktop.systemd1.Unit",
104 return log_warning_errno(r
, "Failed to get state: %s", bus_error_message(&error
, r
));
106 if (STR_IN_SET(state
, "active", "reloading"))
107 return table
[i
].runlevel
;
113 static int on_reboot(Context
*c
) {
119 /* We finished start-up, so let's write the utmp
120 * record and send the audit msg */
123 if (c
->audit_fd
>= 0)
124 if (audit_log_user_comm_message(c
->audit_fd
, AUDIT_SYSTEM_BOOT
, "", "systemd-update-utmp", NULL
, NULL
, NULL
, 1) < 0 &&
126 r
= log_error_errno(errno
, "Failed to send audit message: %m");
130 /* If this call fails it will return 0, which
131 * utmp_put_reboot() will then fix to the current time */
132 t
= get_startup_time(c
);
134 q
= utmp_put_reboot(t
);
136 log_error_errno(q
, "Failed to write utmp record: %m");
143 static int on_shutdown(Context
*c
) {
148 /* We started shut-down, so let's write the utmp
149 * record and send the audit msg */
152 if (c
->audit_fd
>= 0)
153 if (audit_log_user_comm_message(c
->audit_fd
, AUDIT_SYSTEM_SHUTDOWN
, "", "systemd-update-utmp", NULL
, NULL
, NULL
, 1) < 0 &&
155 r
= log_error_errno(errno
, "Failed to send audit message: %m");
159 q
= utmp_put_shutdown();
161 log_error_errno(q
, "Failed to write utmp record: %m");
168 static int on_runlevel(Context
*c
) {
169 int r
= 0, q
, previous
, runlevel
;
173 /* We finished changing runlevel, so let's write the
174 * utmp record and send the audit msg */
176 /* First, get last runlevel */
177 q
= utmp_get_runlevel(&previous
, NULL
);
180 if (!IN_SET(q
, -ESRCH
, -ENOENT
))
181 return log_error_errno(q
, "Failed to get current runlevel: %m");
186 /* Secondly, get new runlevel */
187 runlevel
= get_current_runlevel(c
);
192 if (previous
== runlevel
)
196 if (c
->audit_fd
>= 0) {
197 _cleanup_free_
char *s
= NULL
;
199 if (asprintf(&s
, "old-level=%c new-level=%c",
200 previous
> 0 ? previous
: 'N',
201 runlevel
> 0 ? runlevel
: 'N') < 0)
204 if (audit_log_user_comm_message(c
->audit_fd
, AUDIT_SYSTEM_RUNLEVEL
, s
, "systemd-update-utmp", NULL
, NULL
, NULL
, 1) < 0 && errno
!= EPERM
)
205 r
= log_error_errno(errno
, "Failed to send audit message: %m");
209 q
= utmp_put_runlevel(runlevel
, previous
);
210 if (q
< 0 && !IN_SET(q
, -ESRCH
, -ENOENT
)) {
211 log_error_errno(q
, "Failed to write utmp record: %m");
218 int main(int argc
, char *argv
[]) {
219 _cleanup_(context_clear
) Context c
= {
226 if (getppid() != 1) {
227 log_error("This program should be invoked by init only.");
232 log_error("This program requires one argument.");
241 /* If the kernel lacks netlink or audit support,
242 * don't worry about it. */
243 c
.audit_fd
= audit_open();
244 if (c
.audit_fd
< 0 && !IN_SET(errno
, EAFNOSUPPORT
, EPROTONOSUPPORT
))
245 log_error_errno(errno
, "Failed to connect to audit log: %m");
247 r
= bus_connect_system_systemd(&c
.bus
);
249 log_error_errno(r
, "Failed to get D-Bus connection: %m");
253 log_debug("systemd-update-utmp running as pid "PID_FMT
, getpid_cached());
255 if (streq(argv
[1], "reboot"))
257 else if (streq(argv
[1], "shutdown"))
259 else if (streq(argv
[1], "runlevel"))
262 log_error("Unknown command %s", argv
[1]);
266 log_debug("systemd-update-utmp stopped as pid "PID_FMT
, getpid_cached());
268 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;