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