]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/update-utmp/update-utmp.c
Bug #944: Deletion of unnecessary checks before a few calls of systemd functions
[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 log_error_errno(errno, "Failed to send audit message: %m");
134 r = -errno;
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 log_error_errno(errno, "Failed to send audit message: %m");
164 r = -errno;
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 &&
214 errno != EPERM) {
215 log_error_errno(errno, "Failed to send audit message: %m");
216 r = -errno;
217 }
218 }
219 #endif
220
221 q = utmp_put_runlevel(runlevel, previous);
222 if (q < 0 && q != -ESRCH && q != -ENOENT) {
223 log_error_errno(q, "Failed to write utmp record: %m");
224 r = q;
225 }
226
227 return r;
228 }
229
230 int main(int argc, char *argv[]) {
231 Context c = {
232 #ifdef HAVE_AUDIT
233 .audit_fd = -1
234 #endif
235 };
236 int r;
237
238 if (getppid() != 1) {
239 log_error("This program should be invoked by init only.");
240 return EXIT_FAILURE;
241 }
242
243 if (argc != 2) {
244 log_error("This program requires one argument.");
245 return EXIT_FAILURE;
246 }
247
248 log_set_target(LOG_TARGET_AUTO);
249 log_parse_environment();
250 log_open();
251
252 umask(0022);
253
254 #ifdef HAVE_AUDIT
255 /* If the kernel lacks netlink or audit support,
256 * don't worry about it. */
257 c.audit_fd = audit_open();
258 if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
259 log_error_errno(errno, "Failed to connect to audit log: %m");
260 #endif
261 r = bus_open_system_systemd(&c.bus);
262 if (r < 0) {
263 log_error_errno(r, "Failed to get D-Bus connection: %m");
264 r = -EIO;
265 goto finish;
266 }
267
268 log_debug("systemd-update-utmp running as pid "PID_FMT, getpid());
269
270 if (streq(argv[1], "reboot"))
271 r = on_reboot(&c);
272 else if (streq(argv[1], "shutdown"))
273 r = on_shutdown(&c);
274 else if (streq(argv[1], "runlevel"))
275 r = on_runlevel(&c);
276 else {
277 log_error("Unknown command %s", argv[1]);
278 r = -EINVAL;
279 }
280
281 log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid());
282
283 finish:
284 #ifdef HAVE_AUDIT
285 if (c.audit_fd >= 0)
286 audit_close(c.audit_fd);
287 #endif
288
289 sd_bus_unref(c.bus);
290 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
291 }