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