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";
55 DBusMessage *m = NULL, *reply = NULL;
56 DBusMessageIter iter, sub;
58 dbus_error_init(&error);
62 if (!(m = dbus_message_new_method_call(
63 "org.freedesktop.systemd1",
64 "/org/freedesktop/systemd1",
65 "org.freedesktop.DBus.Properties",
67 log_error("Could not allocate message.");
71 if (!dbus_message_append_args(m,
72 DBUS_TYPE_STRING, &interface,
73 DBUS_TYPE_STRING, &property,
75 log_error("Could not append arguments to message.");
79 if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
80 log_error("Failed to send command: %s", bus_error_message(&error));
84 if (!dbus_message_iter_init(reply, &iter) ||
85 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
86 log_error("Failed to parse reply.");
90 dbus_message_iter_recurse(&iter, &sub);
92 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
93 log_error("Failed to parse reply.");
97 dbus_message_iter_get_basic(&sub, &t);
101 dbus_message_unref(m);
104 dbus_message_unref(reply);
106 dbus_error_free(&error);
111 static int get_current_runlevel(Context *c) {
112 static const struct {
116 /* The first target of this list that is active or has
117 * a job scheduled wins. We prefer runlevels 5 and 3
118 * here over the others, since these are the main
119 * runlevels used on Fedora. It might make sense to
120 * change the order on some distributions. */
121 { '5', SPECIAL_RUNLEVEL5_TARGET },
122 { '3', SPECIAL_RUNLEVEL3_TARGET },
123 { '4', SPECIAL_RUNLEVEL4_TARGET },
124 { '2', SPECIAL_RUNLEVEL2_TARGET },
125 { 'S', SPECIAL_RESCUE_TARGET },
128 *interface = "org.freedesktop.systemd1.Unit",
129 *property = "ActiveState";
131 DBusMessage *m = NULL, *reply = NULL;
138 dbus_error_init(&error);
140 for (i = 0; i < ELEMENTSOF(table); i++) {
141 const char *path = NULL, *state;
142 DBusMessageIter iter, sub;
144 if (!(m = dbus_message_new_method_call(
145 "org.freedesktop.systemd1",
146 "/org/freedesktop/systemd1",
147 "org.freedesktop.systemd1.Manager",
149 log_error("Could not allocate message.");
154 if (!dbus_message_append_args(m,
155 DBUS_TYPE_STRING, &table[i].special,
156 DBUS_TYPE_INVALID)) {
157 log_error("Could not append arguments to message.");
162 if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
163 dbus_error_free(&error);
167 if (!dbus_message_get_args(reply, &error,
168 DBUS_TYPE_OBJECT_PATH, &path,
169 DBUS_TYPE_INVALID)) {
170 log_error("Failed to parse reply: %s", bus_error_message(&error));
175 dbus_message_unref(m);
176 if (!(m = dbus_message_new_method_call(
177 "org.freedesktop.systemd1",
179 "org.freedesktop.DBus.Properties",
181 log_error("Could not allocate message.");
186 if (!dbus_message_append_args(m,
187 DBUS_TYPE_STRING, &interface,
188 DBUS_TYPE_STRING, &property,
189 DBUS_TYPE_INVALID)) {
190 log_error("Could not append arguments to message.");
195 dbus_message_unref(reply);
196 if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
197 log_error("Failed to send command: %s", bus_error_message(&error));
202 if (!dbus_message_iter_init(reply, &iter) ||
203 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
204 log_error("Failed to parse reply.");
209 dbus_message_iter_recurse(&iter, &sub);
211 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
212 log_error("Failed to parse reply.");
217 dbus_message_iter_get_basic(&sub, &state);
219 if (streq(state, "active") || streq(state, "reloading"))
220 r = table[i].runlevel;
222 dbus_message_unref(m);
223 dbus_message_unref(reply);
232 dbus_message_unref(m);
235 dbus_message_unref(reply);
237 dbus_error_free(&error);
242 static int on_reboot(Context *c) {
248 /* We finished start-up, so let's write the utmp
249 * record and send the audit msg */
252 if (c->audit_fd >= 0)
253 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0 &&
255 log_error("Failed to send audit message: %m");
260 /* If this call fails it will return 0, which
261 * utmp_put_reboot() will then fix to the current time */
262 t = get_startup_time(c);
264 if ((q = utmp_put_reboot(t)) < 0) {
265 log_error("Failed to write utmp record: %s", strerror(-q));
272 static int on_shutdown(Context *c) {
277 /* We started shut-down, so let's write the utmp
278 * record and send the audit msg */
281 if (c->audit_fd >= 0)
282 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0 &&
284 log_error("Failed to send audit message: %m");
289 if ((q = utmp_put_shutdown()) < 0) {
290 log_error("Failed to write utmp record: %s", strerror(-q));
297 static int on_runlevel(Context *c) {
298 int r = 0, q, previous, runlevel;
302 /* We finished changing runlevel, so let's write the
303 * utmp record and send the audit msg */
305 /* First, get last runlevel */
306 if ((q = utmp_get_runlevel(&previous, NULL)) < 0) {
308 if (q != -ESRCH && q != -ENOENT) {
309 log_error("Failed to get current runlevel: %s", strerror(-q));
313 /* Hmm, we didn't find any runlevel, that means we
314 * have been rebooted */
319 /* Secondly, get new runlevel */
320 if ((runlevel = get_current_runlevel(c)) < 0)
323 if (previous == runlevel)
327 if (c->audit_fd >= 0) {
330 if (asprintf(&s, "old-level=%c new-level=%c",
331 previous > 0 ? previous : 'N',
332 runlevel > 0 ? runlevel : 'N') < 0)
335 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0 &&
337 log_error("Failed to send audit message: %m");
345 if ((q = utmp_put_runlevel(runlevel, previous)) < 0) {
346 log_error("Failed to write utmp record: %s", strerror(-q));
353 int main(int argc, char *argv[]) {
358 dbus_error_init(&error);
365 if (getppid() != 1) {
366 log_error("This program should be invoked by init only.");
371 log_error("This program requires one argument.");
375 log_set_target(LOG_TARGET_AUTO);
376 log_parse_environment();
382 if ((c.audit_fd = audit_open()) < 0 &&
383 /* If the kernel lacks netlink or audit support,
384 * don't worry about it. */
385 errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
386 log_error("Failed to connect to audit log: %m");
389 if (bus_connect(DBUS_BUS_SYSTEM, &c.bus, NULL, &error) < 0) {
390 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
395 log_debug("systemd-update-utmp running as pid %lu", (unsigned long) getpid());
397 if (streq(argv[1], "reboot"))
399 else if (streq(argv[1], "shutdown"))
401 else if (streq(argv[1], "runlevel"))
404 log_error("Unknown command %s", argv[1]);
408 log_debug("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid());
413 audit_close(c.audit_fd);
417 dbus_connection_flush(c.bus);
418 dbus_connection_close(c.bus);
419 dbus_connection_unref(c.bus);
422 dbus_error_free(&error);
425 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;