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