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