]> git.proxmox.com Git - systemd.git/blame - src/core/dbus-unit.c
Imported Upstream version 219
[systemd.git] / src / core / dbus-unit.c
CommitLineData
663996b3
MS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
60f067b4 22#include "sd-bus.h"
663996b3 23#include "log.h"
663996b3
MS
24#include "selinux-access.h"
25#include "cgroup-util.h"
26#include "strv.h"
27#include "path-util.h"
28#include "fileio.h"
f47781d8 29#include "bus-common-errors.h"
60f067b4
JS
30#include "dbus.h"
31#include "dbus-manager.h"
32#include "dbus-unit.h"
663996b3 33
60f067b4
JS
34static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
35static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
5eef597e 36static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_failure_action, failure_action, FailureAction);
60f067b4
JS
37
38static int property_get_names(
39 sd_bus *bus,
40 const char *path,
41 const char *interface,
42 const char *property,
43 sd_bus_message *reply,
44 void *userdata,
45 sd_bus_error *error) {
46
47 Unit *u = userdata;
48 Iterator i;
49 const char *t;
50 int r;
663996b3 51
60f067b4
JS
52 assert(bus);
53 assert(reply);
54 assert(u);
663996b3 55
60f067b4
JS
56 r = sd_bus_message_open_container(reply, 'a', "s");
57 if (r < 0)
58 return r;
663996b3 59
60f067b4
JS
60 SET_FOREACH(t, u->names, i) {
61 r = sd_bus_message_append(reply, "s", t);
62 if (r < 0)
63 return r;
64 }
663996b3 65
60f067b4 66 return sd_bus_message_close_container(reply);
663996b3
MS
67}
68
60f067b4
JS
69static int property_get_following(
70 sd_bus *bus,
71 const char *path,
72 const char *interface,
73 const char *property,
74 sd_bus_message *reply,
75 void *userdata,
76 sd_bus_error *error) {
77
78 Unit *u = userdata, *f;
663996b3 79
60f067b4
JS
80 assert(bus);
81 assert(reply);
663996b3
MS
82 assert(u);
83
84 f = unit_following(u);
60f067b4 85 return sd_bus_message_append(reply, "s", f ? f->id : "");
663996b3
MS
86}
87
60f067b4
JS
88static int property_get_dependencies(
89 sd_bus *bus,
90 const char *path,
91 const char *interface,
92 const char *property,
93 sd_bus_message *reply,
94 void *userdata,
95 sd_bus_error *error) {
14228c0d 96
60f067b4 97 Set *s = *(Set**) userdata;
663996b3 98 Iterator j;
60f067b4
JS
99 Unit *u;
100 int r;
663996b3 101
60f067b4
JS
102 assert(bus);
103 assert(reply);
663996b3 104
60f067b4
JS
105 r = sd_bus_message_open_container(reply, 'a', "s");
106 if (r < 0)
107 return r;
663996b3 108
60f067b4
JS
109 SET_FOREACH(u, s, j) {
110 r = sd_bus_message_append(reply, "s", u->id);
111 if (r < 0)
112 return r;
113 }
663996b3 114
60f067b4 115 return sd_bus_message_close_container(reply);
663996b3
MS
116}
117
60f067b4
JS
118static int property_get_description(
119 sd_bus *bus,
120 const char *path,
121 const char *interface,
122 const char *property,
123 sd_bus_message *reply,
124 void *userdata,
125 sd_bus_error *error) {
663996b3 126
60f067b4 127 Unit *u = userdata;
663996b3 128
60f067b4
JS
129 assert(bus);
130 assert(reply);
131 assert(u);
663996b3 132
60f067b4 133 return sd_bus_message_append(reply, "s", unit_description(u));
663996b3
MS
134}
135
60f067b4
JS
136static int property_get_active_state(
137 sd_bus *bus,
138 const char *path,
139 const char *interface,
140 const char *property,
141 sd_bus_message *reply,
142 void *userdata,
143 sd_bus_error *error) {
663996b3 144
60f067b4 145 Unit *u = userdata;
663996b3 146
60f067b4
JS
147 assert(bus);
148 assert(reply);
663996b3
MS
149 assert(u);
150
60f067b4 151 return sd_bus_message_append(reply, "s", unit_active_state_to_string(unit_active_state(u)));
663996b3
MS
152}
153
60f067b4
JS
154static int property_get_sub_state(
155 sd_bus *bus,
156 const char *path,
157 const char *interface,
158 const char *property,
159 sd_bus_message *reply,
160 void *userdata,
161 sd_bus_error *error) {
663996b3 162
60f067b4 163 Unit *u = userdata;
663996b3 164
60f067b4
JS
165 assert(bus);
166 assert(reply);
167 assert(u);
663996b3 168
60f067b4 169 return sd_bus_message_append(reply, "s", unit_sub_state_to_string(u));
663996b3
MS
170}
171
f47781d8
MP
172static int property_get_unit_file_preset(
173 sd_bus *bus,
174 const char *path,
175 const char *interface,
176 const char *property,
177 sd_bus_message *reply,
178 void *userdata,
179 sd_bus_error *error) {
180
181 Unit *u = userdata;
182 int r;
183
184 assert(bus);
185 assert(reply);
186 assert(u);
187
188 r = unit_get_unit_file_preset(u);
189
190 return sd_bus_message_append(reply, "s",
191 r < 0 ? "":
192 r > 0 ? "enabled" : "disabled");
193}
194
60f067b4
JS
195static int property_get_unit_file_state(
196 sd_bus *bus,
197 const char *path,
198 const char *interface,
199 const char *property,
200 sd_bus_message *reply,
201 void *userdata,
202 sd_bus_error *error) {
663996b3 203
60f067b4 204 Unit *u = userdata;
663996b3 205
60f067b4
JS
206 assert(bus);
207 assert(reply);
208 assert(u);
663996b3 209
60f067b4 210 return sd_bus_message_append(reply, "s", unit_file_state_to_string(unit_get_unit_file_state(u)));
663996b3
MS
211}
212
60f067b4
JS
213static int property_get_can_start(
214 sd_bus *bus,
215 const char *path,
216 const char *interface,
217 const char *property,
218 sd_bus_message *reply,
219 void *userdata,
220 sd_bus_error *error) {
663996b3 221
60f067b4 222 Unit *u = userdata;
663996b3 223
60f067b4
JS
224 assert(bus);
225 assert(reply);
226 assert(u);
663996b3 227
60f067b4 228 return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_start);
663996b3
MS
229}
230
60f067b4
JS
231static int property_get_can_stop(
232 sd_bus *bus,
233 const char *path,
234 const char *interface,
235 const char *property,
236 sd_bus_message *reply,
237 void *userdata,
238 sd_bus_error *error) {
239
240 Unit *u = userdata;
663996b3 241
60f067b4
JS
242 assert(bus);
243 assert(reply);
663996b3
MS
244 assert(u);
245
246 /* On the lower levels we assume that every unit we can start
247 * we can also stop */
248
60f067b4 249 return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_stop);
663996b3
MS
250}
251
60f067b4
JS
252static int property_get_can_reload(
253 sd_bus *bus,
254 const char *path,
255 const char *interface,
256 const char *property,
257 sd_bus_message *reply,
258 void *userdata,
259 sd_bus_error *error) {
663996b3 260
60f067b4 261 Unit *u = userdata;
663996b3 262
60f067b4
JS
263 assert(bus);
264 assert(reply);
265 assert(u);
663996b3 266
60f067b4 267 return sd_bus_message_append(reply, "b", unit_can_reload(u));
663996b3
MS
268}
269
60f067b4
JS
270static int property_get_can_isolate(
271 sd_bus *bus,
272 const char *path,
273 const char *interface,
274 const char *property,
275 sd_bus_message *reply,
276 void *userdata,
277 sd_bus_error *error) {
663996b3 278
60f067b4 279 Unit *u = userdata;
663996b3 280
60f067b4
JS
281 assert(bus);
282 assert(reply);
283 assert(u);
663996b3 284
60f067b4 285 return sd_bus_message_append(reply, "b", unit_can_isolate(u) && !u->refuse_manual_start);
663996b3
MS
286}
287
60f067b4
JS
288static int property_get_job(
289 sd_bus *bus,
290 const char *path,
291 const char *interface,
292 const char *property,
293 sd_bus_message *reply,
294 void *userdata,
295 sd_bus_error *error) {
296
663996b3 297 _cleanup_free_ char *p = NULL;
60f067b4 298 Unit *u = userdata;
663996b3 299
60f067b4
JS
300 assert(bus);
301 assert(reply);
663996b3
MS
302 assert(u);
303
60f067b4
JS
304 if (!u->job)
305 return sd_bus_message_append(reply, "(uo)", 0, "/");
663996b3 306
60f067b4
JS
307 p = job_dbus_path(u->job);
308 if (!p)
663996b3
MS
309 return -ENOMEM;
310
60f067b4 311 return sd_bus_message_append(reply, "(uo)", u->job->id, p);
663996b3
MS
312}
313
60f067b4
JS
314static int property_get_need_daemon_reload(
315 sd_bus *bus,
316 const char *path,
317 const char *interface,
318 const char *property,
319 sd_bus_message *reply,
320 void *userdata,
321 sd_bus_error *error) {
663996b3 322
60f067b4 323 Unit *u = userdata;
663996b3 324
60f067b4
JS
325 assert(bus);
326 assert(reply);
327 assert(u);
663996b3 328
60f067b4 329 return sd_bus_message_append(reply, "b", unit_need_daemon_reload(u));
663996b3
MS
330}
331
60f067b4
JS
332static int property_get_conditions(
333 sd_bus *bus,
334 const char *path,
335 const char *interface,
336 const char *property,
337 sd_bus_message *reply,
338 void *userdata,
339 sd_bus_error *error) {
663996b3 340
f47781d8
MP
341 const char *(*to_string)(ConditionType type) = NULL;
342 Condition **list = userdata, *c;
60f067b4 343 int r;
663996b3 344
60f067b4
JS
345 assert(bus);
346 assert(reply);
f47781d8
MP
347 assert(list);
348
349 to_string = streq(property, "Asserts") ? assert_type_to_string : condition_type_to_string;
663996b3 350
60f067b4
JS
351 r = sd_bus_message_open_container(reply, 'a', "(sbbsi)");
352 if (r < 0)
353 return r;
663996b3 354
f47781d8
MP
355 LIST_FOREACH(conditions, c, *list) {
356 int tristate;
357
358 tristate =
359 c->result == CONDITION_UNTESTED ? 0 :
360 c->result == CONDITION_SUCCEEDED ? 1 : -1;
361
60f067b4 362 r = sd_bus_message_append(reply, "(sbbsi)",
f47781d8 363 to_string(c->type),
60f067b4 364 c->trigger, c->negate,
f47781d8 365 c->parameter, tristate);
60f067b4
JS
366 if (r < 0)
367 return r;
14228c0d 368
60f067b4 369 }
663996b3 370
60f067b4 371 return sd_bus_message_close_container(reply);
663996b3
MS
372}
373
60f067b4
JS
374static int property_get_load_error(
375 sd_bus *bus,
376 const char *path,
377 const char *interface,
378 const char *property,
379 sd_bus_message *reply,
380 void *userdata,
381 sd_bus_error *error) {
663996b3 382
60f067b4
JS
383 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
384 Unit *u = userdata;
663996b3 385
60f067b4
JS
386 assert(bus);
387 assert(reply);
388 assert(u);
663996b3 389
60f067b4
JS
390 if (u->load_error != 0)
391 sd_bus_error_set_errno(&e, u->load_error);
663996b3 392
60f067b4 393 return sd_bus_message_append(reply, "(ss)", e.name, e.message);
663996b3
MS
394}
395
60f067b4
JS
396int bus_unit_method_start_generic(sd_bus *bus, sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error) {
397 const char *smode;
398 JobMode mode;
663996b3
MS
399 int r;
400
60f067b4
JS
401 assert(bus);
402 assert(message);
403 assert(u);
404 assert(job_type >= 0 && job_type < _JOB_TYPE_MAX);
663996b3 405
60f067b4
JS
406 r = sd_bus_message_read(message, "s", &smode);
407 if (r < 0)
408 return r;
663996b3 409
60f067b4
JS
410 mode = job_mode_from_string(smode);
411 if (mode < 0)
412 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
663996b3 413
60f067b4
JS
414 return bus_unit_queue_job(bus, message, u, job_type, mode, reload_if_possible, error);
415}
663996b3 416
60f067b4
JS
417static int method_start(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
418 return bus_unit_method_start_generic(bus, message, userdata, JOB_START, false, error);
419}
663996b3 420
60f067b4
JS
421static int method_stop(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
422 return bus_unit_method_start_generic(bus, message, userdata, JOB_STOP, false, error);
423}
663996b3 424
60f067b4
JS
425static int method_reload(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
426 return bus_unit_method_start_generic(bus, message, userdata, JOB_RELOAD, false, error);
427}
663996b3 428
60f067b4
JS
429static int method_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
430 return bus_unit_method_start_generic(bus, message, userdata, JOB_RESTART, false, error);
431}
663996b3 432
60f067b4
JS
433static int method_try_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
434 return bus_unit_method_start_generic(bus, message, userdata, JOB_TRY_RESTART, false, error);
435}
663996b3 436
60f067b4
JS
437static int method_reload_or_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
438 return bus_unit_method_start_generic(bus, message, userdata, JOB_RESTART, true, error);
439}
663996b3 440
60f067b4
JS
441static int method_reload_or_try_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
442 return bus_unit_method_start_generic(bus, message, userdata, JOB_TRY_RESTART, true, error);
443}
663996b3 444
60f067b4
JS
445int bus_unit_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
446 Unit *u = userdata;
447 const char *swho;
448 int32_t signo;
449 KillWho who;
450 int r;
663996b3 451
60f067b4
JS
452 assert(bus);
453 assert(message);
454 assert(u);
663996b3 455
5eef597e
MP
456 r = bus_verify_manage_unit_async_for_kill(u->manager, message, error);
457 if (r < 0)
458 return r;
459 if (r == 0)
460 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
461
60f067b4
JS
462 r = sd_bus_message_read(message, "si", &swho, &signo);
463 if (r < 0)
464 return r;
465
466 if (isempty(swho))
467 who = KILL_ALL;
468 else {
469 who = kill_who_from_string(swho);
470 if (who < 0)
471 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho);
663996b3
MS
472 }
473
60f067b4
JS
474 if (signo <= 0 || signo >= _NSIG)
475 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
476
5eef597e 477 r = mac_selinux_unit_access_check(u, message, "stop", error);
60f067b4
JS
478 if (r < 0)
479 return r;
663996b3 480
60f067b4
JS
481 r = unit_kill(u, who, signo, error);
482 if (r < 0)
483 return r;
663996b3 484
60f067b4 485 return sd_bus_reply_method_return(message, NULL);
663996b3
MS
486}
487
60f067b4
JS
488int bus_unit_method_reset_failed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
489 Unit *u = userdata;
663996b3 490 int r;
663996b3 491
60f067b4 492 assert(bus);
663996b3 493 assert(message);
60f067b4 494 assert(u);
663996b3 495
5eef597e
MP
496 r = bus_verify_manage_unit_async(u->manager, message, error);
497 if (r < 0)
498 return r;
499 if (r == 0)
500 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
501
502 r = mac_selinux_unit_access_check(u, message, "reload", error);
60f067b4
JS
503 if (r < 0)
504 return r;
663996b3 505
60f067b4 506 unit_reset_failed(u);
663996b3 507
60f067b4
JS
508 return sd_bus_reply_method_return(message, NULL);
509}
663996b3 510
60f067b4
JS
511int bus_unit_method_set_properties(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
512 Unit *u = userdata;
513 int runtime, r;
663996b3 514
60f067b4
JS
515 assert(bus);
516 assert(message);
517 assert(u);
663996b3 518
5eef597e
MP
519 r = bus_verify_manage_unit_async(u->manager, message, error);
520 if (r < 0)
521 return r;
522 if (r == 0)
523 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
524
60f067b4
JS
525 r = sd_bus_message_read(message, "b", &runtime);
526 if (r < 0)
527 return r;
663996b3 528
5eef597e 529 r = mac_selinux_unit_access_check(u, message, "start", error);
60f067b4
JS
530 if (r < 0)
531 return r;
663996b3 532
60f067b4
JS
533 r = bus_unit_set_properties(u, message, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, error);
534 if (r < 0)
535 return r;
663996b3 536
60f067b4
JS
537 return sd_bus_reply_method_return(message, NULL);
538}
663996b3 539
60f067b4
JS
540const sd_bus_vtable bus_unit_vtable[] = {
541 SD_BUS_VTABLE_START(0),
542
543 SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST),
544 SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
545 SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
546 SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
547 SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
548 SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
549 SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
550 SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST),
551 SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
552 SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
553 SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
554 SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
555 SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
556 SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
557 SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
558 SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTS]), SD_BUS_VTABLE_PROPERTY_CONST),
559 SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
560 SD_BUS_PROPERTY("Before", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BEFORE]), SD_BUS_VTABLE_PROPERTY_CONST),
561 SD_BUS_PROPERTY("After", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_AFTER]), SD_BUS_VTABLE_PROPERTY_CONST),
562 SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_ON_FAILURE]), SD_BUS_VTABLE_PROPERTY_CONST),
563 SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERS]), SD_BUS_VTABLE_PROPERTY_CONST),
564 SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
565 SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
566 SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
567 SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
568 SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
569 SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
570 SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
571 SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
572 SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
573 SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
574 SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST),
575 SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST),
576 SD_BUS_PROPERTY("DropInPaths", "as", NULL, offsetof(Unit, dropin_paths), SD_BUS_VTABLE_PROPERTY_CONST),
577 SD_BUS_PROPERTY("UnitFileState", "s", property_get_unit_file_state, 0, 0),
f47781d8 578 SD_BUS_PROPERTY("UnitFilePreset", "s", property_get_unit_file_preset, 0, 0),
60f067b4
JS
579 BUS_PROPERTY_DUAL_TIMESTAMP("InactiveExitTimestamp", offsetof(Unit, inactive_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
580 BUS_PROPERTY_DUAL_TIMESTAMP("ActiveEnterTimestamp", offsetof(Unit, active_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
581 BUS_PROPERTY_DUAL_TIMESTAMP("ActiveExitTimestamp", offsetof(Unit, active_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
582 BUS_PROPERTY_DUAL_TIMESTAMP("InactiveEnterTimestamp", offsetof(Unit, inactive_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
583 SD_BUS_PROPERTY("CanStart", "b", property_get_can_start, 0, SD_BUS_VTABLE_PROPERTY_CONST),
584 SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST),
585 SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
586 SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
587 SD_BUS_PROPERTY("Job", "(uo)", property_get_job, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
588 SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
589 SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
590 SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST),
591 SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
592 SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
593 SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
594 SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
595 SD_BUS_PROPERTY("IgnoreOnSnapshot", "b", bus_property_get_bool, offsetof(Unit, ignore_on_snapshot), SD_BUS_VTABLE_PROPERTY_CONST),
596 SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
597 SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
5eef597e
MP
598 SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
599 SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
60f067b4 600 SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
f47781d8 601 SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
60f067b4 602 BUS_PROPERTY_DUAL_TIMESTAMP("ConditionTimestamp", offsetof(Unit, condition_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
f47781d8
MP
603 BUS_PROPERTY_DUAL_TIMESTAMP("AssertTimestamp", offsetof(Unit, assert_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
604 SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, offsetof(Unit, conditions), 0),
605 SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
60f067b4
JS
606 SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
607 SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
608
609 SD_BUS_METHOD("Start", "s", "o", method_start, 0),
610 SD_BUS_METHOD("Stop", "s", "o", method_stop, 0),
611 SD_BUS_METHOD("Reload", "s", "o", method_reload, 0),
612 SD_BUS_METHOD("Restart", "s", "o", method_restart, 0),
613 SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, 0),
614 SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, 0),
615 SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, 0),
616 SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, 0),
617 SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, 0),
618 SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, 0),
619
620 SD_BUS_VTABLE_END
621};
663996b3 622
60f067b4
JS
623static int property_get_slice(
624 sd_bus *bus,
625 const char *path,
626 const char *interface,
627 const char *property,
628 sd_bus_message *reply,
629 void *userdata,
630 sd_bus_error *error) {
663996b3 631
60f067b4 632 Unit *u = userdata;
663996b3 633
60f067b4
JS
634 assert(bus);
635 assert(reply);
636 assert(u);
663996b3 637
60f067b4
JS
638 return sd_bus_message_append(reply, "s", unit_slice_name(u));
639}
663996b3 640
e735f4d4
MP
641static int property_get_current_memory(
642 sd_bus *bus,
643 const char *path,
644 const char *interface,
645 const char *property,
646 sd_bus_message *reply,
647 void *userdata,
648 sd_bus_error *error) {
649
650 Unit *u = userdata;
651 uint64_t sz = (uint64_t) -1;
652 int r;
653
654 assert(bus);
655 assert(reply);
656 assert(u);
657
658 if (u->cgroup_path &&
659 (u->cgroup_realized_mask & CGROUP_MEMORY)) {
660 _cleanup_free_ char *v = NULL;
661
662 r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
663 if (r < 0 && r != -ENOENT)
664 log_unit_warning_errno(u->id, r, "Couldn't read memory.usage_in_bytes attribute: %m");
665
666 if (v) {
667 r = safe_atou64(v, &sz);
668 if (r < 0)
669 log_unit_warning_errno(u->id, r, "Failed to parse memory.usage_in_bytes attribute: %m");
670 }
671 }
672
673 return sd_bus_message_append(reply, "t", sz);
674}
675
60f067b4
JS
676const sd_bus_vtable bus_unit_cgroup_vtable[] = {
677 SD_BUS_VTABLE_START(0),
678 SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
679 SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0),
e735f4d4 680 SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
60f067b4
JS
681 SD_BUS_VTABLE_END
682};
663996b3 683
60f067b4
JS
684static int send_new_signal(sd_bus *bus, void *userdata) {
685 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
686 _cleanup_free_ char *p = NULL;
687 Unit *u = userdata;
688 int r;
663996b3 689
60f067b4
JS
690 assert(bus);
691 assert(u);
663996b3 692
60f067b4
JS
693 p = unit_dbus_path(u);
694 if (!u)
695 return -ENOMEM;
663996b3 696
60f067b4
JS
697 r = sd_bus_message_new_signal(
698 bus,
699 &m,
700 "/org/freedesktop/systemd1",
701 "org.freedesktop.systemd1.Manager",
702 "UnitNew");
703 if (r < 0)
704 return r;
663996b3 705
60f067b4
JS
706 r = sd_bus_message_append(m, "so", u->id, p);
707 if (r < 0)
708 return r;
663996b3 709
60f067b4
JS
710 return sd_bus_send(bus, m, NULL);
711}
663996b3 712
60f067b4
JS
713static int send_changed_signal(sd_bus *bus, void *userdata) {
714 _cleanup_free_ char *p = NULL;
715 Unit *u = userdata;
716 int r;
663996b3 717
60f067b4
JS
718 assert(bus);
719 assert(u);
663996b3 720
60f067b4
JS
721 p = unit_dbus_path(u);
722 if (!p)
723 return -ENOMEM;
663996b3 724
60f067b4
JS
725 /* Send a properties changed signal. First for the specific
726 * type, then for the generic unit. The clients may rely on
727 * this order to get atomic behavior if needed. */
663996b3 728
60f067b4
JS
729 r = sd_bus_emit_properties_changed_strv(
730 bus, p,
731 UNIT_VTABLE(u)->bus_interface,
732 NULL);
733 if (r < 0)
734 return r;
663996b3 735
60f067b4
JS
736 return sd_bus_emit_properties_changed_strv(
737 bus, p,
738 "org.freedesktop.systemd1.Unit",
739 NULL);
740}
663996b3
MS
741
742void bus_unit_send_change_signal(Unit *u) {
14228c0d 743 int r;
663996b3
MS
744 assert(u);
745
746 if (u->in_dbus_queue) {
60f067b4 747 LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
663996b3
MS
748 u->in_dbus_queue = false;
749 }
750
751 if (!u->id)
752 return;
753
60f067b4
JS
754 r = bus_foreach_bus(u->manager, NULL, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u);
755 if (r < 0)
f47781d8 756 log_debug_errno(r, "Failed to send unit change signal for %s: %m", u->id);
663996b3 757
60f067b4
JS
758 u->sent_dbus_new_signal = true;
759}
663996b3 760
60f067b4
JS
761static int send_removed_signal(sd_bus *bus, void *userdata) {
762 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
763 _cleanup_free_ char *p = NULL;
764 Unit *u = userdata;
765 int r;
663996b3 766
60f067b4
JS
767 assert(bus);
768 assert(u);
663996b3 769
60f067b4
JS
770 p = unit_dbus_path(u);
771 if (!u)
772 return -ENOMEM;
663996b3 773
60f067b4
JS
774 r = sd_bus_message_new_signal(
775 bus,
776 &m,
777 "/org/freedesktop/systemd1",
778 "org.freedesktop.systemd1.Manager",
779 "UnitRemoved");
780 if (r < 0)
781 return r;
663996b3 782
60f067b4
JS
783 r = sd_bus_message_append(m, "so", u->id, p);
784 if (r < 0)
785 return r;
663996b3 786
60f067b4 787 return sd_bus_send(bus, m, NULL);
663996b3
MS
788}
789
790void bus_unit_send_removed_signal(Unit *u) {
60f067b4 791 int r;
663996b3
MS
792 assert(u);
793
663996b3
MS
794 if (!u->sent_dbus_new_signal)
795 bus_unit_send_change_signal(u);
796
797 if (!u->id)
798 return;
799
60f067b4
JS
800 r = bus_foreach_bus(u->manager, NULL, send_removed_signal, u);
801 if (r < 0)
f47781d8 802 log_debug_errno(r, "Failed to send unit remove signal for %s: %m", u->id);
663996b3
MS
803}
804
60f067b4
JS
805int bus_unit_queue_job(
806 sd_bus *bus,
807 sd_bus_message *message,
663996b3
MS
808 Unit *u,
809 JobType type,
810 JobMode mode,
60f067b4
JS
811 bool reload_if_possible,
812 sd_bus_error *error) {
663996b3 813
663996b3
MS
814 _cleanup_free_ char *path = NULL;
815 Job *j;
663996b3
MS
816 int r;
817
60f067b4 818 assert(bus);
663996b3
MS
819 assert(message);
820 assert(u);
821 assert(type >= 0 && type < _JOB_TYPE_MAX);
822 assert(mode >= 0 && mode < _JOB_MODE_MAX);
823
663996b3
MS
824 if (reload_if_possible && unit_can_reload(u)) {
825 if (type == JOB_RESTART)
826 type = JOB_RELOAD_OR_START;
827 else if (type == JOB_TRY_RESTART)
828 type = JOB_RELOAD;
829 }
830
5eef597e 831 r = mac_selinux_unit_access_check(
60f067b4
JS
832 u, message,
833 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
834 type == JOB_STOP ? "stop" : "reload", error);
835 if (r < 0)
836 return r;
663996b3 837
60f067b4
JS
838 if (type == JOB_STOP &&
839 (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) &&
840 unit_active_state(u) == UNIT_INACTIVE)
841 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
663996b3
MS
842
843 if ((type == JOB_START && u->refuse_manual_start) ||
844 (type == JOB_STOP && u->refuse_manual_stop) ||
60f067b4
JS
845 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)))
846 return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id);
663996b3 847
60f067b4 848 r = manager_add_job(u->manager, type, u, mode, true, error, &j);
663996b3 849 if (r < 0)
60f067b4 850 return r;
663996b3 851
60f067b4 852 if (bus == u->manager->api_bus) {
5eef597e
MP
853 if (!j->clients) {
854 r = sd_bus_track_new(bus, &j->clients, NULL, NULL);
60f067b4
JS
855 if (r < 0)
856 return r;
857 }
663996b3 858
5eef597e 859 r = sd_bus_track_add_sender(j->clients, message);
60f067b4
JS
860 if (r < 0)
861 return r;
862 }
663996b3
MS
863
864 path = job_dbus_path(j);
865 if (!path)
60f067b4 866 return -ENOMEM;
663996b3 867
60f067b4 868 return sd_bus_reply_method_return(message, "o", path);
663996b3
MS
869}
870
14228c0d
MB
871static int bus_unit_set_transient_property(
872 Unit *u,
873 const char *name,
60f067b4 874 sd_bus_message *message,
14228c0d 875 UnitSetPropertiesMode mode,
60f067b4 876 sd_bus_error *error) {
663996b3 877
663996b3
MS
878 int r;
879
880 assert(u);
14228c0d 881 assert(name);
60f067b4 882 assert(message);
663996b3 883
14228c0d 884 if (streq(name, "Description")) {
60f067b4 885 const char *d;
663996b3 886
60f067b4
JS
887 r = sd_bus_message_read(message, "s", &d);
888 if (r < 0)
889 return r;
663996b3 890
60f067b4
JS
891 if (mode != UNIT_CHECK) {
892 r = unit_set_description(u, d);
14228c0d
MB
893 if (r < 0)
894 return r;
663996b3 895
60f067b4 896 unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", d);
14228c0d 897 }
663996b3 898
14228c0d 899 return 1;
e735f4d4
MP
900
901 } else if (streq(name, "DefaultDependencies")) {
902 int b;
903
904 r = sd_bus_message_read(message, "b", &b);
905 if (r < 0)
906 return r;
907
908 if (mode != UNIT_CHECK) {
909 u->default_dependencies = b;
910 unit_write_drop_in_format(u, mode, name, "[Unit]\nDefaultDependencies=%s\n", yes_no(b));
911 }
912
913 return 1;
663996b3 914
14228c0d
MB
915 } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
916 const char *s;
663996b3 917
60f067b4
JS
918 r = sd_bus_message_read(message, "s", &s);
919 if (r < 0)
920 return r;
663996b3 921
60f067b4
JS
922 if (!unit_name_is_valid(s, TEMPLATE_INVALID) || !endswith(s, ".slice"))
923 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s);
663996b3 924
14228c0d
MB
925 if (isempty(s)) {
926 if (mode != UNIT_CHECK) {
927 unit_ref_unset(&u->slice);
928 unit_remove_drop_in(u, mode, name);
929 }
930 } else {
931 Unit *slice;
663996b3 932
14228c0d
MB
933 r = manager_load_unit(u->manager, s, NULL, error, &slice);
934 if (r < 0)
935 return r;
663996b3 936
14228c0d
MB
937 if (slice->type != UNIT_SLICE)
938 return -EINVAL;
663996b3 939
14228c0d
MB
940 if (mode != UNIT_CHECK) {
941 unit_ref_set(&u->slice, slice);
942 unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
943 }
944 }
663996b3 945
14228c0d 946 return 1;
f47781d8
MP
947 } else if (STR_IN_SET(name,
948 "Requires", "RequiresOverridable",
949 "Requisite", "RequisiteOverridable",
950 "Wants",
951 "BindsTo",
952 "Conflicts",
953 "Before", "After",
954 "OnFailure",
955 "PropagatesReloadTo", "ReloadPropagatedFrom",
956 "PartOf")) {
14228c0d
MB
957
958 UnitDependency d;
60f067b4 959 const char *other;
14228c0d
MB
960
961 d = unit_dependency_from_string(name);
962 if (d < 0)
963 return -EINVAL;
663996b3 964
60f067b4
JS
965 r = sd_bus_message_enter_container(message, 'a', "s");
966 if (r < 0)
967 return r;
663996b3 968
60f067b4
JS
969 while ((r = sd_bus_message_read(message, "s", &other)) > 0) {
970 if (!unit_name_is_valid(other, TEMPLATE_INVALID))
971 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name %s", other);
663996b3 972
14228c0d
MB
973 if (mode != UNIT_CHECK) {
974 _cleanup_free_ char *label = NULL;
663996b3 975
14228c0d
MB
976 r = unit_add_dependency_by_name(u, d, other, NULL, true);
977 if (r < 0)
978 return r;
663996b3 979
14228c0d
MB
980 label = strjoin(name, "-", other, NULL);
981 if (!label)
982 return -ENOMEM;
663996b3 983
14228c0d
MB
984 unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
985 }
663996b3 986
14228c0d 987 }
60f067b4
JS
988 if (r < 0)
989 return r;
990
991 r = sd_bus_message_exit_container(message);
992 if (r < 0)
993 return r;
663996b3 994
14228c0d 995 return 1;
663996b3
MS
996 }
997
998 return 0;
999}
1000
14228c0d
MB
1001int bus_unit_set_properties(
1002 Unit *u,
60f067b4 1003 sd_bus_message *message,
14228c0d
MB
1004 UnitSetPropertiesMode mode,
1005 bool commit,
60f067b4 1006 sd_bus_error *error) {
14228c0d
MB
1007
1008 bool for_real = false;
14228c0d 1009 unsigned n = 0;
663996b3
MS
1010 int r;
1011
1012 assert(u);
60f067b4 1013 assert(message);
663996b3 1014
14228c0d
MB
1015 /* We iterate through the array twice. First run we just check
1016 * if all passed data is valid, second run actually applies
1017 * it. This is to implement transaction-like behaviour without
1018 * actually providing full transactions. */
663996b3 1019
60f067b4
JS
1020 r = sd_bus_message_enter_container(message, 'a', "(sv)");
1021 if (r < 0)
1022 return r;
663996b3 1023
14228c0d 1024 for (;;) {
14228c0d 1025 const char *name;
663996b3 1026
60f067b4
JS
1027 r = sd_bus_message_enter_container(message, 'r', "sv");
1028 if (r < 0)
1029 return r;
1030 if (r == 0) {
14228c0d
MB
1031 if (for_real || mode == UNIT_CHECK)
1032 break;
663996b3 1033
14228c0d 1034 /* Reached EOF. Let's try again, and this time for realz... */
60f067b4
JS
1035 r = sd_bus_message_rewind(message, false);
1036 if (r < 0)
1037 return r;
1038
14228c0d 1039 for_real = true;
663996b3 1040 continue;
663996b3 1041 }
663996b3 1042
60f067b4
JS
1043 r = sd_bus_message_read(message, "s", &name);
1044 if (r < 0)
1045 return r;
663996b3 1046
60f067b4
JS
1047 if (!UNIT_VTABLE(u)->bus_set_property)
1048 return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
663996b3 1049
60f067b4
JS
1050 r = sd_bus_message_enter_container(message, 'v', NULL);
1051 if (r < 0)
1052 return r;
663996b3 1053
60f067b4 1054 r = UNIT_VTABLE(u)->bus_set_property(u, name, message, for_real ? mode : UNIT_CHECK, error);
14228c0d 1055 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
60f067b4 1056 r = bus_unit_set_transient_property(u, name, message, for_real ? mode : UNIT_CHECK, error);
663996b3
MS
1057 if (r < 0)
1058 return r;
60f067b4
JS
1059 if (r == 0)
1060 return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
663996b3 1061
60f067b4
JS
1062 r = sd_bus_message_exit_container(message);
1063 if (r < 0)
1064 return r;
1065
1066 r = sd_bus_message_exit_container(message);
1067 if (r < 0)
1068 return r;
663996b3 1069
14228c0d
MB
1070 n += for_real;
1071 }
663996b3 1072
60f067b4
JS
1073 r = sd_bus_message_exit_container(message);
1074 if (r < 0)
1075 return r;
1076
14228c0d
MB
1077 if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
1078 UNIT_VTABLE(u)->bus_commit_properties(u);
663996b3 1079
14228c0d 1080 return n;
663996b3 1081}