]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-unit-util.c
dbus-socket: move truncation check to bus_socket_set_transient_property()
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
291d565a
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2016 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include "alloc-util.h"
22#include "bus-internal.h"
23#include "bus-unit-util.h"
24#include "bus-util.h"
cffaed83 25#include "cap-list.h"
291d565a 26#include "cgroup-util.h"
cffaed83 27#include "cpu-set-util.h"
291d565a 28#include "env-util.h"
cffaed83 29#include "errno-list.h"
291d565a
LP
30#include "escape.h"
31#include "hashmap.h"
08f3be7a 32#include "hexdecoct.h"
3dc5ca97
LP
33#include "hostname-util.h"
34#include "in-addr-util.h"
291d565a
LP
35#include "list.h"
36#include "locale-util.h"
83555251 37#include "mount-util.h"
add00535 38#include "nsflags.h"
291d565a
LP
39#include "parse-util.h"
40#include "path-util.h"
41#include "process-util.h"
42#include "rlimit-util.h"
cffaed83 43#include "securebits-util.h"
291d565a 44#include "signal-util.h"
e045e325 45#include "socket-protocol-list.h"
291d565a
LP
46#include "string-util.h"
47#include "syslog-util.h"
48#include "terminal-util.h"
89ada3ba 49#include "unit-def.h"
cffaed83 50#include "user-util.h"
291d565a
LP
51#include "utf8.h"
52#include "util.h"
53
20b16441
LP
54int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
55 assert(message);
56 assert(u);
57
58 u->machine = NULL;
59
60 return sd_bus_message_read(
61 message,
62 "(ssssssouso)",
63 &u->id,
64 &u->description,
65 &u->load_state,
66 &u->active_state,
67 &u->sub_state,
68 &u->following,
69 &u->unit_path,
70 &u->job_id,
71 &u->job_type,
72 &u->job_path);
73}
74
89ada3ba
YW
75#define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
76 static int bus_append_##parse_func(sd_bus_message *m, const char *field, const char *eq) { \
77 type val; \
78 int r; \
79 \
80 r = parse_func(eq, &val); \
81 if (r < 0) \
82 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
83 \
84 r = sd_bus_message_append(m, "(sv)", field, bus_type, (cast_type) val); \
85 if (r < 0) \
86 return bus_log_create_error(r); \
87 \
88 return 1; \
89 }
3dc5ca97 90
89ada3ba
YW
91#define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
92 static int bus_append_##parse_func(sd_bus_message *m, const char *field, const char *eq) { \
93 int r; \
94 \
95 r = parse_func(eq); \
96 if (r < 0) { \
97 log_error("Failed to parse %s: %s", field, eq); \
98 return -EINVAL; \
99 } \
100 \
101 r = sd_bus_message_append(m, "(sv)", field, bus_type, (int32_t) r); \
102 if (r < 0) \
103 return bus_log_create_error(r); \
104 \
105 return 1; \
106 }
3dc5ca97 107
89ada3ba
YW
108DEFINE_BUS_APPEND_PARSE("b", parse_boolean)
109DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string)
110DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string)
111DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string)
112DEFINE_BUS_APPEND_PARSE("i", log_level_from_string)
113DEFINE_BUS_APPEND_PARSE("i", parse_errno)
114DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string)
115DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string)
116DEFINE_BUS_APPEND_PARSE("i", signal_from_string_try_harder)
e045e325 117DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name)
89ada3ba
YW
118DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority)
119DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice)
120DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi)
121DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec)
122DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse)
123DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse)
124DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse)
125DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string)
126DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, usec_t, parse_sec)
127DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode)
128DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou)
129DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64)
130
131static inline int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
132 int r;
133
134 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
3dc5ca97 135 if (r < 0)
89ada3ba 136 return bus_log_create_error(r);
3dc5ca97 137
89ada3ba
YW
138 return 1;
139}
140
141static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) {
142 const char *p;
143 int r;
144
145 r = sd_bus_message_open_container(m, 'r', "sv");
3dc5ca97 146 if (r < 0)
89ada3ba 147 return bus_log_create_error(r);
3dc5ca97 148
89ada3ba 149 r = sd_bus_message_append_basic(m, 's', field);
3dc5ca97 150 if (r < 0)
89ada3ba 151 return bus_log_create_error(r);
3dc5ca97 152
89ada3ba 153 r = sd_bus_message_open_container(m, 'v', "as");
3dc5ca97 154 if (r < 0)
89ada3ba 155 return bus_log_create_error(r);
3dc5ca97 156
89ada3ba
YW
157 r = sd_bus_message_open_container(m, 'a', "s");
158 if (r < 0)
159 return bus_log_create_error(r);
3dc5ca97 160
89ada3ba
YW
161 for (p = eq;;) {
162 _cleanup_free_ char *word = NULL;
20b16441 163
89ada3ba
YW
164 r = extract_first_word(&p, &word, NULL, flags);
165 if (r == 0)
166 break;
167 if (r == -ENOMEM)
168 return log_oom();
169 if (r < 0)
170 return log_error_errno(r, "Invalid syntax: %s", eq);
20b16441 171
89ada3ba
YW
172 r = sd_bus_message_append_basic(m, 's', word);
173 if (r < 0)
174 return bus_log_create_error(r);
20b16441
LP
175 }
176
89ada3ba 177 r = sd_bus_message_close_container(m);
20b16441
LP
178 if (r < 0)
179 return bus_log_create_error(r);
180
89ada3ba
YW
181 r = sd_bus_message_close_container(m);
182 if (r < 0)
183 return bus_log_create_error(r);
20b16441 184
89ada3ba
YW
185 r = sd_bus_message_close_container(m);
186 if (r < 0)
187 return bus_log_create_error(r);
20b16441 188
89ada3ba
YW
189 return 1;
190}
20b16441 191
89ada3ba
YW
192static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
193 int r;
20b16441 194
89ada3ba
YW
195 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
196 if (r < 0)
197 return bus_log_create_error(r);
20b16441 198
89ada3ba
YW
199 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
200 if (r < 0)
201 return bus_log_create_error(r);
20b16441 202
89ada3ba
YW
203 r = sd_bus_message_open_container(m, 'v', "ay");
204 if (r < 0)
205 return bus_log_create_error(r);
20b16441 206
89ada3ba
YW
207 r = sd_bus_message_append_array(m, 'y', buf, n);
208 if (r < 0)
209 return bus_log_create_error(r);
9184ca48 210
89ada3ba
YW
211 r = sd_bus_message_close_container(m);
212 if (r < 0)
213 return bus_log_create_error(r);
20b16441 214
89ada3ba
YW
215 r = sd_bus_message_close_container(m);
216 if (r < 0)
217 return bus_log_create_error(r);
20b16441 218
89ada3ba
YW
219 return 1;
220}
d58d600e 221
89ada3ba
YW
222static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) {
223 char *n;
224 usec_t t;
225 size_t l;
226 int r;
d3070fbd 227
89ada3ba
YW
228 r = parse_sec(eq, &t);
229 if (r < 0)
230 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
d3070fbd 231
89ada3ba
YW
232 l = strlen(field);
233 n = newa(char, l + 2);
234 /* Change suffix Sec → USec */
235 strcpy(mempcpy(n, field, l - 3), "USec");
d3070fbd 236
89ada3ba
YW
237 r = sd_bus_message_append(m, "(sv)", n, "t", t);
238 if (r < 0)
239 return bus_log_create_error(r);
d3070fbd 240
89ada3ba
YW
241 return 1;
242}
d3070fbd 243
b48e508d 244static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) {
89ada3ba
YW
245 uint64_t v;
246 int r;
d3070fbd 247
b48e508d
YW
248 r = parse_size(eq, base, &v);
249 if (r < 0)
89ada3ba 250 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
d3070fbd 251
89ada3ba
YW
252 r = sd_bus_message_append(m, "(sv)", field, "t", v);
253 if (r < 0)
254 return bus_log_create_error(r);
d58d600e 255
89ada3ba
YW
256 return 1;
257}
d58d600e 258
89ada3ba
YW
259static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
260 bool ignore_failure = false, explicit_path = false, done = false;
261 _cleanup_strv_free_ char **l = NULL;
262 _cleanup_free_ char *path = NULL;
263 int r;
d58d600e 264
89ada3ba
YW
265 do {
266 switch (*eq) {
d58d600e 267
89ada3ba
YW
268 case '-':
269 if (ignore_failure)
270 done = true;
271 else {
272 ignore_failure = true;
273 eq++;
d58d600e 274 }
89ada3ba 275 break;
02638280 276
89ada3ba
YW
277 case '@':
278 if (explicit_path)
279 done = true;
280 else {
281 explicit_path = true;
282 eq++;
02638280 283 }
89ada3ba 284 break;
02638280 285
89ada3ba
YW
286 case '+':
287 case '!':
288 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
289 log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
290 return -EOPNOTSUPP;
83f8e808 291
89ada3ba
YW
292 default:
293 done = true;
294 break;
83f8e808 295 }
89ada3ba 296 } while (!done);
83f8e808 297
89ada3ba
YW
298 if (explicit_path) {
299 r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
300 if (r < 0)
301 return log_error_errno(r, "Failed to parse path: %m");
302 }
2038c3f5 303
89ada3ba
YW
304 r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
305 if (r < 0)
306 return log_error_errno(r, "Failed to parse command line: %m");
2038c3f5 307
89ada3ba
YW
308 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
309 if (r < 0)
310 return bus_log_create_error(r);
2038c3f5 311
89ada3ba
YW
312 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
313 if (r < 0)
314 return bus_log_create_error(r);
2038c3f5 315
89ada3ba
YW
316 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
317 if (r < 0)
318 return bus_log_create_error(r);
08f3be7a 319
89ada3ba
YW
320 r = sd_bus_message_open_container(m, 'a', "(sasb)");
321 if (r < 0)
322 return bus_log_create_error(r);
08f3be7a 323
89ada3ba 324 if (!strv_isempty(l)) {
08f3be7a 325
89ada3ba
YW
326 r = sd_bus_message_open_container(m, 'r', "sasb");
327 if (r < 0)
328 return bus_log_create_error(r);
08f3be7a 329
89ada3ba 330 r = sd_bus_message_append(m, "s", path ?: l[0]);
08f3be7a
LP
331 if (r < 0)
332 return bus_log_create_error(r);
333
89ada3ba 334 r = sd_bus_message_append_strv(m, l);
08f3be7a
LP
335 if (r < 0)
336 return bus_log_create_error(r);
337
89ada3ba 338 r = sd_bus_message_append(m, "b", ignore_failure);
08f3be7a
LP
339 if (r < 0)
340 return bus_log_create_error(r);
341
342 r = sd_bus_message_close_container(m);
89ada3ba
YW
343 if (r < 0)
344 return bus_log_create_error(r);
20b16441
LP
345 }
346
89ada3ba 347 r = sd_bus_message_close_container(m);
20b16441
LP
348 if (r < 0)
349 return bus_log_create_error(r);
350
89ada3ba
YW
351 r = sd_bus_message_close_container(m);
352 if (r < 0)
353 return bus_log_create_error(r);
20b16441 354
89ada3ba
YW
355 r = sd_bus_message_close_container(m);
356 if (r < 0)
357 return bus_log_create_error(r);
20b16441 358
89ada3ba
YW
359 return 1;
360}
20b16441 361
89ada3ba
YW
362static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
363 int r;
20b16441 364
89ada3ba
YW
365 assert(m);
366 assert(prefix);
20b16441 367
89ada3ba
YW
368 r = sd_bus_message_open_container(m, 'r', "iayu");
369 if (r < 0)
370 return r;
20b16441 371
89ada3ba
YW
372 r = sd_bus_message_append(m, "i", family);
373 if (r < 0)
374 return r;
66ebf6c0 375
89ada3ba
YW
376 r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
377 if (r < 0)
378 return r;
66ebf6c0 379
89ada3ba
YW
380 r = sd_bus_message_append(m, "u", prefixlen);
381 if (r < 0)
382 return r;
66ebf6c0 383
89ada3ba
YW
384 return sd_bus_message_close_container(m);
385}
20b16441 386
89ada3ba
YW
387static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
388 int r;
20b16441 389
89ada3ba 390 if (STR_IN_SET(field, "DevicePolicy", "Slice"))
20b16441 391
89ada3ba 392 return bus_append_string(m, field, eq);
13c31542 393
89ada3ba
YW
394 if (STR_IN_SET(field,
395 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
396 "TasksAccounting", "IPAccounting"))
13c31542 397
89ada3ba 398 return bus_append_parse_boolean(m, field, eq);
13c31542 399
89ada3ba 400 if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
20b16441 401
89ada3ba 402 return bus_append_cg_weight_parse(m, field, eq);
20b16441 403
89ada3ba 404 if (STR_IN_SET(field, "CPUShares", "StartupCPUShares"))
20b16441 405
89ada3ba 406 return bus_append_cg_cpu_shares_parse(m, field, eq);
268833ed 407
89ada3ba 408 if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight"))
20b16441 409
89ada3ba 410 return bus_append_cg_blkio_weight_parse(m, field, eq);
08f3be7a 411
89ada3ba 412 if (streq(field, "Delegate")) {
08f3be7a 413
89ada3ba 414 r = parse_boolean(eq);
08f3be7a 415 if (r < 0)
89ada3ba 416 return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_QUOTES);
08f3be7a 417
89ada3ba 418 r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
08f3be7a
LP
419 if (r < 0)
420 return bus_log_create_error(r);
421
89ada3ba
YW
422 return 1;
423 }
08f3be7a 424
89ada3ba
YW
425 if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
426 uint64_t val;
cffaed83 427
89ada3ba
YW
428 if (isempty(eq) || streq(eq, "infinity")) {
429 r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
430 if (r < 0)
431 return bus_log_create_error(r);
432 return 1;
cffaed83
YW
433 }
434
89ada3ba
YW
435 r = parse_percent(eq);
436 if (r >= 0) {
437 char *n;
20b16441 438
89ada3ba
YW
439 /* When this is a percentage we'll convert this into a relative value in the range
440 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
441 * ones). This way the physical memory size can be determined server-side */
20b16441 442
89ada3ba
YW
443 n = strjoina(field, "Scale");
444 r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
445 if (r < 0)
446 return bus_log_create_error(r);
20b16441 447
89ada3ba 448 return 1;
20b16441
LP
449 }
450
89ada3ba
YW
451 if (streq(field, "TasksMax"))
452 r = safe_atou64(eq, &val);
453 else
454 r = parse_size(eq, 1024, &val);
20b16441 455
89ada3ba
YW
456 if (r < 0)
457 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
cffaed83 458
89ada3ba 459 r = sd_bus_message_append(m, "(sv)", field, "t", val);
6bbfdc67 460 if (r < 0)
89ada3ba 461 return bus_log_create_error(r);
cffaed83 462
89ada3ba
YW
463 return 1;
464 }
cffaed83 465
89ada3ba 466 if (streq(field, "CPUQuota")) {
cffaed83 467
89ada3ba
YW
468 if (isempty(eq))
469 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
470 else {
471 r = parse_percent_unbounded(eq);
472 if (r <= 0) {
473 log_error_errno(r, "CPU quota '%s' invalid.", eq);
474 return -EINVAL;
475 }
476
477 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
cffaed83
YW
478 }
479
6bbfdc67 480 if (r < 0)
89ada3ba 481 return bus_log_create_error(r);
cffaed83 482
89ada3ba
YW
483 return 1;
484 }
cffaed83 485
89ada3ba 486 if (streq(field, "DeviceAllow")) {
20b16441
LP
487
488 if (isempty(eq))
89ada3ba 489 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
20b16441
LP
490 else {
491 const char *path, *rwm, *e;
492
493 e = strchr(eq, ' ');
494 if (e) {
495 path = strndupa(eq, e - eq);
496 rwm = e+1;
497 } else {
498 path = eq;
499 rwm = "";
500 }
501
3ccb8862 502 if (!is_deviceallow_pattern(path)) {
20b16441
LP
503 log_error("%s is not a device file in /dev.", path);
504 return -EINVAL;
505 }
506
89ada3ba 507 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, rwm);
20b16441
LP
508 }
509
89ada3ba
YW
510 if (r < 0)
511 return bus_log_create_error(r);
512
513 return 1;
514 }
515
516 if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
20b16441
LP
517
518 if (isempty(eq))
89ada3ba 519 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
20b16441
LP
520 else {
521 const char *path, *bandwidth, *e;
522 uint64_t bytes;
523
524 e = strchr(eq, ' ');
525 if (e) {
526 path = strndupa(eq, e - eq);
527 bandwidth = e+1;
528 } else {
529 log_error("Failed to parse %s value %s.", field, eq);
530 return -EINVAL;
531 }
532
533 if (!path_startswith(path, "/dev")) {
534 log_error("%s is not a device file in /dev.", path);
535 return -EINVAL;
536 }
537
e57c9ce1 538 if (streq(bandwidth, "infinity")) {
13c31542
TH
539 bytes = CGROUP_LIMIT_MAX;
540 } else {
541 r = parse_size(bandwidth, 1000, &bytes);
6bbfdc67
LP
542 if (r < 0)
543 return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
20b16441
LP
544 }
545
89ada3ba 546 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
20b16441
LP
547 }
548
89ada3ba
YW
549 if (r < 0)
550 return bus_log_create_error(r);
551
552 return 1;
553 }
554
555 if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
20b16441
LP
556
557 if (isempty(eq))
89ada3ba 558 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
20b16441
LP
559 else {
560 const char *path, *weight, *e;
561 uint64_t u;
562
563 e = strchr(eq, ' ');
564 if (e) {
565 path = strndupa(eq, e - eq);
566 weight = e+1;
567 } else {
568 log_error("Failed to parse %s value %s.", field, eq);
569 return -EINVAL;
570 }
571
572 if (!path_startswith(path, "/dev")) {
573 log_error("%s is not a device file in /dev.", path);
574 return -EINVAL;
575 }
576
577 r = safe_atou64(weight, &u);
6bbfdc67
LP
578 if (r < 0)
579 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
580
89ada3ba 581 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
20b16441
LP
582 }
583
89ada3ba
YW
584 if (r < 0)
585 return bus_log_create_error(r);
3dc5ca97 586
89ada3ba
YW
587 return 1;
588 }
3dc5ca97 589
89ada3ba
YW
590 if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) {
591 unsigned char prefixlen;
592 union in_addr_union prefix = {};
593 int family;
3dc5ca97 594
89ada3ba
YW
595 if (isempty(eq)) {
596 r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
3dc5ca97
LP
597 if (r < 0)
598 return bus_log_create_error(r);
599
89ada3ba
YW
600 return 1;
601 }
3dc5ca97 602
89ada3ba
YW
603 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
604 if (r < 0)
605 return bus_log_create_error(r);
3dc5ca97 606
89ada3ba
YW
607 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
608 if (r < 0)
609 return bus_log_create_error(r);
3dc5ca97 610
89ada3ba
YW
611 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
612 if (r < 0)
613 return bus_log_create_error(r);
3dc5ca97 614
89ada3ba
YW
615 r = sd_bus_message_open_container(m, 'a', "(iayu)");
616 if (r < 0)
617 return bus_log_create_error(r);
3dc5ca97 618
89ada3ba
YW
619 if (streq(eq, "any")) {
620 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
3dc5ca97 621
89ada3ba
YW
622 r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
623 if (r < 0)
624 return bus_log_create_error(r);
3dc5ca97 625
89ada3ba
YW
626 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
627 if (r < 0)
628 return bus_log_create_error(r);
3dc5ca97 629
89ada3ba
YW
630 } else if (is_localhost(eq)) {
631 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
3dc5ca97 632
89ada3ba
YW
633 prefix.in.s_addr = htobe32(0x7f000000);
634 r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
635 if (r < 0)
636 return bus_log_create_error(r);
3dc5ca97 637
89ada3ba
YW
638 prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
639 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
640 if (r < 0)
641 return r;
3dc5ca97 642
89ada3ba
YW
643 } else if (streq(eq, "link-local")) {
644 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
3dc5ca97 645
89ada3ba
YW
646 prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
647 r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
648 if (r < 0)
649 return bus_log_create_error(r);
3dc5ca97 650
89ada3ba
YW
651 prefix.in6 = (struct in6_addr) {
652 .s6_addr32[0] = htobe32(0xfe800000)
653 };
654 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
655 if (r < 0)
656 return bus_log_create_error(r);
3dc5ca97 657
89ada3ba
YW
658 } else if (streq(eq, "multicast")) {
659 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
3dc5ca97 660
89ada3ba
YW
661 prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
662 r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
663 if (r < 0)
664 return bus_log_create_error(r);
3dc5ca97 665
89ada3ba
YW
666 prefix.in6 = (struct in6_addr) {
667 .s6_addr32[0] = htobe32(0xff000000)
668 };
669 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
3dc5ca97
LP
670 if (r < 0)
671 return bus_log_create_error(r);
672
89ada3ba
YW
673 } else {
674 r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen);
675 if (r < 0)
676 return log_error_errno(r, "Failed to parse IP address prefix: %s", eq);
677
678 r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
3dc5ca97
LP
679 if (r < 0)
680 return bus_log_create_error(r);
681 }
682
89ada3ba
YW
683 r = sd_bus_message_close_container(m);
684 if (r < 0)
685 return bus_log_create_error(r);
cffaed83 686
89ada3ba
YW
687 r = sd_bus_message_close_container(m);
688 if (r < 0)
689 return bus_log_create_error(r);
cffaed83 690
89ada3ba
YW
691 r = sd_bus_message_close_container(m);
692 if (r < 0)
693 return bus_log_create_error(r);
cffaed83 694
89ada3ba
YW
695 return 1;
696 }
cffaed83 697
89ada3ba
YW
698 return 0;
699}
cffaed83 700
89ada3ba
YW
701static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
702 int r, rl;
cffaed83 703
89ada3ba
YW
704 if (STR_IN_SET(field,
705 "User", "Group",
706 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
707 "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
708 "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
709 "RuntimeDirectoryPreserve", "Personality", "KeyringMode"))
cffaed83 710
89ada3ba 711 return bus_append_string(m, field, eq);
cffaed83 712
89ada3ba
YW
713 if (STR_IN_SET(field,
714 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
715 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
716 "NoNewPrivileges", "SyslogLevelPrefix",
717 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
718 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups",
719 "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality"))
cffaed83 720
89ada3ba 721 return bus_append_parse_boolean(m, field, eq);
cffaed83 722
89ada3ba
YW
723 if (STR_IN_SET(field,
724 "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
725 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
726 "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
727 "SupplementaryGroups", "SystemCallArchitectures"))
cffaed83 728
89ada3ba 729 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
20b16441 730
89ada3ba 731 if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
20b16441 732
89ada3ba 733 return bus_append_log_level_from_string(m, field, eq);
20b16441 734
89ada3ba 735 if (streq(field, "SyslogFacility"))
cffaed83 736
89ada3ba 737 return bus_append_log_facility_unshifted_from_string(m, field, eq);
cffaed83 738
89ada3ba 739 if (streq(field, "SecureBits"))
cffaed83 740
89ada3ba 741 return bus_append_secure_bits_from_string(m, field, eq);
cffaed83 742
89ada3ba 743 if (streq(field, "CPUSchedulingPolicy"))
cffaed83 744
89ada3ba 745 return bus_append_sched_policy_from_string(m, field, eq);
cffaed83 746
89ada3ba 747 if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust"))
3f856a28 748
89ada3ba 749 return bus_append_safe_atoi(m, field, eq);
3f856a28 750
89ada3ba 751 if (streq(field, "Nice"))
3f856a28 752
89ada3ba 753 return bus_append_parse_nice(m, field, eq);
3f856a28 754
89ada3ba 755 if (streq(field, "SystemCallErrorNumber"))
cffaed83 756
89ada3ba 757 return bus_append_parse_errno(m, field, eq);
cffaed83 758
89ada3ba 759 if (streq(field, "IOSchedulingClass"))
cffaed83 760
89ada3ba 761 return bus_append_ioprio_class_from_string(m, field, eq);
cffaed83 762
89ada3ba 763 if (streq(field, "IOSchedulingPriority"))
cffaed83 764
89ada3ba 765 return bus_append_ioprio_parse_priority(m, field, eq);
cffaed83 766
89ada3ba
YW
767 if (STR_IN_SET(field,
768 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
769 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
cffaed83 770
89ada3ba 771 return bus_append_parse_mode(m, field, eq);
cffaed83 772
89ada3ba 773 if (streq(field, "TimerSlackNSec"))
cffaed83 774
89ada3ba 775 return bus_append_parse_nsec(m, field, eq);
cffaed83 776
89ada3ba 777 if (streq(field, "MountFlags"))
cffaed83 778
89ada3ba 779 return bus_append_mount_propagation_flags_from_string(m, field, eq);
cffaed83 780
89ada3ba 781 if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
cffaed83 782
89ada3ba 783 return bus_append_strv(m, field, eq, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
cffaed83 784
89ada3ba 785 if (streq(field, "EnvironmentFile")) {
cffaed83 786
89ada3ba
YW
787 if (isempty(eq))
788 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
789 else
790 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
791 eq[0] == '-' ? eq + 1 : eq,
792 eq[0] == '-');
793 if (r < 0)
794 return bus_log_create_error(r);
cffaed83 795
89ada3ba
YW
796 return 1;
797 }
cffaed83 798
89ada3ba 799 if (streq(field, "LogExtraFields")) {
cffaed83 800
89ada3ba 801 r = sd_bus_message_open_container(m, 'r', "sv");
3f856a28
YW
802 if (r < 0)
803 return bus_log_create_error(r);
804
89ada3ba 805 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
3f856a28
YW
806 if (r < 0)
807 return bus_log_create_error(r);
808
89ada3ba 809 r = sd_bus_message_open_container(m, 'v', "aay");
3f856a28
YW
810 if (r < 0)
811 return bus_log_create_error(r);
812
89ada3ba 813 r = sd_bus_message_open_container(m, 'a', "ay");
cffaed83
YW
814 if (r < 0)
815 return bus_log_create_error(r);
816
89ada3ba 817 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
3f856a28
YW
818 if (r < 0)
819 return bus_log_create_error(r);
cffaed83 820
3f856a28
YW
821 r = sd_bus_message_close_container(m);
822 if (r < 0)
823 return bus_log_create_error(r);
9f617cd0 824
89ada3ba
YW
825 r = sd_bus_message_close_container(m);
826 if (r < 0)
827 return bus_log_create_error(r);
9efb9df9 828
89ada3ba 829 r = sd_bus_message_close_container(m);
9efb9df9 830 if (r < 0)
89ada3ba
YW
831 return bus_log_create_error(r);
832
833 return 1;
834 }
9efb9df9 835
89ada3ba
YW
836 if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
837 const char *n, *appended;
9efb9df9 838
89ada3ba
YW
839 if ((n = startswith(eq, "fd:"))) {
840 appended = strjoina(field, "FileDescriptorName");
841 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
842 } else if ((n = startswith(eq, "file:"))) {
843 appended = strjoina(field, "File");
844 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
845 } else
846 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
7f452159 847
89ada3ba
YW
848 if (r < 0)
849 return bus_log_create_error(r);
7f452159 850
89ada3ba
YW
851 return 1;
852 }
7f452159 853
89ada3ba
YW
854 if (streq(field, "StandardInputText")) {
855 _cleanup_free_ char *unescaped = NULL;
7f452159 856
89ada3ba 857 r = cunescape(eq, 0, &unescaped);
7f452159 858 if (r < 0)
89ada3ba 859 return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
7f452159 860
89ada3ba
YW
861 if (!strextend(&unescaped, "\n", NULL))
862 return log_oom();
7f452159 863
89ada3ba
YW
864 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
865 * interface anyway */
20b16441 866
89ada3ba
YW
867 return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
868 }
20b16441 869
89ada3ba
YW
870 if (streq(field, "StandardInputData")) {
871 _cleanup_free_ void *decoded = NULL;
872 size_t sz;
873
874 r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
20b16441 875 if (r < 0)
89ada3ba 876 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
20b16441 877
89ada3ba
YW
878 return bus_append_byte_array(m, field, decoded, sz);
879 }
20b16441 880
89ada3ba
YW
881 rl = rlimit_from_string(field);
882 if (rl >= 0) {
883 const char *sn;
884 struct rlimit l;
20b16441 885
89ada3ba
YW
886 r = rlimit_parse(rl, eq, &l);
887 if (r < 0)
888 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
20b16441 889
89ada3ba
YW
890 r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
891 if (r < 0)
892 return bus_log_create_error(r);
20b16441 893
89ada3ba
YW
894 sn = strjoina(field, "Soft");
895 r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
20b16441
LP
896 if (r < 0)
897 return bus_log_create_error(r);
898
89ada3ba
YW
899 return 1;
900 }
20b16441 901
89ada3ba
YW
902 if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
903 int ignore = 0;
904 const char *s = eq;
20b16441 905
89ada3ba
YW
906 if (eq[0] == '-') {
907 ignore = 1;
908 s = eq + 1;
20b16441
LP
909 }
910
89ada3ba 911 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
6bbfdc67 912 if (r < 0)
89ada3ba 913 return bus_log_create_error(r);
20b16441 914
89ada3ba
YW
915 return 1;
916 }
20b16441 917
89ada3ba
YW
918 if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
919 uint64_t sum = 0;
920 bool invert = false;
921 const char *p = eq;
afcb1cd3 922
89ada3ba
YW
923 if (*p == '~') {
924 invert = true;
925 p++;
926 }
20b16441 927
89ada3ba 928 r = capability_set_from_string(p, &sum);
20b16441 929 if (r < 0)
89ada3ba 930 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
20b16441 931
89ada3ba
YW
932 sum = invert ? ~sum : sum;
933
934 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
20b16441
LP
935 if (r < 0)
936 return bus_log_create_error(r);
937
89ada3ba
YW
938 return 1;
939 }
20b16441 940
89ada3ba
YW
941 if (streq(field, "CPUAffinity")) {
942 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
20b16441 943
89ada3ba
YW
944 r = parse_cpu_set(eq, &cpuset);
945 if (r < 0)
946 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
20b16441 947
89ada3ba
YW
948 return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r));
949 }
20b7a007 950
89ada3ba
YW
951 if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) {
952 int whitelist = 1;
953 const char *p = eq;
20b16441 954
89ada3ba
YW
955 if (*p == '~') {
956 whitelist = 0;
957 p++;
20b16441
LP
958 }
959
89ada3ba 960 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
20b16441
LP
961 if (r < 0)
962 return bus_log_create_error(r);
963
89ada3ba
YW
964 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
965 if (r < 0)
966 return bus_log_create_error(r);
967
968 r = sd_bus_message_open_container(m, 'v', "(bas)");
969 if (r < 0)
970 return bus_log_create_error(r);
20b16441 971
89ada3ba
YW
972 r = sd_bus_message_open_container(m, 'r', "bas");
973 if (r < 0)
974 return bus_log_create_error(r);
cffaed83 975
89ada3ba 976 r = sd_bus_message_append_basic(m, 'b', &whitelist);
cffaed83
YW
977 if (r < 0)
978 return bus_log_create_error(r);
979
980 r = sd_bus_message_open_container(m, 'a', "s");
981 if (r < 0)
982 return bus_log_create_error(r);
983
984 for (p = eq;;) {
985 _cleanup_free_ char *word = NULL;
986
987 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
cffaed83
YW
988 if (r == 0)
989 break;
89ada3ba
YW
990 if (r == -ENOMEM)
991 return log_oom();
992 if (r < 0)
993 return log_error_errno(r, "Invalid syntax: %s", eq);
cffaed83
YW
994
995 r = sd_bus_message_append_basic(m, 's', word);
996 if (r < 0)
997 return bus_log_create_error(r);
998 }
999
1000 r = sd_bus_message_close_container(m);
1001 if (r < 0)
1002 return bus_log_create_error(r);
1003
1004 r = sd_bus_message_close_container(m);
20b16441
LP
1005 if (r < 0)
1006 return bus_log_create_error(r);
1007
89ada3ba 1008 r = sd_bus_message_close_container(m);
20b16441
LP
1009 if (r < 0)
1010 return bus_log_create_error(r);
1011
20b16441
LP
1012 r = sd_bus_message_close_container(m);
1013 if (r < 0)
1014 return bus_log_create_error(r);
1015
89ada3ba
YW
1016 return 1;
1017 }
20b16441 1018
89ada3ba 1019 if (streq(field, "RestrictNamespaces")) {
add00535 1020 bool invert = false;
0d7578dc 1021 unsigned long flags = 0;
add00535
LP
1022
1023 if (eq[0] == '~') {
1024 invert = true;
1025 eq++;
1026 }
1027
1028 r = parse_boolean(eq);
1029 if (r > 0)
1030 flags = 0;
1031 else if (r == 0)
1032 flags = NAMESPACE_FLAGS_ALL;
1033 else {
1034 r = namespace_flag_from_string_many(eq, &flags);
1035 if (r < 0)
1036 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1037 }
1038
1039 if (invert)
1040 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1041
89ada3ba
YW
1042 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1043 if (r < 0)
1044 return bus_log_create_error(r);
afcb1cd3 1045
89ada3ba
YW
1046 return 1;
1047 }
afcb1cd3 1048
89ada3ba
YW
1049 if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
1050 const char *p = eq;
83555251 1051
89ada3ba 1052 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
c7383828 1053 if (r < 0)
89ada3ba 1054 return bus_log_create_error(r);
afcb1cd3 1055
89ada3ba
YW
1056 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1057 if (r < 0)
1058 return bus_log_create_error(r);
d2d6c096
LP
1059
1060 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1061 if (r < 0)
89ada3ba 1062 return bus_log_create_error(r);
d2d6c096
LP
1063
1064 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1065 if (r < 0)
89ada3ba 1066 return bus_log_create_error(r);
d2d6c096
LP
1067
1068 for (;;) {
1069 _cleanup_free_ char *source = NULL, *destination = NULL;
1070 char *s = NULL, *d = NULL;
1071 bool ignore_enoent = false;
1072 uint64_t flags = MS_REC;
1073
1074 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1075 if (r < 0)
1076 return log_error_errno(r, "Failed to parse argument: %m");
1077 if (r == 0)
1078 break;
1079
1080 s = source;
1081 if (s[0] == '-') {
1082 ignore_enoent = true;
1083 s++;
1084 }
1085
1086 if (p && p[-1] == ':') {
1087 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1088 if (r < 0)
1089 return log_error_errno(r, "Failed to parse argument: %m");
1090 if (r == 0) {
1091 log_error("Missing argument after ':': %s", eq);
1092 return -EINVAL;
1093 }
1094
1095 d = destination;
1096
1097 if (p && p[-1] == ':') {
1098 _cleanup_free_ char *options = NULL;
1099
1100 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
1101 if (r < 0)
1102 return log_error_errno(r, "Failed to parse argument: %m");
1103
1104 if (isempty(options) || streq(options, "rbind"))
1105 flags = MS_REC;
1106 else if (streq(options, "norbind"))
1107 flags = 0;
1108 else {
1109 log_error("Unknown options: %s", eq);
1110 return -EINVAL;
1111 }
1112 }
1113 } else
1114 d = s;
1115
1116
1117 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1118 if (r < 0)
89ada3ba 1119 return bus_log_create_error(r);
d2d6c096
LP
1120 }
1121
1122 r = sd_bus_message_close_container(m);
1123 if (r < 0)
89ada3ba
YW
1124 return bus_log_create_error(r);
1125
1126 r = sd_bus_message_close_container(m);
1127 if (r < 0)
1128 return bus_log_create_error(r);
d2d6c096
LP
1129
1130 r = sd_bus_message_close_container(m);
89ada3ba
YW
1131 if (r < 0)
1132 return bus_log_create_error(r);
f6c66be1 1133
89ada3ba
YW
1134 return 1;
1135 }
f6c66be1 1136
89ada3ba
YW
1137 return 0;
1138}
f6c66be1 1139
89ada3ba 1140static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
f6c66be1 1141
89ada3ba 1142 if (streq(field, "KillMode"))
f6c66be1 1143
89ada3ba 1144 return bus_append_string(m, field, eq);
f6c66be1 1145
89ada3ba 1146 if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
f6c66be1 1147
89ada3ba 1148 return bus_append_parse_boolean(m, field, eq);
f6c66be1 1149
89ada3ba
YW
1150 if (streq(field, "KillSignal"))
1151
1152 return bus_append_signal_from_string_try_harder(m, field, eq);
1153
1154 return 0;
1155}
1156
1157static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
1158
1159 if (STR_IN_SET(field,
1160 "PathExists", "PathExistsGlob", "PathChanged",
1161 "PathModified", "DirectoryNotEmpty"))
1162
1163 return bus_append_string(m, field, eq);
1164
1165 if (streq(field, "MakeDirectory"))
1166
1167 return bus_append_parse_boolean(m, field, eq);
1168
1169 if (streq(field, "DirectoryMode"))
1170
1171 return bus_append_parse_mode(m, field, eq);
1172
1173 return 0;
1174}
1175
1176static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
1177
1178 if (STR_IN_SET(field, "Type", "Restart", "NotifyAccess"))
1179
1180 return bus_append_string(m, field, eq);
f6c66be1 1181
89ada3ba
YW
1182 if (streq(field, "RemainAfterExit"))
1183
1184 return bus_append_parse_boolean(m, field, eq);
1185
1186 if (streq(field, "RuntimeMaxSec"))
1187
1188 return bus_append_parse_sec_rename(m, field, eq);
1189
1190 if (streq(field, "FileDescriptorStoreMax"))
1191
1192 return bus_append_safe_atou(m, field, eq);
1193
1194 if (STR_IN_SET(field,
1195 "ExecStartPre", "ExecStart", "ExecStartPost",
1196 "ExecReload", "ExecStop", "ExecStopPost"))
1197
1198 return bus_append_exec_command(m, field, eq);
1199
1200 return 0;
1201}
1202
1203static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
1204 int r;
1205
1206 if (STR_IN_SET(field,
1207 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1208 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1209
1210 return bus_append_parse_boolean(m, field, eq);
1211
1212 if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
1213
1214 return bus_append_safe_atoi(m, field, eq);
1215
1216 if (streq(field, "IPTOS"))
1217
1218 return bus_append_ip_tos_from_string(m, field, eq);
1219
1220 if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1221
1222 return bus_append_safe_atou(m, field, eq);
1223
1224 if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
1225
1226 return bus_append_parse_mode(m, field, eq);
1227
1228 if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1229
1230 return bus_append_safe_atoi64(m, field, eq);
1231
1232 if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1233
1234 return bus_append_parse_sec_rename(m, field, eq);
1235
1236 if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1237
b48e508d 1238 return bus_append_parse_size(m, field, eq, 1024);
89ada3ba
YW
1239
1240 if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1241
1242 return bus_append_exec_command(m, field, eq);
1243
1244 if (STR_IN_SET(field,
1245 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1246 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1247 "SocketUser", "SocketGroup"))
1248
1249 return bus_append_string(m, field, eq);
1250
1251 if (streq(field, "Symlinks"))
1252
1253 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
1254
e045e325 1255 if (streq(field, "SocketProtocol"))
89ada3ba 1256
e045e325 1257 return bus_append_socket_protocol_from_name(m, field, eq);
89ada3ba
YW
1258
1259 if (STR_IN_SET(field,
1260 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1261 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1262
1263 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + strlen("Listen"), eq);
f6c66be1 1264 if (r < 0)
89ada3ba
YW
1265 return bus_log_create_error(r);
1266
1267 return 1;
1268 }
1269
1270 return 0;
1271}
1272static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
1273
1274 if (streq(field, "OnCalendar"))
1275
1276 return bus_append_string(m, field, eq);
1277
1278 if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent"))
1279
1280 return bus_append_parse_boolean(m, field, eq);
1281
1282 if (STR_IN_SET(field,
1283 "OnActiveSec", "OnBootSec", "OnStartupSec",
1284 "OnUnitActiveSec","OnUnitInactiveSec"))
1285
1286 return bus_append_parse_sec(m, field, eq);
1287
1288 if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
1289
1290 return bus_append_parse_sec_rename(m, field, eq);
1291
1292 return 0;
1293}
1294
1295static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
1296 UnitDependency dep;
1297 int r;
f6c66be1 1298
89ada3ba
YW
1299 if (STR_IN_SET(field, "Description", "CollectMode", "FailureAction", "SuccessAction"))
1300
1301 return bus_append_string(m, field, eq);
1302
1303 if (streq(field, "DefaultDependencies"))
1304
1305 return bus_append_parse_boolean(m, field, eq);
1306
1307 if ((dep = unit_dependency_from_string(field)) >= 0) {
1308
1309 r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
f6c66be1 1310 if (r < 0)
89ada3ba
YW
1311 return bus_log_create_error(r);
1312
1313 return 1;
1314 }
1315
1316 return 0;
1317}
1318
1319int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
1320 const char *eq, *field;
1321 int r;
1322
1323 assert(m);
1324 assert(assignment);
1325
1326 eq = strchr(assignment, '=');
1327 if (!eq) {
1328 log_error("Not an assignment: %s", assignment);
1329 return -EINVAL;
1330 }
1331
1332 field = strndupa(assignment, eq - assignment);
1333 eq++;
1334
1335 switch (t) {
1336 case UNIT_SERVICE:
1337 r = bus_append_cgroup_property(m, field, eq);
1338 if (r != 0)
f6c66be1
LP
1339 return r;
1340
89ada3ba
YW
1341 r = bus_append_execute_property(m, field, eq);
1342 if (r != 0)
1343 return r;
f6c66be1 1344
89ada3ba
YW
1345 r = bus_append_kill_property(m, field, eq);
1346 if (r != 0)
1347 return r;
f6c66be1 1348
89ada3ba
YW
1349 r = bus_append_service_property(m, field, eq);
1350 if (r != 0)
1351 return r;
1352 break;
f6c66be1 1353
89ada3ba
YW
1354 case UNIT_SOCKET:
1355 r = bus_append_cgroup_property(m, field, eq);
1356 if (r != 0)
1357 return r;
f6c66be1 1358
89ada3ba
YW
1359 r = bus_append_execute_property(m, field, eq);
1360 if (r != 0)
1361 return r;
f6c66be1 1362
89ada3ba
YW
1363 r = bus_append_kill_property(m, field, eq);
1364 if (r != 0)
1365 return r;
f6c66be1 1366
89ada3ba
YW
1367 r = bus_append_socket_property(m, field, eq);
1368 if (r != 0)
f6c66be1 1369 return r;
89ada3ba 1370 break;
f6c66be1 1371
89ada3ba
YW
1372 case UNIT_TIMER:
1373 r = bus_append_timer_property(m, field, eq);
1374 if (r != 0)
1375 return r;
1376 break;
f6c66be1 1377
89ada3ba
YW
1378 case UNIT_PATH:
1379 r = bus_append_path_property(m, field, eq);
1380 if (r != 0)
1381 return r;
1382 break;
268833ed 1383
89ada3ba
YW
1384 case UNIT_SLICE:
1385 r = bus_append_cgroup_property(m, field, eq);
1386 if (r != 0)
1387 return r;
1388 break;
268833ed 1389
89ada3ba
YW
1390 case UNIT_SCOPE:
1391 r = bus_append_cgroup_property(m, field, eq);
1392 if (r != 0)
1393 return r;
268833ed 1394
89ada3ba
YW
1395 r = bus_append_kill_property(m, field, eq);
1396 if (r != 0)
1397 return r;
1398 break;
535e0d19 1399
89ada3ba
YW
1400 case UNIT_MOUNT:
1401 case UNIT_AUTOMOUNT:
1402 break;
535e0d19 1403
89ada3ba
YW
1404 case UNIT_TARGET:
1405 case UNIT_DEVICE:
1406 case UNIT_SWAP:
1407 log_error("Not supported unit type");
1408 return -EINVAL;
535e0d19 1409
89ada3ba
YW
1410 default:
1411 log_error("Invalid unit type");
20b16441
LP
1412 return -EINVAL;
1413 }
1414
89ada3ba
YW
1415 r = bus_append_unit_property(m, field, eq);
1416 if (r != 0)
1417 return r;
20b16441 1418
89ada3ba
YW
1419 log_error("Unknown assignment: %s", assignment);
1420 return -EINVAL;
20b16441
LP
1421}
1422
89ada3ba 1423int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
8673cf13
LP
1424 char **i;
1425 int r;
1426
1427 assert(m);
1428
1429 STRV_FOREACH(i, l) {
89ada3ba 1430 r = bus_append_unit_property_assignment(m, t, *i);
8673cf13
LP
1431 if (r < 0)
1432 return r;
1433 }
1434
1435 return 0;
1436}
1437
20b16441
LP
1438typedef struct BusWaitForJobs {
1439 sd_bus *bus;
1440 Set *jobs;
1441
1442 char *name;
1443 char *result;
1444
1445 sd_bus_slot *slot_job_removed;
1446 sd_bus_slot *slot_disconnected;
1447} BusWaitForJobs;
1448
1449static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1450 assert(m);
1451
1452 log_error("Warning! D-Bus connection terminated.");
1453 sd_bus_close(sd_bus_message_get_bus(m));
1454
1455 return 0;
1456}
1457
1458static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1459 const char *path, *unit, *result;
1460 BusWaitForJobs *d = userdata;
1461 uint32_t id;
1462 char *found;
1463 int r;
1464
1465 assert(m);
1466 assert(d);
1467
1468 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
1469 if (r < 0) {
1470 bus_log_parse_error(r);
1471 return 0;
1472 }
1473
1474 found = set_remove(d->jobs, (char*) path);
1475 if (!found)
1476 return 0;
1477
1478 free(found);
1479
1480 if (!isempty(result))
1481 d->result = strdup(result);
1482
1483 if (!isempty(unit))
1484 d->name = strdup(unit);
1485
1486 return 0;
1487}
1488
1489void bus_wait_for_jobs_free(BusWaitForJobs *d) {
1490 if (!d)
1491 return;
1492
1493 set_free_free(d->jobs);
1494
1495 sd_bus_slot_unref(d->slot_disconnected);
1496 sd_bus_slot_unref(d->slot_job_removed);
1497
1498 sd_bus_unref(d->bus);
1499
1500 free(d->name);
1501 free(d->result);
1502
1503 free(d);
1504}
1505
1506int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
1507 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
1508 int r;
1509
1510 assert(bus);
1511 assert(ret);
1512
1513 d = new0(BusWaitForJobs, 1);
1514 if (!d)
1515 return -ENOMEM;
1516
1517 d->bus = sd_bus_ref(bus);
1518
1519 /* When we are a bus client we match by sender. Direct
1520 * connections OTOH have no initialized sender field, and
1521 * hence we ignore the sender then */
1522 r = sd_bus_add_match(
1523 bus,
1524 &d->slot_job_removed,
1525 bus->bus_client ?
1526 "type='signal',"
1527 "sender='org.freedesktop.systemd1',"
1528 "interface='org.freedesktop.systemd1.Manager',"
1529 "member='JobRemoved',"
1530 "path='/org/freedesktop/systemd1'" :
1531 "type='signal',"
1532 "interface='org.freedesktop.systemd1.Manager',"
1533 "member='JobRemoved',"
1534 "path='/org/freedesktop/systemd1'",
1535 match_job_removed, d);
1536 if (r < 0)
1537 return r;
1538
1539 r = sd_bus_add_match(
1540 bus,
1541 &d->slot_disconnected,
1542 "type='signal',"
1543 "sender='org.freedesktop.DBus.Local',"
1544 "interface='org.freedesktop.DBus.Local',"
1545 "member='Disconnected'",
1546 match_disconnected, d);
1547 if (r < 0)
1548 return r;
1549
1550 *ret = d;
1551 d = NULL;
1552
1553 return 0;
1554}
1555
1556static int bus_process_wait(sd_bus *bus) {
1557 int r;
1558
1559 for (;;) {
1560 r = sd_bus_process(bus, NULL);
1561 if (r < 0)
1562 return r;
1563 if (r > 0)
1564 return 0;
1565
1566 r = sd_bus_wait(bus, (uint64_t) -1);
1567 if (r < 0)
1568 return r;
1569 }
1570}
1571
1572static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1573 _cleanup_free_ char *dbus_path = NULL;
1574
1575 assert(d);
1576 assert(d->name);
1577 assert(result);
1578
bd5a1c91
LP
1579 if (!endswith(d->name, ".service"))
1580 return -EINVAL;
1581
20b16441
LP
1582 dbus_path = unit_dbus_path_from_name(d->name);
1583 if (!dbus_path)
1584 return -ENOMEM;
1585
1586 return sd_bus_get_property_string(d->bus,
1587 "org.freedesktop.systemd1",
1588 dbus_path,
1589 "org.freedesktop.systemd1.Service",
1590 "Result",
1591 NULL,
1592 result);
1593}
1594
1595static const struct {
1596 const char *result, *explanation;
1597} explanations [] = {
0b2de9d9 1598 { "resources", "of unavailable resources or another system error" },
7ed0a4c5 1599 { "protocol", "the service did not take the steps required by its unit configuration" },
20b16441
LP
1600 { "timeout", "a timeout was exceeded" },
1601 { "exit-code", "the control process exited with error code" },
1602 { "signal", "a fatal signal was delivered to the control process" },
1603 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1604 { "watchdog", "the service failed to send watchdog ping" },
1605 { "start-limit", "start of the service was attempted too often" }
1606};
1607
1608static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1609 _cleanup_free_ char *service_shell_quoted = NULL;
1610 const char *systemctl = "systemctl", *journalctl = "journalctl";
1611
1612 assert(service);
1613
804ee07c 1614 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
20b16441 1615
3a58ca47 1616 if (!strv_isempty((char**) extra_args)) {
20b16441
LP
1617 _cleanup_free_ char *t;
1618
1619 t = strv_join((char**) extra_args, " ");
1620 systemctl = strjoina("systemctl ", t ? : "<args>");
1621 journalctl = strjoina("journalctl ", t ? : "<args>");
1622 }
1623
1624 if (!isempty(result)) {
1625 unsigned i;
1626
1627 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1628 if (streq(result, explanations[i].result))
1629 break;
1630
1631 if (i < ELEMENTSOF(explanations)) {
1632 log_error("Job for %s failed because %s.\n"
1633 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1634 service,
1635 explanations[i].explanation,
1636 systemctl,
1637 service_shell_quoted ?: "<service>",
1638 journalctl);
1639 goto finish;
1640 }
1641 }
1642
1643 log_error("Job for %s failed.\n"
1644 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1645 service,
1646 systemctl,
1647 service_shell_quoted ?: "<service>",
1648 journalctl);
1649
1650finish:
1651 /* For some results maybe additional explanation is required */
1652 if (streq_ptr(result, "start-limit"))
1653 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1654 "followed by \"%1$s start %2$s\" again.",
1655 systemctl,
1656 service_shell_quoted ?: "<service>");
1657}
1658
1659static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1660 int r = 0;
1661
1662 assert(d->result);
1663
1664 if (!quiet) {
1665 if (streq(d->result, "canceled"))
1666 log_error("Job for %s canceled.", strna(d->name));
1667 else if (streq(d->result, "timeout"))
1668 log_error("Job for %s timed out.", strna(d->name));
1669 else if (streq(d->result, "dependency"))
1670 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1671 else if (streq(d->result, "invalid"))
1672 log_error("%s is not active, cannot reload.", strna(d->name));
1673 else if (streq(d->result, "assert"))
1674 log_error("Assertion failed on job for %s.", strna(d->name));
1675 else if (streq(d->result, "unsupported"))
1676 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
c5a97ed1
LP
1677 else if (streq(d->result, "collected"))
1678 log_error("Queued job for %s was garbage collected.", strna(d->name));
ae835bc7 1679 else if (!STR_IN_SET(d->result, "done", "skipped")) {
20b16441 1680 if (d->name) {
20b16441 1681 _cleanup_free_ char *result = NULL;
bd5a1c91 1682 int q;
20b16441
LP
1683
1684 q = bus_job_get_service_result(d, &result);
1685 if (q < 0)
bd5a1c91 1686 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
20b16441
LP
1687
1688 log_job_error_with_service_result(d->name, result, extra_args);
1689 } else
1690 log_error("Job failed. See \"journalctl -xe\" for details.");
1691 }
1692 }
1693
c5a97ed1 1694 if (STR_IN_SET(d->result, "canceled", "collected"))
20b16441
LP
1695 r = -ECANCELED;
1696 else if (streq(d->result, "timeout"))
1697 r = -ETIME;
1698 else if (streq(d->result, "dependency"))
1699 r = -EIO;
1700 else if (streq(d->result, "invalid"))
1701 r = -ENOEXEC;
1702 else if (streq(d->result, "assert"))
1703 r = -EPROTO;
1704 else if (streq(d->result, "unsupported"))
1705 r = -EOPNOTSUPP;
ae835bc7 1706 else if (!STR_IN_SET(d->result, "done", "skipped"))
20b16441
LP
1707 r = -EIO;
1708
1709 return r;
1710}
1711
1712int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1713 int r = 0;
1714
1715 assert(d);
1716
1717 while (!set_isempty(d->jobs)) {
1718 int q;
1719
1720 q = bus_process_wait(d->bus);
1721 if (q < 0)
1722 return log_error_errno(q, "Failed to wait for response: %m");
1723
1724 if (d->result) {
1725 q = check_wait_response(d, quiet, extra_args);
1726 /* Return the first error as it is most likely to be
1727 * meaningful. */
1728 if (q < 0 && r == 0)
1729 r = q;
1730
1731 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1732 }
1733
1734 d->name = mfree(d->name);
1735 d->result = mfree(d->result);
1736 }
1737
1738 return r;
1739}
1740
1741int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1742 int r;
1743
1744 assert(d);
1745
1746 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
1747 if (r < 0)
1748 return r;
1749
1750 return set_put_strdup(d->jobs, path);
1751}
1752
1753int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
1754 int r;
1755
1756 r = bus_wait_for_jobs_add(d, path);
1757 if (r < 0)
1758 return log_oom();
1759
1760 return bus_wait_for_jobs(d, quiet, NULL);
1761}
1762
1763int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
1764 const char *type, *path, *source;
1765 int r;
1766
acc0269c
CH
1767 /* changes is dereferenced when calling unit_file_dump_changes() later,
1768 * so we have to make sure this is not NULL. */
1769 assert(changes);
1770 assert(n_changes);
1771
20b16441
LP
1772 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
1773 if (r < 0)
1774 return bus_log_parse_error(r);
1775
1776 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
1777 /* We expect only "success" changes to be sent over the bus.
1778 Hence, reject anything negative. */
1779 UnitFileChangeType ch = unit_file_change_type_from_string(type);
1780
1781 if (ch < 0) {
1782 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
1783 continue;
1784 }
1785
1786 r = unit_file_changes_add(changes, n_changes, ch, path, source);
1787 if (r < 0)
1788 return r;
1789 }
1790 if (r < 0)
1791 return bus_log_parse_error(r);
1792
1793 r = sd_bus_message_exit_container(m);
1794 if (r < 0)
1795 return bus_log_parse_error(r);
1796
35d379b2 1797 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
20b16441
LP
1798 return 0;
1799}
1800
291d565a
LP
1801struct CGroupInfo {
1802 char *cgroup_path;
1803 bool is_const; /* If false, cgroup_path should be free()'d */
1804
1805 Hashmap *pids; /* PID → process name */
1806 bool done;
1807
1808 struct CGroupInfo *parent;
1809 LIST_FIELDS(struct CGroupInfo, siblings);
1810 LIST_HEAD(struct CGroupInfo, children);
1811 size_t n_children;
1812};
1813
1814static bool IS_ROOT(const char *p) {
1815 return isempty(p) || streq(p, "/");
1816}
1817
1818static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
1819 struct CGroupInfo *parent = NULL, *cg;
1820 int r;
1821
1822 assert(cgroups);
1823 assert(ret);
1824
1825 if (IS_ROOT(path))
1826 path = "/";
1827
1828 cg = hashmap_get(cgroups, path);
1829 if (cg) {
1830 *ret = cg;
1831 return 0;
1832 }
1833
1834 if (!IS_ROOT(path)) {
1835 const char *e, *pp;
1836
1837 e = strrchr(path, '/');
1838 if (!e)
1839 return -EINVAL;
1840
1841 pp = strndupa(path, e - path);
1842 if (!pp)
1843 return -ENOMEM;
1844
1845 r = add_cgroup(cgroups, pp, false, &parent);
1846 if (r < 0)
1847 return r;
1848 }
1849
1850 cg = new0(struct CGroupInfo, 1);
1851 if (!cg)
1852 return -ENOMEM;
1853
1854 if (is_const)
1855 cg->cgroup_path = (char*) path;
1856 else {
1857 cg->cgroup_path = strdup(path);
1858 if (!cg->cgroup_path) {
1859 free(cg);
1860 return -ENOMEM;
1861 }
1862 }
1863
1864 cg->is_const = is_const;
1865 cg->parent = parent;
1866
1867 r = hashmap_put(cgroups, cg->cgroup_path, cg);
1868 if (r < 0) {
1869 if (!is_const)
1870 free(cg->cgroup_path);
1871 free(cg);
1872 return r;
1873 }
1874
1875 if (parent) {
1876 LIST_PREPEND(siblings, parent->children, cg);
1877 parent->n_children++;
1878 }
1879
1880 *ret = cg;
1881 return 1;
1882}
1883
1884static int add_process(
1885 Hashmap *cgroups,
1886 const char *path,
1887 pid_t pid,
1888 const char *name) {
1889
1890 struct CGroupInfo *cg;
1891 int r;
1892
1893 assert(cgroups);
1894 assert(name);
1895 assert(pid > 0);
1896
1897 r = add_cgroup(cgroups, path, true, &cg);
1898 if (r < 0)
1899 return r;
1900
1901 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
1902 if (r < 0)
1903 return r;
1904
1905 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
1906}
1907
1908static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
1909 assert(cgroups);
1910 assert(cg);
1911
1912 while (cg->children)
1913 remove_cgroup(cgroups, cg->children);
1914
1915 hashmap_remove(cgroups, cg->cgroup_path);
1916
1917 if (!cg->is_const)
1918 free(cg->cgroup_path);
1919
1920 hashmap_free(cg->pids);
1921
1922 if (cg->parent)
1923 LIST_REMOVE(siblings, cg->parent->children, cg);
1924
1925 free(cg);
1926}
1927
1928static int cgroup_info_compare_func(const void *a, const void *b) {
1929 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
1930
1931 assert(x);
1932 assert(y);
1933
1934 return strcmp(x->cgroup_path, y->cgroup_path);
1935}
1936
1937static int dump_processes(
1938 Hashmap *cgroups,
1939 const char *cgroup_path,
1940 const char *prefix,
1941 unsigned n_columns,
1942 OutputFlags flags) {
1943
1944 struct CGroupInfo *cg;
1945 int r;
1946
1947 assert(prefix);
1948
1949 if (IS_ROOT(cgroup_path))
1950 cgroup_path = "/";
1951
1952 cg = hashmap_get(cgroups, cgroup_path);
1953 if (!cg)
1954 return 0;
1955
1956 if (!hashmap_isempty(cg->pids)) {
1957 const char *name;
1958 size_t n = 0, i;
1959 pid_t *pids;
1960 void *pidp;
1961 Iterator j;
1962 int width;
1963
1964 /* Order processes by their PID */
1965 pids = newa(pid_t, hashmap_size(cg->pids));
1966
1967 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
1968 pids[n++] = PTR_TO_PID(pidp);
1969
1970 assert(n == hashmap_size(cg->pids));
1971 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1972
1973 width = DECIMAL_STR_WIDTH(pids[n-1]);
1974
1975 for (i = 0; i < n; i++) {
1976 _cleanup_free_ char *e = NULL;
1977 const char *special;
1978 bool more;
1979
1980 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
1981 assert(name);
1982
1983 if (n_columns != 0) {
1984 unsigned k;
1985
1986 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1987
1988 e = ellipsize(name, k, 100);
1989 if (e)
1990 name = e;
1991 }
1992
1993 more = i+1 < n || cg->children;
323b7dc9 1994 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
291d565a
LP
1995
1996 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
1997 prefix,
1998 special,
1999 width, pids[i],
2000 name);
2001 }
2002 }
2003
2004 if (cg->children) {
2005 struct CGroupInfo **children, *child;
2006 size_t n = 0, i;
2007
2008 /* Order subcgroups by their name */
2009 children = newa(struct CGroupInfo*, cg->n_children);
2010 LIST_FOREACH(siblings, child, cg->children)
2011 children[n++] = child;
2012 assert(n == cg->n_children);
2013 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
2014
7351ded5
IL
2015 if (n_columns != 0)
2016 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
291d565a
LP
2017
2018 for (i = 0; i < n; i++) {
2019 _cleanup_free_ char *pp = NULL;
2020 const char *name, *special;
2021 bool more;
2022
2023 child = children[i];
2024
2025 name = strrchr(child->cgroup_path, '/');
2026 if (!name)
2027 return -EINVAL;
2028 name++;
2029
2030 more = i+1 < n;
323b7dc9 2031 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
291d565a
LP
2032
2033 fputs(prefix, stdout);
2034 fputs(special, stdout);
2035 fputs(name, stdout);
2036 fputc('\n', stdout);
2037
323b7dc9 2038 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
291d565a
LP
2039
2040 pp = strappend(prefix, special);
2041 if (!pp)
2042 return -ENOMEM;
2043
2044 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
2045 if (r < 0)
2046 return r;
2047 }
2048 }
2049
2050 cg->done = true;
2051 return 0;
2052}
2053
2054static int dump_extra_processes(
2055 Hashmap *cgroups,
2056 const char *prefix,
2057 unsigned n_columns,
2058 OutputFlags flags) {
2059
2060 _cleanup_free_ pid_t *pids = NULL;
2061 _cleanup_hashmap_free_ Hashmap *names = NULL;
2062 struct CGroupInfo *cg;
2063 size_t n_allocated = 0, n = 0, k;
2064 Iterator i;
2065 int width, r;
2066
2067 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2068 * combined, sorted, linear list. */
2069
2070 HASHMAP_FOREACH(cg, cgroups, i) {
2071 const char *name;
2072 void *pidp;
2073 Iterator j;
2074
2075 if (cg->done)
2076 continue;
2077
2078 if (hashmap_isempty(cg->pids))
2079 continue;
2080
2081 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
2082 if (r < 0)
2083 return r;
2084
2085 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
2086 return -ENOMEM;
2087
2088 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
2089 pids[n++] = PTR_TO_PID(pidp);
2090
2091 r = hashmap_put(names, pidp, (void*) name);
2092 if (r < 0)
2093 return r;
2094 }
2095 }
2096
2097 if (n == 0)
2098 return 0;
2099
2100 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
2101 width = DECIMAL_STR_WIDTH(pids[n-1]);
2102
2103 for (k = 0; k < n; k++) {
2104 _cleanup_free_ char *e = NULL;
2105 const char *name;
2106
2107 name = hashmap_get(names, PID_TO_PTR(pids[k]));
2108 assert(name);
2109
2110 if (n_columns != 0) {
2111 unsigned z;
2112
2113 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2114
2115 e = ellipsize(name, z, 100);
2116 if (e)
2117 name = e;
2118 }
2119
2120 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
2121 prefix,
323b7dc9 2122 special_glyph(TRIANGULAR_BULLET),
291d565a
LP
2123 width, pids[k],
2124 name);
2125 }
2126
2127 return 0;
2128}
2129
2130int unit_show_processes(
2131 sd_bus *bus,
2132 const char *unit,
2133 const char *cgroup_path,
2134 const char *prefix,
2135 unsigned n_columns,
2136 OutputFlags flags,
2137 sd_bus_error *error) {
2138
2139 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2140 Hashmap *cgroups = NULL;
2141 struct CGroupInfo *cg;
2142 int r;
2143
2144 assert(bus);
2145 assert(unit);
2146
2147 if (flags & OUTPUT_FULL_WIDTH)
2148 n_columns = 0;
2149 else if (n_columns <= 0)
2150 n_columns = columns();
2151
2152 prefix = strempty(prefix);
2153
2154 r = sd_bus_call_method(
2155 bus,
2156 "org.freedesktop.systemd1",
2157 "/org/freedesktop/systemd1",
2158 "org.freedesktop.systemd1.Manager",
2159 "GetUnitProcesses",
2160 error,
2161 &reply,
2162 "s",
2163 unit);
2164 if (r < 0)
2165 return r;
2166
2167 cgroups = hashmap_new(&string_hash_ops);
2168 if (!cgroups)
2169 return -ENOMEM;
2170
2171 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
2172 if (r < 0)
2173 goto finish;
2174
2175 for (;;) {
2176 const char *path = NULL, *name = NULL;
2177 uint32_t pid;
2178
2179 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
2180 if (r < 0)
2181 goto finish;
2182 if (r == 0)
2183 break;
2184
2185 r = add_process(cgroups, path, pid, name);
2186 if (r < 0)
2187 goto finish;
2188 }
2189
2190 r = sd_bus_message_exit_container(reply);
2191 if (r < 0)
2192 goto finish;
2193
2194 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
2195 if (r < 0)
2196 goto finish;
2197
2198 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
2199
2200finish:
2201 while ((cg = hashmap_first(cgroups)))
2202 remove_cgroup(cgroups, cg);
2203
2204 hashmap_free(cgroups);
2205
2206 return r;
2207}