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