]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-unit.c
util: move string_is_safe() to string-util.[ch]
[thirdparty/systemd.git] / src / core / dbus-unit.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "sd-bus.h"
23
24 #include "bus-common-errors.h"
25 #include "cgroup-util.h"
26 #include "dbus.h"
27 #include "log.h"
28 #include "selinux-access.h"
29 #include "special.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "dbus-unit.h"
33
34 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
35 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
36 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_failure_action, failure_action, FailureAction);
37
38 static 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;
51
52 assert(bus);
53 assert(reply);
54 assert(u);
55
56 r = sd_bus_message_open_container(reply, 'a', "s");
57 if (r < 0)
58 return r;
59
60 SET_FOREACH(t, u->names, i) {
61 r = sd_bus_message_append(reply, "s", t);
62 if (r < 0)
63 return r;
64 }
65
66 return sd_bus_message_close_container(reply);
67 }
68
69 static 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;
79
80 assert(bus);
81 assert(reply);
82 assert(u);
83
84 f = unit_following(u);
85 return sd_bus_message_append(reply, "s", f ? f->id : "");
86 }
87
88 static 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) {
96
97 Set *s = *(Set**) userdata;
98 Iterator j;
99 Unit *u;
100 int r;
101
102 assert(bus);
103 assert(reply);
104
105 r = sd_bus_message_open_container(reply, 'a', "s");
106 if (r < 0)
107 return r;
108
109 SET_FOREACH(u, s, j) {
110 r = sd_bus_message_append(reply, "s", u->id);
111 if (r < 0)
112 return r;
113 }
114
115 return sd_bus_message_close_container(reply);
116 }
117
118 static 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) {
126
127 Unit *u = userdata;
128
129 assert(bus);
130 assert(reply);
131 assert(u);
132
133 return sd_bus_message_append(reply, "s", unit_description(u));
134 }
135
136 static 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) {
144
145 Unit *u = userdata;
146
147 assert(bus);
148 assert(reply);
149 assert(u);
150
151 return sd_bus_message_append(reply, "s", unit_active_state_to_string(unit_active_state(u)));
152 }
153
154 static 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) {
162
163 Unit *u = userdata;
164
165 assert(bus);
166 assert(reply);
167 assert(u);
168
169 return sd_bus_message_append(reply, "s", unit_sub_state_to_string(u));
170 }
171
172 static 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
195 static 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) {
203
204 Unit *u = userdata;
205
206 assert(bus);
207 assert(reply);
208 assert(u);
209
210 return sd_bus_message_append(reply, "s", unit_file_state_to_string(unit_get_unit_file_state(u)));
211 }
212
213 static 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) {
221
222 Unit *u = userdata;
223
224 assert(bus);
225 assert(reply);
226 assert(u);
227
228 return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_start);
229 }
230
231 static 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;
241
242 assert(bus);
243 assert(reply);
244 assert(u);
245
246 /* On the lower levels we assume that every unit we can start
247 * we can also stop */
248
249 return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_stop);
250 }
251
252 static 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) {
260
261 Unit *u = userdata;
262
263 assert(bus);
264 assert(reply);
265 assert(u);
266
267 return sd_bus_message_append(reply, "b", unit_can_reload(u));
268 }
269
270 static 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) {
278
279 Unit *u = userdata;
280
281 assert(bus);
282 assert(reply);
283 assert(u);
284
285 return sd_bus_message_append(reply, "b", unit_can_isolate(u) && !u->refuse_manual_start);
286 }
287
288 static 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
297 _cleanup_free_ char *p = NULL;
298 Unit *u = userdata;
299
300 assert(bus);
301 assert(reply);
302 assert(u);
303
304 if (!u->job)
305 return sd_bus_message_append(reply, "(uo)", 0, "/");
306
307 p = job_dbus_path(u->job);
308 if (!p)
309 return -ENOMEM;
310
311 return sd_bus_message_append(reply, "(uo)", u->job->id, p);
312 }
313
314 static 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) {
322
323 Unit *u = userdata;
324
325 assert(bus);
326 assert(reply);
327 assert(u);
328
329 return sd_bus_message_append(reply, "b", unit_need_daemon_reload(u));
330 }
331
332 static 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) {
340
341 const char *(*to_string)(ConditionType type) = NULL;
342 Condition **list = userdata, *c;
343 int r;
344
345 assert(bus);
346 assert(reply);
347 assert(list);
348
349 to_string = streq(property, "Asserts") ? assert_type_to_string : condition_type_to_string;
350
351 r = sd_bus_message_open_container(reply, 'a', "(sbbsi)");
352 if (r < 0)
353 return r;
354
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
362 r = sd_bus_message_append(reply, "(sbbsi)",
363 to_string(c->type),
364 c->trigger, c->negate,
365 c->parameter, tristate);
366 if (r < 0)
367 return r;
368
369 }
370
371 return sd_bus_message_close_container(reply);
372 }
373
374 static 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) {
382
383 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
384 Unit *u = userdata;
385
386 assert(bus);
387 assert(reply);
388 assert(u);
389
390 if (u->load_error != 0)
391 sd_bus_error_set_errno(&e, u->load_error);
392
393 return sd_bus_message_append(reply, "(ss)", e.name, e.message);
394 }
395
396 static int bus_verify_manage_units_async_full(
397 Unit *u,
398 const char *verb,
399 int capability,
400 const char *polkit_message,
401 sd_bus_message *call,
402 sd_bus_error *error) {
403
404 const char *details[9] = {
405 "unit", u->id,
406 "verb", verb,
407 };
408
409 if (polkit_message) {
410 details[4] = "polkit.message";
411 details[5] = polkit_message;
412 details[6] = "polkit.gettext_domain";
413 details[7] = GETTEXT_PACKAGE;
414 }
415
416 return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error);
417 }
418
419 int bus_unit_method_start_generic(
420 sd_bus_message *message,
421 Unit *u,
422 JobType job_type,
423 bool reload_if_possible,
424 sd_bus_error *error) {
425
426 const char *smode;
427 JobMode mode;
428 _cleanup_free_ char *verb = NULL;
429 static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = {
430 [JOB_START] = N_("Authentication is required to start '$(unit)'."),
431 [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."),
432 [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."),
433 [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."),
434 [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."),
435 };
436 int r;
437
438 assert(message);
439 assert(u);
440 assert(job_type >= 0 && job_type < _JOB_TYPE_MAX);
441
442 r = mac_selinux_unit_access_check(u, message, job_type == JOB_STOP ? "stop" : "start", error);
443 if (r < 0)
444 return r;
445
446 r = sd_bus_message_read(message, "s", &smode);
447 if (r < 0)
448 return r;
449
450 mode = job_mode_from_string(smode);
451 if (mode < 0)
452 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
453
454 if (reload_if_possible)
455 verb = strjoin("reload-or-", job_type_to_string(job_type), NULL);
456 else
457 verb = strdup(job_type_to_string(job_type));
458 if (!verb)
459 return -ENOMEM;
460
461 r = bus_verify_manage_units_async_full(
462 u,
463 verb,
464 CAP_SYS_ADMIN,
465 job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
466 message,
467 error);
468 if (r < 0)
469 return r;
470 if (r == 0)
471 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
472
473 return bus_unit_queue_job(message, u, job_type, mode, reload_if_possible, error);
474 }
475
476 static int method_start(sd_bus_message *message, void *userdata, sd_bus_error *error) {
477 return bus_unit_method_start_generic(message, userdata, JOB_START, false, error);
478 }
479
480 static int method_stop(sd_bus_message *message, void *userdata, sd_bus_error *error) {
481 return bus_unit_method_start_generic(message, userdata, JOB_STOP, false, error);
482 }
483
484 static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
485 return bus_unit_method_start_generic(message, userdata, JOB_RELOAD, false, error);
486 }
487
488 static int method_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) {
489 return bus_unit_method_start_generic(message, userdata, JOB_RESTART, false, error);
490 }
491
492 static int method_try_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) {
493 return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, false, error);
494 }
495
496 static int method_reload_or_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) {
497 return bus_unit_method_start_generic(message, userdata, JOB_RESTART, true, error);
498 }
499
500 static int method_reload_or_try_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) {
501 return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, true, error);
502 }
503
504 int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
505 Unit *u = userdata;
506 const char *swho;
507 int32_t signo;
508 KillWho who;
509 int r;
510
511 assert(message);
512 assert(u);
513
514 r = mac_selinux_unit_access_check(u, message, "stop", error);
515 if (r < 0)
516 return r;
517
518 r = sd_bus_message_read(message, "si", &swho, &signo);
519 if (r < 0)
520 return r;
521
522 if (isempty(swho))
523 who = KILL_ALL;
524 else {
525 who = kill_who_from_string(swho);
526 if (who < 0)
527 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho);
528 }
529
530 if (signo <= 0 || signo >= _NSIG)
531 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
532
533 r = bus_verify_manage_units_async_full(
534 u,
535 "kill",
536 CAP_KILL,
537 N_("Authentication is required to kill '$(unit)'."),
538 message,
539 error);
540 if (r < 0)
541 return r;
542 if (r == 0)
543 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
544
545 r = unit_kill(u, who, signo, error);
546 if (r < 0)
547 return r;
548
549 return sd_bus_reply_method_return(message, NULL);
550 }
551
552 int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
553 Unit *u = userdata;
554 int r;
555
556 assert(message);
557 assert(u);
558
559 r = mac_selinux_unit_access_check(u, message, "reload", error);
560 if (r < 0)
561 return r;
562
563 r = bus_verify_manage_units_async_full(
564 u,
565 "reset-failed",
566 CAP_SYS_ADMIN,
567 N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
568 message,
569 error);
570 if (r < 0)
571 return r;
572 if (r == 0)
573 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
574
575 unit_reset_failed(u);
576
577 return sd_bus_reply_method_return(message, NULL);
578 }
579
580 int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) {
581 Unit *u = userdata;
582 int runtime, r;
583
584 assert(message);
585 assert(u);
586
587 r = mac_selinux_unit_access_check(u, message, "start", error);
588 if (r < 0)
589 return r;
590
591 r = sd_bus_message_read(message, "b", &runtime);
592 if (r < 0)
593 return r;
594
595 r = bus_verify_manage_units_async_full(
596 u,
597 "set-property",
598 CAP_SYS_ADMIN,
599 N_("Authentication is required to set properties on '$(unit)'."),
600 message,
601 error);
602 if (r < 0)
603 return r;
604 if (r == 0)
605 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
606
607 r = bus_unit_set_properties(u, message, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, error);
608 if (r < 0)
609 return r;
610
611 return sd_bus_reply_method_return(message, NULL);
612 }
613
614 const sd_bus_vtable bus_unit_vtable[] = {
615 SD_BUS_VTABLE_START(0),
616
617 SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST),
618 SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
619 SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
620 SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
621 SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
622 SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
623 SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
624 SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST),
625 SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
626 SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
627 SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
628 SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
629 SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
630 SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
631 SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
632 SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
633 SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
634 SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTS]), SD_BUS_VTABLE_PROPERTY_CONST),
635 SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
636 SD_BUS_PROPERTY("Before", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BEFORE]), SD_BUS_VTABLE_PROPERTY_CONST),
637 SD_BUS_PROPERTY("After", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_AFTER]), SD_BUS_VTABLE_PROPERTY_CONST),
638 SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_ON_FAILURE]), SD_BUS_VTABLE_PROPERTY_CONST),
639 SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERS]), SD_BUS_VTABLE_PROPERTY_CONST),
640 SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
641 SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
642 SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
643 SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
644 SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
645 SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
646 SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
647 SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
648 SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
649 SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
650 SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST),
651 SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST),
652 SD_BUS_PROPERTY("DropInPaths", "as", NULL, offsetof(Unit, dropin_paths), SD_BUS_VTABLE_PROPERTY_CONST),
653 SD_BUS_PROPERTY("UnitFileState", "s", property_get_unit_file_state, 0, 0),
654 SD_BUS_PROPERTY("UnitFilePreset", "s", property_get_unit_file_preset, 0, 0),
655 BUS_PROPERTY_DUAL_TIMESTAMP("InactiveExitTimestamp", offsetof(Unit, inactive_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
656 BUS_PROPERTY_DUAL_TIMESTAMP("ActiveEnterTimestamp", offsetof(Unit, active_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
657 BUS_PROPERTY_DUAL_TIMESTAMP("ActiveExitTimestamp", offsetof(Unit, active_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
658 BUS_PROPERTY_DUAL_TIMESTAMP("InactiveEnterTimestamp", offsetof(Unit, inactive_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
659 SD_BUS_PROPERTY("CanStart", "b", property_get_can_start, 0, SD_BUS_VTABLE_PROPERTY_CONST),
660 SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST),
661 SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
662 SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
663 SD_BUS_PROPERTY("Job", "(uo)", property_get_job, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
664 SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
665 SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
666 SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST),
667 SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
668 SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
669 SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
670 SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
671 SD_BUS_PROPERTY("IgnoreOnSnapshot", "b", bus_property_get_bool, offsetof(Unit, ignore_on_snapshot), SD_BUS_VTABLE_PROPERTY_CONST),
672 SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
673 SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
674 SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
675 SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
676 SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
677 SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
678 BUS_PROPERTY_DUAL_TIMESTAMP("ConditionTimestamp", offsetof(Unit, condition_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
679 BUS_PROPERTY_DUAL_TIMESTAMP("AssertTimestamp", offsetof(Unit, assert_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
680 SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, offsetof(Unit, conditions), 0),
681 SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
682 SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
683 SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
684 SD_BUS_PROPERTY("NetClass", "u", NULL, offsetof(Unit, cgroup_netclass_id), 0),
685
686 SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
687 SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
688 SD_BUS_METHOD("Reload", "s", "o", method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
689 SD_BUS_METHOD("Restart", "s", "o", method_restart, SD_BUS_VTABLE_UNPRIVILEGED),
690 SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED),
691 SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED),
692 SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED),
693 SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
694 SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
695 SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
696
697 SD_BUS_VTABLE_END
698 };
699
700 static int property_get_slice(
701 sd_bus *bus,
702 const char *path,
703 const char *interface,
704 const char *property,
705 sd_bus_message *reply,
706 void *userdata,
707 sd_bus_error *error) {
708
709 Unit *u = userdata;
710
711 assert(bus);
712 assert(reply);
713 assert(u);
714
715 return sd_bus_message_append(reply, "s", unit_slice_name(u));
716 }
717
718 static int property_get_current_memory(
719 sd_bus *bus,
720 const char *path,
721 const char *interface,
722 const char *property,
723 sd_bus_message *reply,
724 void *userdata,
725 sd_bus_error *error) {
726
727 uint64_t sz = (uint64_t) -1;
728 Unit *u = userdata;
729 int r;
730
731 assert(bus);
732 assert(reply);
733 assert(u);
734
735 r = unit_get_memory_current(u, &sz);
736 if (r < 0 && r != -ENODATA)
737 log_unit_warning_errno(u, r, "Failed to get memory.usage_in_bytes attribute: %m");
738
739 return sd_bus_message_append(reply, "t", sz);
740 }
741
742 static int property_get_current_tasks(
743 sd_bus *bus,
744 const char *path,
745 const char *interface,
746 const char *property,
747 sd_bus_message *reply,
748 void *userdata,
749 sd_bus_error *error) {
750
751 uint64_t cn = (uint64_t) -1;
752 Unit *u = userdata;
753 int r;
754
755 assert(bus);
756 assert(reply);
757 assert(u);
758
759 r = unit_get_tasks_current(u, &cn);
760 if (r < 0 && r != -ENODATA)
761 log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m");
762
763 return sd_bus_message_append(reply, "t", cn);
764 }
765
766 static int property_get_cpu_usage(
767 sd_bus *bus,
768 const char *path,
769 const char *interface,
770 const char *property,
771 sd_bus_message *reply,
772 void *userdata,
773 sd_bus_error *error) {
774
775 nsec_t ns = (nsec_t) -1;
776 Unit *u = userdata;
777 int r;
778
779 assert(bus);
780 assert(reply);
781 assert(u);
782
783 r = unit_get_cpu_usage(u, &ns);
784 if (r < 0 && r != -ENODATA)
785 log_unit_warning_errno(u, r, "Failed to get cpuacct.usage attribute: %m");
786
787 return sd_bus_message_append(reply, "t", ns);
788 }
789
790 static int property_get_cgroup(
791 sd_bus *bus,
792 const char *path,
793 const char *interface,
794 const char *property,
795 sd_bus_message *reply,
796 void *userdata,
797 sd_bus_error *error) {
798
799 Unit *u = userdata;
800 const char *t;
801
802 assert(bus);
803 assert(reply);
804 assert(u);
805
806 /* Three cases: a) u->cgroup_path is NULL, in which case the
807 * unit has no control group, which we report as the empty
808 * string. b) u->cgroup_path is the empty string, which
809 * indicates the root cgroup, which we report as "/". c) all
810 * other cases we report as-is. */
811
812 if (u->cgroup_path)
813 t = isempty(u->cgroup_path) ? "/" : u->cgroup_path;
814 else
815 t = "";
816
817 return sd_bus_message_append(reply, "s", t);
818 }
819
820 const sd_bus_vtable bus_unit_cgroup_vtable[] = {
821 SD_BUS_VTABLE_START(0),
822 SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
823 SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
824 SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
825 SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
826 SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
827 SD_BUS_VTABLE_END
828 };
829
830 static int send_new_signal(sd_bus *bus, void *userdata) {
831 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
832 _cleanup_free_ char *p = NULL;
833 Unit *u = userdata;
834 int r;
835
836 assert(bus);
837 assert(u);
838
839 p = unit_dbus_path(u);
840 if (!p)
841 return -ENOMEM;
842
843 r = sd_bus_message_new_signal(
844 bus,
845 &m,
846 "/org/freedesktop/systemd1",
847 "org.freedesktop.systemd1.Manager",
848 "UnitNew");
849 if (r < 0)
850 return r;
851
852 r = sd_bus_message_append(m, "so", u->id, p);
853 if (r < 0)
854 return r;
855
856 return sd_bus_send(bus, m, NULL);
857 }
858
859 static int send_changed_signal(sd_bus *bus, void *userdata) {
860 _cleanup_free_ char *p = NULL;
861 Unit *u = userdata;
862 int r;
863
864 assert(bus);
865 assert(u);
866
867 p = unit_dbus_path(u);
868 if (!p)
869 return -ENOMEM;
870
871 /* Send a properties changed signal. First for the specific
872 * type, then for the generic unit. The clients may rely on
873 * this order to get atomic behavior if needed. */
874
875 r = sd_bus_emit_properties_changed_strv(
876 bus, p,
877 unit_dbus_interface_from_type(u->type),
878 NULL);
879 if (r < 0)
880 return r;
881
882 return sd_bus_emit_properties_changed_strv(
883 bus, p,
884 "org.freedesktop.systemd1.Unit",
885 NULL);
886 }
887
888 void bus_unit_send_change_signal(Unit *u) {
889 int r;
890 assert(u);
891
892 if (u->in_dbus_queue) {
893 LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
894 u->in_dbus_queue = false;
895 }
896
897 if (!u->id)
898 return;
899
900 r = bus_foreach_bus(u->manager, NULL, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u);
901 if (r < 0)
902 log_unit_debug_errno(u, r, "Failed to send unit change signal for %s: %m", u->id);
903
904 u->sent_dbus_new_signal = true;
905 }
906
907 static int send_removed_signal(sd_bus *bus, void *userdata) {
908 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
909 _cleanup_free_ char *p = NULL;
910 Unit *u = userdata;
911 int r;
912
913 assert(bus);
914 assert(u);
915
916 p = unit_dbus_path(u);
917 if (!p)
918 return -ENOMEM;
919
920 r = sd_bus_message_new_signal(
921 bus,
922 &m,
923 "/org/freedesktop/systemd1",
924 "org.freedesktop.systemd1.Manager",
925 "UnitRemoved");
926 if (r < 0)
927 return r;
928
929 r = sd_bus_message_append(m, "so", u->id, p);
930 if (r < 0)
931 return r;
932
933 return sd_bus_send(bus, m, NULL);
934 }
935
936 void bus_unit_send_removed_signal(Unit *u) {
937 int r;
938 assert(u);
939
940 if (!u->sent_dbus_new_signal)
941 bus_unit_send_change_signal(u);
942
943 if (!u->id)
944 return;
945
946 r = bus_foreach_bus(u->manager, NULL, send_removed_signal, u);
947 if (r < 0)
948 log_unit_debug_errno(u, r, "Failed to send unit remove signal for %s: %m", u->id);
949 }
950
951 int bus_unit_queue_job(
952 sd_bus_message *message,
953 Unit *u,
954 JobType type,
955 JobMode mode,
956 bool reload_if_possible,
957 sd_bus_error *error) {
958
959 _cleanup_free_ char *path = NULL;
960 Job *j;
961 int r;
962
963 assert(message);
964 assert(u);
965 assert(type >= 0 && type < _JOB_TYPE_MAX);
966 assert(mode >= 0 && mode < _JOB_MODE_MAX);
967
968 if (reload_if_possible && unit_can_reload(u)) {
969 if (type == JOB_RESTART)
970 type = JOB_RELOAD_OR_START;
971 else if (type == JOB_TRY_RESTART)
972 type = JOB_RELOAD;
973 }
974
975 r = mac_selinux_unit_access_check(
976 u, message,
977 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
978 type == JOB_STOP ? "stop" : "reload", error);
979 if (r < 0)
980 return r;
981
982 if (type == JOB_STOP &&
983 (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) &&
984 unit_active_state(u) == UNIT_INACTIVE)
985 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
986
987 if ((type == JOB_START && u->refuse_manual_start) ||
988 (type == JOB_STOP && u->refuse_manual_stop) ||
989 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) ||
990 (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start))
991 return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id);
992
993 r = manager_add_job(u->manager, type, u, mode, true, error, &j);
994 if (r < 0)
995 return r;
996
997 if (sd_bus_message_get_bus(message) == u->manager->api_bus) {
998 if (!j->clients) {
999 r = sd_bus_track_new(sd_bus_message_get_bus(message), &j->clients, NULL, NULL);
1000 if (r < 0)
1001 return r;
1002 }
1003
1004 r = sd_bus_track_add_sender(j->clients, message);
1005 if (r < 0)
1006 return r;
1007 }
1008
1009 path = job_dbus_path(j);
1010 if (!path)
1011 return -ENOMEM;
1012
1013 return sd_bus_reply_method_return(message, "o", path);
1014 }
1015
1016 static int bus_unit_set_transient_property(
1017 Unit *u,
1018 const char *name,
1019 sd_bus_message *message,
1020 UnitSetPropertiesMode mode,
1021 sd_bus_error *error) {
1022
1023 int r;
1024
1025 assert(u);
1026 assert(name);
1027 assert(message);
1028
1029 if (streq(name, "Description")) {
1030 const char *d;
1031
1032 r = sd_bus_message_read(message, "s", &d);
1033 if (r < 0)
1034 return r;
1035
1036 if (mode != UNIT_CHECK) {
1037 r = unit_set_description(u, d);
1038 if (r < 0)
1039 return r;
1040
1041 unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", d);
1042 }
1043
1044 return 1;
1045
1046 } else if (streq(name, "DefaultDependencies")) {
1047 int b;
1048
1049 r = sd_bus_message_read(message, "b", &b);
1050 if (r < 0)
1051 return r;
1052
1053 if (mode != UNIT_CHECK) {
1054 u->default_dependencies = b;
1055 unit_write_drop_in_format(u, mode, name, "[Unit]\nDefaultDependencies=%s\n", yes_no(b));
1056 }
1057
1058 return 1;
1059
1060 } else if (streq(name, "Slice")) {
1061 Unit *slice;
1062 const char *s;
1063
1064 if (!UNIT_HAS_CGROUP_CONTEXT(u))
1065 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
1066 if (u->type == UNIT_SLICE)
1067 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
1068 if (unit_has_name(u, SPECIAL_INIT_SCOPE))
1069 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
1070
1071 r = sd_bus_message_read(message, "s", &s);
1072 if (r < 0)
1073 return r;
1074
1075 if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
1076 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s);
1077
1078 r = manager_load_unit(u->manager, s, NULL, error, &slice);
1079 if (r < 0)
1080 return r;
1081
1082 if (slice->type != UNIT_SLICE)
1083 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s);
1084
1085 if (mode != UNIT_CHECK) {
1086 r = unit_set_slice(u, slice);
1087 if (r < 0)
1088 return r;
1089
1090 unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
1091 }
1092
1093 return 1;
1094
1095 } else if (STR_IN_SET(name,
1096 "Requires", "RequiresOverridable",
1097 "Requisite", "RequisiteOverridable",
1098 "Wants",
1099 "BindsTo",
1100 "Conflicts",
1101 "Before", "After",
1102 "OnFailure",
1103 "PropagatesReloadTo", "ReloadPropagatedFrom",
1104 "PartOf")) {
1105
1106 UnitDependency d;
1107 const char *other;
1108
1109 d = unit_dependency_from_string(name);
1110 if (d < 0)
1111 return -EINVAL;
1112
1113 r = sd_bus_message_enter_container(message, 'a', "s");
1114 if (r < 0)
1115 return r;
1116
1117 while ((r = sd_bus_message_read(message, "s", &other)) > 0) {
1118 if (!unit_name_is_valid(other, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
1119 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name %s", other);
1120
1121 if (mode != UNIT_CHECK) {
1122 _cleanup_free_ char *label = NULL;
1123
1124 r = unit_add_dependency_by_name(u, d, other, NULL, true);
1125 if (r < 0)
1126 return r;
1127
1128 label = strjoin(name, "-", other, NULL);
1129 if (!label)
1130 return -ENOMEM;
1131
1132 unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
1133 }
1134
1135 }
1136 if (r < 0)
1137 return r;
1138
1139 r = sd_bus_message_exit_container(message);
1140 if (r < 0)
1141 return r;
1142
1143 return 1;
1144 }
1145
1146 return 0;
1147 }
1148
1149 int bus_unit_set_properties(
1150 Unit *u,
1151 sd_bus_message *message,
1152 UnitSetPropertiesMode mode,
1153 bool commit,
1154 sd_bus_error *error) {
1155
1156 bool for_real = false;
1157 unsigned n = 0;
1158 int r;
1159
1160 assert(u);
1161 assert(message);
1162
1163 /* We iterate through the array twice. First run we just check
1164 * if all passed data is valid, second run actually applies
1165 * it. This is to implement transaction-like behaviour without
1166 * actually providing full transactions. */
1167
1168 r = sd_bus_message_enter_container(message, 'a', "(sv)");
1169 if (r < 0)
1170 return r;
1171
1172 for (;;) {
1173 const char *name;
1174
1175 r = sd_bus_message_enter_container(message, 'r', "sv");
1176 if (r < 0)
1177 return r;
1178 if (r == 0) {
1179 if (for_real || mode == UNIT_CHECK)
1180 break;
1181
1182 /* Reached EOF. Let's try again, and this time for realz... */
1183 r = sd_bus_message_rewind(message, false);
1184 if (r < 0)
1185 return r;
1186
1187 for_real = true;
1188 continue;
1189 }
1190
1191 r = sd_bus_message_read(message, "s", &name);
1192 if (r < 0)
1193 return r;
1194
1195 if (!UNIT_VTABLE(u)->bus_set_property)
1196 return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
1197
1198 r = sd_bus_message_enter_container(message, 'v', NULL);
1199 if (r < 0)
1200 return r;
1201
1202 r = UNIT_VTABLE(u)->bus_set_property(u, name, message, for_real ? mode : UNIT_CHECK, error);
1203 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
1204 r = bus_unit_set_transient_property(u, name, message, for_real ? mode : UNIT_CHECK, error);
1205 if (r < 0)
1206 return r;
1207 if (r == 0)
1208 return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
1209
1210 r = sd_bus_message_exit_container(message);
1211 if (r < 0)
1212 return r;
1213
1214 r = sd_bus_message_exit_container(message);
1215 if (r < 0)
1216 return r;
1217
1218 n += for_real;
1219 }
1220
1221 r = sd_bus_message_exit_container(message);
1222 if (r < 0)
1223 return r;
1224
1225 if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
1226 UNIT_VTABLE(u)->bus_commit_properties(u);
1227
1228 return n;
1229 }