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