]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/dbus-manager.c
unit-name: Fix unescaping
[thirdparty/systemd.git] / src / 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
LP
22#include <errno.h>
23
24#include "dbus.h"
25#include "log.h"
4139c1b2 26#include "dbus-manager.h"
1137a57c 27#include "strv.h"
398ef8ba 28#include "bus-errors.h"
ea430986 29
07459bb6
FF
30#define BUS_MANAGER_INTERFACE_BEGIN \
31 " <interface name=\"org.freedesktop.systemd1.Manager\">\n"
32
33#define BUS_MANAGER_INTERFACE_METHODS \
4288f619
LP
34 " <method name=\"GetUnit\">\n" \
35 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
36 " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
37 " </method>\n" \
598b557b 38 " <method name=\"GetUnitByPID\">\n" \
07459bb6 39 " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
598b557b
LP
40 " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
41 " </method>\n" \
4288f619
LP
42 " <method name=\"LoadUnit\">\n" \
43 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
44 " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
45 " </method>\n" \
c87eba54
LP
46 " <method name=\"StartUnit\">\n" \
47 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
48 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
49 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
50 " </method>\n" \
90bb85e1
LP
51 " <method name=\"StartUnitReplace\">\n" \
52 " <arg name=\"old_unit\" type=\"s\" direction=\"in\"/>\n" \
53 " <arg name=\"new_unit\" type=\"s\" direction=\"in\"/>\n" \
54 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
55 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
56 " </method>\n" \
c87eba54
LP
57 " <method name=\"StopUnit\">\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=\"ReloadUnit\">\n" \
63 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
64 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
65 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
66 " </method>\n" \
67 " <method name=\"RestartUnit\">\n" \
68 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
69 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
70 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
71 " </method>\n" \
9a1ac7b9
LP
72 " <method name=\"TryRestartUnit\">\n" \
73 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
74 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
75 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
76 " </method>\n" \
6f28c033
LP
77 " <method name=\"ReloadOrRestartUnit\">\n" \
78 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
79 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
80 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
81 " </method>\n" \
82 " <method name=\"ReloadOrTryRestartUnit\">\n" \
83 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
84 " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
85 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
86 " </method>\n" \
fdf20a31 87 " <method name=\"ResetFailedUnit\">\n" \
5632e374
LP
88 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
89 " </method>\n" \
4288f619
LP
90 " <method name=\"GetJob\">\n" \
91 " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
92 " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
93 " </method>\n" \
94 " <method name=\"ClearJobs\"/>\n" \
fdf20a31 95 " <method name=\"ResetFailed\"/>\n" \
4288f619 96 " <method name=\"ListUnits\">\n" \
8fe914ec 97 " <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \
4288f619
LP
98 " </method>\n" \
99 " <method name=\"ListJobs\">\n" \
100 " <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \
101 " </method>\n" \
102 " <method name=\"Subscribe\"/>\n" \
103 " <method name=\"Unsubscribe\"/>\n" \
104 " <method name=\"Dump\"/>\n" \
105 " <method name=\"CreateSnapshot\">\n" \
106 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
5948ee7c 107 " <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \
4288f619
LP
108 " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
109 " </method>\n" \
110 " <method name=\"Reload\"/>\n" \
111 " <method name=\"Reexecute\"/>\n" \
112 " <method name=\"Exit\"/>\n" \
6652a2b9
LP
113 " <method name=\"Reboot\"/>\n" \
114 " <method name=\"PowerOff\"/>\n" \
115 " <method name=\"Halt\"/>\n" \
116 " <method name=\"KExec\"/>\n" \
4288f619
LP
117 " <method name=\"SetEnvironment\">\n" \
118 " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \
119 " </method>\n" \
120 " <method name=\"UnsetEnvironment\">\n" \
121 " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \
07459bb6
FF
122 " </method>\n"
123
124#define BUS_MANAGER_INTERFACE_SIGNALS \
4288f619
LP
125 " <signal name=\"UnitNew\">\n" \
126 " <arg name=\"id\" type=\"s\"/>\n" \
127 " <arg name=\"unit\" type=\"o\"/>\n" \
128 " </signal>\n" \
129 " <signal name=\"UnitRemoved\">\n" \
130 " <arg name=\"id\" type=\"s\"/>\n" \
131 " <arg name=\"unit\" type=\"o\"/>\n" \
132 " </signal>\n" \
133 " <signal name=\"JobNew\">\n" \
134 " <arg name=\"id\" type=\"u\"/>\n" \
135 " <arg name=\"job\" type=\"o\"/>\n" \
136 " </signal>\n" \
137 " <signal name=\"JobRemoved\">\n" \
138 " <arg name=\"id\" type=\"u\"/>\n" \
139 " <arg name=\"job\" type=\"o\"/>\n" \
140 " <arg name=\"success\" type=\"b\"/>\n" \
07459bb6
FF
141 " </signal>"
142
143
144#define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
4288f619
LP
145 " <property name=\"Version\" type=\"s\" access=\"read\"/>\n" \
146 " <property name=\"RunningAs\" type=\"s\" access=\"read\"/>\n" \
0442c13b 147 " <property name=\"StartupTimestamp\" type=\"t\" access=\"read\"/>\n" \
4288f619
LP
148 " <property name=\"LogLevel\" type=\"s\" access=\"read\"/>\n" \
149 " <property name=\"LogTarget\" type=\"s\" access=\"read\"/>\n" \
150 " <property name=\"NNames\" type=\"u\" access=\"read\"/>\n" \
151 " <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n" \
e409f875 152 " <property name=\"NInstalledJobs\" type=\"u\" access=\"read\"/>\n" \
76bf48b7 153 " <property name=\"NFailedJobs\" type=\"u\" access=\"read\"/>\n" \
fa70128d 154 " <property name=\"Progress\" type=\"d\" access=\"read\"/>\n" \
4288f619 155 " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
f295f5c0 156 " <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \
9e58ff9c 157 " <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \
f295f5c0 158 " <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \
f295f5c0 159 " <property name=\"NotifySocket\" type=\"s\" access=\"read\"/>\n" \
c6c18be3 160 " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
d3689161 161 " <property name=\"MountAuto\" type=\"b\" access=\"read\"/>\n" \
07459bb6
FF
162 " <property name=\"SwapAuto\" type=\"b\" access=\"read\"/>\n"
163
164#ifdef HAVE_SYSV_COMPAT
165#define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV \
166 " <property name=\"SysVConsole\" type=\"b\" access=\"read\"/>\n" \
167 " <property name=\"SysVInitPath\" type=\"as\" access=\"read\"/>\n" \
168 " <property name=\"SysVRcndPath\" type=\"as\" access=\"read\"/>\n"
169#else
170#define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV
171#endif
172
173#define BUS_MANAGER_INTERFACE_END \
4288f619
LP
174 " </interface>\n"
175
07459bb6
FF
176#define BUS_MANAGER_INTERFACE \
177 BUS_MANAGER_INTERFACE_BEGIN \
178 BUS_MANAGER_INTERFACE_METHODS \
179 BUS_MANAGER_INTERFACE_SIGNALS \
180 BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
181 BUS_MANAGER_INTERFACE_PROPERTIES_SYSV \
182 BUS_MANAGER_INTERFACE_END
183
ea430986
LP
184#define INTROSPECTION_BEGIN \
185 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
4288f619
LP
186 "<node>\n" \
187 BUS_MANAGER_INTERFACE \
ea430986 188 BUS_PROPERTIES_INTERFACE \
c4e2ceae 189 BUS_PEER_INTERFACE \
ea430986
LP
190 BUS_INTROSPECTABLE_INTERFACE
191
192#define INTROSPECTION_END \
4288f619
LP
193 "</node>\n"
194
195const char bus_manager_interface[] = BUS_MANAGER_INTERFACE;
ea430986 196
4139c1b2 197static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_running_as, manager_running_as, ManagerRunningAs);
1adf1049
LP
198
199static int bus_manager_append_log_target(Manager *m, DBusMessageIter *i, const char *property, void *data) {
200 const char *t;
201
202 assert(m);
203 assert(i);
204 assert(property);
205
206 t = log_target_to_string(log_get_target());
207
208 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
209 return -ENOMEM;
210
211 return 0;
212}
213
214static int bus_manager_append_log_level(Manager *m, DBusMessageIter *i, const char *property, void *data) {
215 const char *t;
216
217 assert(m);
218 assert(i);
219 assert(property);
220
221 t = log_level_to_string(log_get_max_level());
222
223 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
224 return -ENOMEM;
225
226 return 0;
227}
228
4f0f902f
LP
229static int bus_manager_append_n_names(Manager *m, DBusMessageIter *i, const char *property, void *data) {
230 uint32_t u;
231
232 assert(m);
233 assert(i);
234 assert(property);
235
236 u = hashmap_size(m->units);
237
238 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
239 return -ENOMEM;
240
241 return 0;
242}
243
244static int bus_manager_append_n_jobs(Manager *m, DBusMessageIter *i, const char *property, void *data) {
245 uint32_t u;
246
247 assert(m);
248 assert(i);
249 assert(property);
250
251 u = hashmap_size(m->jobs);
252
253 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
254 return -ENOMEM;
255
256 return 0;
257}
258
05d6a3b6
LP
259static int bus_manager_append_progress(Manager *m, DBusMessageIter *i, const char *property, void *data) {
260 double d;
261
262 assert(m);
263 assert(i);
264 assert(property);
265
266 if (dual_timestamp_is_set(&m->finish_timestamp))
267 d = 1.0;
268 else
269 d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
270
271 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_DOUBLE, &d))
272 return -ENOMEM;
273
274 return 0;
275}
276
894ba510
LP
277static const char *message_get_sender_with_fallback(DBusMessage *m) {
278 const char *s;
279
280 assert(m);
281
282 if ((s = dbus_message_get_sender(m)))
283 return s;
284
285 /* When the message came in from a direct connection the
286 * message will have no sender. We fix that here. */
287
288 return ":no-sender";
289}
290
5e8d1c9a 291static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
ea430986 292 Manager *m = data;
1adf1049
LP
293
294 const BusProperty properties[] = {
f295f5c0
LP
295 { "org.freedesktop.systemd1.Manager", "Version", bus_property_append_string, "s", PACKAGE_STRING },
296 { "org.freedesktop.systemd1.Manager", "RunningAs", bus_manager_append_running_as, "s", &m->running_as },
297 { "org.freedesktop.systemd1.Manager", "StartupTimestamp", bus_property_append_uint64, "t", &m->startup_timestamp.realtime },
b0c918b9 298 { "org.freedesktop.systemd1.Manager", "FinishTimestamp", bus_property_append_uint64, "t", &m->finish_timestamp.realtime },
f295f5c0
LP
299 { "org.freedesktop.systemd1.Manager", "LogLevel", bus_manager_append_log_level, "s", NULL },
300 { "org.freedesktop.systemd1.Manager", "LogTarget", bus_manager_append_log_target, "s", NULL },
301 { "org.freedesktop.systemd1.Manager", "NNames", bus_manager_append_n_names, "u", NULL },
302 { "org.freedesktop.systemd1.Manager", "NJobs", bus_manager_append_n_jobs, "u", NULL },
e409f875 303 { "org.freedesktop.systemd1.Manager", "NInstalledJobs",bus_property_append_uint32, "u", &m->n_installed_jobs },
76bf48b7 304 { "org.freedesktop.systemd1.Manager", "NFailedJobs", bus_property_append_uint32, "u", &m->n_failed_jobs },
05d6a3b6 305 { "org.freedesktop.systemd1.Manager", "Progress", bus_manager_append_progress, "d", NULL },
f295f5c0
LP
306 { "org.freedesktop.systemd1.Manager", "Environment", bus_property_append_strv, "as", m->environment },
307 { "org.freedesktop.systemd1.Manager", "ConfirmSpawn", bus_property_append_bool, "b", &m->confirm_spawn },
9e58ff9c 308 { "org.freedesktop.systemd1.Manager", "ShowStatus", bus_property_append_bool, "b", &m->show_status },
f295f5c0 309 { "org.freedesktop.systemd1.Manager", "UnitPath", bus_property_append_strv, "as", m->lookup_paths.unit_path },
f295f5c0 310 { "org.freedesktop.systemd1.Manager", "NotifySocket", bus_property_append_string, "s", m->notify_socket },
c6c18be3 311 { "org.freedesktop.systemd1.Manager", "ControlGroupHierarchy", bus_property_append_string, "s", m->cgroup_hierarchy },
d3689161 312 { "org.freedesktop.systemd1.Manager", "MountAuto", bus_property_append_bool, "b", &m->mount_auto },
173a8d04 313 { "org.freedesktop.systemd1.Manager", "SwapAuto", bus_property_append_bool, "b", &m->swap_auto },
07459bb6
FF
314#ifdef HAVE_SYSV_COMPAT
315 { "org.freedesktop.systemd1.Manager", "SysVConsole", bus_property_append_bool, "b", &m->sysv_console },
316 { "org.freedesktop.systemd1.Manager", "SysVInitPath", bus_property_append_strv, "as", m->lookup_paths.sysvinit_path },
317 { "org.freedesktop.systemd1.Manager", "SysVRcndPath", bus_property_append_strv, "as", m->lookup_paths.sysvrcnd_path },
318#endif
1adf1049
LP
319 { NULL, NULL, NULL, NULL, NULL }
320 };
321
322 int r;
ea430986
LP
323 DBusError error;
324 DBusMessage *reply = NULL;
325 char * path = NULL;
c87eba54 326 JobType job_type = _JOB_TYPE_INVALID;
6f28c033 327 bool reload_if_possible = false;
ea430986
LP
328
329 assert(connection);
330 assert(message);
331 assert(m);
332
333 dbus_error_init(&error);
334
4139c1b2 335 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
ea430986
LP
336 const char *name;
337 Unit *u;
338
339 if (!dbus_message_get_args(
340 message,
341 &error,
342 DBUS_TYPE_STRING, &name,
343 DBUS_TYPE_INVALID))
5e8d1c9a 344 return bus_send_error_reply(m, connection, message, &error, -EINVAL);
ea430986 345
398ef8ba
LP
346 if (!(u = manager_get_unit(m, name))) {
347 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
348 return bus_send_error_reply(m, connection, message, &error, -ENOENT);
349 }
ea430986
LP
350
351 if (!(reply = dbus_message_new_method_return(message)))
352 goto oom;
353
354 if (!(path = unit_dbus_path(u)))
355 goto oom;
356
357 if (!dbus_message_append_args(
358 reply,
359 DBUS_TYPE_OBJECT_PATH, &path,
360 DBUS_TYPE_INVALID))
361 goto oom;
598b557b 362 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) {
598b557b
LP
363 Unit *u;
364 uint32_t pid;
365
366 if (!dbus_message_get_args(
367 message,
368 &error,
369 DBUS_TYPE_UINT32, &pid,
370 DBUS_TYPE_INVALID))
371 return bus_send_error_reply(m, connection, message, &error, -EINVAL);
372
373 if (!(u = cgroup_unit_by_pid(m, (pid_t) pid))) {
374 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
375 return bus_send_error_reply(m, connection, message, &error, -ENOENT);
376 }
377
378 if (!(reply = dbus_message_new_method_return(message)))
379 goto oom;
380
381 if (!(path = unit_dbus_path(u)))
382 goto oom;
ea430986 383
598b557b
LP
384 if (!dbus_message_append_args(
385 reply,
386 DBUS_TYPE_OBJECT_PATH, &path,
387 DBUS_TYPE_INVALID))
388 goto oom;
4139c1b2 389 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
ea430986
LP
390 const char *name;
391 Unit *u;
392
393 if (!dbus_message_get_args(
394 message,
395 &error,
396 DBUS_TYPE_STRING, &name,
397 DBUS_TYPE_INVALID))
5e8d1c9a 398 return bus_send_error_reply(m, connection, message, &error, -EINVAL);
ea430986 399
398ef8ba
LP
400 if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
401 return bus_send_error_reply(m, connection, message, &error, r);
ea430986
LP
402
403 if (!(reply = dbus_message_new_method_return(message)))
404 goto oom;
405
406 if (!(path = unit_dbus_path(u)))
407 goto oom;
408
409 if (!dbus_message_append_args(
410 reply,
411 DBUS_TYPE_OBJECT_PATH, &path,
412 DBUS_TYPE_INVALID))
413 goto oom;
414
c87eba54
LP
415 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit"))
416 job_type = JOB_START;
90bb85e1
LP
417 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
418 job_type = JOB_START;
c87eba54
LP
419 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit"))
420 job_type = JOB_STOP;
421 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit"))
422 job_type = JOB_RELOAD;
423 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit"))
424 job_type = JOB_RESTART;
9a1ac7b9
LP
425 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit"))
426 job_type = JOB_TRY_RESTART;
6f28c033
LP
427 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) {
428 reload_if_possible = true;
429 job_type = JOB_RESTART;
430 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
431 reload_if_possible = true;
432 job_type = JOB_TRY_RESTART;
433 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
ea430986
LP
434 uint32_t id;
435 Job *j;
436
437 if (!dbus_message_get_args(
438 message,
439 &error,
440 DBUS_TYPE_UINT32, &id,
441 DBUS_TYPE_INVALID))
5e8d1c9a 442 return bus_send_error_reply(m, connection, message, &error, -EINVAL);
ea430986 443
398ef8ba
LP
444 if (!(j = manager_get_job(m, id))) {
445 dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
446 return bus_send_error_reply(m, connection, message, &error, -ENOENT);
447 }
ea430986
LP
448
449 if (!(reply = dbus_message_new_method_return(message)))
450 goto oom;
451
452 if (!(path = job_dbus_path(j)))
453 goto oom;
454
455 if (!dbus_message_append_args(
456 reply,
457 DBUS_TYPE_OBJECT_PATH, &path,
458 DBUS_TYPE_INVALID))
459 goto oom;
460
4139c1b2 461 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
ea430986
LP
462
463 manager_clear_jobs(m);
464
465 if (!(reply = dbus_message_new_method_return(message)))
466 goto oom;
467
fdf20a31 468 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
5632e374 469
fdf20a31 470 manager_reset_failed(m);
5632e374
LP
471
472 if (!(reply = dbus_message_new_method_return(message)))
473 goto oom;
474
fdf20a31 475 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
5632e374
LP
476 const char *name;
477 Unit *u;
478
479 if (!dbus_message_get_args(
480 message,
481 &error,
482 DBUS_TYPE_STRING, &name,
483 DBUS_TYPE_INVALID))
484 return bus_send_error_reply(m, connection, message, &error, -EINVAL);
485
486 if (!(u = manager_get_unit(m, name))) {
487 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
488 return bus_send_error_reply(m, connection, message, &error, -ENOENT);
489 }
490
fdf20a31 491 unit_reset_failed(u);
5632e374
LP
492
493 if (!(reply = dbus_message_new_method_return(message)))
494 goto oom;
495
4139c1b2 496 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
ea430986
LP
497 DBusMessageIter iter, sub;
498 Iterator i;
499 Unit *u;
500 const char *k;
501
502 if (!(reply = dbus_message_new_method_return(message)))
503 goto oom;
504
505 dbus_message_iter_init_append(reply, &iter);
506
8fe914ec 507 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub))
ea430986
LP
508 goto oom;
509
510 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
47be870b 511 char *u_path, *j_path;
8fe914ec 512 const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following;
ea430986
LP
513 DBusMessageIter sub2;
514 uint32_t job_id;
a7f241db 515 Unit *f;
ea430986 516
9e2f7c11 517 if (k != u->meta.id)
ea430986
LP
518 continue;
519
520 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
521 goto oom;
522
523 description = unit_description(u);
524 load_state = unit_load_state_to_string(u->meta.load_state);
525 active_state = unit_active_state_to_string(unit_active_state(u));
10a94420 526 sub_state = unit_sub_state_to_string(u);
a7f241db
LP
527
528 f = unit_following(u);
529 following = f ? f->meta.id : "";
ea430986 530
47be870b 531 if (!(u_path = unit_dbus_path(u)))
ea430986
LP
532 goto oom;
533
534 if (u->meta.job) {
535 job_id = (uint32_t) u->meta.job->id;
536
47be870b
LP
537 if (!(j_path = job_dbus_path(u->meta.job))) {
538 free(u_path);
ea430986
LP
539 goto oom;
540 }
541
c87eba54 542 sjob_type = job_type_to_string(u->meta.job->type);
ea430986
LP
543 } else {
544 job_id = 0;
47be870b 545 j_path = u_path;
c87eba54 546 sjob_type = "";
ea430986
LP
547 }
548
9e2f7c11 549 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->meta.id) ||
ea430986
LP
550 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
551 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
552 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
10a94420 553 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
8fe914ec 554 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) ||
47be870b 555 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
ea430986 556 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
c87eba54 557 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) ||
47be870b
LP
558 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
559 free(u_path);
ea430986 560 if (u->meta.job)
47be870b 561 free(j_path);
ea430986
LP
562 goto oom;
563 }
564
47be870b 565 free(u_path);
ea430986 566 if (u->meta.job)
47be870b 567 free(j_path);
ea430986
LP
568
569 if (!dbus_message_iter_close_container(&sub, &sub2))
570 goto oom;
571 }
572
573 if (!dbus_message_iter_close_container(&iter, &sub))
574 goto oom;
575
4139c1b2 576 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
ea430986
LP
577 DBusMessageIter iter, sub;
578 Iterator i;
579 Job *j;
580
581 if (!(reply = dbus_message_new_method_return(message)))
582 goto oom;
583
584 dbus_message_iter_init_append(reply, &iter);
585
586 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
587 goto oom;
588
589 HASHMAP_FOREACH(j, m->jobs, i) {
47be870b 590 char *u_path, *j_path;
9e2f7c11 591 const char *state, *type;
ea430986
LP
592 uint32_t id;
593 DBusMessageIter sub2;
594
595 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
596 goto oom;
597
598 id = (uint32_t) j->id;
ea430986 599 state = job_state_to_string(j->state);
2b53c70b 600 type = job_type_to_string(j->type);
ea430986 601
47be870b 602 if (!(j_path = job_dbus_path(j)))
ea430986
LP
603 goto oom;
604
47be870b
LP
605 if (!(u_path = unit_dbus_path(j->unit))) {
606 free(j_path);
ea430986
LP
607 goto oom;
608 }
609
610 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
9e2f7c11 611 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->meta.id) ||
ea430986
LP
612 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
613 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
47be870b
LP
614 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
615 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
616 free(j_path);
617 free(u_path);
ea430986
LP
618 goto oom;
619 }
620
47be870b
LP
621 free(j_path);
622 free(u_path);
ea430986
LP
623
624 if (!dbus_message_iter_close_container(&sub, &sub2))
625 goto oom;
626 }
627
628 if (!dbus_message_iter_close_container(&iter, &sub))
629 goto oom;
630
4139c1b2 631 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
c1e1601e 632 char *client;
a567261a
LP
633 Set *s;
634
635 if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) {
636 if (!(s = set_new(string_hash_func, string_compare_func)))
637 goto oom;
638
639 if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) {
640 set_free(s);
641 goto oom;
642 }
643 }
c1e1601e 644
894ba510 645 if (!(client = strdup(message_get_sender_with_fallback(message))))
c1e1601e
LP
646 goto oom;
647
a567261a
LP
648 if ((r = set_put(s, client)) < 0) {
649 free(client);
5e8d1c9a 650 return bus_send_error_reply(m, connection, message, NULL, r);
a567261a 651 }
c1e1601e
LP
652
653 if (!(reply = dbus_message_new_method_return(message)))
654 goto oom;
655
4139c1b2 656 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
c1e1601e
LP
657 char *client;
658
398ef8ba
LP
659 if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) message_get_sender_with_fallback(message)))) {
660 dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
661 return bus_send_error_reply(m, connection, message, &error, -ENOENT);
662 }
c1e1601e
LP
663
664 free(client);
665
666 if (!(reply = dbus_message_new_method_return(message)))
667 goto oom;
668
4139c1b2 669 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
b152adec
LP
670 FILE *f;
671 char *dump = NULL;
672 size_t size;
673
674 if (!(reply = dbus_message_new_method_return(message)))
675 goto oom;
676
677 if (!(f = open_memstream(&dump, &size)))
678 goto oom;
679
680 manager_dump_units(m, f, NULL);
681 manager_dump_jobs(m, f, NULL);
682
683 if (ferror(f)) {
684 fclose(f);
685 free(dump);
686 goto oom;
687 }
688
689 fclose(f);
690
691 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
692 free(dump);
693 goto oom;
694 }
695
696 free(dump);
4139c1b2
LP
697 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
698 const char *name;
699 dbus_bool_t cleanup;
700 Snapshot *s;
701
702 if (!dbus_message_get_args(
703 message,
704 &error,
705 DBUS_TYPE_STRING, &name,
706 DBUS_TYPE_BOOLEAN, &cleanup,
707 DBUS_TYPE_INVALID))
5e8d1c9a 708 return bus_send_error_reply(m, connection, message, &error, -EINVAL);
4139c1b2
LP
709
710 if (name && name[0] == 0)
711 name = NULL;
712
398ef8ba
LP
713 if ((r = snapshot_create(m, name, cleanup, &error, &s)) < 0)
714 return bus_send_error_reply(m, connection, message, &error, r);
4139c1b2
LP
715
716 if (!(reply = dbus_message_new_method_return(message)))
717 goto oom;
718
719 if (!(path = unit_dbus_path(UNIT(s))))
720 goto oom;
721
722 if (!dbus_message_append_args(
723 reply,
724 DBUS_TYPE_OBJECT_PATH, &path,
725 DBUS_TYPE_INVALID))
726 goto oom;
b152adec 727
ea430986
LP
728 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
729 char *introspection = NULL;
730 FILE *f;
731 Iterator i;
732 Unit *u;
733 Job *j;
734 const char *k;
735 size_t size;
736
737 if (!(reply = dbus_message_new_method_return(message)))
738 goto oom;
739
740 /* We roll our own introspection code here, instead of
741 * relying on bus_default_message_handler() because we
742 * need to generate our introspection string
743 * dynamically. */
744
745 if (!(f = open_memstream(&introspection, &size)))
746 goto oom;
747
748 fputs(INTROSPECTION_BEGIN, f);
749
750 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
751 char *p;
752
9e2f7c11 753 if (k != u->meta.id)
ea430986
LP
754 continue;
755
756 if (!(p = bus_path_escape(k))) {
757 fclose(f);
758 free(introspection);
759 goto oom;
760 }
761
762 fprintf(f, "<node name=\"unit/%s\"/>", p);
763 free(p);
764 }
765
766 HASHMAP_FOREACH(j, m->jobs, i)
767 fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
768
769 fputs(INTROSPECTION_END, f);
770
771 if (ferror(f)) {
772 fclose(f);
773 free(introspection);
774 goto oom;
775 }
776
777 fclose(f);
778
779 if (!introspection)
780 goto oom;
781
782 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
783 free(introspection);
784 goto oom;
785 }
786
787 free(introspection);
788
a16e1123
LP
789 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
790
791 assert(!m->queued_message);
792
793 /* Instead of sending the reply back right away, we
794 * just remember that we need to and then send it
795 * after the reload is finished. That way the caller
796 * knows when the reload finished. */
797
798 if (!(m->queued_message = dbus_message_new_method_return(message)))
799 goto oom;
800
7b97f477 801 m->queued_message_connection = connection;
a16e1123
LP
802 m->exit_code = MANAGER_RELOAD;
803
804 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
805
806 if (!(reply = dbus_message_new_method_return(message)))
807 goto oom;
808
809 m->exit_code = MANAGER_REEXECUTE;
810
811 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
812
398ef8ba
LP
813 if (m->running_as == MANAGER_SYSTEM) {
814 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for session managers.");
815 return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
816 }
a16e1123
LP
817
818 if (!(reply = dbus_message_new_method_return(message)))
819 goto oom;
820
821 m->exit_code = MANAGER_EXIT;
822
6652a2b9
LP
823 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
824
825 if (m->running_as != MANAGER_SYSTEM) {
826 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
827 return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
828 }
829
830 if (!(reply = dbus_message_new_method_return(message)))
831 goto oom;
832
833 m->exit_code = MANAGER_REBOOT;
834
835 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
836
837 if (m->running_as != MANAGER_SYSTEM) {
838 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
839 return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
840 }
841
842 if (!(reply = dbus_message_new_method_return(message)))
843 goto oom;
844
845 m->exit_code = MANAGER_POWEROFF;
846
847 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
848
849 if (m->running_as != MANAGER_SYSTEM) {
850 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
851 return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
852 }
853
854 if (!(reply = dbus_message_new_method_return(message)))
855 goto oom;
856
857 m->exit_code = MANAGER_HALT;
858
859 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
860
861 if (m->running_as != MANAGER_SYSTEM) {
862 dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
863 return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
864 }
865
866 if (!(reply = dbus_message_new_method_return(message)))
867 goto oom;
868
869 m->exit_code = MANAGER_KEXEC;
870
1137a57c
LP
871 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
872 char **l = NULL, **e = NULL;
873
874 if ((r = bus_parse_strv(message, &l)) < 0) {
875 if (r == -ENOMEM)
876 goto oom;
877
5e8d1c9a 878 return bus_send_error_reply(m, connection, message, NULL, r);
1137a57c
LP
879 }
880
5b6319dc 881 e = strv_env_merge(2, m->environment, l);
1137a57c
LP
882 strv_free(l);
883
884 if (!e)
885 goto oom;
886
887 if (!(reply = dbus_message_new_method_return(message))) {
888 strv_free(e);
889 goto oom;
890 }
891
892 strv_free(m->environment);
893 m->environment = e;
894
895 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
896 char **l = NULL, **e = NULL;
897
898 if ((r = bus_parse_strv(message, &l)) < 0) {
899 if (r == -ENOMEM)
900 goto oom;
901
5e8d1c9a 902 return bus_send_error_reply(m, connection, message, NULL, r);
1137a57c
LP
903 }
904
5b6319dc 905 e = strv_env_delete(m->environment, 1, l);
1137a57c
LP
906 strv_free(l);
907
908 if (!e)
909 goto oom;
910
911 if (!(reply = dbus_message_new_method_return(message)))
912 goto oom;
913
914 strv_free(m->environment);
915 m->environment = e;
916
ea430986 917 } else
5e8d1c9a 918 return bus_default_message_handler(m, connection, message, NULL, properties);
ea430986 919
c87eba54 920 if (job_type != _JOB_TYPE_INVALID) {
90bb85e1 921 const char *name, *smode, *old_name = NULL;
c87eba54
LP
922 JobMode mode;
923 Job *j;
924 Unit *u;
90bb85e1
LP
925 bool b;
926
927 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
928 b = dbus_message_get_args(
929 message,
930 &error,
931 DBUS_TYPE_STRING, &old_name,
932 DBUS_TYPE_STRING, &name,
933 DBUS_TYPE_STRING, &smode,
934 DBUS_TYPE_INVALID);
935 else
936 b = dbus_message_get_args(
937 message,
938 &error,
939 DBUS_TYPE_STRING, &name,
940 DBUS_TYPE_STRING, &smode,
941 DBUS_TYPE_INVALID);
942
943 if (!b)
5e8d1c9a 944 return bus_send_error_reply(m, connection, message, &error, -EINVAL);
c87eba54 945
90bb85e1
LP
946 if (old_name)
947 if (!(u = manager_get_unit(m, old_name)) ||
948 !u->meta.job ||
949 u->meta.job->type != JOB_START) {
950 dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
951 return bus_send_error_reply(m, connection, message, &error, -ENOENT);
952 }
953
954
398ef8ba
LP
955 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
956 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
957 return bus_send_error_reply(m, connection, message, &error, -EINVAL);
958 }
c87eba54 959
398ef8ba
LP
960 if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
961 return bus_send_error_reply(m, connection, message, &error, r);
c87eba54 962
6f28c033
LP
963 if (reload_if_possible && unit_can_reload(u)) {
964 if (job_type == JOB_RESTART)
965 job_type = JOB_RELOAD_OR_START;
966 else if (job_type == JOB_TRY_RESTART)
967 job_type = JOB_RELOAD;
968 }
969
b5e9dba8
LP
970 if ((job_type == JOB_START && u->meta.refuse_manual_start) ||
971 (job_type == JOB_STOP && u->meta.refuse_manual_stop) ||
972 ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
973 (u->meta.refuse_manual_start || u->meta.refuse_manual_stop))) {
974 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
398ef8ba
LP
975 return bus_send_error_reply(m, connection, message, &error, -EPERM);
976 }
c87eba54 977
398ef8ba
LP
978 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
979 return bus_send_error_reply(m, connection, message, &error, r);
c87eba54 980
894ba510 981 if (!(j->bus_client = strdup(message_get_sender_with_fallback(message))))
a567261a
LP
982 goto oom;
983
984 j->bus = connection;
985
c87eba54
LP
986 if (!(reply = dbus_message_new_method_return(message)))
987 goto oom;
988
989 if (!(path = job_dbus_path(j)))
990 goto oom;
991
992 if (!dbus_message_append_args(
993 reply,
994 DBUS_TYPE_OBJECT_PATH, &path,
995 DBUS_TYPE_INVALID))
996 goto oom;
997 }
998
ea430986
LP
999 free(path);
1000
1001 if (reply) {
1002 if (!dbus_connection_send(connection, reply, NULL))
1003 goto oom;
1004
1005 dbus_message_unref(reply);
1006 }
1007
1008 return DBUS_HANDLER_RESULT_HANDLED;
1009
1010oom:
1011 free(path);
1012
1013 if (reply)
1014 dbus_message_unref(reply);
1015
1016 dbus_error_free(&error);
1017
1018 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1019}
1020
1021const DBusObjectPathVTable bus_manager_vtable = {
1022 .message_function = bus_manager_message_handler
1023};