]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/update-utmp/update-utmp.c
Merge pull request #1527 from keszybz/lz4
[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_errno(r, "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 return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r));
110
111 if (streq(state, "active") || streq(state, "reloading"))
112 return table[i].runlevel;
113 }
114
115 return 0;
116 }
117
118 static int on_reboot(Context *c) {
119 int r = 0, q;
120 usec_t t;
121
122 assert(c);
123
124 /* We finished start-up, so let's write the utmp
125 * record and send the audit msg */
126
127 #ifdef HAVE_AUDIT
128 if (c->audit_fd >= 0)
129 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
130 errno != EPERM) {
131 r = log_error_errno(errno, "Failed to send audit message: %m");
132 }
133 #endif
134
135 /* If this call fails it will return 0, which
136 * utmp_put_reboot() will then fix to the current time */
137 t = get_startup_time(c);
138
139 q = utmp_put_reboot(t);
140 if (q < 0) {
141 log_error_errno(q, "Failed to write utmp record: %m");
142 r = q;
143 }
144
145 return r;
146 }
147
148 static int on_shutdown(Context *c) {
149 int r = 0, q;
150
151 assert(c);
152
153 /* We started shut-down, so let's write the utmp
154 * record and send the audit msg */
155
156 #ifdef HAVE_AUDIT
157 if (c->audit_fd >= 0)
158 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
159 errno != EPERM) {
160 r = log_error_errno(errno, "Failed to send audit message: %m");
161 }
162 #endif
163
164 q = utmp_put_shutdown();
165 if (q < 0) {
166 log_error_errno(q, "Failed to write utmp record: %m");
167 r = q;
168 }
169
170 return r;
171 }
172
173 static int on_runlevel(Context *c) {
174 int r = 0, q, previous, runlevel;
175
176 assert(c);
177
178 /* We finished changing runlevel, so let's write the
179 * utmp record and send the audit msg */
180
181 /* First, get last runlevel */
182 q = utmp_get_runlevel(&previous, NULL);
183
184 if (q < 0) {
185 if (q != -ESRCH && q != -ENOENT)
186 return log_error_errno(q, "Failed to get current runlevel: %m");
187
188 previous = 0;
189 }
190
191 /* Secondly, get new runlevel */
192 runlevel = get_current_runlevel(c);
193
194 if (runlevel < 0)
195 return runlevel;
196
197 if (previous == runlevel)
198 return 0;
199
200 #ifdef HAVE_AUDIT
201 if (c->audit_fd >= 0) {
202 _cleanup_free_ char *s = NULL;
203
204 if (asprintf(&s, "old-level=%c new-level=%c",
205 previous > 0 ? previous : 'N',
206 runlevel > 0 ? runlevel : 'N') < 0)
207 return log_oom();
208
209 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM)
210 r = log_error_errno(errno, "Failed to send audit message: %m");
211 }
212 #endif
213
214 q = utmp_put_runlevel(runlevel, previous);
215 if (q < 0 && q != -ESRCH && q != -ENOENT) {
216 log_error_errno(q, "Failed to write utmp record: %m");
217 r = q;
218 }
219
220 return r;
221 }
222
223 int main(int argc, char *argv[]) {
224 Context c = {
225 #ifdef HAVE_AUDIT
226 .audit_fd = -1
227 #endif
228 };
229 int r;
230
231 if (getppid() != 1) {
232 log_error("This program should be invoked by init only.");
233 return EXIT_FAILURE;
234 }
235
236 if (argc != 2) {
237 log_error("This program requires one argument.");
238 return EXIT_FAILURE;
239 }
240
241 log_set_target(LOG_TARGET_AUTO);
242 log_parse_environment();
243 log_open();
244
245 umask(0022);
246
247 #ifdef HAVE_AUDIT
248 /* If the kernel lacks netlink or audit support,
249 * don't worry about it. */
250 c.audit_fd = audit_open();
251 if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
252 log_error_errno(errno, "Failed to connect to audit log: %m");
253 #endif
254 r = bus_connect_system_systemd(&c.bus);
255 if (r < 0) {
256 log_error_errno(r, "Failed to get D-Bus connection: %m");
257 r = -EIO;
258 goto finish;
259 }
260
261 log_debug("systemd-update-utmp running as pid "PID_FMT, getpid());
262
263 if (streq(argv[1], "reboot"))
264 r = on_reboot(&c);
265 else if (streq(argv[1], "shutdown"))
266 r = on_shutdown(&c);
267 else if (streq(argv[1], "runlevel"))
268 r = on_runlevel(&c);
269 else {
270 log_error("Unknown command %s", argv[1]);
271 r = -EINVAL;
272 }
273
274 log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid());
275
276 finish:
277 #ifdef HAVE_AUDIT
278 if (c.audit_fd >= 0)
279 audit_close(c.audit_fd);
280 #endif
281
282 sd_bus_flush_close_unref(c.bus);
283 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
284 }