]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/update-utmp/update-utmp.c
treewide: use log_*_errno whenever %m is in the format string
[thirdparty/systemd.git] / src / update-utmp / update-utmp.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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
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.
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
16 Lesser General Public License for more details.
17
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/>.
20 ***/
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #ifdef HAVE_AUDIT
29 #include <libaudit.h>
30 #endif
31
32 #include "sd-bus.h"
33
34 #include "log.h"
35 #include "macro.h"
36 #include "util.h"
37 #include "special.h"
38 #include "utmp-wtmp.h"
39 #include "bus-util.h"
40 #include "bus-error.h"
41 #include "unit-name.h"
42
43 typedef struct Context {
44 sd_bus *bus;
45 #ifdef HAVE_AUDIT
46 int audit_fd;
47 #endif
48 } Context;
49
50 static usec_t get_startup_time(Context *c) {
51 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
52 usec_t t = 0;
53 int r;
54
55 assert(c);
56
57 r = sd_bus_get_property_trivial(
58 c->bus,
59 "org.freedesktop.systemd1",
60 "/org/freedesktop/systemd1",
61 "org.freedesktop.systemd1.Manager",
62 "UserspaceTimestamp",
63 &error,
64 't', &t);
65 if (r < 0) {
66 log_error("Failed to get timestamp: %s", bus_error_message(&error, -r));
67 return 0;
68 }
69
70 return t;
71 }
72
73 static 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
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. */
83 { '5', SPECIAL_RUNLEVEL5_TARGET },
84 { '3', SPECIAL_RUNLEVEL3_TARGET },
85 { '4', SPECIAL_RUNLEVEL4_TARGET },
86 { '2', SPECIAL_RUNLEVEL2_TARGET },
87 { '1', SPECIAL_RESCUE_TARGET },
88 };
89
90 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
91 int r;
92 unsigned i;
93
94 assert(c);
95
96 for (i = 0; i < ELEMENTSOF(table); i++) {
97 _cleanup_free_ char *state = NULL, *path = NULL;
98
99 path = unit_dbus_path_from_name(table[i].special);
100 if (!path)
101 return log_oom();
102
103 r = sd_bus_get_property_string(
104 c->bus,
105 "org.freedesktop.systemd1",
106 path,
107 "org.freedesktop.systemd1.Unit",
108 "ActiveState",
109 &error,
110 &state);
111 if (r < 0) {
112 log_warning("Failed to get state: %s", bus_error_message(&error, -r));
113 return r;
114 }
115
116 if (streq(state, "active") || streq(state, "reloading"))
117 return table[i].runlevel;
118 }
119
120 return 0;
121 }
122
123 static 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)
134 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
135 errno != EPERM) {
136 log_error_errno(errno, "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
145 q = utmp_put_reboot(t);
146 if (q < 0) {
147 log_error_errno(q, "Failed to write utmp record: %m");
148 r = q;
149 }
150
151 return r;
152 }
153
154 static 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)
164 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
165 errno != EPERM) {
166 log_error_errno(errno, "Failed to send audit message: %m");
167 r = -errno;
168 }
169 #endif
170
171 q = utmp_put_shutdown();
172 if (q < 0) {
173 log_error_errno(q, "Failed to write utmp record: %m");
174 r = q;
175 }
176
177 return r;
178 }
179
180 static 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 */
189 q = utmp_get_runlevel(&previous, NULL);
190
191 if (q < 0) {
192 if (q != -ESRCH && q != -ENOENT)
193 return log_error_errno(q, "Failed to get current runlevel: %m");
194
195 previous = 0;
196 }
197
198 /* Secondly, get new runlevel */
199 runlevel = get_current_runlevel(c);
200
201 if (runlevel < 0)
202 return runlevel;
203
204 if (previous == runlevel)
205 return 0;
206
207 #ifdef HAVE_AUDIT
208 if (c->audit_fd >= 0) {
209 _cleanup_free_ char *s = NULL;
210
211 if (asprintf(&s, "old-level=%c new-level=%c",
212 previous > 0 ? previous : 'N',
213 runlevel > 0 ? runlevel : 'N') < 0)
214 return log_oom();
215
216 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
217 errno != EPERM) {
218 log_error_errno(errno, "Failed to send audit message: %m");
219 r = -errno;
220 }
221 }
222 #endif
223
224 q = utmp_put_runlevel(runlevel, previous);
225 if (q < 0 && q != -ESRCH && q != -ENOENT) {
226 log_error_errno(q, "Failed to write utmp record: %m");
227 r = q;
228 }
229
230 return r;
231 }
232
233 int main(int argc, char *argv[]) {
234 Context c = {
235 #ifdef HAVE_AUDIT
236 .audit_fd = -1
237 #endif
238 };
239 int r;
240
241 if (getppid() != 1) {
242 log_error("This program should be invoked by init only.");
243 return EXIT_FAILURE;
244 }
245
246 if (argc != 2) {
247 log_error("This program requires one argument.");
248 return EXIT_FAILURE;
249 }
250
251 log_set_target(LOG_TARGET_AUTO);
252 log_parse_environment();
253 log_open();
254
255 umask(0022);
256
257 #ifdef HAVE_AUDIT
258 /* If the kernel lacks netlink or audit support,
259 * don't worry about it. */
260 c.audit_fd = audit_open();
261 if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
262 log_error_errno(errno, "Failed to connect to audit log: %m");
263 #endif
264 r = bus_open_system_systemd(&c.bus);
265 if (r < 0) {
266 log_error_errno(r, "Failed to get D-Bus connection: %m");
267 r = -EIO;
268 goto finish;
269 }
270
271 log_debug("systemd-update-utmp running as pid "PID_FMT, getpid());
272
273 if (streq(argv[1], "reboot"))
274 r = on_reboot(&c);
275 else if (streq(argv[1], "shutdown"))
276 r = on_shutdown(&c);
277 else if (streq(argv[1], "runlevel"))
278 r = on_runlevel(&c);
279 else {
280 log_error("Unknown command %s", argv[1]);
281 r = -EINVAL;
282 }
283
284 log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid());
285
286 finish:
287 #ifdef HAVE_AUDIT
288 if (c.audit_fd >= 0)
289 audit_close(c.audit_fd);
290 #endif
291
292 if (c.bus)
293 sd_bus_unref(c.bus);
294
295 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
296 }