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