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