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>
28 #include <dbus/dbus.h>
38 #include "utmp-wtmp.h"
39 #include "dbus-common.h"
41 typedef struct Context {
48 static usec_t get_startup_time(Context *c) {
50 *interface = "org.freedesktop.systemd1.Manager",
51 *property = "StartupTimestamp";
54 DBusMessage *reply = NULL;
55 DBusMessageIter iter, sub;
59 if (bus_method_call_with_reply (
61 "org.freedesktop.systemd1",
62 "/org/freedesktop/systemd1",
63 "org.freedesktop.DBus.Properties",
67 DBUS_TYPE_STRING, &interface,
68 DBUS_TYPE_STRING, &property,
72 if (!dbus_message_iter_init(reply, &iter) ||
73 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
74 log_error("Failed to parse reply.");
78 dbus_message_iter_recurse(&iter, &sub);
80 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
81 log_error("Failed to parse reply.");
85 dbus_message_iter_get_basic(&sub, &t);
89 dbus_message_unref(reply);
93 static int get_current_runlevel(Context *c) {
98 /* The first target of this list that is active or has
99 * a job scheduled wins. We prefer runlevels 5 and 3
100 * here over the others, since these are the main
101 * runlevels used on Fedora. It might make sense to
102 * change the order on some distributions. */
103 { '5', SPECIAL_RUNLEVEL5_TARGET },
104 { '3', SPECIAL_RUNLEVEL3_TARGET },
105 { '4', SPECIAL_RUNLEVEL4_TARGET },
106 { '2', SPECIAL_RUNLEVEL2_TARGET },
107 { 'S', SPECIAL_RESCUE_TARGET },
110 *interface = "org.freedesktop.systemd1.Unit",
111 *property = "ActiveState";
113 DBusMessage *reply = NULL;
120 dbus_error_init(&error);
122 for (i = 0; i < ELEMENTSOF(table); i++) {
123 const char *path = NULL, *state;
124 DBusMessageIter iter, sub;
126 r = bus_method_call_with_reply (
128 "org.freedesktop.systemd1",
129 "/org/freedesktop/systemd1",
130 "org.freedesktop.systemd1.Manager",
134 DBUS_TYPE_STRING, &table[i].special,
141 if (!dbus_message_get_args(reply, &error,
142 DBUS_TYPE_OBJECT_PATH, &path,
143 DBUS_TYPE_INVALID)) {
144 log_error("Failed to parse reply: %s", bus_error_message(&error));
149 dbus_message_unref(reply);
150 r = bus_method_call_with_reply (
152 "org.freedesktop.systemd1",
154 "org.freedesktop.DBus.Properties",
158 DBUS_TYPE_STRING, &interface,
159 DBUS_TYPE_STRING, &property,
164 if (!dbus_message_iter_init(reply, &iter) ||
165 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
166 log_error("Failed to parse reply.");
171 dbus_message_iter_recurse(&iter, &sub);
173 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
174 log_error("Failed to parse reply.");
179 dbus_message_iter_get_basic(&sub, &state);
181 if (streq(state, "active") || streq(state, "reloading"))
182 r = table[i].runlevel;
184 dbus_message_unref(reply);
193 dbus_message_unref(reply);
195 dbus_error_free(&error);
200 static int on_reboot(Context *c) {
206 /* We finished start-up, so let's write the utmp
207 * record and send the audit msg */
210 if (c->audit_fd >= 0)
211 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0 &&
213 log_error("Failed to send audit message: %m");
218 /* If this call fails it will return 0, which
219 * utmp_put_reboot() will then fix to the current time */
220 t = get_startup_time(c);
222 if ((q = utmp_put_reboot(t)) < 0) {
223 log_error("Failed to write utmp record: %s", strerror(-q));
230 static int on_shutdown(Context *c) {
235 /* We started shut-down, so let's write the utmp
236 * record and send the audit msg */
239 if (c->audit_fd >= 0)
240 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0 &&
242 log_error("Failed to send audit message: %m");
247 if ((q = utmp_put_shutdown()) < 0) {
248 log_error("Failed to write utmp record: %s", strerror(-q));
255 static int on_runlevel(Context *c) {
256 int r = 0, q, previous, runlevel;
260 /* We finished changing runlevel, so let's write the
261 * utmp record and send the audit msg */
263 /* First, get last runlevel */
264 if ((q = utmp_get_runlevel(&previous, NULL)) < 0) {
266 if (q != -ESRCH && q != -ENOENT) {
267 log_error("Failed to get current runlevel: %s", strerror(-q));
271 /* Hmm, we didn't find any runlevel, that means we
272 * have been rebooted */
277 /* Secondly, get new runlevel */
278 if ((runlevel = get_current_runlevel(c)) < 0)
281 if (previous == runlevel)
285 if (c->audit_fd >= 0) {
288 if (asprintf(&s, "old-level=%c new-level=%c",
289 previous > 0 ? previous : 'N',
290 runlevel > 0 ? runlevel : 'N') < 0)
293 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0 &&
295 log_error("Failed to send audit message: %m");
303 if ((q = utmp_put_runlevel(runlevel, previous)) < 0) {
304 if (q != -ESRCH && q != -ENOENT) {
305 log_error("Failed to write utmp record: %s", strerror(-q));
313 int main(int argc, char *argv[]) {
318 dbus_error_init(&error);
325 if (getppid() != 1) {
326 log_error("This program should be invoked by init only.");
331 log_error("This program requires one argument.");
335 log_set_target(LOG_TARGET_AUTO);
336 log_parse_environment();
342 if ((c.audit_fd = audit_open()) < 0 &&
343 /* If the kernel lacks netlink or audit support,
344 * don't worry about it. */
345 errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
346 log_error("Failed to connect to audit log: %m");
349 if (bus_connect(DBUS_BUS_SYSTEM, &c.bus, NULL, &error) < 0) {
350 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
355 log_debug("systemd-update-utmp running as pid %lu", (unsigned long) getpid());
357 if (streq(argv[1], "reboot"))
359 else if (streq(argv[1], "shutdown"))
361 else if (streq(argv[1], "runlevel"))
364 log_error("Unknown command %s", argv[1]);
368 log_debug("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid());
373 audit_close(c.audit_fd);
377 dbus_connection_flush(c.bus);
378 dbus_connection_close(c.bus);
379 dbus_connection_unref(c.bus);
382 dbus_error_free(&error);
385 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;