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