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