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