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