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/types.h>
38 #include "utmp-wtmp.h"
40 #include "bus-error.h"
42 typedef struct Context {
49 static usec_t get_startup_time(Context *c) {
51 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
52 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
57 r = sd_bus_call_method(
59 "org.freedesktop.systemd1",
60 "/org/freedesktop/systemd1",
61 "org.freedesktop.DBus.Properties",
66 "org.freedesktop.systemd1.Manager",
67 "UserspaceTimestamp");
69 log_error("Failed to get timestamp: %s", bus_error_message(&error, -r));
73 r = sd_bus_message_read(reply, "v", "t", &t);
75 log_error("Failed to parse reply: %s", strerror(-r));
81 static int get_current_runlevel(Context *c) {
86 /* The first target of this list that is active or has
87 * a job scheduled wins. We prefer runlevels 5 and 3
88 * here over the others, since these are the main
89 * runlevels used on Fedora. It might make sense to
90 * change the order on some distributions. */
91 { '5', SPECIAL_RUNLEVEL5_TARGET },
92 { '3', SPECIAL_RUNLEVEL3_TARGET },
93 { '4', SPECIAL_RUNLEVEL4_TARGET },
94 { '2', SPECIAL_RUNLEVEL2_TARGET },
95 { '1', SPECIAL_RESCUE_TARGET },
98 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
104 for (i = 0; i < ELEMENTSOF(table); i++) {
105 _cleanup_bus_message_unref_ sd_bus_message *reply1 = NULL, *reply2 = NULL;
106 const char *path = NULL, *state;
108 r = sd_bus_call_method(
110 "org.freedesktop.systemd1",
111 "/org/freedesktop/systemd1",
112 "org.freedesktop.systemd1.Manager",
116 "s", table[i].special);
118 log_error("Failed to get runlevel: %s", bus_error_message(&error, -r));
125 r = sd_bus_message_read(reply1, "o", &path);
127 log_error("Failed to parse reply: %s", strerror(-r));
131 r = sd_bus_call_method(
133 "org.freedesktop.systemd1",
135 "org.freedesktop.DBus.Properties",
139 "ss", "org.freedesktop.systemd1.Unit", "ActiveState");
141 log_error("Failed to get state: %s", bus_error_message(&error, -r));
145 r = sd_bus_message_read(reply2, "v", "s", &state);
147 log_error("Failed to parse reply: %s", strerror(-r));
151 if (streq(state, "active") || streq(state, "reloading"))
152 return table[i].runlevel;
158 static int on_reboot(Context *c) {
164 /* We finished start-up, so let's write the utmp
165 * record and send the audit msg */
168 if (c->audit_fd >= 0)
169 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0 &&
171 log_error("Failed to send audit message: %m");
176 /* If this call fails it will return 0, which
177 * utmp_put_reboot() will then fix to the current time */
178 t = get_startup_time(c);
180 if ((q = utmp_put_reboot(t)) < 0) {
181 log_error("Failed to write utmp record: %s", strerror(-q));
188 static int on_shutdown(Context *c) {
193 /* We started shut-down, so let's write the utmp
194 * record and send the audit msg */
197 if (c->audit_fd >= 0)
198 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0 &&
200 log_error("Failed to send audit message: %m");
205 if ((q = utmp_put_shutdown()) < 0) {
206 log_error("Failed to write utmp record: %s", strerror(-q));
213 static int on_runlevel(Context *c) {
214 int r = 0, q, previous, runlevel;
218 /* We finished changing runlevel, so let's write the
219 * utmp record and send the audit msg */
221 /* First, get last runlevel */
222 if ((q = utmp_get_runlevel(&previous, NULL)) < 0) {
224 if (q != -ESRCH && q != -ENOENT) {
225 log_error("Failed to get current runlevel: %s", strerror(-q));
229 /* Hmm, we didn't find any runlevel, that means we
230 * have been rebooted */
235 /* Secondly, get new runlevel */
236 if ((runlevel = get_current_runlevel(c)) < 0)
239 if (previous == runlevel)
243 if (c->audit_fd >= 0) {
246 if (asprintf(&s, "old-level=%c new-level=%c",
247 previous > 0 ? previous : 'N',
248 runlevel > 0 ? runlevel : 'N') < 0)
251 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0 &&
253 log_error("Failed to send audit message: %m");
261 if ((q = utmp_put_runlevel(runlevel, previous)) < 0) {
262 if (q != -ESRCH && q != -ENOENT) {
263 log_error("Failed to write utmp record: %s", strerror(-q));
271 int main(int argc, char *argv[]) {
279 if (getppid() != 1) {
280 log_error("This program should be invoked by init only.");
285 log_error("This program requires one argument.");
289 log_set_target(LOG_TARGET_AUTO);
290 log_parse_environment();
296 if ((c.audit_fd = audit_open()) < 0 &&
297 /* If the kernel lacks netlink or audit support,
298 * don't worry about it. */
299 errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
300 log_error("Failed to connect to audit log: %m");
302 r = bus_connect_system(&c.bus);
304 log_error("Failed to get D-Bus connection: %s", strerror(-r));
309 log_debug("systemd-update-utmp running as pid %lu", (unsigned long) getpid());
311 if (streq(argv[1], "reboot"))
313 else if (streq(argv[1], "shutdown"))
315 else if (streq(argv[1], "runlevel"))
318 log_error("Unknown command %s", argv[1]);
322 log_debug("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid());
327 audit_close(c.audit_fd);
333 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;