]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/update-utmp/update-utmp.c
logind: fix printf format
[thirdparty/systemd.git] / src / update-utmp / update-utmp.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4927fcae
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
4927fcae
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
4927fcae 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
4927fcae
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <assert.h>
23#include <errno.h>
24#include <string.h>
4927fcae
LP
25#include <sys/types.h>
26#include <unistd.h>
27
830f6caa
LP
28#ifdef HAVE_AUDIT
29#include <libaudit.h>
30#endif
31
89456fce
TG
32#include "sd-bus.h"
33
4927fcae
LP
34#include "log.h"
35#include "macro.h"
36#include "util.h"
37#include "special.h"
38#include "utmp-wtmp.h"
89456fce
TG
39#include "bus-util.h"
40#include "bus-error.h"
e7fb33ff 41#include "unit-name.h"
4927fcae
LP
42
43typedef struct Context {
89456fce 44 sd_bus *bus;
4927fcae
LP
45#ifdef HAVE_AUDIT
46 int audit_fd;
47#endif
48} Context;
49
50static usec_t get_startup_time(Context *c) {
89456fce 51 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
47c649b5 52 usec_t t = 0;
89456fce 53 int r;
4927fcae 54
4927fcae
LP
55 assert(c);
56
47c649b5 57 r = sd_bus_get_property_trivial(
436ddded
SP
58 c->bus,
59 "org.freedesktop.systemd1",
60 "/org/freedesktop/systemd1",
89456fce 61 "org.freedesktop.systemd1.Manager",
47c649b5
LP
62 "UserspaceTimestamp",
63 &error,
64 't', &t);
89456fce
TG
65 if (r < 0) {
66 log_error("Failed to get timestamp: %s", bus_error_message(&error, -r));
47c649b5 67 return 0;
4927fcae
LP
68 }
69
4927fcae
LP
70 return t;
71}
72
73static int get_current_runlevel(Context *c) {
74 static const struct {
75 const int runlevel;
76 const char *special;
77 } table[] = {
78 /* The first target of this list that is active or has
ee2083d5
LP
79 * a job scheduled wins. We prefer runlevels 5 and 3
80 * here over the others, since these are the main
81 * runlevels used on Fedora. It might make sense to
82 * change the order on some distributions. */
4927fcae 83 { '5', SPECIAL_RUNLEVEL5_TARGET },
4927fcae 84 { '3', SPECIAL_RUNLEVEL3_TARGET },
3006982d 85 { '4', SPECIAL_RUNLEVEL4_TARGET },
4927fcae 86 { '2', SPECIAL_RUNLEVEL2_TARGET },
3f92e4b4 87 { '1', SPECIAL_RESCUE_TARGET },
4927fcae 88 };
4927fcae 89
89456fce
TG
90 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
91 int r;
4927fcae 92 unsigned i;
4927fcae
LP
93
94 assert(c);
95
4927fcae 96 for (i = 0; i < ELEMENTSOF(table); i++) {
e7fb33ff 97 _cleanup_free_ char *state = NULL, *path = NULL;
4927fcae 98
e7fb33ff
LP
99 path = unit_dbus_path_from_name(table[i].special);
100 if (!path)
101 return log_oom();
89456fce 102
47c649b5 103 r = sd_bus_get_property_string(
436ddded
SP
104 c->bus,
105 "org.freedesktop.systemd1",
106 path,
47c649b5
LP
107 "org.freedesktop.systemd1.Unit",
108 "ActiveState",
89456fce 109 &error,
47c649b5 110 &state);
89456fce 111 if (r < 0) {
47c649b5 112 log_warning("Failed to get state: %s", bus_error_message(&error, -r));
89456fce 113 return r;
4927fcae
LP
114 }
115
4927fcae 116 if (streq(state, "active") || streq(state, "reloading"))
89456fce 117 return table[i].runlevel;
4927fcae
LP
118 }
119
89456fce 120 return 0;
4927fcae
LP
121}
122
123static int on_reboot(Context *c) {
124 int r = 0, q;
125 usec_t t;
126
127 assert(c);
128
129 /* We finished start-up, so let's write the utmp
130 * record and send the audit msg */
131
132#ifdef HAVE_AUDIT
133 if (c->audit_fd >= 0)
44785992
LP
134 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0 &&
135 errno != EPERM) {
4927fcae
LP
136 log_error("Failed to send audit message: %m");
137 r = -errno;
138 }
139#endif
140
141 /* If this call fails it will return 0, which
142 * utmp_put_reboot() will then fix to the current time */
143 t = get_startup_time(c);
144
1cea22a5
LP
145 q = utmp_put_reboot(t);
146 if (q < 0) {
4927fcae
LP
147 log_error("Failed to write utmp record: %s", strerror(-q));
148 r = q;
149 }
150
151 return r;
152}
153
154static int on_shutdown(Context *c) {
155 int r = 0, q;
156
157 assert(c);
158
159 /* We started shut-down, so let's write the utmp
160 * record and send the audit msg */
161
162#ifdef HAVE_AUDIT
163 if (c->audit_fd >= 0)
44785992
LP
164 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0 &&
165 errno != EPERM) {
4927fcae
LP
166 log_error("Failed to send audit message: %m");
167 r = -errno;
168 }
169#endif
170
1cea22a5
LP
171 q = utmp_put_shutdown();
172 if (q < 0) {
4927fcae
LP
173 log_error("Failed to write utmp record: %s", strerror(-q));
174 r = q;
175 }
176
177 return r;
178}
179
180static int on_runlevel(Context *c) {
181 int r = 0, q, previous, runlevel;
182
183 assert(c);
184
185 /* We finished changing runlevel, so let's write the
186 * utmp record and send the audit msg */
187
188 /* First, get last runlevel */
1cea22a5 189 q = utmp_get_runlevel(&previous, NULL);
4927fcae 190
e7fb33ff 191 if (q < 0) {
4927fcae
LP
192 if (q != -ESRCH && q != -ENOENT) {
193 log_error("Failed to get current runlevel: %s", strerror(-q));
194 return q;
195 }
196
4927fcae
LP
197 previous = 0;
198 }
199
830f6caa 200 /* Secondly, get new runlevel */
1cea22a5 201 runlevel = get_current_runlevel(c);
e7fb33ff 202
1cea22a5 203 if (runlevel < 0)
4927fcae
LP
204 return runlevel;
205
206 if (previous == runlevel)
207 return 0;
208
209#ifdef HAVE_AUDIT
210 if (c->audit_fd >= 0) {
1cea22a5 211 _cleanup_free_ char *s = NULL;
4927fcae 212
cd6d0a45
LP
213 if (asprintf(&s, "old-level=%c new-level=%c",
214 previous > 0 ? previous : 'N',
215 runlevel > 0 ? runlevel : 'N') < 0)
1cea22a5 216 return log_oom();
4927fcae 217
44785992
LP
218 if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0 &&
219 errno != EPERM) {
4927fcae
LP
220 log_error("Failed to send audit message: %m");
221 r = -errno;
222 }
4927fcae
LP
223 }
224#endif
225
1cea22a5
LP
226 q = utmp_put_runlevel(runlevel, previous);
227 if (q < 0 && q != -ESRCH && q != -ENOENT) {
228 log_error("Failed to write utmp record: %s", strerror(-q));
229 r = q;
4927fcae
LP
230 }
231
232 return r;
233}
234
235int main(int argc, char *argv[]) {
1cea22a5 236 Context c = {
4927fcae 237#ifdef HAVE_AUDIT
1cea22a5 238 .audit_fd = -1
4927fcae 239#endif
1cea22a5
LP
240 };
241 int r;
4927fcae 242
cd6d0a45
LP
243 if (getppid() != 1) {
244 log_error("This program should be invoked by init only.");
22f4096c 245 return EXIT_FAILURE;
cd6d0a45 246 }
4927fcae
LP
247
248 if (argc != 2) {
249 log_error("This program requires one argument.");
22f4096c 250 return EXIT_FAILURE;
4927fcae
LP
251 }
252
4cfa2c99 253 log_set_target(LOG_TARGET_AUTO);
4927fcae 254 log_parse_environment();
2396fb04 255 log_open();
4927fcae 256
4c12626c
LP
257 umask(0022);
258
4927fcae 259#ifdef HAVE_AUDIT
1cea22a5
LP
260 /* If the kernel lacks netlink or audit support,
261 * don't worry about it. */
262 c.audit_fd = audit_open();
263 if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
4927fcae
LP
264 log_error("Failed to connect to audit log: %m");
265#endif
0f8bd8de 266 r = bus_open_system_systemd(&c.bus);
89456fce
TG
267 if (r < 0) {
268 log_error("Failed to get D-Bus connection: %s", strerror(-r));
4927fcae
LP
269 r = -EIO;
270 goto finish;
271 }
272
cd6d0a45 273 log_debug("systemd-update-utmp running as pid %lu", (unsigned long) getpid());
4927fcae
LP
274
275 if (streq(argv[1], "reboot"))
276 r = on_reboot(&c);
277 else if (streq(argv[1], "shutdown"))
278 r = on_shutdown(&c);
279 else if (streq(argv[1], "runlevel"))
280 r = on_runlevel(&c);
281 else {
282 log_error("Unknown command %s", argv[1]);
283 r = -EINVAL;
284 }
285
cd6d0a45 286 log_debug("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid());
4927fcae 287
cd6d0a45 288finish:
4927fcae
LP
289#ifdef HAVE_AUDIT
290 if (c.audit_fd >= 0)
291 audit_close(c.audit_fd);
292#endif
293
89456fce
TG
294 if (c.bus)
295 sd_bus_unref(c.bus);
4927fcae 296
22f4096c 297 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
4927fcae 298}