chiark / gitweb /
machinectl: fix handling of --verify= argument for dkr downloads
[elogind.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 <assert.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #ifdef HAVE_AUDIT
29 #include <libaudit.h>
30 #endif
31
32 #include "sd-bus.h"
33
34 #include "log.h"
35 #include "macro.h"
36 #include "util.h"
37 #include "special.h"
38 #include "utmp-wtmp.h"
39 #include "bus-util.h"
40 #include "bus-error.h"
41 #include "unit-name.h"
42
43 typedef struct Context {
44         sd_bus *bus;
45 #ifdef HAVE_AUDIT
46         int audit_fd;
47 #endif
48 } Context;
49
50 static usec_t get_startup_time(Context *c) {
51         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
52         usec_t t = 0;
53         int r;
54
55         assert(c);
56
57         r = sd_bus_get_property_trivial(
58                         c->bus,
59                         "org.freedesktop.systemd1",
60                         "/org/freedesktop/systemd1",
61                         "org.freedesktop.systemd1.Manager",
62                         "UserspaceTimestamp",
63                         &error,
64                         't', &t);
65         if (r < 0) {
66                 log_error("Failed to get timestamp: %s", bus_error_message(&error, -r));
67                 return 0;
68         }
69
70         return t;
71 }
72
73 static 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
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. */
83                 { '5', SPECIAL_RUNLEVEL5_TARGET },
84                 { '3', SPECIAL_RUNLEVEL3_TARGET },
85                 { '4', SPECIAL_RUNLEVEL4_TARGET },
86                 { '2', SPECIAL_RUNLEVEL2_TARGET },
87                 { '1', SPECIAL_RESCUE_TARGET },
88         };
89
90         _cleanup_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                         log_warning("Failed to get state: %s", bus_error_message(&error, -r));
113                         return r;
114                 }
115
116                 if (streq(state, "active") || streq(state, "reloading"))
117                         return table[i].runlevel;
118         }
119
120         return 0;
121 }
122
123 static int on_reboot(Context *c) {
124         int r = 0, q;
125         usec_t t;
126
127         assert(c);
128
129         /* We finished start-up, so let's write the utmp
130          * record and send the audit msg */
131
132 #ifdef HAVE_AUDIT
133         if (c->audit_fd >= 0)
134                 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
135                     errno != EPERM) {
136                         log_error_errno(errno, "Failed to send audit message: %m");
137                         r = -errno;
138                 }
139 #endif
140
141         /* If this call fails it will return 0, which
142          * utmp_put_reboot() will then fix to the current time */
143         t = get_startup_time(c);
144
145         q = utmp_put_reboot(t);
146         if (q < 0) {
147                 log_error_errno(q, "Failed to write utmp record: %m");
148                 r = q;
149         }
150
151         return r;
152 }
153
154 static int on_shutdown(Context *c) {
155         int r = 0, q;
156
157         assert(c);
158
159         /* We started shut-down, so let's write the utmp
160          * record and send the audit msg */
161
162 #ifdef HAVE_AUDIT
163         if (c->audit_fd >= 0)
164                 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
165                     errno != EPERM) {
166                         log_error_errno(errno, "Failed to send audit message: %m");
167                         r = -errno;
168                 }
169 #endif
170
171         q = utmp_put_shutdown();
172         if (q < 0) {
173                 log_error_errno(q, "Failed to write utmp record: %m");
174                 r = q;
175         }
176
177         return r;
178 }
179
180 static int on_runlevel(Context *c) {
181         int r = 0, q, previous, runlevel;
182
183         assert(c);
184
185         /* We finished changing runlevel, so let's write the
186          * utmp record and send the audit msg */
187
188         /* First, get last runlevel */
189         q = utmp_get_runlevel(&previous, NULL);
190
191         if (q < 0) {
192                 if (q != -ESRCH && q != -ENOENT)
193                         return log_error_errno(q, "Failed to get current runlevel: %m");
194
195                 previous = 0;
196         }
197
198         /* Secondly, get new runlevel */
199         runlevel = get_current_runlevel(c);
200
201         if (runlevel < 0)
202                 return runlevel;
203
204         if (previous == runlevel)
205                 return 0;
206
207 #ifdef HAVE_AUDIT
208         if (c->audit_fd >= 0) {
209                 _cleanup_free_ char *s = NULL;
210
211                 if (asprintf(&s, "old-level=%c new-level=%c",
212                              previous > 0 ? previous : 'N',
213                              runlevel > 0 ? runlevel : 'N') < 0)
214                         return log_oom();
215
216                 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
217                     errno != EPERM) {
218                         log_error_errno(errno, "Failed to send audit message: %m");
219                         r = -errno;
220                 }
221         }
222 #endif
223
224         q = utmp_put_runlevel(runlevel, previous);
225         if (q < 0 && q != -ESRCH && q != -ENOENT) {
226                 log_error_errno(q, "Failed to write utmp record: %m");
227                 r = q;
228         }
229
230         return r;
231 }
232
233 int main(int argc, char *argv[]) {
234         Context c = {
235 #ifdef HAVE_AUDIT
236                 .audit_fd = -1
237 #endif
238         };
239         int r;
240
241         if (getppid() != 1) {
242                 log_error("This program should be invoked by init only.");
243                 return EXIT_FAILURE;
244         }
245
246         if (argc != 2) {
247                 log_error("This program requires one argument.");
248                 return EXIT_FAILURE;
249         }
250
251         log_set_target(LOG_TARGET_AUTO);
252         log_parse_environment();
253         log_open();
254
255         umask(0022);
256
257 #ifdef HAVE_AUDIT
258         /* If the kernel lacks netlink or audit support,
259          * don't worry about it. */
260         c.audit_fd = audit_open();
261         if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
262                 log_error_errno(errno, "Failed to connect to audit log: %m");
263 #endif
264         r = bus_open_system_systemd(&c.bus);
265         if (r < 0) {
266                 log_error_errno(r, "Failed to get D-Bus connection: %m");
267                 r = -EIO;
268                 goto finish;
269         }
270
271         log_debug("systemd-update-utmp running as pid "PID_FMT, getpid());
272
273         if (streq(argv[1], "reboot"))
274                 r = on_reboot(&c);
275         else if (streq(argv[1], "shutdown"))
276                 r = on_shutdown(&c);
277         else if (streq(argv[1], "runlevel"))
278                 r = on_runlevel(&c);
279         else {
280                 log_error("Unknown command %s", argv[1]);
281                 r = -EINVAL;
282         }
283
284         log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid());
285
286 finish:
287 #ifdef HAVE_AUDIT
288         if (c.audit_fd >= 0)
289                 audit_close(c.audit_fd);
290 #endif
291
292         if (c.bus)
293                 sd_bus_unref(c.bus);
294
295         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
296 }