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