]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-manager.c
manager: extend performance measurement interface to include firmware/loader times
[thirdparty/systemd.git] / src / core / dbus-manager.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 <unistd.h>
24
25 #include "dbus.h"
26 #include "log.h"
27 #include "dbus-manager.h"
28 #include "strv.h"
29 #include "bus-errors.h"
30 #include "build.h"
31 #include "dbus-common.h"
32 #include "install.h"
33 #include "watchdog.h"
34 #include "hwclock.h"
35 #include "path-util.h"
36
37 #define BUS_MANAGER_INTERFACE_BEGIN \
38 " <interface name=\"org.freedesktop.systemd1.Manager\">\n"
39
40 #define BUS_MANAGER_INTERFACE_METHODS \
41 " <method name=\"GetUnit\">\n" \
42 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
43 " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
44 " </method>\n" \
45 " <method name=\"GetUnitByPID\">\n" \
46 " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
47 " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
48 " </method>\n" \
49 " <method name=\"LoadUnit\">\n" \
50 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
51 " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
52 " </method>\n" \
53 " <method name=\"StartUnit\">\n" \
54 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
55 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
56 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
57 " </method>\n" \
58 " <method name=\"StartUnitReplace\">\n" \
59 " <arg name=\"old_unit\" type=\"s\" direction=\"in\"/>\n" \
60 " <arg name=\"new_unit\" type=\"s\" direction=\"in\"/>\n" \
61 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
62 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
63 " </method>\n" \
64 " <method name=\"StopUnit\">\n" \
65 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
66 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
67 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
68 " </method>\n" \
69 " <method name=\"ReloadUnit\">\n" \
70 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
71 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
72 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
73 " </method>\n" \
74 " <method name=\"RestartUnit\">\n" \
75 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
76 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
77 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
78 " </method>\n" \
79 " <method name=\"TryRestartUnit\">\n" \
80 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
81 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
82 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
83 " </method>\n" \
84 " <method name=\"ReloadOrRestartUnit\">\n" \
85 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
86 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
87 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
88 " </method>\n" \
89 " <method name=\"ReloadOrTryRestartUnit\">\n" \
90 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
91 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
92 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
93 " </method>\n" \
94 " <method name=\"KillUnit\">\n" \
95 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
96 " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
97 " <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n" \
98 " </method>\n" \
99 " <method name=\"ResetFailedUnit\">\n" \
100 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
101 " </method>\n" \
102 " <method name=\"GetJob\">\n" \
103 " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
104 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
105 " </method>\n" \
106 " <method name=\"ClearJobs\"/>\n" \
107 " <method name=\"ResetFailed\"/>\n" \
108 " <method name=\"ListUnits\">\n" \
109 " <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \
110 " </method>\n" \
111 " <method name=\"ListJobs\">\n" \
112 " <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \
113 " </method>\n" \
114 " <method name=\"Subscribe\"/>\n" \
115 " <method name=\"Unsubscribe\"/>\n" \
116 " <method name=\"Dump\">\n" \
117 " <arg name=\"dump\" type=\"s\" direction=\"out\"/>\n" \
118 " </method>\n" \
119 " <method name=\"CreateSnapshot\">\n" \
120 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
121 " <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \
122 " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
123 " </method>\n" \
124 " <method name=\"Reload\"/>\n" \
125 " <method name=\"Reexecute\"/>\n" \
126 " <method name=\"Exit\"/>\n" \
127 " <method name=\"Reboot\"/>\n" \
128 " <method name=\"PowerOff\"/>\n" \
129 " <method name=\"Halt\"/>\n" \
130 " <method name=\"KExec\"/>\n" \
131 " <method name=\"SwitchRoot\">\n" \
132 " <arg name=\"new_root\" type=\"s\" direction=\"in\"/>\n" \
133 " <arg name=\"init\" type=\"s\" direction=\"in\"/>\n" \
134 " </method>\n" \
135 " <method name=\"SetEnvironment\">\n" \
136 " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \
137 " </method>\n" \
138 " <method name=\"UnsetEnvironment\">\n" \
139 " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \
140 " </method>\n" \
141 " <method name=\"UnsetAndSetEnvironment\">\n" \
142 " <arg name=\"unset\" type=\"as\" direction=\"in\"/>\n" \
143 " <arg name=\"set\" type=\"as\" direction=\"in\"/>\n" \
144 " </method>\n" \
145 " <method name=\"ListUnitFiles\">\n" \
146 " <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
147 " </method>\n" \
148 " <method name=\"GetUnitFileState\">\n" \
149 " <arg name=\"file\" type=\"s\" direction=\"in\"/>\n" \
150 " <arg name=\"state\" type=\"s\" direction=\"out\"/>\n" \
151 " </method>\n" \
152 " <method name=\"EnableUnitFiles\">\n" \
153 " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
154 " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
155 " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
156 " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
157 " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
158 " </method>\n" \
159 " <method name=\"DisableUnitFiles\">\n" \
160 " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
161 " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
162 " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
163 " </method>\n" \
164 " <method name=\"ReenableUnitFiles\">\n" \
165 " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
166 " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
167 " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
168 " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
169 " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
170 " </method>\n" \
171 " <method name=\"LinkUnitFiles\">\n" \
172 " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
173 " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
174 " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
175 " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
176 " </method>\n" \
177 " <method name=\"PresetUnitFiles\">\n" \
178 " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
179 " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
180 " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
181 " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
182 " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
183 " </method>\n" \
184 " <method name=\"MaskUnitFiles\">\n" \
185 " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
186 " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
187 " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
188 " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
189 " </method>\n" \
190 " <method name=\"UnmaskUnitFiles\">\n" \
191 " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
192 " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
193 " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
194 " </method>\n"
195
196 #define BUS_MANAGER_INTERFACE_SIGNALS \
197 " <signal name=\"UnitNew\">\n" \
198 " <arg name=\"id\" type=\"s\"/>\n" \
199 " <arg name=\"unit\" type=\"o\"/>\n" \
200 " </signal>\n" \
201 " <signal name=\"UnitRemoved\">\n" \
202 " <arg name=\"id\" type=\"s\"/>\n" \
203 " <arg name=\"unit\" type=\"o\"/>\n" \
204 " </signal>\n" \
205 " <signal name=\"JobNew\">\n" \
206 " <arg name=\"id\" type=\"u\"/>\n" \
207 " <arg name=\"job\" type=\"o\"/>\n" \
208 " <arg name=\"unit\" type=\"s\"/>\n" \
209 " </signal>\n" \
210 " <signal name=\"JobRemoved\">\n" \
211 " <arg name=\"id\" type=\"u\"/>\n" \
212 " <arg name=\"job\" type=\"o\"/>\n" \
213 " <arg name=\"unit\" type=\"s\"/>\n" \
214 " <arg name=\"result\" type=\"s\"/>\n" \
215 " </signal>" \
216 " <signal name=\"StartupFinished\">\n" \
217 " <arg name=\"firmware\" type=\"t\"/>\n" \
218 " <arg name=\"loader\" type=\"t\"/>\n" \
219 " <arg name=\"kernel\" type=\"t\"/>\n" \
220 " <arg name=\"initrd\" type=\"t\"/>\n" \
221 " <arg name=\"userspace\" type=\"t\"/>\n" \
222 " <arg name=\"total\" type=\"t\"/>\n" \
223 " </signal>" \
224 " <signal name=\"UnitFilesChanged\"/>\n"
225
226 #define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
227 " <property name=\"Version\" type=\"s\" access=\"read\"/>\n" \
228 " <property name=\"Distribution\" type=\"s\" access=\"read\"/>\n" \
229 " <property name=\"Features\" type=\"s\" access=\"read\"/>\n" \
230 " <property name=\"Tainted\" type=\"s\" access=\"read\"/>\n" \
231 " <property name=\"FirmwareTimestamp\" type=\"t\" access=\"read\"/>\n" \
232 " <property name=\"FirmwareTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
233 " <property name=\"LoaderTimestamp\" type=\"t\" access=\"read\"/>\n" \
234 " <property name=\"LoaderTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
235 " <property name=\"KernelTimestamp\" type=\"t\" access=\"read\"/>\n" \
236 " <property name=\"KernelTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
237 " <property name=\"InitRDTimestamp\" type=\"t\" access=\"read\"/>\n" \
238 " <property name=\"InitRDTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
239 " <property name=\"UserspaceTimestamp\" type=\"t\" access=\"read\"/>\n" \
240 " <property name=\"UserspaceTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
241 " <property name=\"FinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
242 " <property name=\"FinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
243 " <property name=\"LogLevel\" type=\"s\" access=\"readwrite\"/>\n" \
244 " <property name=\"LogTarget\" type=\"s\" access=\"readwrite\"/>\n" \
245 " <property name=\"NNames\" type=\"u\" access=\"read\"/>\n" \
246 " <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n" \
247 " <property name=\"NInstalledJobs\" type=\"u\" access=\"read\"/>\n" \
248 " <property name=\"NFailedJobs\" type=\"u\" access=\"read\"/>\n" \
249 " <property name=\"Progress\" type=\"d\" access=\"read\"/>\n" \
250 " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
251 " <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \
252 " <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \
253 " <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \
254 " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
255 " <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \
256 " <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \
257 " <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \
258 " <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
259 " <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n"
260
261 #define BUS_MANAGER_INTERFACE_END \
262 " </interface>\n"
263
264 #define BUS_MANAGER_INTERFACE \
265 BUS_MANAGER_INTERFACE_BEGIN \
266 BUS_MANAGER_INTERFACE_METHODS \
267 BUS_MANAGER_INTERFACE_SIGNALS \
268 BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
269 BUS_MANAGER_INTERFACE_END
270
271 #define INTROSPECTION_BEGIN \
272 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
273 "<node>\n" \
274 BUS_MANAGER_INTERFACE \
275 BUS_PROPERTIES_INTERFACE \
276 BUS_PEER_INTERFACE \
277 BUS_INTROSPECTABLE_INTERFACE
278
279 #define INTROSPECTION_END \
280 "</node>\n"
281
282 #define INTERFACES_LIST \
283 BUS_GENERIC_INTERFACES_LIST \
284 "org.freedesktop.systemd1.Manager\0"
285
286 const char bus_manager_interface[] _introspect_("Manager") = BUS_MANAGER_INTERFACE;
287
288 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_exec_output, exec_output, ExecOutput);
289
290 static int bus_manager_append_tainted(DBusMessageIter *i, const char *property, void *data) {
291 const char *t;
292 Manager *m = data;
293 char buf[LINE_MAX] = "", *e = buf, *p = NULL;
294
295 assert(i);
296 assert(property);
297 assert(m);
298
299 if (m->taint_usr)
300 e = stpcpy(e, "split-usr:");
301
302 if (readlink_malloc("/etc/mtab", &p) < 0)
303 e = stpcpy(e, "mtab-not-symlink:");
304 else
305 free(p);
306
307 if (access("/proc/cgroups", F_OK) < 0)
308 e = stpcpy(e, "cgroups-missing:");
309
310 if (hwclock_is_localtime() > 0)
311 e = stpcpy(e, "local-hwclock:");
312
313 /* remove the last ':' */
314 if (e != buf)
315 e[-1] = 0;
316
317 t = buf;
318
319 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
320 return -ENOMEM;
321
322 return 0;
323 }
324
325 static int bus_manager_append_log_target(DBusMessageIter *i, const char *property, void *data) {
326 const char *t;
327
328 assert(i);
329 assert(property);
330
331 t = log_target_to_string(log_get_target());
332
333 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
334 return -ENOMEM;
335
336 return 0;
337 }
338
339 static int bus_manager_set_log_target(DBusMessageIter *i, const char *property, void *data) {
340 const char *t;
341
342 assert(i);
343 assert(property);
344
345 dbus_message_iter_get_basic(i, &t);
346
347 return log_set_target_from_string(t);
348 }
349
350 static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) {
351 const char *t;
352
353 assert(i);
354 assert(property);
355
356 t = log_level_to_string(log_get_max_level());
357
358 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
359 return -ENOMEM;
360
361 return 0;
362 }
363
364 static int bus_manager_set_log_level(DBusMessageIter *i, const char *property, void *data) {
365 const char *t;
366
367 assert(i);
368 assert(property);
369
370 dbus_message_iter_get_basic(i, &t);
371
372 return log_set_max_level_from_string(t);
373 }
374
375 static int bus_manager_append_n_names(DBusMessageIter *i, const char *property, void *data) {
376 Manager *m = data;
377 uint32_t u;
378
379 assert(i);
380 assert(property);
381 assert(m);
382
383 u = hashmap_size(m->units);
384
385 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
386 return -ENOMEM;
387
388 return 0;
389 }
390
391 static int bus_manager_append_n_jobs(DBusMessageIter *i, const char *property, void *data) {
392 Manager *m = data;
393 uint32_t u;
394
395 assert(i);
396 assert(property);
397 assert(m);
398
399 u = hashmap_size(m->jobs);
400
401 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
402 return -ENOMEM;
403
404 return 0;
405 }
406
407 static int bus_manager_append_progress(DBusMessageIter *i, const char *property, void *data) {
408 double d;
409 Manager *m = data;
410
411 assert(i);
412 assert(property);
413 assert(m);
414
415 if (dual_timestamp_is_set(&m->finish_timestamp))
416 d = 1.0;
417 else
418 d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
419
420 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_DOUBLE, &d))
421 return -ENOMEM;
422
423 return 0;
424 }
425
426 static const char *message_get_sender_with_fallback(DBusMessage *m) {
427 const char *s;
428
429 assert(m);
430
431 if ((s = dbus_message_get_sender(m)))
432 return s;
433
434 /* When the message came in from a direct connection the
435 * message will have no sender. We fix that here. */
436
437 return ":no-sender";
438 }
439
440 static DBusMessage *message_from_file_changes(
441 DBusMessage *m,
442 UnitFileChange *changes,
443 unsigned n_changes,
444 int carries_install_info) {
445
446 DBusMessageIter iter, sub, sub2;
447 DBusMessage *reply;
448 unsigned i;
449
450 reply = dbus_message_new_method_return(m);
451 if (!reply)
452 return NULL;
453
454 dbus_message_iter_init_append(reply, &iter);
455
456 if (carries_install_info >= 0) {
457 dbus_bool_t b;
458
459 b = !!carries_install_info;
460 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b))
461 goto oom;
462 }
463
464 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub))
465 goto oom;
466
467 for (i = 0; i < n_changes; i++) {
468 const char *type, *path, *source;
469
470 type = unit_file_change_type_to_string(changes[i].type);
471 path = strempty(changes[i].path);
472 source = strempty(changes[i].source);
473
474 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
475 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
476 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &path) ||
477 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &source) ||
478 !dbus_message_iter_close_container(&sub, &sub2))
479 goto oom;
480 }
481
482 if (!dbus_message_iter_close_container(&iter, &sub))
483 goto oom;
484
485 return reply;
486
487 oom:
488 dbus_message_unref(reply);
489 return NULL;
490 }
491
492 static int bus_manager_send_unit_files_changed(Manager *m) {
493 DBusMessage *s;
494 int r;
495
496 s = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged");
497 if (!s)
498 return -ENOMEM;
499
500 r = bus_broadcast(m, s);
501 dbus_message_unref(s);
502
503 return r;
504 }
505
506 static int bus_manager_set_runtime_watchdog_usec(DBusMessageIter *i, const char *property, void *data) {
507 uint64_t *t = data;
508
509 assert(i);
510 assert(property);
511
512 dbus_message_iter_get_basic(i, t);
513
514 return watchdog_set_timeout(t);
515 }
516
517 static const char systemd_property_string[] =
518 PACKAGE_STRING "\0"
519 DISTRIBUTION "\0"
520 SYSTEMD_FEATURES;
521
522 static const BusProperty bus_systemd_properties[] = {
523 { "Version", bus_property_append_string, "s", 0 },
524 { "Distribution", bus_property_append_string, "s", sizeof(PACKAGE_STRING) },
525 { "Features", bus_property_append_string, "s", sizeof(PACKAGE_STRING) + sizeof(DISTRIBUTION) },
526 { NULL, }
527 };
528
529 static const BusProperty bus_manager_properties[] = {
530 { "Tainted", bus_manager_append_tainted, "s", 0 },
531 { "FirmwareTimestamp", bus_property_append_uint64, "t", offsetof(Manager, firmware_timestamp.realtime) },
532 { "FirmwareTimestampMonotonic", bus_property_append_uint64,"t", offsetof(Manager, firmware_timestamp.monotonic)},
533 { "LoaderTimestamp", bus_property_append_uint64, "t", offsetof(Manager, loader_timestamp.realtime) },
534 { "LoaderTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, loader_timestamp.monotonic) },
535 { "KernelTimestamp", bus_property_append_uint64, "t", offsetof(Manager, kernel_timestamp.realtime) },
536 { "KernelTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, kernel_timestamp.monotonic) },
537 { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) },
538 { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) },
539 { "UserspaceTimestamp", bus_property_append_uint64, "t", offsetof(Manager, userspace_timestamp.realtime)},
540 { "UserspaceTimestampMonotonic", bus_property_append_uint64,"t",offsetof(Manager, userspace_timestamp.monotonic)},
541 { "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) },
542 { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) },
543 { "LogLevel", bus_manager_append_log_level, "s", 0, false, bus_manager_set_log_level },
544 { "LogTarget", bus_manager_append_log_target, "s", 0, false, bus_manager_set_log_target },
545 { "NNames", bus_manager_append_n_names, "u", 0 },
546 { "NJobs", bus_manager_append_n_jobs, "u", 0 },
547 { "NInstalledJobs",bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) },
548 { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) },
549 { "Progress", bus_manager_append_progress, "d", 0 },
550 { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true },
551 { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) },
552 { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) },
553 { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true },
554 { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true },
555 { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true },
556 { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) },
557 { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) },
558 { "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), false, bus_manager_set_runtime_watchdog_usec },
559 { "ShutdownWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, shutdown_watchdog), false, bus_property_set_usec },
560 { NULL, }
561 };
562
563 static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
564 Manager *m = data;
565
566 int r;
567 DBusError error;
568 DBusMessage *reply = NULL;
569 char * path = NULL;
570 JobType job_type = _JOB_TYPE_INVALID;
571 bool reload_if_possible = false;
572 const char *member;
573
574 assert(connection);
575 assert(message);
576 assert(m);
577
578 dbus_error_init(&error);
579
580 member = dbus_message_get_member(message);
581
582 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
583 const char *name;
584 Unit *u;
585
586 if (!dbus_message_get_args(
587 message,
588 &error,
589 DBUS_TYPE_STRING, &name,
590 DBUS_TYPE_INVALID))
591 return bus_send_error_reply(connection, message, &error, -EINVAL);
592
593 if (!(u = manager_get_unit(m, name))) {
594 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
595 return bus_send_error_reply(connection, message, &error, -ENOENT);
596 }
597
598 if (!(reply = dbus_message_new_method_return(message)))
599 goto oom;
600
601 if (!(path = unit_dbus_path(u)))
602 goto oom;
603
604 if (!dbus_message_append_args(
605 reply,
606 DBUS_TYPE_OBJECT_PATH, &path,
607 DBUS_TYPE_INVALID))
608 goto oom;
609 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) {
610 Unit *u;
611 uint32_t pid;
612
613 if (!dbus_message_get_args(
614 message,
615 &error,
616 DBUS_TYPE_UINT32, &pid,
617 DBUS_TYPE_INVALID))
618 return bus_send_error_reply(connection, message, &error, -EINVAL);
619
620 if (!(u = cgroup_unit_by_pid(m, (pid_t) pid))) {
621 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
622 return bus_send_error_reply(connection, message, &error, -ENOENT);
623 }
624
625 if (!(reply = dbus_message_new_method_return(message)))
626 goto oom;
627
628 if (!(path = unit_dbus_path(u)))
629 goto oom;
630
631 if (!dbus_message_append_args(
632 reply,
633 DBUS_TYPE_OBJECT_PATH, &path,
634 DBUS_TYPE_INVALID))
635 goto oom;
636 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
637 const char *name;
638 Unit *u;
639
640 if (!dbus_message_get_args(
641 message,
642 &error,
643 DBUS_TYPE_STRING, &name,
644 DBUS_TYPE_INVALID))
645 return bus_send_error_reply(connection, message, &error, -EINVAL);
646
647 if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
648 return bus_send_error_reply(connection, message, &error, r);
649
650 if (!(reply = dbus_message_new_method_return(message)))
651 goto oom;
652
653 if (!(path = unit_dbus_path(u)))
654 goto oom;
655
656 if (!dbus_message_append_args(
657 reply,
658 DBUS_TYPE_OBJECT_PATH, &path,
659 DBUS_TYPE_INVALID))
660 goto oom;
661
662 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit"))
663 job_type = JOB_START;
664 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
665 job_type = JOB_START;
666 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit"))
667 job_type = JOB_STOP;
668 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit"))
669 job_type = JOB_RELOAD;
670 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit"))
671 job_type = JOB_RESTART;
672 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit"))
673 job_type = JOB_TRY_RESTART;
674 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) {
675 reload_if_possible = true;
676 job_type = JOB_RESTART;
677 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
678 reload_if_possible = true;
679 job_type = JOB_TRY_RESTART;
680 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KillUnit")) {
681 const char *name, *swho;
682 int32_t signo;
683 Unit *u;
684 KillWho who;
685
686 if (!dbus_message_get_args(
687 message,
688 &error,
689 DBUS_TYPE_STRING, &name,
690 DBUS_TYPE_STRING, &swho,
691 DBUS_TYPE_INT32, &signo,
692 DBUS_TYPE_INVALID))
693 return bus_send_error_reply(connection, message, &error, -EINVAL);
694
695 if (isempty(swho))
696 who = KILL_ALL;
697 else {
698 who = kill_who_from_string(swho);
699 if (who < 0)
700 return bus_send_error_reply(connection, message, &error, -EINVAL);
701 }
702
703 if (signo <= 0 || signo >= _NSIG)
704 return bus_send_error_reply(connection, message, &error, -EINVAL);
705
706 u = manager_get_unit(m, name);
707 if (!u) {
708 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
709 return bus_send_error_reply(connection, message, &error, -ENOENT);
710 }
711
712 r = unit_kill(u, who, signo, &error);
713 if (r < 0)
714 return bus_send_error_reply(connection, message, &error, r);
715
716 if (!(reply = dbus_message_new_method_return(message)))
717 goto oom;
718
719 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
720 uint32_t id;
721 Job *j;
722
723 if (!dbus_message_get_args(
724 message,
725 &error,
726 DBUS_TYPE_UINT32, &id,
727 DBUS_TYPE_INVALID))
728 return bus_send_error_reply(connection, message, &error, -EINVAL);
729
730 if (!(j = manager_get_job(m, id))) {
731 dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
732 return bus_send_error_reply(connection, message, &error, -ENOENT);
733 }
734
735 if (!(reply = dbus_message_new_method_return(message)))
736 goto oom;
737
738 if (!(path = job_dbus_path(j)))
739 goto oom;
740
741 if (!dbus_message_append_args(
742 reply,
743 DBUS_TYPE_OBJECT_PATH, &path,
744 DBUS_TYPE_INVALID))
745 goto oom;
746
747 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
748
749 manager_clear_jobs(m);
750
751 if (!(reply = dbus_message_new_method_return(message)))
752 goto oom;
753
754 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
755
756 manager_reset_failed(m);
757
758 if (!(reply = dbus_message_new_method_return(message)))
759 goto oom;
760
761 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
762 const char *name;
763 Unit *u;
764
765 if (!dbus_message_get_args(
766 message,
767 &error,
768 DBUS_TYPE_STRING, &name,
769 DBUS_TYPE_INVALID))
770 return bus_send_error_reply(connection, message, &error, -EINVAL);
771
772 if (!(u = manager_get_unit(m, name))) {
773 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
774 return bus_send_error_reply(connection, message, &error, -ENOENT);
775 }
776
777 unit_reset_failed(u);
778
779 if (!(reply = dbus_message_new_method_return(message)))
780 goto oom;
781
782 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
783 DBusMessageIter iter, sub;
784 Iterator i;
785 Unit *u;
786 const char *k;
787
788 if (!(reply = dbus_message_new_method_return(message)))
789 goto oom;
790
791 dbus_message_iter_init_append(reply, &iter);
792
793 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub))
794 goto oom;
795
796 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
797 char *u_path, *j_path;
798 const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following;
799 DBusMessageIter sub2;
800 uint32_t job_id;
801 Unit *f;
802
803 if (k != u->id)
804 continue;
805
806 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
807 goto oom;
808
809 description = unit_description(u);
810 load_state = unit_load_state_to_string(u->load_state);
811 active_state = unit_active_state_to_string(unit_active_state(u));
812 sub_state = unit_sub_state_to_string(u);
813
814 f = unit_following(u);
815 following = f ? f->id : "";
816
817 if (!(u_path = unit_dbus_path(u)))
818 goto oom;
819
820 if (u->job) {
821 job_id = (uint32_t) u->job->id;
822
823 if (!(j_path = job_dbus_path(u->job))) {
824 free(u_path);
825 goto oom;
826 }
827
828 sjob_type = job_type_to_string(u->job->type);
829 } else {
830 job_id = 0;
831 j_path = u_path;
832 sjob_type = "";
833 }
834
835 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->id) ||
836 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
837 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
838 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
839 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
840 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) ||
841 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
842 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
843 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) ||
844 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
845 free(u_path);
846 if (u->job)
847 free(j_path);
848 goto oom;
849 }
850
851 free(u_path);
852 if (u->job)
853 free(j_path);
854
855 if (!dbus_message_iter_close_container(&sub, &sub2))
856 goto oom;
857 }
858
859 if (!dbus_message_iter_close_container(&iter, &sub))
860 goto oom;
861
862 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
863 DBusMessageIter iter, sub;
864 Iterator i;
865 Job *j;
866
867 if (!(reply = dbus_message_new_method_return(message)))
868 goto oom;
869
870 dbus_message_iter_init_append(reply, &iter);
871
872 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
873 goto oom;
874
875 HASHMAP_FOREACH(j, m->jobs, i) {
876 char *u_path, *j_path;
877 const char *state, *type;
878 uint32_t id;
879 DBusMessageIter sub2;
880
881 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
882 goto oom;
883
884 id = (uint32_t) j->id;
885 state = job_state_to_string(j->state);
886 type = job_type_to_string(j->type);
887
888 if (!(j_path = job_dbus_path(j)))
889 goto oom;
890
891 if (!(u_path = unit_dbus_path(j->unit))) {
892 free(j_path);
893 goto oom;
894 }
895
896 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
897 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->id) ||
898 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
899 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
900 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
901 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
902 free(j_path);
903 free(u_path);
904 goto oom;
905 }
906
907 free(j_path);
908 free(u_path);
909
910 if (!dbus_message_iter_close_container(&sub, &sub2))
911 goto oom;
912 }
913
914 if (!dbus_message_iter_close_container(&iter, &sub))
915 goto oom;
916
917 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
918 char *client;
919 Set *s;
920
921 if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) {
922 if (!(s = set_new(string_hash_func, string_compare_func)))
923 goto oom;
924
925 if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) {
926 set_free(s);
927 goto oom;
928 }
929 }
930
931 if (!(client = strdup(message_get_sender_with_fallback(message))))
932 goto oom;
933
934 if ((r = set_put(s, client)) < 0) {
935 free(client);
936 return bus_send_error_reply(connection, message, NULL, r);
937 }
938
939 if (!(reply = dbus_message_new_method_return(message)))
940 goto oom;
941
942 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
943 char *client;
944
945 if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) message_get_sender_with_fallback(message)))) {
946 dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
947 return bus_send_error_reply(connection, message, &error, -ENOENT);
948 }
949
950 free(client);
951
952 if (!(reply = dbus_message_new_method_return(message)))
953 goto oom;
954
955 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
956 FILE *f;
957 char *dump = NULL;
958 size_t size;
959
960 if (!(reply = dbus_message_new_method_return(message)))
961 goto oom;
962
963 if (!(f = open_memstream(&dump, &size)))
964 goto oom;
965
966 manager_dump_units(m, f, NULL);
967 manager_dump_jobs(m, f, NULL);
968
969 if (ferror(f)) {
970 fclose(f);
971 free(dump);
972 goto oom;
973 }
974
975 fclose(f);
976
977 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
978 free(dump);
979 goto oom;
980 }
981
982 free(dump);
983 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
984 const char *name;
985 dbus_bool_t cleanup;
986 Snapshot *s;
987
988 if (!dbus_message_get_args(
989 message,
990 &error,
991 DBUS_TYPE_STRING, &name,
992 DBUS_TYPE_BOOLEAN, &cleanup,
993 DBUS_TYPE_INVALID))
994 return bus_send_error_reply(connection, message, &error, -EINVAL);
995
996 if (name && name[0] == 0)
997 name = NULL;
998
999 if ((r = snapshot_create(m, name, cleanup, &error, &s)) < 0)
1000 return bus_send_error_reply(connection, message, &error, r);
1001
1002 if (!(reply = dbus_message_new_method_return(message)))
1003 goto oom;
1004
1005 if (!(path = unit_dbus_path(UNIT(s))))
1006 goto oom;
1007
1008 if (!dbus_message_append_args(
1009 reply,
1010 DBUS_TYPE_OBJECT_PATH, &path,
1011 DBUS_TYPE_INVALID))
1012 goto oom;
1013
1014 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1015 char *introspection = NULL;
1016 FILE *f;
1017 Iterator i;
1018 Unit *u;
1019 Job *j;
1020 const char *k;
1021 size_t size;
1022
1023 if (!(reply = dbus_message_new_method_return(message)))
1024 goto oom;
1025
1026 /* We roll our own introspection code here, instead of
1027 * relying on bus_default_message_handler() because we
1028 * need to generate our introspection string
1029 * dynamically. */
1030
1031 if (!(f = open_memstream(&introspection, &size)))
1032 goto oom;
1033
1034 fputs(INTROSPECTION_BEGIN, f);
1035
1036 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1037 char *p;
1038
1039 if (k != u->id)
1040 continue;
1041
1042 if (!(p = bus_path_escape(k))) {
1043 fclose(f);
1044 free(introspection);
1045 goto oom;
1046 }
1047
1048 fprintf(f, "<node name=\"unit/%s\"/>", p);
1049 free(p);
1050 }
1051
1052 HASHMAP_FOREACH(j, m->jobs, i)
1053 fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
1054
1055 fputs(INTROSPECTION_END, f);
1056
1057 if (ferror(f)) {
1058 fclose(f);
1059 free(introspection);
1060 goto oom;
1061 }
1062
1063 fclose(f);
1064
1065 if (!introspection)
1066 goto oom;
1067
1068 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1069 free(introspection);
1070 goto oom;
1071 }
1072
1073 free(introspection);
1074
1075 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
1076
1077 assert(!m->queued_message);
1078
1079 /* Instead of sending the reply back right away, we
1080 * just remember that we need to and then send it
1081 * after the reload is finished. That way the caller
1082 * knows when the reload finished. */
1083
1084 if (!(m->queued_message = dbus_message_new_method_return(message)))
1085 goto oom;
1086
1087 m->queued_message_connection = connection;
1088 m->exit_code = MANAGER_RELOAD;
1089
1090 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
1091
1092 /* We don't send a reply back here, the client should
1093 * just wait for us disconnecting. */
1094
1095 m->exit_code = MANAGER_REEXECUTE;
1096
1097 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
1098
1099 if (m->running_as == MANAGER_SYSTEM) {
1100 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
1101 return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1102 }
1103
1104 if (!(reply = dbus_message_new_method_return(message)))
1105 goto oom;
1106
1107 m->exit_code = MANAGER_EXIT;
1108
1109 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
1110
1111 if (m->running_as != MANAGER_SYSTEM) {
1112 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
1113 return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1114 }
1115
1116 if (!(reply = dbus_message_new_method_return(message)))
1117 goto oom;
1118
1119 m->exit_code = MANAGER_REBOOT;
1120
1121 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
1122
1123 if (m->running_as != MANAGER_SYSTEM) {
1124 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
1125 return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1126 }
1127
1128 if (!(reply = dbus_message_new_method_return(message)))
1129 goto oom;
1130
1131 m->exit_code = MANAGER_POWEROFF;
1132
1133 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
1134
1135 if (m->running_as != MANAGER_SYSTEM) {
1136 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
1137 return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1138 }
1139
1140 if (!(reply = dbus_message_new_method_return(message)))
1141 goto oom;
1142
1143 m->exit_code = MANAGER_HALT;
1144
1145 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
1146
1147 if (m->running_as != MANAGER_SYSTEM) {
1148 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
1149 return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1150 }
1151
1152 if (!(reply = dbus_message_new_method_return(message)))
1153 goto oom;
1154
1155 m->exit_code = MANAGER_KEXEC;
1156
1157 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
1158 const char *switch_root, *switch_root_init;
1159 char *u, *v;
1160 int k;
1161
1162 if (!dbus_message_get_args(
1163 message,
1164 &error,
1165 DBUS_TYPE_STRING, &switch_root,
1166 DBUS_TYPE_STRING, &switch_root_init,
1167 DBUS_TYPE_INVALID))
1168 return bus_send_error_reply(connection, message, &error, -EINVAL);
1169
1170 if (path_equal(switch_root, "/") || !path_is_absolute(switch_root))
1171 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1172
1173 if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init))
1174 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1175
1176 if (m->running_as != MANAGER_SYSTEM) {
1177 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers.");
1178 return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1179 }
1180
1181 /* Safety check */
1182 if (isempty(switch_root_init))
1183 k = access(switch_root, F_OK);
1184 else {
1185 char *p;
1186
1187 p = strjoin(switch_root, "/", switch_root_init, NULL);
1188 if (!p)
1189 goto oom;
1190
1191 k = access(p, X_OK);
1192 free(p);
1193 }
1194 if (k < 0)
1195 return bus_send_error_reply(connection, message, NULL, -errno);
1196
1197 u = strdup(switch_root);
1198 if (!u)
1199 goto oom;
1200
1201 if (!isempty(switch_root_init)) {
1202 v = strdup(switch_root_init);
1203 if (!v) {
1204 free(u);
1205 goto oom;
1206 }
1207 } else
1208 v = NULL;
1209
1210 free(m->switch_root);
1211 free(m->switch_root_init);
1212 m->switch_root = u;
1213 m->switch_root_init = v;
1214
1215 reply = dbus_message_new_method_return(message);
1216 if (!reply)
1217 goto oom;
1218
1219 m->exit_code = MANAGER_SWITCH_ROOT;
1220
1221 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
1222 char **l = NULL, **e = NULL;
1223
1224 if ((r = bus_parse_strv(message, &l)) < 0) {
1225 if (r == -ENOMEM)
1226 goto oom;
1227
1228 return bus_send_error_reply(connection, message, NULL, r);
1229 }
1230
1231 e = strv_env_merge(2, m->environment, l);
1232 strv_free(l);
1233
1234 if (!e)
1235 goto oom;
1236
1237 if (!(reply = dbus_message_new_method_return(message))) {
1238 strv_free(e);
1239 goto oom;
1240 }
1241
1242 strv_free(m->environment);
1243 m->environment = e;
1244
1245 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
1246 char **l = NULL, **e = NULL;
1247
1248 if ((r = bus_parse_strv(message, &l)) < 0) {
1249 if (r == -ENOMEM)
1250 goto oom;
1251
1252 return bus_send_error_reply(connection, message, NULL, r);
1253 }
1254
1255 e = strv_env_delete(m->environment, 1, l);
1256 strv_free(l);
1257
1258 if (!e)
1259 goto oom;
1260
1261 if (!(reply = dbus_message_new_method_return(message))) {
1262 strv_free(e);
1263 goto oom;
1264 }
1265
1266 strv_free(m->environment);
1267 m->environment = e;
1268
1269 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
1270 char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
1271 DBusMessageIter iter;
1272
1273 if (!dbus_message_iter_init(message, &iter))
1274 goto oom;
1275
1276 r = bus_parse_strv_iter(&iter, &l_unset);
1277 if (r < 0) {
1278 if (r == -ENOMEM)
1279 goto oom;
1280
1281 return bus_send_error_reply(connection, message, NULL, r);
1282 }
1283
1284 if (!dbus_message_iter_next(&iter)) {
1285 strv_free(l_unset);
1286 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1287 }
1288
1289 r = bus_parse_strv_iter(&iter, &l_set);
1290 if (r < 0) {
1291 strv_free(l_unset);
1292 if (r == -ENOMEM)
1293 goto oom;
1294
1295 return bus_send_error_reply(connection, message, NULL, r);
1296 }
1297
1298 e = strv_env_delete(m->environment, 1, l_unset);
1299 strv_free(l_unset);
1300
1301 if (!e) {
1302 strv_free(l_set);
1303 goto oom;
1304 }
1305
1306 f = strv_env_merge(2, e, l_set);
1307 strv_free(l_set);
1308 strv_free(e);
1309
1310 if (!f)
1311 goto oom;
1312
1313 if (!(reply = dbus_message_new_method_return(message))) {
1314 strv_free(f);
1315 goto oom;
1316 }
1317
1318 strv_free(m->environment);
1319 m->environment = f;
1320 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnitFiles")) {
1321 DBusMessageIter iter, sub, sub2;
1322 Hashmap *h;
1323 Iterator i;
1324 UnitFileList *item;
1325
1326 reply = dbus_message_new_method_return(message);
1327 if (!reply)
1328 goto oom;
1329
1330 h = hashmap_new(string_hash_func, string_compare_func);
1331 if (!h)
1332 goto oom;
1333
1334 r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
1335 if (r < 0) {
1336 unit_file_list_free(h);
1337 dbus_message_unref(reply);
1338 return bus_send_error_reply(connection, message, NULL, r);
1339 }
1340
1341 dbus_message_iter_init_append(reply, &iter);
1342
1343 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) {
1344 unit_file_list_free(h);
1345 goto oom;
1346 }
1347
1348 HASHMAP_FOREACH(item, h, i) {
1349 const char *state;
1350
1351 state = unit_file_state_to_string(item->state);
1352 assert(state);
1353
1354 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
1355 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &item->path) ||
1356 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
1357 !dbus_message_iter_close_container(&sub, &sub2)) {
1358 unit_file_list_free(h);
1359 goto oom;
1360 }
1361 }
1362
1363 unit_file_list_free(h);
1364
1365 if (!dbus_message_iter_close_container(&iter, &sub))
1366 goto oom;
1367
1368 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitFileState")) {
1369 const char *name;
1370 UnitFileState state;
1371 const char *s;
1372
1373 if (!dbus_message_get_args(
1374 message,
1375 &error,
1376 DBUS_TYPE_STRING, &name,
1377 DBUS_TYPE_INVALID))
1378 return bus_send_error_reply(connection, message, &error, -EINVAL);
1379
1380 state = unit_file_get_state(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
1381 if (state < 0)
1382 return bus_send_error_reply(connection, message, NULL, state);
1383
1384 s = unit_file_state_to_string(state);
1385 assert(s);
1386
1387 reply = dbus_message_new_method_return(message);
1388 if (!reply)
1389 goto oom;
1390
1391 if (!dbus_message_append_args(
1392 reply,
1393 DBUS_TYPE_STRING, &s,
1394 DBUS_TYPE_INVALID))
1395 goto oom;
1396 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "EnableUnitFiles") ||
1397 dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") ||
1398 dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") ||
1399 dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") ||
1400 dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles")) {
1401
1402 char **l = NULL;
1403 DBusMessageIter iter;
1404 UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1405 UnitFileChange *changes = NULL;
1406 unsigned n_changes = 0;
1407 dbus_bool_t runtime, force;
1408 int carries_install_info = -1;
1409
1410 if (!dbus_message_iter_init(message, &iter))
1411 goto oom;
1412
1413 r = bus_parse_strv_iter(&iter, &l);
1414 if (r < 0) {
1415 if (r == -ENOMEM)
1416 goto oom;
1417
1418 return bus_send_error_reply(connection, message, NULL, r);
1419 }
1420
1421 if (!dbus_message_iter_next(&iter) ||
1422 bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0 ||
1423 bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &force, false) < 0) {
1424 strv_free(l);
1425 return bus_send_error_reply(connection, message, NULL, -EIO);
1426 }
1427
1428 if (streq(member, "EnableUnitFiles")) {
1429 r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
1430 carries_install_info = r;
1431 } else if (streq(member, "ReenableUnitFiles")) {
1432 r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
1433 carries_install_info = r;
1434 } else if (streq(member, "LinkUnitFiles"))
1435 r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
1436 else if (streq(member, "PresetUnitFiles")) {
1437 r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
1438 carries_install_info = r;
1439 } else if (streq(member, "MaskUnitFiles"))
1440 r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
1441 else
1442 assert_not_reached("Uh? Wrong method");
1443
1444 strv_free(l);
1445 bus_manager_send_unit_files_changed(m);
1446
1447 if (r < 0) {
1448 unit_file_changes_free(changes, n_changes);
1449 return bus_send_error_reply(connection, message, NULL, r);
1450 }
1451
1452 reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
1453 unit_file_changes_free(changes, n_changes);
1454
1455 if (!reply)
1456 goto oom;
1457
1458 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "DisableUnitFiles") ||
1459 dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnmaskUnitFiles")) {
1460
1461 char **l = NULL;
1462 DBusMessageIter iter;
1463 UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1464 UnitFileChange *changes = NULL;
1465 unsigned n_changes = 0;
1466 dbus_bool_t runtime;
1467
1468 if (!dbus_message_iter_init(message, &iter))
1469 goto oom;
1470
1471 r = bus_parse_strv_iter(&iter, &l);
1472 if (r < 0) {
1473 if (r == -ENOMEM)
1474 goto oom;
1475
1476 return bus_send_error_reply(connection, message, NULL, r);
1477 }
1478
1479 if (!dbus_message_iter_next(&iter) ||
1480 bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, false) < 0) {
1481 strv_free(l);
1482 return bus_send_error_reply(connection, message, NULL, -EIO);
1483 }
1484
1485 if (streq(member, "DisableUnitFiles"))
1486 r = unit_file_disable(scope, runtime, NULL, l, &changes, &n_changes);
1487 else if (streq(member, "UnmaskUnitFiles"))
1488 r = unit_file_unmask(scope, runtime, NULL, l, &changes, &n_changes);
1489 else
1490 assert_not_reached("Uh? Wrong method");
1491
1492 strv_free(l);
1493 bus_manager_send_unit_files_changed(m);
1494
1495 if (r < 0) {
1496 unit_file_changes_free(changes, n_changes);
1497 return bus_send_error_reply(connection, message, NULL, r);
1498 }
1499
1500 reply = message_from_file_changes(message, changes, n_changes, -1);
1501 unit_file_changes_free(changes, n_changes);
1502
1503 if (!reply)
1504 goto oom;
1505
1506 } else {
1507 const BusBoundProperties bps[] = {
1508 { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string },
1509 { "org.freedesktop.systemd1.Manager", bus_manager_properties, m },
1510 { NULL, }
1511 };
1512 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1513 }
1514
1515 if (job_type != _JOB_TYPE_INVALID) {
1516 const char *name, *smode, *old_name = NULL;
1517 JobMode mode;
1518 Job *j;
1519 JobBusClient *cl;
1520 Unit *u;
1521 bool b;
1522
1523 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
1524 b = dbus_message_get_args(
1525 message,
1526 &error,
1527 DBUS_TYPE_STRING, &old_name,
1528 DBUS_TYPE_STRING, &name,
1529 DBUS_TYPE_STRING, &smode,
1530 DBUS_TYPE_INVALID);
1531 else
1532 b = dbus_message_get_args(
1533 message,
1534 &error,
1535 DBUS_TYPE_STRING, &name,
1536 DBUS_TYPE_STRING, &smode,
1537 DBUS_TYPE_INVALID);
1538
1539 if (!b)
1540 return bus_send_error_reply(connection, message, &error, -EINVAL);
1541
1542 if (old_name)
1543 if (!(u = manager_get_unit(m, old_name)) ||
1544 !u->job ||
1545 u->job->type != JOB_START) {
1546 dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
1547 return bus_send_error_reply(connection, message, &error, -ENOENT);
1548 }
1549
1550
1551 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
1552 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
1553 return bus_send_error_reply(connection, message, &error, -EINVAL);
1554 }
1555
1556 if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
1557 return bus_send_error_reply(connection, message, &error, r);
1558
1559 if (reload_if_possible && unit_can_reload(u)) {
1560 if (job_type == JOB_RESTART)
1561 job_type = JOB_RELOAD_OR_START;
1562 else if (job_type == JOB_TRY_RESTART)
1563 job_type = JOB_RELOAD;
1564 }
1565
1566 if (job_type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
1567 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name);
1568 return bus_send_error_reply(connection, message, &error, -EPERM);
1569 }
1570
1571 if ((job_type == JOB_START && u->refuse_manual_start) ||
1572 (job_type == JOB_STOP && u->refuse_manual_stop) ||
1573 ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
1574 (u->refuse_manual_start || u->refuse_manual_stop))) {
1575 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
1576 return bus_send_error_reply(connection, message, &error, -EPERM);
1577 }
1578
1579 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
1580 return bus_send_error_reply(connection, message, &error, r);
1581
1582 cl = job_bus_client_new(connection, message_get_sender_with_fallback(message));
1583 if (!cl)
1584 goto oom;
1585
1586 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
1587
1588 if (!(reply = dbus_message_new_method_return(message)))
1589 goto oom;
1590
1591 if (!(path = job_dbus_path(j)))
1592 goto oom;
1593
1594 if (!dbus_message_append_args(
1595 reply,
1596 DBUS_TYPE_OBJECT_PATH, &path,
1597 DBUS_TYPE_INVALID))
1598 goto oom;
1599 }
1600
1601 if (reply) {
1602 if (!dbus_connection_send(connection, reply, NULL))
1603 goto oom;
1604
1605 dbus_message_unref(reply);
1606 }
1607
1608 free(path);
1609
1610 return DBUS_HANDLER_RESULT_HANDLED;
1611
1612 oom:
1613 free(path);
1614
1615 if (reply)
1616 dbus_message_unref(reply);
1617
1618 dbus_error_free(&error);
1619
1620 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1621 }
1622
1623 const DBusObjectPathVTable bus_manager_vtable = {
1624 .message_function = bus_manager_message_handler
1625 };