2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
30 #include "alloc-util.h"
31 #include "bus-error.h"
33 #include "formats-util.h"
37 #include "unit-name.h"
39 #include "utmp-wtmp.h"
41 typedef struct Context
{
48 static usec_t
get_startup_time(Context
*c
) {
49 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
55 r
= sd_bus_get_property_trivial(
57 "org.freedesktop.systemd1",
58 "/org/freedesktop/systemd1",
59 "org.freedesktop.systemd1.Manager",
64 log_error_errno(r
, "Failed to get timestamp: %s", bus_error_message(&error
, r
));
71 static int get_current_runlevel(Context
*c
) {
76 /* The first target of this list that is active or has
77 * a job scheduled wins. We prefer runlevels 5 and 3
78 * here over the others, since these are the main
79 * runlevels used on Fedora. It might make sense to
80 * change the order on some distributions. */
81 { '5', SPECIAL_GRAPHICAL_TARGET
},
82 { '3', SPECIAL_MULTI_USER_TARGET
},
83 { '1', SPECIAL_RESCUE_TARGET
},
86 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
92 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
93 _cleanup_free_
char *state
= NULL
, *path
= NULL
;
95 path
= unit_dbus_path_from_name(table
[i
].special
);
99 r
= sd_bus_get_property_string(
101 "org.freedesktop.systemd1",
103 "org.freedesktop.systemd1.Unit",
108 return log_warning_errno(r
, "Failed to get state: %s", bus_error_message(&error
, r
));
110 if (streq(state
, "active") || streq(state
, "reloading"))
111 return table
[i
].runlevel
;
117 static int on_reboot(Context
*c
) {
123 /* We finished start-up, so let's write the utmp
124 * record and send the audit msg */
127 if (c
->audit_fd
>= 0)
128 if (audit_log_user_comm_message(c
->audit_fd
, AUDIT_SYSTEM_BOOT
, "", "systemd-update-utmp", NULL
, NULL
, NULL
, 1) < 0 &&
130 r
= log_error_errno(errno
, "Failed to send audit message: %m");
134 /* If this call fails it will return 0, which
135 * utmp_put_reboot() will then fix to the current time */
136 t
= get_startup_time(c
);
138 q
= utmp_put_reboot(t
);
140 log_error_errno(q
, "Failed to write utmp record: %m");
147 static int on_shutdown(Context
*c
) {
152 /* We started shut-down, so let's write the utmp
153 * record and send the audit msg */
156 if (c
->audit_fd
>= 0)
157 if (audit_log_user_comm_message(c
->audit_fd
, AUDIT_SYSTEM_SHUTDOWN
, "", "systemd-update-utmp", NULL
, NULL
, NULL
, 1) < 0 &&
159 r
= log_error_errno(errno
, "Failed to send audit message: %m");
163 q
= utmp_put_shutdown();
165 log_error_errno(q
, "Failed to write utmp record: %m");
172 static int on_runlevel(Context
*c
) {
173 int r
= 0, q
, previous
, runlevel
;
177 /* We finished changing runlevel, so let's write the
178 * utmp record and send the audit msg */
180 /* First, get last runlevel */
181 q
= utmp_get_runlevel(&previous
, NULL
);
184 if (q
!= -ESRCH
&& q
!= -ENOENT
)
185 return log_error_errno(q
, "Failed to get current runlevel: %m");
190 /* Secondly, get new runlevel */
191 runlevel
= get_current_runlevel(c
);
196 if (previous
== runlevel
)
200 if (c
->audit_fd
>= 0) {
201 _cleanup_free_
char *s
= NULL
;
203 if (asprintf(&s
, "old-level=%c new-level=%c",
204 previous
> 0 ? previous
: 'N',
205 runlevel
> 0 ? runlevel
: 'N') < 0)
208 if (audit_log_user_comm_message(c
->audit_fd
, AUDIT_SYSTEM_RUNLEVEL
, s
, "systemd-update-utmp", NULL
, NULL
, NULL
, 1) < 0 && errno
!= EPERM
)
209 r
= log_error_errno(errno
, "Failed to send audit message: %m");
213 q
= utmp_put_runlevel(runlevel
, previous
);
214 if (q
< 0 && q
!= -ESRCH
&& q
!= -ENOENT
) {
215 log_error_errno(q
, "Failed to write utmp record: %m");
222 int main(int argc
, char *argv
[]) {
230 if (getppid() != 1) {
231 log_error("This program should be invoked by init only.");
236 log_error("This program requires one argument.");
240 log_set_target(LOG_TARGET_AUTO
);
241 log_parse_environment();
247 /* If the kernel lacks netlink or audit support,
248 * don't worry about it. */
249 c
.audit_fd
= audit_open();
250 if (c
.audit_fd
< 0 && errno
!= EAFNOSUPPORT
&& errno
!= EPROTONOSUPPORT
)
251 log_error_errno(errno
, "Failed to connect to audit log: %m");
253 r
= bus_connect_system_systemd(&c
.bus
);
255 log_error_errno(r
, "Failed to get D-Bus connection: %m");
260 log_debug("systemd-update-utmp running as pid "PID_FMT
, getpid());
262 if (streq(argv
[1], "reboot"))
264 else if (streq(argv
[1], "shutdown"))
266 else if (streq(argv
[1], "runlevel"))
269 log_error("Unknown command %s", argv
[1]);
273 log_debug("systemd-update-utmp stopped as pid "PID_FMT
, getpid());
278 audit_close(c
.audit_fd
);
281 sd_bus_flush_close_unref(c
.bus
);
282 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;