]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/update-utmp/update-utmp.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / update-utmp / update-utmp.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
4927fcae
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
4927fcae
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
4927fcae 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
4927fcae
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
4927fcae
LP
21#include <errno.h>
22#include <string.h>
4927fcae
LP
23#include <unistd.h>
24
349cc4a5 25#if HAVE_AUDIT
830f6caa
LP
26#include <libaudit.h>
27#endif
28
89456fce
TG
29#include "sd-bus.h"
30
b5efdb8a
LP
31#include "alloc-util.h"
32#include "bus-error.h"
33#include "bus-util.h"
f97b34a6 34#include "format-util.h"
4927fcae
LP
35#include "log.h"
36#include "macro.h"
df0ff127 37#include "process-util.h"
4927fcae 38#include "special.h"
dfc7f594 39#include "strv.h"
e7fb33ff 40#include "unit-name.h"
b5efdb8a
LP
41#include "util.h"
42#include "utmp-wtmp.h"
4927fcae
LP
43
44typedef struct Context {
89456fce 45 sd_bus *bus;
349cc4a5 46#if HAVE_AUDIT
4927fcae
LP
47 int audit_fd;
48#endif
49} Context;
50
51static usec_t get_startup_time(Context *c) {
4afd3348 52 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
47c649b5 53 usec_t t = 0;
89456fce 54 int r;
4927fcae 55
4927fcae
LP
56 assert(c);
57
47c649b5 58 r = sd_bus_get_property_trivial(
436ddded
SP
59 c->bus,
60 "org.freedesktop.systemd1",
61 "/org/freedesktop/systemd1",
89456fce 62 "org.freedesktop.systemd1.Manager",
47c649b5
LP
63 "UserspaceTimestamp",
64 &error,
65 't', &t);
89456fce 66 if (r < 0) {
f0960da0 67 log_error_errno(r, "Failed to get timestamp: %s", bus_error_message(&error, r));
47c649b5 68 return 0;
4927fcae
LP
69 }
70
4927fcae
LP
71 return t;
72}
73
74static int get_current_runlevel(Context *c) {
75 static const struct {
76 const int runlevel;
77 const char *special;
78 } table[] = {
79 /* The first target of this list that is active or has
ee2083d5
LP
80 * a job scheduled wins. We prefer runlevels 5 and 3
81 * here over the others, since these are the main
82 * runlevels used on Fedora. It might make sense to
83 * change the order on some distributions. */
d5d8429a
LP
84 { '5', SPECIAL_GRAPHICAL_TARGET },
85 { '3', SPECIAL_MULTI_USER_TARGET },
86 { '1', SPECIAL_RESCUE_TARGET },
4927fcae 87 };
4927fcae 88
4afd3348 89 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
89456fce 90 int r;
4927fcae 91 unsigned i;
4927fcae
LP
92
93 assert(c);
94
4927fcae 95 for (i = 0; i < ELEMENTSOF(table); i++) {
e7fb33ff 96 _cleanup_free_ char *state = NULL, *path = NULL;
4927fcae 97
e7fb33ff
LP
98 path = unit_dbus_path_from_name(table[i].special);
99 if (!path)
100 return log_oom();
89456fce 101
47c649b5 102 r = sd_bus_get_property_string(
436ddded
SP
103 c->bus,
104 "org.freedesktop.systemd1",
105 path,
47c649b5
LP
106 "org.freedesktop.systemd1.Unit",
107 "ActiveState",
89456fce 108 &error,
47c649b5 109 &state);
f0960da0
LP
110 if (r < 0)
111 return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r));
4927fcae 112
dfc7f594 113 if (STR_IN_SET(state, "active", "reloading"))
89456fce 114 return table[i].runlevel;
4927fcae
LP
115 }
116
89456fce 117 return 0;
4927fcae
LP
118}
119
120static 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
349cc4a5 129#if HAVE_AUDIT
4927fcae 130 if (c->audit_fd >= 0)
0aa281df 131 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
44785992 132 errno != EPERM) {
f0960da0 133 r = log_error_errno(errno, "Failed to send audit message: %m");
4927fcae
LP
134 }
135#endif
136
137 /* If this call fails it will return 0, which
138 * utmp_put_reboot() will then fix to the current time */
139 t = get_startup_time(c);
140
1cea22a5
LP
141 q = utmp_put_reboot(t);
142 if (q < 0) {
da927ba9 143 log_error_errno(q, "Failed to write utmp record: %m");
4927fcae
LP
144 r = q;
145 }
146
147 return r;
148}
149
150static int on_shutdown(Context *c) {
151 int r = 0, q;
152
153 assert(c);
154
155 /* We started shut-down, so let's write the utmp
156 * record and send the audit msg */
157
349cc4a5 158#if HAVE_AUDIT
4927fcae 159 if (c->audit_fd >= 0)
0aa281df 160 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
44785992 161 errno != EPERM) {
f0960da0 162 r = log_error_errno(errno, "Failed to send audit message: %m");
4927fcae
LP
163 }
164#endif
165
1cea22a5
LP
166 q = utmp_put_shutdown();
167 if (q < 0) {
da927ba9 168 log_error_errno(q, "Failed to write utmp record: %m");
4927fcae
LP
169 r = q;
170 }
171
172 return r;
173}
174
175static int on_runlevel(Context *c) {
176 int r = 0, q, previous, runlevel;
177
178 assert(c);
179
180 /* We finished changing runlevel, so let's write the
181 * utmp record and send the audit msg */
182
183 /* First, get last runlevel */
1cea22a5 184 q = utmp_get_runlevel(&previous, NULL);
4927fcae 185
e7fb33ff 186 if (q < 0) {
4c701096 187 if (!IN_SET(q, -ESRCH, -ENOENT))
f647962d 188 return log_error_errno(q, "Failed to get current runlevel: %m");
4927fcae 189
4927fcae
LP
190 previous = 0;
191 }
192
830f6caa 193 /* Secondly, get new runlevel */
1cea22a5 194 runlevel = get_current_runlevel(c);
e7fb33ff 195
1cea22a5 196 if (runlevel < 0)
4927fcae
LP
197 return runlevel;
198
199 if (previous == runlevel)
200 return 0;
201
349cc4a5 202#if HAVE_AUDIT
4927fcae 203 if (c->audit_fd >= 0) {
1cea22a5 204 _cleanup_free_ char *s = NULL;
4927fcae 205
cd6d0a45
LP
206 if (asprintf(&s, "old-level=%c new-level=%c",
207 previous > 0 ? previous : 'N',
208 runlevel > 0 ? runlevel : 'N') < 0)
1cea22a5 209 return log_oom();
4927fcae 210
ece174c5 211 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM)
f0960da0 212 r = log_error_errno(errno, "Failed to send audit message: %m");
4927fcae
LP
213 }
214#endif
215
1cea22a5 216 q = utmp_put_runlevel(runlevel, previous);
4c701096 217 if (q < 0 && !IN_SET(q, -ESRCH, -ENOENT)) {
da927ba9 218 log_error_errno(q, "Failed to write utmp record: %m");
1cea22a5 219 r = q;
4927fcae
LP
220 }
221
222 return r;
223}
224
225int main(int argc, char *argv[]) {
1cea22a5 226 Context c = {
349cc4a5 227#if HAVE_AUDIT
1cea22a5 228 .audit_fd = -1
4927fcae 229#endif
1cea22a5
LP
230 };
231 int r;
4927fcae 232
cd6d0a45
LP
233 if (getppid() != 1) {
234 log_error("This program should be invoked by init only.");
22f4096c 235 return EXIT_FAILURE;
cd6d0a45 236 }
4927fcae
LP
237
238 if (argc != 2) {
239 log_error("This program requires one argument.");
22f4096c 240 return EXIT_FAILURE;
4927fcae
LP
241 }
242
4cfa2c99 243 log_set_target(LOG_TARGET_AUTO);
4927fcae 244 log_parse_environment();
2396fb04 245 log_open();
4927fcae 246
4c12626c
LP
247 umask(0022);
248
349cc4a5 249#if HAVE_AUDIT
1cea22a5
LP
250 /* If the kernel lacks netlink or audit support,
251 * don't worry about it. */
252 c.audit_fd = audit_open();
4c701096 253 if (c.audit_fd < 0 && !IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT))
56f64d95 254 log_error_errno(errno, "Failed to connect to audit log: %m");
4927fcae 255#endif
266f3e26 256 r = bus_connect_system_systemd(&c.bus);
89456fce 257 if (r < 0) {
da927ba9 258 log_error_errno(r, "Failed to get D-Bus connection: %m");
4927fcae
LP
259 r = -EIO;
260 goto finish;
261 }
262
df0ff127 263 log_debug("systemd-update-utmp running as pid "PID_FMT, getpid_cached());
4927fcae
LP
264
265 if (streq(argv[1], "reboot"))
266 r = on_reboot(&c);
267 else if (streq(argv[1], "shutdown"))
268 r = on_shutdown(&c);
269 else if (streq(argv[1], "runlevel"))
270 r = on_runlevel(&c);
271 else {
272 log_error("Unknown command %s", argv[1]);
273 r = -EINVAL;
274 }
275
df0ff127 276 log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid_cached());
4927fcae 277
cd6d0a45 278finish:
349cc4a5 279#if HAVE_AUDIT
4927fcae
LP
280 if (c.audit_fd >= 0)
281 audit_close(c.audit_fd);
282#endif
283
f0792aae 284 sd_bus_flush_close_unref(c.bus);
22f4096c 285 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
4927fcae 286}