]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/update-utmp/update-utmp.c
treewide: no need to negate errno for log_*_errno()
[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("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("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 log_error_errno(q, "Failed to get current runlevel: %m");
194 return q;
195 }
196
197 previous = 0;
198 }
199
200 /* Secondly, get new runlevel */
201 runlevel = get_current_runlevel(c);
202
203 if (runlevel < 0)
204 return runlevel;
205
206 if (previous == runlevel)
207 return 0;
208
209 #ifdef HAVE_AUDIT
210 if (c->audit_fd >= 0) {
211 _cleanup_free_ char *s = NULL;
212
213 if (asprintf(&s, "old-level=%c new-level=%c",
214 previous > 0 ? previous : 'N',
215 runlevel > 0 ? runlevel : 'N') < 0)
216 return log_oom();
217
218 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
219 errno != EPERM) {
220 log_error("Failed to send audit message: %m");
221 r = -errno;
222 }
223 }
224 #endif
225
226 q = utmp_put_runlevel(runlevel, previous);
227 if (q < 0 && q != -ESRCH && q != -ENOENT) {
228 log_error_errno(q, "Failed to write utmp record: %m");
229 r = q;
230 }
231
232 return r;
233 }
234
235 int main(int argc, char *argv[]) {
236 Context c = {
237 #ifdef HAVE_AUDIT
238 .audit_fd = -1
239 #endif
240 };
241 int r;
242
243 if (getppid() != 1) {
244 log_error("This program should be invoked by init only.");
245 return EXIT_FAILURE;
246 }
247
248 if (argc != 2) {
249 log_error("This program requires one argument.");
250 return EXIT_FAILURE;
251 }
252
253 log_set_target(LOG_TARGET_AUTO);
254 log_parse_environment();
255 log_open();
256
257 umask(0022);
258
259 #ifdef HAVE_AUDIT
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)
264 log_error("Failed to connect to audit log: %m");
265 #endif
266 r = bus_open_system_systemd(&c.bus);
267 if (r < 0) {
268 log_error_errno(r, "Failed to get D-Bus connection: %m");
269 r = -EIO;
270 goto finish;
271 }
272
273 log_debug("systemd-update-utmp running as pid "PID_FMT, getpid());
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
286 log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid());
287
288 finish:
289 #ifdef HAVE_AUDIT
290 if (c.audit_fd >= 0)
291 audit_close(c.audit_fd);
292 #endif
293
294 if (c.bus)
295 sd_bus_unref(c.bus);
296
297 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
298 }