]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/update-utmp/update-utmp.c
tree-wide: drop {} from one-line if blocks
[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 <errno.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #ifdef HAVE_AUDIT
27 #include <libaudit.h>
28 #endif
29
30 #include "sd-bus.h"
31
32 #include "log.h"
33 #include "macro.h"
34 #include "util.h"
35 #include "special.h"
36 #include "utmp-wtmp.h"
37 #include "bus-util.h"
38 #include "bus-error.h"
39 #include "unit-name.h"
40 #include "formats-util.h"
41
42 typedef struct Context {
43 sd_bus *bus;
44 #ifdef HAVE_AUDIT
45 int audit_fd;
46 #endif
47 } Context;
48
49 static usec_t get_startup_time(Context *c) {
50 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
51 usec_t t = 0;
52 int r;
53
54 assert(c);
55
56 r = sd_bus_get_property_trivial(
57 c->bus,
58 "org.freedesktop.systemd1",
59 "/org/freedesktop/systemd1",
60 "org.freedesktop.systemd1.Manager",
61 "UserspaceTimestamp",
62 &error,
63 't', &t);
64 if (r < 0) {
65 log_error("Failed to get timestamp: %s", bus_error_message(&error, -r));
66 return 0;
67 }
68
69 return t;
70 }
71
72 static 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
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. */
82 { '5', SPECIAL_GRAPHICAL_TARGET },
83 { '3', SPECIAL_MULTI_USER_TARGET },
84 { '1', SPECIAL_RESCUE_TARGET },
85 };
86
87 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
88 int r;
89 unsigned i;
90
91 assert(c);
92
93 for (i = 0; i < ELEMENTSOF(table); i++) {
94 _cleanup_free_ char *state = NULL, *path = NULL;
95
96 path = unit_dbus_path_from_name(table[i].special);
97 if (!path)
98 return log_oom();
99
100 r = sd_bus_get_property_string(
101 c->bus,
102 "org.freedesktop.systemd1",
103 path,
104 "org.freedesktop.systemd1.Unit",
105 "ActiveState",
106 &error,
107 &state);
108 if (r < 0) {
109 log_warning("Failed to get state: %s", bus_error_message(&error, -r));
110 return r;
111 }
112
113 if (streq(state, "active") || streq(state, "reloading"))
114 return table[i].runlevel;
115 }
116
117 return 0;
118 }
119
120 static int on_reboot(Context *c) {
121 int r = 0, q;
122 usec_t t;
123
124 assert(c);
125
126 /* We finished start-up, so let's write the utmp
127 * record and send the audit msg */
128
129 #ifdef HAVE_AUDIT
130 if (c->audit_fd >= 0)
131 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
132 errno != EPERM) {
133 r = log_error_errno(errno,
134 "Failed to send audit message: %m");
135 }
136 #endif
137
138 /* If this call fails it will return 0, which
139 * utmp_put_reboot() will then fix to the current time */
140 t = get_startup_time(c);
141
142 q = utmp_put_reboot(t);
143 if (q < 0) {
144 log_error_errno(q, "Failed to write utmp record: %m");
145 r = q;
146 }
147
148 return r;
149 }
150
151 static int on_shutdown(Context *c) {
152 int r = 0, q;
153
154 assert(c);
155
156 /* We started shut-down, so let's write the utmp
157 * record and send the audit msg */
158
159 #ifdef HAVE_AUDIT
160 if (c->audit_fd >= 0)
161 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
162 errno != EPERM) {
163 r = log_error_errno(errno,
164 "Failed to send audit message: %m");
165 }
166 #endif
167
168 q = utmp_put_shutdown();
169 if (q < 0) {
170 log_error_errno(q, "Failed to write utmp record: %m");
171 r = q;
172 }
173
174 return r;
175 }
176
177 static int on_runlevel(Context *c) {
178 int r = 0, q, previous, runlevel;
179
180 assert(c);
181
182 /* We finished changing runlevel, so let's write the
183 * utmp record and send the audit msg */
184
185 /* First, get last runlevel */
186 q = utmp_get_runlevel(&previous, NULL);
187
188 if (q < 0) {
189 if (q != -ESRCH && q != -ENOENT)
190 return log_error_errno(q, "Failed to get current runlevel: %m");
191
192 previous = 0;
193 }
194
195 /* Secondly, get new runlevel */
196 runlevel = get_current_runlevel(c);
197
198 if (runlevel < 0)
199 return runlevel;
200
201 if (previous == runlevel)
202 return 0;
203
204 #ifdef HAVE_AUDIT
205 if (c->audit_fd >= 0) {
206 _cleanup_free_ char *s = NULL;
207
208 if (asprintf(&s, "old-level=%c new-level=%c",
209 previous > 0 ? previous : 'N',
210 runlevel > 0 ? runlevel : 'N') < 0)
211 return log_oom();
212
213 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM)
214 r = log_error_errno(errno,
215 "Failed to send audit message: %m");
216 }
217 #endif
218
219 q = utmp_put_runlevel(runlevel, previous);
220 if (q < 0 && q != -ESRCH && q != -ENOENT) {
221 log_error_errno(q, "Failed to write utmp record: %m");
222 r = q;
223 }
224
225 return r;
226 }
227
228 int main(int argc, char *argv[]) {
229 Context c = {
230 #ifdef HAVE_AUDIT
231 .audit_fd = -1
232 #endif
233 };
234 int r;
235
236 if (getppid() != 1) {
237 log_error("This program should be invoked by init only.");
238 return EXIT_FAILURE;
239 }
240
241 if (argc != 2) {
242 log_error("This program requires one argument.");
243 return EXIT_FAILURE;
244 }
245
246 log_set_target(LOG_TARGET_AUTO);
247 log_parse_environment();
248 log_open();
249
250 umask(0022);
251
252 #ifdef HAVE_AUDIT
253 /* If the kernel lacks netlink or audit support,
254 * don't worry about it. */
255 c.audit_fd = audit_open();
256 if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
257 log_error_errno(errno, "Failed to connect to audit log: %m");
258 #endif
259 r = bus_open_system_systemd(&c.bus);
260 if (r < 0) {
261 log_error_errno(r, "Failed to get D-Bus connection: %m");
262 r = -EIO;
263 goto finish;
264 }
265
266 log_debug("systemd-update-utmp running as pid "PID_FMT, getpid());
267
268 if (streq(argv[1], "reboot"))
269 r = on_reboot(&c);
270 else if (streq(argv[1], "shutdown"))
271 r = on_shutdown(&c);
272 else if (streq(argv[1], "runlevel"))
273 r = on_runlevel(&c);
274 else {
275 log_error("Unknown command %s", argv[1]);
276 r = -EINVAL;
277 }
278
279 log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid());
280
281 finish:
282 #ifdef HAVE_AUDIT
283 if (c.audit_fd >= 0)
284 audit_close(c.audit_fd);
285 #endif
286
287 sd_bus_unref(c.bus);
288 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
289 }