]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
core: never allow perpetual units to be masked
[thirdparty/systemd.git] / src / core / load-fragment.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a7334b09 2/***
96b2fb93 3 Copyright © 2012 Holger Hans Peter Freyther
a7334b09
LP
4***/
5
3efd4195 6#include <errno.h>
87f0e418 7#include <fcntl.h>
25e870b5 8#include <linux/fs.h>
5f5d8eab 9#include <linux/oom.h>
349cc4a5 10#if HAVE_SECCOMP
618234a5
LP
11#include <seccomp.h>
12#endif
5f5d8eab 13#include <sched.h>
3d57c6ab 14#include <sys/resource.h>
3efd4195 15
5f5d8eab 16#include "af-list.h"
cf0fbc49 17#include "alloc-util.h"
57b7a260 18#include "all-units.h"
fab34748 19#include "bpf-firewall.h"
5f5d8eab
LP
20#include "bus-error.h"
21#include "bus-internal.h"
22#include "bus-util.h"
23#include "cap-list.h"
a103496c 24#include "capability-util.h"
fdb3deca 25#include "cgroup-setup.h"
3efd4195 26#include "conf-parser.h"
618234a5 27#include "cpu-set-util.h"
5f5d8eab
LP
28#include "env-util.h"
29#include "errno-list.h"
4f5dd394 30#include "escape.h"
3ffd4af2 31#include "fd-util.h"
f4f15635 32#include "fs-util.h"
08f3be7a 33#include "hexdecoct.h"
d3070fbd 34#include "io-util.h"
9eba9da4 35#include "ioprio.h"
da96ad5a 36#include "ip-protocol-list.h"
d3070fbd 37#include "journal-util.h"
eefc66aa 38#include "limits-util.h"
3ffd4af2 39#include "load-fragment.h"
5f5d8eab 40#include "log.h"
049af8ad 41#include "mountpoint-util.h"
d8b4d14d 42#include "nulstr-util.h"
6bedfcbb 43#include "parse-util.h"
9eb977db 44#include "path-util.h"
7b3e062c 45#include "process-util.h"
349cc4a5 46#if HAVE_SECCOMP
57183d11
LP
47#include "seccomp-util.h"
48#endif
07d46372 49#include "securebits-util.h"
5f5d8eab 50#include "signal-util.h"
5c3fa98d 51#include "socket-netlink.h"
8fcde012 52#include "stat-util.h"
07630cea 53#include "string-util.h"
5f5d8eab
LP
54#include "strv.h"
55#include "unit-name.h"
56#include "unit-printf.h"
66dccd8d 57#include "user-util.h"
10f28641 58#include "time-util.h"
49cf4170 59#include "web-util.h"
57183d11 60
d2b42d63 61static int parse_socket_protocol(const char *s) {
53577580
YW
62 int r;
63
d2b42d63 64 r = parse_ip_protocol(s);
53577580 65 if (r < 0)
acf4d158 66 return r;
53577580
YW
67 if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP))
68 return -EPROTONOSUPPORT;
69
70 return r;
71}
72
a07a7324
FS
73int parse_crash_chvt(const char *value, int *data) {
74 int b;
75
76 if (safe_atoi(value, data) >= 0)
77 return 0;
78
79 b = parse_boolean(value);
80 if (b < 0)
81 return b;
82
83 if (b > 0)
84 *data = 0; /* switch to where kmsg goes */
85 else
86 *data = -1; /* turn off switching */
87
88 return 0;
89}
90
91int parse_confirm_spawn(const char *value, char **console) {
92 char *s;
93 int r;
94
95 r = value ? parse_boolean(value) : 1;
96 if (r == 0) {
97 *console = NULL;
98 return 0;
4a8daee7 99 } else if (r > 0) /* on with default tty */
a07a7324
FS
100 s = strdup("/dev/console");
101 else if (is_path(value)) /* on with fully qualified path */
102 s = strdup(value);
103 else /* on with only a tty file name, not a fully qualified path */
4a8daee7 104 s = path_join("/dev/", value);
a07a7324
FS
105 if (!s)
106 return -ENOMEM;
107
108 *console = s;
109 return 0;
110}
111
d2b42d63 112DEFINE_CONFIG_PARSE(config_parse_socket_protocol, parse_socket_protocol, "Failed to parse socket protocol");
53577580 113DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
5afe510c 114DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
53577580 115DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
53577580
YW
116DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
117DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
118DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
119DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
120DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1e8c7bd5
YW
121DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value");
122DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
53577580
YW
123DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
124DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
125DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
126DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value");
afcfaa69 127DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy, "Failed to parse OOM policy");
53577580
YW
128DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value");
129DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint64_t, "Invalid block IO weight");
130DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight");
131DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
132DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
b070c7c0 133DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
6327aa9f 134DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format");
5afe510c 135
f32b43bd
LP
136int config_parse_unit_deps(
137 const char *unit,
138 const char *filename,
139 unsigned line,
140 const char *section,
141 unsigned section_line,
142 const char *lvalue,
143 int ltype,
144 const char *rvalue,
145 void *data,
146 void *userdata) {
3efd4195 147
f975e971 148 UnitDependency d = ltype;
87f0e418 149 Unit *u = userdata;
3d793d29 150 const char *p;
3efd4195
LP
151
152 assert(filename);
153 assert(lvalue);
154 assert(rvalue);
3efd4195 155
3d793d29 156 p = rvalue;
9ed794a3 157 for (;;) {
3d793d29 158 _cleanup_free_ char *word = NULL, *k = NULL;
3efd4195 159 int r;
3efd4195 160
c89f52ac 161 r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
3d793d29
SS
162 if (r == 0)
163 break;
164 if (r == -ENOMEM)
74051b9b 165 return log_oom();
3d793d29
SS
166 if (r < 0) {
167 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
168 break;
169 }
3efd4195 170
3d793d29 171 r = unit_name_printf(u, word, &k);
19f6d710 172 if (r < 0) {
063c4b1a 173 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
19f6d710
LP
174 continue;
175 }
9e2f7c11 176
35d8c19a 177 r = unit_add_dependency_by_name(u, d, k, true, UNIT_DEPENDENCY_FILE);
57020a3a 178 if (r < 0)
12ca818f 179 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
3efd4195
LP
180 }
181
182 return 0;
183}
184
f32b43bd
LP
185int config_parse_obsolete_unit_deps(
186 const char *unit,
187 const char *filename,
188 unsigned line,
189 const char *section,
190 unsigned section_line,
191 const char *lvalue,
192 int ltype,
193 const char *rvalue,
194 void *data,
195 void *userdata) {
196
197 log_syntax(unit, LOG_WARNING, filename, line, 0,
198 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype));
199
200 return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
201}
202
b02cb41c
LP
203int config_parse_unit_string_printf(
204 const char *unit,
205 const char *filename,
206 unsigned line,
207 const char *section,
208 unsigned section_line,
209 const char *lvalue,
210 int ltype,
211 const char *rvalue,
212 void *data,
213 void *userdata) {
932921b5 214
74051b9b 215 _cleanup_free_ char *k = NULL;
47538b76 216 const Unit *u = userdata;
19f6d710 217 int r;
932921b5
LP
218
219 assert(filename);
220 assert(lvalue);
221 assert(rvalue);
f2d3769a 222 assert(u);
932921b5 223
19f6d710 224 r = unit_full_printf(u, rvalue, &k);
b02cb41c 225 if (r < 0) {
063c4b1a 226 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
b02cb41c
LP
227 return 0;
228 }
932921b5 229
b02cb41c 230 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
932921b5
LP
231}
232
12ca818f
LP
233int config_parse_unit_strv_printf(
234 const char *unit,
235 const char *filename,
236 unsigned line,
237 const char *section,
238 unsigned section_line,
239 const char *lvalue,
240 int ltype,
241 const char *rvalue,
242 void *data,
243 void *userdata) {
8fef7659 244
47538b76 245 const Unit *u = userdata;
74051b9b 246 _cleanup_free_ char *k = NULL;
19f6d710 247 int r;
8fef7659
LP
248
249 assert(filename);
250 assert(lvalue);
251 assert(rvalue);
252 assert(u);
253
19f6d710 254 r = unit_full_printf(u, rvalue, &k);
12ca818f 255 if (r < 0) {
063c4b1a 256 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f
LP
257 return 0;
258 }
8fef7659 259
12ca818f 260 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
8fef7659
LP
261}
262
5f5d8eab
LP
263int config_parse_unit_path_printf(
264 const char *unit,
265 const char *filename,
266 unsigned line,
267 const char *section,
268 unsigned section_line,
269 const char *lvalue,
270 int ltype,
271 const char *rvalue,
272 void *data,
273 void *userdata) {
6ea832a2 274
74051b9b 275 _cleanup_free_ char *k = NULL;
47538b76 276 const Unit *u = userdata;
19f6d710 277 int r;
2c75fb73 278 bool fatal = ltype;
6ea832a2
LP
279
280 assert(filename);
281 assert(lvalue);
282 assert(rvalue);
6ea832a2
LP
283 assert(u);
284
e3c3d676
ZJS
285 /* Let's not bother with anything that is too long */
286 if (strlen(rvalue) >= PATH_MAX) {
287 log_syntax(unit, LOG_ERR, filename, line, 0,
288 "%s value too long%s.",
289 lvalue, fatal ? "" : ", ignoring");
290 return fatal ? -ENAMETOOLONG : 0;
291 }
292
19f6d710 293 r = unit_full_printf(u, rvalue, &k);
811ba7a0 294 if (r < 0) {
2c75fb73 295 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 296 "Failed to resolve unit specifiers in '%s'%s: %m",
e3c3d676 297 rvalue, fatal ? "" : ", ignoring");
2c75fb73 298 return fatal ? -ENOEXEC : 0;
811ba7a0 299 }
6ea832a2 300
811ba7a0
LP
301 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
302}
303
304int config_parse_unit_path_strv_printf(
305 const char *unit,
306 const char *filename,
307 unsigned line,
308 const char *section,
309 unsigned section_line,
310 const char *lvalue,
311 int ltype,
312 const char *rvalue,
313 void *data,
314 void *userdata) {
315
a2a5291b 316 char ***x = data;
47538b76 317 const Unit *u = userdata;
811ba7a0 318 int r;
035fe294 319 const char *p;
811ba7a0
LP
320
321 assert(filename);
322 assert(lvalue);
323 assert(rvalue);
324 assert(u);
325
499295fb 326 if (isempty(rvalue)) {
9f2d41a6 327 *x = strv_free(*x);
499295fb
YW
328 return 0;
329 }
330
035fe294
ZJS
331 for (p = rvalue;;) {
332 _cleanup_free_ char *word = NULL, *k = NULL;
811ba7a0 333
4ec85141 334 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294
ZJS
335 if (r == 0)
336 return 0;
337 if (r == -ENOMEM)
338 return log_oom();
339 if (r < 0) {
340 log_syntax(unit, LOG_WARNING, filename, line, r,
341 "Invalid syntax, ignoring: %s", rvalue);
342 return 0;
343 }
811ba7a0 344
035fe294 345 r = unit_full_printf(u, word, &k);
811ba7a0 346 if (r < 0) {
035fe294 347 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 348 "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
811ba7a0
LP
349 return 0;
350 }
351
2f4d31c1
YW
352 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
353 if (r < 0)
811ba7a0 354 return 0;
811ba7a0 355
7d2c9c6b 356 r = strv_consume(x, TAKE_PTR(k));
811ba7a0
LP
357 if (r < 0)
358 return log_oom();
811ba7a0 359 }
6ea832a2
LP
360}
361
4a66b5c9
LP
362static int patch_var_run(
363 const char *unit,
364 const char *filename,
365 unsigned line,
366 const char *lvalue,
367 char **path) {
368
369 const char *e;
370 char *z;
371
372 e = path_startswith(*path, "/var/run/");
373 if (!e)
374 return 0;
375
376 z = path_join("/run/", e);
377 if (!z)
378 return log_oom();
379
380 log_syntax(unit, LOG_NOTICE, filename, line, 0,
381 "%s= references a path below legacy directory /var/run/, updating %s → %s; "
382 "please update the unit file accordingly.", lvalue, *path, z);
383
384 free_and_replace(*path, z);
385
386 return 1;
387}
388
389int config_parse_socket_listen(
390 const char *unit,
391 const char *filename,
392 unsigned line,
393 const char *section,
394 unsigned section_line,
395 const char *lvalue,
396 int ltype,
397 const char *rvalue,
398 void *data,
399 void *userdata) {
42f4e3c4 400
b1389b0d
ZJS
401 _cleanup_free_ SocketPort *p = NULL;
402 SocketPort *tail;
542563ba 403 Socket *s;
19f6d710 404 int r;
16354eff 405
42f4e3c4
LP
406 assert(filename);
407 assert(lvalue);
408 assert(rvalue);
409 assert(data);
410
595ed347 411 s = SOCKET(data);
542563ba 412
74051b9b
LP
413 if (isempty(rvalue)) {
414 /* An empty assignment removes all ports */
415 socket_free_ports(s);
416 return 0;
417 }
418
7f110ff9
LP
419 p = new0(SocketPort, 1);
420 if (!p)
74051b9b 421 return log_oom();
916abb21 422
74051b9b 423 if (ltype != SOCKET_SOCKET) {
2f4d31c1 424 _cleanup_free_ char *k = NULL;
916abb21 425
2f4d31c1 426 r = unit_full_printf(UNIT(s), rvalue, &k);
19f6d710 427 if (r < 0) {
063c4b1a 428 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f 429 return 0;
916abb21
LP
430 }
431
2f4d31c1
YW
432 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
433 if (r < 0)
434 return 0;
435
4a66b5c9
LP
436 if (ltype == SOCKET_FIFO) {
437 r = patch_var_run(unit, filename, line, lvalue, &k);
438 if (r < 0)
439 return r;
440 }
441
2f4d31c1
YW
442 free_and_replace(p->path, k);
443 p->type = ltype;
916abb21 444
7a22745a 445 } else if (streq(lvalue, "ListenNetlink")) {
74051b9b 446 _cleanup_free_ char *k = NULL;
1fd45a90 447
19f6d710 448 r = unit_full_printf(UNIT(s), rvalue, &k);
12ca818f 449 if (r < 0) {
063c4b1a 450 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f
LP
451 return 0;
452 }
7a22745a 453
12ca818f 454 r = socket_address_parse_netlink(&p->address, k);
1fd45a90 455 if (r < 0) {
063c4b1a 456 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k);
7a22745a
LP
457 return 0;
458 }
459
2f4d31c1
YW
460 p->type = SOCKET_SOCKET;
461
542563ba 462 } else {
74051b9b 463 _cleanup_free_ char *k = NULL;
1fd45a90 464
19f6d710 465 r = unit_full_printf(UNIT(s), rvalue, &k);
12ca818f 466 if (r < 0) {
063c4b1a 467 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f
LP
468 return 0;
469 }
542563ba 470
4a66b5c9
LP
471 if (k[0] == '/') { /* Only for AF_UNIX file system sockets… */
472 r = patch_var_run(unit, filename, line, lvalue, &k);
473 if (r < 0)
474 return r;
475 }
476
12ca818f 477 r = socket_address_parse_and_warn(&p->address, k);
1fd45a90 478 if (r < 0) {
f847b8b7 479 if (r != -EAFNOSUPPORT)
063c4b1a 480 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k);
c0b34696 481 return 0;
542563ba
LP
482 }
483
484 if (streq(lvalue, "ListenStream"))
485 p->address.type = SOCK_STREAM;
486 else if (streq(lvalue, "ListenDatagram"))
487 p->address.type = SOCK_DGRAM;
488 else {
489 assert(streq(lvalue, "ListenSequentialPacket"));
490 p->address.type = SOCK_SEQPACKET;
491 }
492
493 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
12ca818f 494 log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
c0b34696 495 return 0;
542563ba 496 }
2f4d31c1
YW
497
498 p->type = SOCKET_SOCKET;
16354eff
LP
499 }
500
542563ba 501 p->fd = -1;
15087cdb
PS
502 p->auxiliary_fds = NULL;
503 p->n_auxiliary_fds = 0;
2e41a51e 504 p->socket = s;
49f91047 505
533f8a67
YW
506 LIST_FIND_TAIL(port, s->ports, tail);
507 LIST_INSERT_AFTER(port, s->ports, tail, p);
508
b1389b0d 509 p = NULL;
542563ba 510
16354eff 511 return 0;
42f4e3c4
LP
512}
513
41bf0590
LP
514int config_parse_exec_nice(
515 const char *unit,
516 const char *filename,
517 unsigned line,
518 const char *section,
519 unsigned section_line,
520 const char *lvalue,
521 int ltype,
522 const char *rvalue,
523 void *data,
524 void *userdata) {
034c6ed7 525
fb33a393 526 ExecContext *c = data;
e8e581bf 527 int priority, r;
034c6ed7
LP
528
529 assert(filename);
530 assert(lvalue);
531 assert(rvalue);
532 assert(data);
533
de5e6038
YW
534 if (isempty(rvalue)) {
535 c->nice_set = false;
536 return 0;
537 }
538
41bf0590 539 r = parse_nice(rvalue, &priority);
e8e581bf 540 if (r < 0) {
41bf0590
LP
541 if (r == -ERANGE)
542 log_syntax(unit, LOG_ERR, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue);
543 else
063c4b1a 544 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority '%s', ignoring: %m", rvalue);
c0b34696 545 return 0;
034c6ed7
LP
546 }
547
fb33a393 548 c->nice = priority;
71155933 549 c->nice_set = true;
fb33a393 550
034c6ed7
LP
551 return 0;
552}
553
e9eb2c02
LP
554int config_parse_exec_oom_score_adjust(
555 const char* unit,
556 const char *filename,
557 unsigned line,
558 const char *section,
559 unsigned section_line,
560 const char *lvalue,
561 int ltype,
562 const char *rvalue,
563 void *data,
564 void *userdata) {
034c6ed7 565
fb33a393 566 ExecContext *c = data;
e8e581bf 567 int oa, r;
034c6ed7
LP
568
569 assert(filename);
570 assert(lvalue);
571 assert(rvalue);
572 assert(data);
573
e9eb2c02
LP
574 if (isempty(rvalue)) {
575 c->oom_score_adjust_set = false;
c0b34696 576 return 0;
034c6ed7
LP
577 }
578
e9eb2c02 579 r = parse_oom_score_adjust(rvalue, &oa);
e9eb2c02 580 if (r < 0) {
063c4b1a
YW
581 if (r == -ERANGE)
582 log_syntax(unit, LOG_ERR, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue);
583 else
584 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value '%s', ignoring: %m", rvalue);
c0b34696 585 return 0;
034c6ed7
LP
586 }
587
dd6c17b1
LP
588 c->oom_score_adjust = oa;
589 c->oom_score_adjust_set = true;
fb33a393 590
034c6ed7
LP
591 return 0;
592}
593
527b7a42
LP
594int config_parse_exec(
595 const char *unit,
596 const char *filename,
597 unsigned line,
598 const char *section,
599 unsigned section_line,
600 const char *lvalue,
601 int ltype,
602 const char *rvalue,
603 void *data,
604 void *userdata) {
034c6ed7 605
46a0d98a 606 ExecCommand **e = data;
47538b76 607 const Unit *u = userdata;
46a0d98a
FB
608 const char *p;
609 bool semicolon;
7f110ff9 610 int r;
034c6ed7
LP
611
612 assert(filename);
613 assert(lvalue);
614 assert(rvalue);
61e5d8ed 615 assert(e);
034c6ed7 616
74051b9b 617 e += ltype;
c83f1f30 618
74051b9b
LP
619 if (isempty(rvalue)) {
620 /* An empty assignment resets the list */
f1acf85a 621 *e = exec_command_free_list(*e);
74051b9b
LP
622 return 0;
623 }
624
bd1b973f 625 p = rvalue;
46a0d98a 626 do {
dea7b6b0 627 _cleanup_free_ char *path = NULL, *firstword = NULL;
165a31c0
LP
628 ExecCommandFlags flags = 0;
629 bool ignore = false, separate_argv0 = false;
dea7b6b0 630 _cleanup_free_ ExecCommand *nce = NULL;
46a0d98a
FB
631 _cleanup_strv_free_ char **n = NULL;
632 size_t nlen = 0, nbufsize = 0;
5125e762 633 const char *f;
6c666e26 634
46a0d98a
FB
635 semicolon = false;
636
4ec85141 637 r = extract_first_word_and_warn(&p, &firstword, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
46a0d98a
FB
638 if (r <= 0)
639 return 0;
6c666e26 640
46a0d98a 641 f = firstword;
007f48bb 642 for (;;) {
165a31c0
LP
643 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
644 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
7ca69792
AZ
645 * argv[0]; if it's prefixed with :, we will not do environment variable substitution;
646 * if it's prefixed with +, it will be run with full privileges and no sandboxing; if
165a31c0
LP
647 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
648 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
649 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
650 * other sandboxing, with some special exceptions for changing UID.
651 *
652 * The idea is that '!!' may be used to write services that can take benefit of systemd's
653 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
654 * privilege dropping within the daemon if the kernel does not offer that. */
655
656 if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE)) {
657 flags |= EXEC_COMMAND_IGNORE_FAILURE;
46a0d98a 658 ignore = true;
165a31c0 659 } else if (*f == '@' && !separate_argv0)
46a0d98a 660 separate_argv0 = true;
7ca69792
AZ
661 else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
662 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
165a31c0
LP
663 else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
664 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
665 else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
666 flags |= EXEC_COMMAND_NO_SETUID;
667 else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))) {
668 flags &= ~EXEC_COMMAND_NO_SETUID;
669 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
670 } else
46a0d98a 671 break;
313cefa1 672 f++;
61e5d8ed 673 }
46a0d98a 674
5125e762
LP
675 r = unit_full_printf(u, f, &path);
676 if (r < 0) {
bb28e684 677 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 678 "Failed to resolve unit specifiers in '%s'%s: %m",
bb28e684
ZJS
679 f, ignore ? ", ignoring" : "");
680 return ignore ? 0 : -ENOEXEC;
5125e762
LP
681 }
682
683 if (isempty(path)) {
46a0d98a 684 /* First word is either "-" or "@" with no command. */
bb28e684 685 log_syntax(unit, LOG_ERR, filename, line, 0,
063c4b1a 686 "Empty path in command line%s: '%s'",
bb28e684
ZJS
687 ignore ? ", ignoring" : "", rvalue);
688 return ignore ? 0 : -ENOEXEC;
b2fadec6 689 }
5125e762 690 if (!string_is_safe(path)) {
bb28e684 691 log_syntax(unit, LOG_ERR, filename, line, 0,
5008da1e
ZJS
692 "Executable name contains special characters%s: %s",
693 ignore ? ", ignoring" : "", path);
bb28e684 694 return ignore ? 0 : -ENOEXEC;
46a0d98a 695 }
5125e762 696 if (endswith(path, "/")) {
bb28e684
ZJS
697 log_syntax(unit, LOG_ERR, filename, line, 0,
698 "Executable path specifies a directory%s: %s",
5008da1e 699 ignore ? ", ignoring" : "", path);
bb28e684 700 return ignore ? 0 : -ENOEXEC;
46a0d98a 701 }
61e5d8ed 702
5008da1e
ZJS
703 if (!path_is_absolute(path)) {
704 const char *prefix;
705 bool found = false;
706
707 if (!filename_is_valid(path)) {
708 log_syntax(unit, LOG_ERR, filename, line, 0,
709 "Neither a valid executable name nor an absolute path%s: %s",
710 ignore ? ", ignoring" : "", path);
711 return ignore ? 0 : -ENOEXEC;
712 }
713
714 /* Resolve a single-component name to a full path */
715 NULSTR_FOREACH(prefix, DEFAULT_PATH_NULSTR) {
716 _cleanup_free_ char *fullpath = NULL;
717
657ee2d8 718 fullpath = path_join(prefix, path);
5008da1e
ZJS
719 if (!fullpath)
720 return log_oom();
721
722 if (access(fullpath, F_OK) >= 0) {
723 free_and_replace(path, fullpath);
724 found = true;
725 break;
726 }
727 }
728
729 if (!found) {
730 log_syntax(unit, LOG_ERR, filename, line, 0,
731 "Executable \"%s\" not found in path \"%s\"%s",
732 path, DEFAULT_PATH, ignore ? ", ignoring" : "");
733 return ignore ? 0 : -ENOEXEC;
734 }
735 }
736
46a0d98a 737 if (!separate_argv0) {
5125e762
LP
738 char *w = NULL;
739
46a0d98a
FB
740 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
741 return log_oom();
5125e762
LP
742
743 w = strdup(path);
744 if (!w)
46a0d98a 745 return log_oom();
5125e762 746 n[nlen++] = w;
46a0d98a
FB
747 n[nlen] = NULL;
748 }
7f110ff9 749
858d36c1 750 path_simplify(path, false);
46a0d98a 751
4b1c1753 752 while (!isempty(p)) {
5125e762 753 _cleanup_free_ char *word = NULL, *resolved = NULL;
46a0d98a
FB
754
755 /* Check explicitly for an unquoted semicolon as
756 * command separator token. */
757 if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
313cefa1 758 p++;
46a0d98a
FB
759 p += strspn(p, WHITESPACE);
760 semicolon = true;
761 break;
c8539536 762 }
7f110ff9 763
5125e762
LP
764 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
765 * extract_first_word() would return the same for all of those. */
46a0d98a 766 if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
5125e762
LP
767 char *w;
768
46a0d98a
FB
769 p += 2;
770 p += strspn(p, WHITESPACE);
5125e762 771
46a0d98a
FB
772 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
773 return log_oom();
5125e762
LP
774
775 w = strdup(";");
776 if (!w)
46a0d98a 777 return log_oom();
5125e762 778 n[nlen++] = w;
46a0d98a
FB
779 n[nlen] = NULL;
780 continue;
61e5d8ed 781 }
c8539536 782
4ec85141 783 r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
46a0d98a
FB
784 if (r == 0)
785 break;
5125e762 786 if (r < 0)
bb28e684 787 return ignore ? 0 : -ENOEXEC;
5125e762
LP
788
789 r = unit_full_printf(u, word, &resolved);
790 if (r < 0) {
bb28e684 791 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 792 "Failed to resolve unit specifiers in %s%s: %m",
bb28e684
ZJS
793 word, ignore ? ", ignoring" : "");
794 return ignore ? 0 : -ENOEXEC;
5125e762 795 }
46a0d98a
FB
796
797 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
798 return log_oom();
1cc6c93a
YW
799
800 n[nlen++] = TAKE_PTR(resolved);
46a0d98a 801 n[nlen] = NULL;
61e5d8ed
LP
802 }
803
46a0d98a 804 if (!n || !n[0]) {
bb28e684
ZJS
805 log_syntax(unit, LOG_ERR, filename, line, 0,
806 "Empty executable name or zeroeth argument%s: %s",
807 ignore ? ", ignoring" : "", rvalue);
808 return ignore ? 0 : -ENOEXEC;
7f110ff9 809 }
6c666e26 810
7f110ff9 811 nce = new0(ExecCommand, 1);
46a0d98a
FB
812 if (!nce)
813 return log_oom();
61e5d8ed 814
1cc6c93a
YW
815 nce->argv = TAKE_PTR(n);
816 nce->path = TAKE_PTR(path);
165a31c0 817 nce->flags = flags;
034c6ed7 818
61e5d8ed 819 exec_command_append_list(e, nce);
01f78473 820
46a0d98a 821 /* Do not _cleanup_free_ these. */
46a0d98a 822 nce = NULL;
034c6ed7 823
46a0d98a
FB
824 rvalue = p;
825 } while (semicolon);
034c6ed7 826
46a0d98a 827 return 0;
034c6ed7
LP
828}
829
d31645ad
LP
830int config_parse_socket_bindtodevice(
831 const char* unit,
832 const char *filename,
833 unsigned line,
834 const char *section,
835 unsigned section_line,
836 const char *lvalue,
837 int ltype,
838 const char *rvalue,
839 void *data,
840 void *userdata) {
acbb0225
LP
841
842 Socket *s = data;
acbb0225
LP
843
844 assert(filename);
845 assert(lvalue);
846 assert(rvalue);
847 assert(data);
848
063c4b1a
YW
849 if (isempty(rvalue) || streq(rvalue, "*")) {
850 s->bind_to_device = mfree(s->bind_to_device);
851 return 0;
852 }
d31645ad 853
063c4b1a
YW
854 if (!ifname_valid(rvalue)) {
855 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid interface name, ignoring: %s", rvalue);
856 return 0;
857 }
acbb0225 858
3c381a67
YW
859 if (free_and_strdup(&s->bind_to_device, rvalue) < 0)
860 return log_oom();
acbb0225
LP
861
862 return 0;
863}
864
9bd6a50e
LP
865int config_parse_exec_input(
866 const char *unit,
867 const char *filename,
868 unsigned line,
869 const char *section,
870 unsigned section_line,
871 const char *lvalue,
872 int ltype,
873 const char *rvalue,
874 void *data,
875 void *userdata) {
52c239d7 876
52c239d7 877 ExecContext *c = data;
47538b76 878 const Unit *u = userdata;
2038c3f5
LP
879 const char *n;
880 ExecInput ei;
52c239d7
LB
881 int r;
882
883 assert(data);
884 assert(filename);
885 assert(line);
886 assert(rvalue);
887
2038c3f5
LP
888 n = startswith(rvalue, "fd:");
889 if (n) {
890 _cleanup_free_ char *resolved = NULL;
891
892 r = unit_full_printf(u, n, &resolved);
893 if (r < 0)
063c4b1a 894 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s': %m", n);
2038c3f5
LP
895
896 if (isempty(resolved))
897 resolved = mfree(resolved);
898 else if (!fdname_is_valid(resolved)) {
899 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved);
6f40aa45 900 return -ENOEXEC;
52c239d7 901 }
9bd6a50e 902
2038c3f5
LP
903 free_and_replace(c->stdio_fdname[STDIN_FILENO], resolved);
904
905 ei = EXEC_INPUT_NAMED_FD;
906
907 } else if ((n = startswith(rvalue, "file:"))) {
908 _cleanup_free_ char *resolved = NULL;
909
910 r = unit_full_printf(u, n, &resolved);
52c239d7 911 if (r < 0)
063c4b1a 912 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s': %m", n);
9bd6a50e 913
2f4d31c1
YW
914 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
915 if (r < 0)
6f40aa45 916 return -ENOEXEC;
2038c3f5
LP
917
918 free_and_replace(c->stdio_file[STDIN_FILENO], resolved);
919
920 ei = EXEC_INPUT_FILE;
9bd6a50e 921
52c239d7 922 } else {
9bd6a50e
LP
923 ei = exec_input_from_string(rvalue);
924 if (ei < 0) {
52c239d7 925 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse input specifier, ignoring: %s", rvalue);
9bd6a50e
LP
926 return 0;
927 }
52c239d7 928 }
9bd6a50e 929
2038c3f5 930 c->std_input = ei;
9bd6a50e 931 return 0;
52c239d7
LB
932}
933
08f3be7a
LP
934int config_parse_exec_input_text(
935 const char *unit,
936 const char *filename,
937 unsigned line,
938 const char *section,
939 unsigned section_line,
940 const char *lvalue,
941 int ltype,
942 const char *rvalue,
943 void *data,
944 void *userdata) {
945
946 _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
947 ExecContext *c = data;
47538b76 948 const Unit *u = userdata;
08f3be7a
LP
949 size_t sz;
950 void *p;
951 int r;
952
953 assert(data);
954 assert(filename);
955 assert(line);
956 assert(rvalue);
957
958 if (isempty(rvalue)) {
959 /* Reset if the empty string is assigned */
960 c->stdin_data = mfree(c->stdin_data);
961 c->stdin_data_size = 0;
52c239d7
LB
962 return 0;
963 }
08f3be7a
LP
964
965 r = cunescape(rvalue, 0, &unescaped);
2038c3f5 966 if (r < 0)
063c4b1a 967 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode C escaped text '%s': %m", rvalue);
08f3be7a
LP
968
969 r = unit_full_printf(u, unescaped, &resolved);
2038c3f5 970 if (r < 0)
063c4b1a 971 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s': %m", unescaped);
08f3be7a
LP
972
973 sz = strlen(resolved);
974 if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */
975 c->stdin_data_size + sz + 1 > EXEC_STDIN_DATA_MAX) {
2038c3f5
LP
976 log_syntax(unit, LOG_ERR, filename, line, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
977 return -E2BIG;
08f3be7a
LP
978 }
979
980 p = realloc(c->stdin_data, c->stdin_data_size + sz + 1);
981 if (!p)
982 return log_oom();
983
984 *((char*) mempcpy((char*) p + c->stdin_data_size, resolved, sz)) = '\n';
985
986 c->stdin_data = p;
987 c->stdin_data_size += sz + 1;
988
989 return 0;
52c239d7
LB
990}
991
08f3be7a
LP
992int config_parse_exec_input_data(
993 const char *unit,
994 const char *filename,
995 unsigned line,
996 const char *section,
997 unsigned section_line,
998 const char *lvalue,
999 int ltype,
1000 const char *rvalue,
1001 void *data,
1002 void *userdata) {
1003
08f3be7a
LP
1004 _cleanup_free_ void *p = NULL;
1005 ExecContext *c = data;
1006 size_t sz;
1007 void *q;
1008 int r;
1009
1010 assert(data);
1011 assert(filename);
1012 assert(line);
1013 assert(rvalue);
1014
1015 if (isempty(rvalue)) {
1016 /* Reset if the empty string is assigned */
1017 c->stdin_data = mfree(c->stdin_data);
1018 c->stdin_data_size = 0;
1019 return 0;
1020 }
1021
081f36d8 1022 r = unbase64mem(rvalue, (size_t) -1, &p, &sz);
2038c3f5 1023 if (r < 0)
081f36d8 1024 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode base64 data, ignoring: %s", rvalue);
08f3be7a
LP
1025
1026 assert(sz > 0);
1027
1028 if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */
1029 c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) {
2038c3f5
LP
1030 log_syntax(unit, LOG_ERR, filename, line, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
1031 return -E2BIG;
08f3be7a
LP
1032 }
1033
1034 q = realloc(c->stdin_data, c->stdin_data_size + sz);
1035 if (!q)
1036 return log_oom();
1037
1038 memcpy((uint8_t*) q + c->stdin_data_size, p, sz);
1039
1040 c->stdin_data = q;
1041 c->stdin_data_size += sz;
1042
1043 return 0;
1044}
1045
2038c3f5
LP
1046int config_parse_exec_output(
1047 const char *unit,
1048 const char *filename,
1049 unsigned line,
1050 const char *section,
1051 unsigned section_line,
1052 const char *lvalue,
1053 int ltype,
1054 const char *rvalue,
1055 void *data,
1056 void *userdata) {
1057
1058 _cleanup_free_ char *resolved = NULL;
1059 const char *n;
52c239d7 1060 ExecContext *c = data;
47538b76 1061 const Unit *u = userdata;
52c239d7 1062 ExecOutput eo;
52c239d7
LB
1063 int r;
1064
1065 assert(data);
1066 assert(filename);
1067 assert(line);
1068 assert(lvalue);
1069 assert(rvalue);
1070
2038c3f5
LP
1071 n = startswith(rvalue, "fd:");
1072 if (n) {
1073 r = unit_full_printf(u, n, &resolved);
1074 if (r < 0)
063c4b1a 1075 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n);
2038c3f5
LP
1076
1077 if (isempty(resolved))
1078 resolved = mfree(resolved);
1079 else if (!fdname_is_valid(resolved)) {
1080 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved);
6f40aa45 1081 return -ENOEXEC;
52c239d7 1082 }
2038c3f5 1083
52c239d7 1084 eo = EXEC_OUTPUT_NAMED_FD;
2038c3f5
LP
1085
1086 } else if ((n = startswith(rvalue, "file:"))) {
1087
1088 r = unit_full_printf(u, n, &resolved);
1089 if (r < 0)
063c4b1a 1090 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n);
2038c3f5 1091
2f4d31c1
YW
1092 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1093 if (r < 0)
6f40aa45 1094 return -ENOEXEC;
2038c3f5
LP
1095
1096 eo = EXEC_OUTPUT_FILE;
1097
566b7d23
ZD
1098 } else if ((n = startswith(rvalue, "append:"))) {
1099
1100 r = unit_full_printf(u, n, &resolved);
1101 if (r < 0)
1102 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n);
1103
1104 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1105 if (r < 0)
1106 return -ENOEXEC;
1107
1108 eo = EXEC_OUTPUT_FILE_APPEND;
52c239d7
LB
1109 } else {
1110 eo = exec_output_from_string(rvalue);
2038c3f5 1111 if (eo < 0) {
52c239d7
LB
1112 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output specifier, ignoring: %s", rvalue);
1113 return 0;
1114 }
1115 }
1116
1117 if (streq(lvalue, "StandardOutput")) {
2038c3f5
LP
1118 if (eo == EXEC_OUTPUT_NAMED_FD)
1119 free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved);
1120 else
1121 free_and_replace(c->stdio_file[STDOUT_FILENO], resolved);
1122
52c239d7 1123 c->std_output = eo;
2038c3f5 1124
52c239d7 1125 } else {
2038c3f5
LP
1126 assert(streq(lvalue, "StandardError"));
1127
1128 if (eo == EXEC_OUTPUT_NAMED_FD)
1129 free_and_replace(c->stdio_fdname[STDERR_FILENO], resolved);
1130 else
1131 free_and_replace(c->stdio_file[STDERR_FILENO], resolved);
1132
1133 c->std_error = eo;
52c239d7 1134 }
2038c3f5
LP
1135
1136 return 0;
52c239d7 1137}
87f0e418 1138
e8e581bf
ZJS
1139int config_parse_exec_io_class(const char *unit,
1140 const char *filename,
1141 unsigned line,
1142 const char *section,
71a61510 1143 unsigned section_line,
e8e581bf
ZJS
1144 const char *lvalue,
1145 int ltype,
1146 const char *rvalue,
1147 void *data,
1148 void *userdata) {
94f04347
LP
1149
1150 ExecContext *c = data;
1151 int x;
1152
1153 assert(filename);
1154 assert(lvalue);
1155 assert(rvalue);
1156 assert(data);
1157
617d253a
YW
1158 if (isempty(rvalue)) {
1159 c->ioprio_set = false;
1160 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
1161 return 0;
1162 }
1163
f8b69d1d
MS
1164 x = ioprio_class_from_string(rvalue);
1165 if (x < 0) {
12ca818f 1166 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
c0b34696 1167 return 0;
0d87eb42 1168 }
94f04347
LP
1169
1170 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
1171 c->ioprio_set = true;
1172
1173 return 0;
1174}
1175
e8e581bf
ZJS
1176int config_parse_exec_io_priority(const char *unit,
1177 const char *filename,
1178 unsigned line,
1179 const char *section,
71a61510 1180 unsigned section_line,
e8e581bf
ZJS
1181 const char *lvalue,
1182 int ltype,
1183 const char *rvalue,
1184 void *data,
1185 void *userdata) {
94f04347
LP
1186
1187 ExecContext *c = data;
e8e581bf 1188 int i, r;
94f04347
LP
1189
1190 assert(filename);
1191 assert(lvalue);
1192 assert(rvalue);
1193 assert(data);
1194
617d253a
YW
1195 if (isempty(rvalue)) {
1196 c->ioprio_set = false;
1197 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
1198 return 0;
1199 }
1200
7f452159
LP
1201 r = ioprio_parse_priority(rvalue, &i);
1202 if (r < 0) {
12ca818f 1203 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
c0b34696 1204 return 0;
071830ff
LP
1205 }
1206
94f04347
LP
1207 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
1208 c->ioprio_set = true;
1209
071830ff
LP
1210 return 0;
1211}
1212
e8e581bf
ZJS
1213int config_parse_exec_cpu_sched_policy(const char *unit,
1214 const char *filename,
1215 unsigned line,
1216 const char *section,
71a61510 1217 unsigned section_line,
e8e581bf
ZJS
1218 const char *lvalue,
1219 int ltype,
1220 const char *rvalue,
1221 void *data,
1222 void *userdata) {
9eba9da4 1223
94f04347
LP
1224 ExecContext *c = data;
1225 int x;
1226
1227 assert(filename);
1228 assert(lvalue);
1229 assert(rvalue);
1230 assert(data);
1231
b00e1a9e
YW
1232 if (isempty(rvalue)) {
1233 c->cpu_sched_set = false;
1234 c->cpu_sched_policy = SCHED_OTHER;
1235 c->cpu_sched_priority = 0;
1236 return 0;
1237 }
1238
f8b69d1d
MS
1239 x = sched_policy_from_string(rvalue);
1240 if (x < 0) {
12ca818f 1241 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 1242 return 0;
0d87eb42 1243 }
94f04347
LP
1244
1245 c->cpu_sched_policy = x;
bb112710
HHPF
1246 /* Moving to or from real-time policy? We need to adjust the priority */
1247 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
94f04347
LP
1248 c->cpu_sched_set = true;
1249
1250 return 0;
1251}
1252
b070c7c0
MS
1253int config_parse_numa_mask(const char *unit,
1254 const char *filename,
1255 unsigned line,
1256 const char *section,
1257 unsigned section_line,
1258 const char *lvalue,
1259 int ltype,
1260 const char *rvalue,
1261 void *data,
1262 void *userdata) {
1263 int r;
1264 NUMAPolicy *p = data;
1265
1266 assert(filename);
1267 assert(lvalue);
1268 assert(rvalue);
1269 assert(data);
1270
1271 r = parse_cpu_set_extend(rvalue, &p->nodes, true, unit, filename, line, lvalue);
1272 if (r < 0) {
1273 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse NUMA node mask, ignoring: %s", rvalue);
1274 return 0;
1275 }
1276
1277 return r;
1278}
1279
e8e581bf
ZJS
1280int config_parse_exec_cpu_sched_prio(const char *unit,
1281 const char *filename,
1282 unsigned line,
1283 const char *section,
71a61510 1284 unsigned section_line,
e8e581bf
ZJS
1285 const char *lvalue,
1286 int ltype,
1287 const char *rvalue,
1288 void *data,
1289 void *userdata) {
9eba9da4
LP
1290
1291 ExecContext *c = data;
e8e581bf 1292 int i, min, max, r;
9eba9da4
LP
1293
1294 assert(filename);
1295 assert(lvalue);
1296 assert(rvalue);
1297 assert(data);
1298
e8e581bf
ZJS
1299 r = safe_atoi(rvalue, &i);
1300 if (r < 0) {
063c4b1a 1301 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling priority, ignoring: %s", rvalue);
c0b34696 1302 return 0;
94f04347 1303 }
9eba9da4 1304
bb112710
HHPF
1305 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1306 min = sched_get_priority_min(c->cpu_sched_policy);
1307 max = sched_get_priority_max(c->cpu_sched_policy);
1308
1309 if (i < min || i > max) {
12ca818f 1310 log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
bb112710
HHPF
1311 return 0;
1312 }
1313
94f04347
LP
1314 c->cpu_sched_priority = i;
1315 c->cpu_sched_set = true;
1316
1317 return 0;
1318}
1319
e8e581bf
ZJS
1320int config_parse_exec_cpu_affinity(const char *unit,
1321 const char *filename,
1322 unsigned line,
1323 const char *section,
71a61510 1324 unsigned section_line,
e8e581bf
ZJS
1325 const char *lvalue,
1326 int ltype,
1327 const char *rvalue,
1328 void *data,
1329 void *userdata) {
94f04347
LP
1330
1331 ExecContext *c = data;
94f04347
LP
1332
1333 assert(filename);
1334 assert(lvalue);
1335 assert(rvalue);
1336 assert(data);
1337
0985c7c4 1338 return parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue);
94f04347
LP
1339}
1340
a103496c 1341int config_parse_capability_set(
65dce264
LP
1342 const char *unit,
1343 const char *filename,
1344 unsigned line,
1345 const char *section,
1346 unsigned section_line,
1347 const char *lvalue,
1348 int ltype,
1349 const char *rvalue,
1350 void *data,
1351 void *userdata) {
94f04347 1352
a103496c
IP
1353 uint64_t *capability_set = data;
1354 uint64_t sum = 0, initial = 0;
260abb78 1355 bool invert = false;
dd1f5bd0 1356 int r;
94f04347
LP
1357
1358 assert(filename);
1359 assert(lvalue);
1360 assert(rvalue);
1361 assert(data);
1362
260abb78
LP
1363 if (rvalue[0] == '~') {
1364 invert = true;
1365 rvalue++;
1366 }
1367
70d54d90 1368 if (streq(lvalue, "CapabilityBoundingSet"))
a103496c 1369 initial = CAP_ALL; /* initialized to all bits on */
755d4b67 1370 /* else "AmbientCapabilities" initialized to all bits off */
260abb78 1371
dd1f5bd0 1372 r = capability_set_from_string(rvalue, &sum);
dd1f5bd0 1373 if (r < 0) {
063c4b1a 1374 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue, rvalue);
dd1f5bd0 1375 return 0;
94f04347 1376 }
9eba9da4 1377
a103496c 1378 if (sum == 0 || *capability_set == initial)
c792ec2e
IP
1379 /* "", "~" or uninitialized data -> replace */
1380 *capability_set = invert ? ~sum : sum;
1381 else {
a103496c 1382 /* previous data -> merge */
c792ec2e
IP
1383 if (invert)
1384 *capability_set &= ~sum;
1385 else
1386 *capability_set |= sum;
1387 }
260abb78 1388
9eba9da4
LP
1389 return 0;
1390}
1391
5f8640fb
LP
1392int config_parse_exec_selinux_context(
1393 const char *unit,
1394 const char *filename,
1395 unsigned line,
1396 const char *section,
1397 unsigned section_line,
1398 const char *lvalue,
1399 int ltype,
1400 const char *rvalue,
1401 void *data,
1402 void *userdata) {
1403
1404 ExecContext *c = data;
47538b76 1405 const Unit *u = userdata;
5f8640fb
LP
1406 bool ignore;
1407 char *k;
1408 int r;
1409
1410 assert(filename);
1411 assert(lvalue);
1412 assert(rvalue);
1413 assert(data);
1414
1415 if (isempty(rvalue)) {
a1e58e8e 1416 c->selinux_context = mfree(c->selinux_context);
5f8640fb
LP
1417 c->selinux_context_ignore = false;
1418 return 0;
1419 }
1420
1421 if (rvalue[0] == '-') {
1422 ignore = true;
1423 rvalue++;
1424 } else
1425 ignore = false;
1426
18913df9 1427 r = unit_full_printf(u, rvalue, &k);
5f8640fb 1428 if (r < 0) {
bb28e684 1429 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a
YW
1430 "Failed to resolve unit specifiers in '%s'%s: %m",
1431 rvalue, ignore ? ", ignoring" : "");
bb28e684 1432 return ignore ? 0 : -ENOEXEC;
5f8640fb
LP
1433 }
1434
063c4b1a 1435 free_and_replace(c->selinux_context, k);
5f8640fb
LP
1436 c->selinux_context_ignore = ignore;
1437
1438 return 0;
1439}
1440
eef65bf3
MS
1441int config_parse_exec_apparmor_profile(
1442 const char *unit,
1443 const char *filename,
1444 unsigned line,
1445 const char *section,
1446 unsigned section_line,
1447 const char *lvalue,
1448 int ltype,
1449 const char *rvalue,
1450 void *data,
1451 void *userdata) {
1452
1453 ExecContext *c = data;
47538b76 1454 const Unit *u = userdata;
eef65bf3
MS
1455 bool ignore;
1456 char *k;
1457 int r;
1458
1459 assert(filename);
1460 assert(lvalue);
1461 assert(rvalue);
1462 assert(data);
1463
1464 if (isempty(rvalue)) {
a1e58e8e 1465 c->apparmor_profile = mfree(c->apparmor_profile);
eef65bf3
MS
1466 c->apparmor_profile_ignore = false;
1467 return 0;
1468 }
1469
1470 if (rvalue[0] == '-') {
1471 ignore = true;
1472 rvalue++;
1473 } else
1474 ignore = false;
1475
18913df9 1476 r = unit_full_printf(u, rvalue, &k);
eef65bf3 1477 if (r < 0) {
bb28e684 1478 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a
YW
1479 "Failed to resolve unit specifiers in '%s'%s: %m",
1480 rvalue, ignore ? ", ignoring" : "");
bb28e684 1481 return ignore ? 0 : -ENOEXEC;
eef65bf3
MS
1482 }
1483
063c4b1a 1484 free_and_replace(c->apparmor_profile, k);
eef65bf3
MS
1485 c->apparmor_profile_ignore = ignore;
1486
1487 return 0;
1488}
1489
2ca620c4
WC
1490int config_parse_exec_smack_process_label(
1491 const char *unit,
1492 const char *filename,
1493 unsigned line,
1494 const char *section,
1495 unsigned section_line,
1496 const char *lvalue,
1497 int ltype,
1498 const char *rvalue,
1499 void *data,
1500 void *userdata) {
1501
1502 ExecContext *c = data;
47538b76 1503 const Unit *u = userdata;
2ca620c4
WC
1504 bool ignore;
1505 char *k;
1506 int r;
1507
1508 assert(filename);
1509 assert(lvalue);
1510 assert(rvalue);
1511 assert(data);
1512
1513 if (isempty(rvalue)) {
a1e58e8e 1514 c->smack_process_label = mfree(c->smack_process_label);
2ca620c4
WC
1515 c->smack_process_label_ignore = false;
1516 return 0;
1517 }
1518
1519 if (rvalue[0] == '-') {
1520 ignore = true;
1521 rvalue++;
1522 } else
1523 ignore = false;
1524
18913df9 1525 r = unit_full_printf(u, rvalue, &k);
2ca620c4 1526 if (r < 0) {
bb28e684 1527 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a
YW
1528 "Failed to resolve unit specifiers in '%s'%s: %m",
1529 rvalue, ignore ? ", ignoring" : "");
bb28e684 1530 return ignore ? 0 : -ENOEXEC;
2ca620c4
WC
1531 }
1532
063c4b1a 1533 free_and_replace(c->smack_process_label, k);
2ca620c4
WC
1534 c->smack_process_label_ignore = ignore;
1535
1536 return 0;
1537}
1538
25a04ae5
LP
1539int config_parse_timer(
1540 const char *unit,
1541 const char *filename,
1542 unsigned line,
1543 const char *section,
1544 unsigned section_line,
1545 const char *lvalue,
1546 int ltype,
1547 const char *rvalue,
1548 void *data,
1549 void *userdata) {
871d7de4 1550
25a04ae5
LP
1551 _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
1552 _cleanup_free_ char *k = NULL;
47538b76 1553 const Unit *u = userdata;
871d7de4 1554 Timer *t = data;
2507992f 1555 usec_t usec = 0;
871d7de4 1556 TimerValue *v;
2507992f 1557 int r;
871d7de4
LP
1558
1559 assert(filename);
1560 assert(lvalue);
1561 assert(rvalue);
1562 assert(data);
1563
74051b9b
LP
1564 if (isempty(rvalue)) {
1565 /* Empty assignment resets list */
1566 timer_free_values(t);
1567 return 0;
1568 }
1569
2507992f
DC
1570 r = unit_full_printf(u, rvalue, &k);
1571 if (r < 0) {
063c4b1a 1572 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2507992f
DC
1573 return 0;
1574 }
1575
25a04ae5 1576 if (ltype == TIMER_CALENDAR) {
dc44c96d
LP
1577 r = calendar_spec_from_string(k, &c);
1578 if (r < 0) {
1579 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse calendar specification, ignoring: %s", k);
36697dc0
LP
1580 return 0;
1581 }
dc44c96d
LP
1582 } else {
1583 r = parse_sec(k, &usec);
1584 if (r < 0) {
1585 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse timer value, ignoring: %s", k);
36697dc0
LP
1586 return 0;
1587 }
dc44c96d 1588 }
871d7de4 1589
25a04ae5 1590 v = new(TimerValue, 1);
921b5987 1591 if (!v)
74051b9b 1592 return log_oom();
871d7de4 1593
25a04ae5
LP
1594 *v = (TimerValue) {
1595 .base = ltype,
1596 .value = usec,
1597 .calendar_spec = TAKE_PTR(c),
1598 };
871d7de4 1599
71fda00f 1600 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1601
1602 return 0;
1603}
1604
3ecaa09b
LP
1605int config_parse_trigger_unit(
1606 const char *unit,
1607 const char *filename,
1608 unsigned line,
1609 const char *section,
71a61510 1610 unsigned section_line,
3ecaa09b
LP
1611 const char *lvalue,
1612 int ltype,
1613 const char *rvalue,
1614 void *data,
1615 void *userdata) {
871d7de4 1616
74051b9b 1617 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1618 Unit *u = data;
1619 UnitType type;
1620 int r;
398ef8ba
LP
1621
1622 assert(filename);
1623 assert(lvalue);
1624 assert(rvalue);
1625 assert(data);
1626
eef85c4a 1627 if (!hashmap_isempty(u->dependencies[UNIT_TRIGGERS])) {
12ca818f 1628 log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
3ecaa09b
LP
1629 return 0;
1630 }
871d7de4 1631
19f6d710 1632 r = unit_name_printf(u, rvalue, &p);
12ca818f 1633 if (r < 0) {
063c4b1a 1634 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
12ca818f
LP
1635 return 0;
1636 }
74051b9b 1637
12ca818f 1638 type = unit_name_to_type(p);
3ecaa09b 1639 if (type < 0) {
12ca818f 1640 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1641 return 0;
871d7de4 1642 }
49219a1c
JR
1643 if (unit_has_name(u, p)) {
1644 log_syntax(unit, LOG_ERR, filename, line, 0, "Units cannot trigger themselves, ignoring: %s", rvalue);
3ecaa09b
LP
1645 return 0;
1646 }
1647
5a724170 1648 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, true, UNIT_DEPENDENCY_FILE);
57020a3a 1649 if (r < 0) {
12ca818f 1650 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
c0b34696 1651 return 0;
871d7de4
LP
1652 }
1653
1654 return 0;
1655}
1656
e8e581bf
ZJS
1657int config_parse_path_spec(const char *unit,
1658 const char *filename,
1659 unsigned line,
1660 const char *section,
71a61510 1661 unsigned section_line,
e8e581bf
ZJS
1662 const char *lvalue,
1663 int ltype,
1664 const char *rvalue,
1665 void *data,
1666 void *userdata) {
01f78473
LP
1667
1668 Path *p = data;
1669 PathSpec *s;
1670 PathType b;
7fd1b19b 1671 _cleanup_free_ char *k = NULL;
19f6d710 1672 int r;
01f78473
LP
1673
1674 assert(filename);
1675 assert(lvalue);
1676 assert(rvalue);
1677 assert(data);
1678
74051b9b
LP
1679 if (isempty(rvalue)) {
1680 /* Empty assignment clears list */
1681 path_free_specs(p);
1682 return 0;
1683 }
1684
93e4c84b
LP
1685 b = path_type_from_string(lvalue);
1686 if (b < 0) {
12ca818f 1687 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1688 return 0;
01f78473
LP
1689 }
1690
19f6d710
LP
1691 r = unit_full_printf(UNIT(p), rvalue, &k);
1692 if (r < 0) {
063c4b1a 1693 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
12ca818f 1694 return 0;
487060c2 1695 }
93e4c84b 1696
2f4d31c1
YW
1697 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1698 if (r < 0)
c0b34696 1699 return 0;
01f78473 1700
93e4c84b 1701 s = new0(PathSpec, 1);
543295ad 1702 if (!s)
93e4c84b 1703 return log_oom();
01f78473 1704
718db961 1705 s->unit = UNIT(p);
063c4b1a 1706 s->path = TAKE_PTR(k);
01f78473
LP
1707 s->type = b;
1708 s->inotify_fd = -1;
1709
71fda00f 1710 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
1711
1712 return 0;
1713}
1714
b02cb41c
LP
1715int config_parse_socket_service(
1716 const char *unit,
1717 const char *filename,
1718 unsigned line,
1719 const char *section,
1720 unsigned section_line,
1721 const char *lvalue,
1722 int ltype,
1723 const char *rvalue,
1724 void *data,
1725 void *userdata) {
d9ff321a 1726
4afd3348 1727 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
8dd4c05b 1728 _cleanup_free_ char *p = NULL;
d9ff321a 1729 Socket *s = data;
4ff77f66 1730 Unit *x;
8dd4c05b 1731 int r;
d9ff321a
LP
1732
1733 assert(filename);
1734 assert(lvalue);
1735 assert(rvalue);
1736 assert(data);
1737
19f6d710 1738 r = unit_name_printf(UNIT(s), rvalue, &p);
613b411c 1739 if (r < 0) {
063c4b1a 1740 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
bb28e684 1741 return -ENOEXEC;
613b411c 1742 }
74051b9b 1743
613b411c 1744 if (!endswith(p, ".service")) {
bb28e684
ZJS
1745 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service: %s", rvalue);
1746 return -ENOEXEC;
d9ff321a
LP
1747 }
1748
613b411c 1749 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
4ff77f66 1750 if (r < 0) {
bb28e684
ZJS
1751 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s: %s", rvalue, bus_error_message(&error, r));
1752 return -ENOEXEC;
d9ff321a
LP
1753 }
1754
7f7d01ed 1755 unit_ref_set(&s->service, UNIT(s), x);
4ff77f66 1756
d9ff321a
LP
1757 return 0;
1758}
1759
8dd4c05b
LP
1760int config_parse_fdname(
1761 const char *unit,
1762 const char *filename,
1763 unsigned line,
1764 const char *section,
1765 unsigned section_line,
1766 const char *lvalue,
1767 int ltype,
1768 const char *rvalue,
1769 void *data,
1770 void *userdata) {
1771
1772 _cleanup_free_ char *p = NULL;
1773 Socket *s = data;
1774 int r;
1775
1776 assert(filename);
1777 assert(lvalue);
1778 assert(rvalue);
1779 assert(data);
1780
1781 if (isempty(rvalue)) {
1782 s->fdname = mfree(s->fdname);
1783 return 0;
1784 }
1785
18913df9 1786 r = unit_full_printf(UNIT(s), rvalue, &p);
8dd4c05b 1787 if (r < 0) {
063c4b1a 1788 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
8dd4c05b
LP
1789 return 0;
1790 }
1791
1792 if (!fdname_is_valid(p)) {
1793 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
1794 return 0;
1795 }
1796
3b319885 1797 return free_and_replace(s->fdname, p);
8dd4c05b
LP
1798}
1799
b02cb41c
LP
1800int config_parse_service_sockets(
1801 const char *unit,
1802 const char *filename,
1803 unsigned line,
1804 const char *section,
1805 unsigned section_line,
1806 const char *lvalue,
1807 int ltype,
1808 const char *rvalue,
1809 void *data,
1810 void *userdata) {
f976f3f6
LP
1811
1812 Service *s = data;
7b2313f5 1813 const char *p;
b02cb41c 1814 int r;
f976f3f6
LP
1815
1816 assert(filename);
1817 assert(lvalue);
1818 assert(rvalue);
1819 assert(data);
1820
7b2313f5 1821 p = rvalue;
9ed794a3 1822 for (;;) {
6a0f3175 1823 _cleanup_free_ char *word = NULL, *k = NULL;
f976f3f6 1824
7b2313f5
SS
1825 r = extract_first_word(&p, &word, NULL, 0);
1826 if (r == 0)
1827 break;
1828 if (r == -ENOMEM)
74051b9b 1829 return log_oom();
7b2313f5
SS
1830 if (r < 0) {
1831 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
1832 break;
1833 }
f976f3f6 1834
7b2313f5 1835 r = unit_name_printf(UNIT(s), word, &k);
b02cb41c 1836 if (r < 0) {
063c4b1a 1837 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
b02cb41c
LP
1838 continue;
1839 }
57020a3a 1840
b02cb41c 1841 if (!endswith(k, ".socket")) {
12ca818f 1842 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
f976f3f6
LP
1843 continue;
1844 }
1845
5a724170 1846 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, true, UNIT_DEPENDENCY_FILE);
57020a3a 1847 if (r < 0)
b02cb41c 1848 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6 1849
35d8c19a 1850 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, true, UNIT_DEPENDENCY_FILE);
57020a3a 1851 if (r < 0)
b02cb41c 1852 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6
LP
1853 }
1854
1855 return 0;
1856}
1857
b02cb41c
LP
1858int config_parse_bus_name(
1859 const char *unit,
1860 const char *filename,
1861 unsigned line,
1862 const char *section,
1863 unsigned section_line,
1864 const char *lvalue,
1865 int ltype,
1866 const char *rvalue,
1867 void *data,
1868 void *userdata) {
1869
1870 _cleanup_free_ char *k = NULL;
47538b76 1871 const Unit *u = userdata;
b02cb41c
LP
1872 int r;
1873
1874 assert(filename);
1875 assert(lvalue);
1876 assert(rvalue);
1877 assert(u);
1878
1879 r = unit_full_printf(u, rvalue, &k);
1880 if (r < 0) {
063c4b1a 1881 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
b02cb41c
LP
1882 return 0;
1883 }
1884
1885 if (!service_name_is_valid(k)) {
063c4b1a 1886 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name, ignoring: %s", k);
b02cb41c
LP
1887 return 0;
1888 }
1889
1890 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
1891}
1892
aad41f08
LP
1893int config_parse_service_timeout(
1894 const char *unit,
1895 const char *filename,
1896 unsigned line,
1897 const char *section,
1898 unsigned section_line,
1899 const char *lvalue,
1900 int ltype,
1901 const char *rvalue,
1902 void *data,
1903 void *userdata) {
98709151
LN
1904
1905 Service *s = userdata;
aad41f08 1906 usec_t usec;
98709151
LN
1907 int r;
1908
1909 assert(filename);
1910 assert(lvalue);
1911 assert(rvalue);
1912 assert(s);
1913
6c58305a 1914 /* This is called for two cases: TimeoutSec= and TimeoutStartSec=. */
98709151 1915
fb27be3f
YW
1916 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1917 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1918 * all other timeouts. */
1919 r = parse_sec_fix_0(rvalue, &usec);
aad41f08
LP
1920 if (r < 0) {
1921 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1922 return 0;
1923 }
d568a335 1924
6c58305a
YW
1925 s->start_timeout_defined = true;
1926 s->timeout_start_usec = usec;
36c16a7c 1927
6c58305a 1928 if (streq(lvalue, "TimeoutSec"))
aad41f08 1929 s->timeout_stop_usec = usec;
36c16a7c 1930
d568a335 1931 return 0;
98709151
LN
1932}
1933
a61d6874 1934int config_parse_timeout_abort(
dc653bf4
JK
1935 const char *unit,
1936 const char *filename,
1937 unsigned line,
1938 const char *section,
1939 unsigned section_line,
1940 const char *lvalue,
1941 int ltype,
1942 const char *rvalue,
1943 void *data,
1944 void *userdata) {
1945
a61d6874 1946 usec_t *ret = data;
dc653bf4
JK
1947 int r;
1948
1949 assert(filename);
1950 assert(lvalue);
1951 assert(rvalue);
a61d6874
ZJS
1952 assert(ret);
1953
1954 /* Note: apart from setting the arg, this returns an extra bit of information in the return value. */
dc653bf4 1955
dc653bf4 1956 if (isempty(rvalue)) {
a61d6874
ZJS
1957 *ret = 0;
1958 return 0; /* "not set" */
dc653bf4
JK
1959 }
1960
a61d6874
ZJS
1961 r = parse_sec(rvalue, ret);
1962 if (r < 0)
1963 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= setting, ignoring: %s", lvalue, rvalue);
1964
1965 return 1; /* "set" */
1966}
1967
1968int config_parse_service_timeout_abort(
1969 const char *unit,
1970 const char *filename,
1971 unsigned line,
1972 const char *section,
1973 unsigned section_line,
1974 const char *lvalue,
1975 int ltype,
1976 const char *rvalue,
1977 void *data,
1978 void *userdata) {
1979
1980 Service *s = userdata;
1981 int r;
dc653bf4 1982
a61d6874
ZJS
1983 assert(s);
1984
1985 r = config_parse_timeout_abort(unit, filename, line, section, section_line, lvalue, ltype, rvalue,
1986 &s->timeout_abort_usec, s);
1987 if (r >= 0)
1988 s->timeout_abort_set = r;
dc653bf4
JK
1989 return 0;
1990}
1991
89beff89
LP
1992int config_parse_sec_fix_0(
1993 const char *unit,
1994 const char *filename,
1995 unsigned line,
1996 const char *section,
1997 unsigned section_line,
1998 const char *lvalue,
1999 int ltype,
2000 const char *rvalue,
2001 void *data,
2002 void *userdata) {
2003
2004 usec_t *usec = data;
2005 int r;
2006
2007 assert(filename);
2008 assert(lvalue);
2009 assert(rvalue);
2010 assert(usec);
2011
2012 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2013 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2014 * timeout. */
2015
0004f698 2016 r = parse_sec_fix_0(rvalue, usec);
89beff89
LP
2017 if (r < 0) {
2018 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
2019 return 0;
2020 }
2021
89beff89
LP
2022 return 0;
2023}
2024
ae480f0b 2025int config_parse_user_group_compat(
66dccd8d
LP
2026 const char *unit,
2027 const char *filename,
2028 unsigned line,
2029 const char *section,
2030 unsigned section_line,
2031 const char *lvalue,
2032 int ltype,
2033 const char *rvalue,
2034 void *data,
2035 void *userdata) {
2036
063c4b1a
YW
2037 _cleanup_free_ char *k = NULL;
2038 char **user = data;
47538b76 2039 const Unit *u = userdata;
66dccd8d
LP
2040 int r;
2041
2042 assert(filename);
2043 assert(lvalue);
2044 assert(rvalue);
2045 assert(u);
2046
063c4b1a
YW
2047 if (isempty(rvalue)) {
2048 *user = mfree(*user);
2049 return 0;
2050 }
66dccd8d 2051
063c4b1a
YW
2052 r = unit_full_printf(u, rvalue, &k);
2053 if (r < 0) {
2054 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
2055 return -ENOEXEC;
66dccd8d
LP
2056 }
2057
ae480f0b 2058 if (!valid_user_group_name_or_id_compat(k)) {
063c4b1a
YW
2059 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2060 return -ENOEXEC;
2061 }
66dccd8d 2062
063c4b1a 2063 return free_and_replace(*user, k);
66dccd8d
LP
2064}
2065
ae480f0b 2066int config_parse_user_group_strv_compat(
66dccd8d
LP
2067 const char *unit,
2068 const char *filename,
2069 unsigned line,
2070 const char *section,
2071 unsigned section_line,
2072 const char *lvalue,
2073 int ltype,
2074 const char *rvalue,
2075 void *data,
2076 void *userdata) {
2077
2078 char ***users = data;
47538b76 2079 const Unit *u = userdata;
063c4b1a 2080 const char *p = rvalue;
66dccd8d
LP
2081 int r;
2082
2083 assert(filename);
2084 assert(lvalue);
2085 assert(rvalue);
2086 assert(u);
2087
2088 if (isempty(rvalue)) {
9f2d41a6 2089 *users = strv_free(*users);
66dccd8d
LP
2090 return 0;
2091 }
2092
66dccd8d
LP
2093 for (;;) {
2094 _cleanup_free_ char *word = NULL, *k = NULL;
2095
9a82ab95 2096 r = extract_first_word(&p, &word, NULL, 0);
66dccd8d
LP
2097 if (r == 0)
2098 break;
2099 if (r == -ENOMEM)
2100 return log_oom();
2101 if (r < 0) {
bb28e684
ZJS
2102 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue);
2103 return -ENOEXEC;
66dccd8d
LP
2104 }
2105
2106 r = unit_full_printf(u, word, &k);
2107 if (r < 0) {
bb28e684
ZJS
2108 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word);
2109 return -ENOEXEC;
66dccd8d
LP
2110 }
2111
ae480f0b 2112 if (!valid_user_group_name_or_id_compat(k)) {
bb28e684
ZJS
2113 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2114 return -ENOEXEC;
66dccd8d
LP
2115 }
2116
2117 r = strv_push(users, k);
2118 if (r < 0)
2119 return log_oom();
2120
2121 k = NULL;
2122 }
2123
2124 return 0;
2125}
2126
5f5d8eab
LP
2127int config_parse_working_directory(
2128 const char *unit,
2129 const char *filename,
2130 unsigned line,
2131 const char *section,
2132 unsigned section_line,
2133 const char *lvalue,
2134 int ltype,
2135 const char *rvalue,
2136 void *data,
2137 void *userdata) {
2138
2139 ExecContext *c = data;
47538b76 2140 const Unit *u = userdata;
5f5d8eab
LP
2141 bool missing_ok;
2142 int r;
2143
2144 assert(filename);
2145 assert(lvalue);
2146 assert(rvalue);
2147 assert(c);
2148 assert(u);
2149
862fcffd
YW
2150 if (isempty(rvalue)) {
2151 c->working_directory_home = false;
2152 c->working_directory = mfree(c->working_directory);
2153 return 0;
2154 }
2155
5f5d8eab
LP
2156 if (rvalue[0] == '-') {
2157 missing_ok = true;
2158 rvalue++;
2159 } else
2160 missing_ok = false;
2161
2162 if (streq(rvalue, "~")) {
2163 c->working_directory_home = true;
2164 c->working_directory = mfree(c->working_directory);
2165 } else {
2166 _cleanup_free_ char *k = NULL;
2167
2168 r = unit_full_printf(u, rvalue, &k);
2169 if (r < 0) {
bb28e684
ZJS
2170 log_syntax(unit, LOG_ERR, filename, line, r,
2171 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2172 rvalue, missing_ok ? ", ignoring" : "");
2173 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab
LP
2174 }
2175
2f4d31c1
YW
2176 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE | (missing_ok ? 0 : PATH_CHECK_FATAL), unit, filename, line, lvalue);
2177 if (r < 0)
bb28e684 2178 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab 2179
5f5d8eab 2180 c->working_directory_home = false;
bb28e684 2181 free_and_replace(c->working_directory, k);
5f5d8eab
LP
2182 }
2183
2184 c->working_directory_missing_ok = missing_ok;
2185 return 0;
2186}
2187
e8e581bf
ZJS
2188int config_parse_unit_env_file(const char *unit,
2189 const char *filename,
2190 unsigned line,
2191 const char *section,
71a61510 2192 unsigned section_line,
e8e581bf
ZJS
2193 const char *lvalue,
2194 int ltype,
2195 const char *rvalue,
2196 void *data,
2197 void *userdata) {
ddb26e18 2198
853b8397 2199 char ***env = data;
47538b76 2200 const Unit *u = userdata;
19f6d710 2201 _cleanup_free_ char *n = NULL;
853b8397 2202 int r;
ddb26e18
LP
2203
2204 assert(filename);
2205 assert(lvalue);
2206 assert(rvalue);
2207 assert(data);
2208
74051b9b
LP
2209 if (isempty(rvalue)) {
2210 /* Empty assignment frees the list */
6796073e 2211 *env = strv_free(*env);
74051b9b
LP
2212 return 0;
2213 }
2214
19f6d710 2215 r = unit_full_printf(u, rvalue, &n);
12ca818f 2216 if (r < 0) {
063c4b1a 2217 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f
LP
2218 return 0;
2219 }
8fef7659 2220
2f4d31c1
YW
2221 r = path_simplify_and_warn(n[0] == '-' ? n + 1 : n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2222 if (r < 0)
afe4bfe2 2223 return 0;
afe4bfe2 2224
2f4d31c1 2225 r = strv_push(env, n);
853b8397
LP
2226 if (r < 0)
2227 return log_oom();
2228
2f4d31c1
YW
2229 n = NULL;
2230
853b8397
LP
2231 return 0;
2232}
2233
f7f3f5c3
LP
2234int config_parse_environ(
2235 const char *unit,
2236 const char *filename,
2237 unsigned line,
2238 const char *section,
2239 unsigned section_line,
2240 const char *lvalue,
2241 int ltype,
2242 const char *rvalue,
2243 void *data,
2244 void *userdata) {
853b8397 2245
47538b76 2246 const Unit *u = userdata;
035fe294
ZJS
2247 char ***env = data;
2248 const char *p;
19f6d710 2249 int r;
853b8397
LP
2250
2251 assert(filename);
2252 assert(lvalue);
2253 assert(rvalue);
97d0e5f8 2254 assert(data);
853b8397
LP
2255
2256 if (isempty(rvalue)) {
2257 /* Empty assignment resets the list */
6796073e 2258 *env = strv_free(*env);
853b8397
LP
2259 return 0;
2260 }
2261
035fe294
ZJS
2262 for (p = rvalue;; ) {
2263 _cleanup_free_ char *word = NULL, *k = NULL;
035fe294 2264
4ec85141 2265 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
035fe294
ZJS
2266 if (r == 0)
2267 return 0;
2268 if (r == -ENOMEM)
2269 return log_oom();
12ca818f 2270 if (r < 0) {
035fe294
ZJS
2271 log_syntax(unit, LOG_WARNING, filename, line, r,
2272 "Invalid syntax, ignoring: %s", rvalue);
12ca818f
LP
2273 return 0;
2274 }
97d0e5f8 2275
035fe294
ZJS
2276 if (u) {
2277 r = unit_full_printf(u, word, &k);
2278 if (r < 0) {
2279 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 2280 "Failed to resolve unit specifiers in %s, ignoring: %m", word);
035fe294
ZJS
2281 continue;
2282 }
ae2a15bc
LP
2283 } else
2284 k = TAKE_PTR(word);
853b8397 2285
035fe294
ZJS
2286 if (!env_assignment_is_valid(k)) {
2287 log_syntax(unit, LOG_ERR, filename, line, 0,
2288 "Invalid environment assignment, ignoring: %s", k);
853b8397
LP
2289 continue;
2290 }
2291
54ac3494
ZJS
2292 r = strv_env_replace(env, k);
2293 if (r < 0)
853b8397 2294 return log_oom();
f7f3f5c3 2295
54ac3494 2296 k = NULL;
853b8397 2297 }
ddb26e18
LP
2298}
2299
00819cc1
LP
2300int config_parse_pass_environ(
2301 const char *unit,
2302 const char *filename,
2303 unsigned line,
2304 const char *section,
2305 unsigned section_line,
2306 const char *lvalue,
2307 int ltype,
2308 const char *rvalue,
2309 void *data,
2310 void *userdata) {
b4c14404 2311
b4c14404
FB
2312 _cleanup_strv_free_ char **n = NULL;
2313 size_t nlen = 0, nbufsize = 0;
41de9cc2 2314 char*** passenv = data;
063c4b1a 2315 const char *p = rvalue;
47538b76 2316 const Unit *u = userdata;
b4c14404
FB
2317 int r;
2318
2319 assert(filename);
2320 assert(lvalue);
2321 assert(rvalue);
2322 assert(data);
2323
2324 if (isempty(rvalue)) {
2325 /* Empty assignment resets the list */
2326 *passenv = strv_free(*passenv);
2327 return 0;
2328 }
2329
2330 for (;;) {
41de9cc2 2331 _cleanup_free_ char *word = NULL, *k = NULL;
b4c14404 2332
4ec85141 2333 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
b4c14404
FB
2334 if (r == 0)
2335 break;
2336 if (r == -ENOMEM)
2337 return log_oom();
2338 if (r < 0) {
2339 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 2340 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
b4c14404
FB
2341 break;
2342 }
2343
41de9cc2
LP
2344 if (u) {
2345 r = unit_full_printf(u, word, &k);
2346 if (r < 0) {
2347 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 2348 "Failed to resolve specifiers in %s, ignoring: %m", word);
41de9cc2
LP
2349 continue;
2350 }
ae2a15bc
LP
2351 } else
2352 k = TAKE_PTR(word);
41de9cc2
LP
2353
2354 if (!env_name_is_valid(k)) {
2355 log_syntax(unit, LOG_ERR, filename, line, 0,
2356 "Invalid environment name for %s, ignoring: %s", lvalue, k);
b4c14404
FB
2357 continue;
2358 }
2359
2360 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2361 return log_oom();
41de9cc2 2362
1cc6c93a 2363 n[nlen++] = TAKE_PTR(k);
b4c14404 2364 n[nlen] = NULL;
b4c14404
FB
2365 }
2366
2367 if (n) {
2368 r = strv_extend_strv(passenv, n, true);
2369 if (r < 0)
2370 return r;
2371 }
2372
2373 return 0;
2374}
2375
00819cc1
LP
2376int config_parse_unset_environ(
2377 const char *unit,
2378 const char *filename,
2379 unsigned line,
2380 const char *section,
2381 unsigned section_line,
2382 const char *lvalue,
2383 int ltype,
2384 const char *rvalue,
2385 void *data,
2386 void *userdata) {
2387
2388 _cleanup_strv_free_ char **n = NULL;
00819cc1
LP
2389 size_t nlen = 0, nbufsize = 0;
2390 char*** unsetenv = data;
063c4b1a 2391 const char *p = rvalue;
47538b76 2392 const Unit *u = userdata;
00819cc1
LP
2393 int r;
2394
2395 assert(filename);
2396 assert(lvalue);
2397 assert(rvalue);
2398 assert(data);
2399
2400 if (isempty(rvalue)) {
2401 /* Empty assignment resets the list */
2402 *unsetenv = strv_free(*unsetenv);
2403 return 0;
2404 }
2405
2406 for (;;) {
2407 _cleanup_free_ char *word = NULL, *k = NULL;
2408
4ec85141 2409 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
00819cc1
LP
2410 if (r == 0)
2411 break;
2412 if (r == -ENOMEM)
2413 return log_oom();
2414 if (r < 0) {
2415 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 2416 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
00819cc1
LP
2417 break;
2418 }
2419
2420 if (u) {
2421 r = unit_full_printf(u, word, &k);
2422 if (r < 0) {
2423 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 2424 "Failed to resolve unit specifiers in %s, ignoring: %m", word);
00819cc1
LP
2425 continue;
2426 }
ae2a15bc
LP
2427 } else
2428 k = TAKE_PTR(word);
00819cc1
LP
2429
2430 if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) {
2431 log_syntax(unit, LOG_ERR, filename, line, 0,
2432 "Invalid environment name or assignment %s, ignoring: %s", lvalue, k);
2433 continue;
2434 }
2435
2436 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2437 return log_oom();
2438
1cc6c93a 2439 n[nlen++] = TAKE_PTR(k);
00819cc1 2440 n[nlen] = NULL;
00819cc1
LP
2441 }
2442
2443 if (n) {
2444 r = strv_extend_strv(unsetenv, n, true);
2445 if (r < 0)
2446 return r;
2447 }
2448
2449 return 0;
2450}
2451
d3070fbd
LP
2452int config_parse_log_extra_fields(
2453 const char *unit,
2454 const char *filename,
2455 unsigned line,
2456 const char *section,
2457 unsigned section_line,
2458 const char *lvalue,
2459 int ltype,
2460 const char *rvalue,
2461 void *data,
2462 void *userdata) {
2463
2464 ExecContext *c = data;
47538b76 2465 const Unit *u = userdata;
063c4b1a 2466 const char *p = rvalue;
d3070fbd
LP
2467 int r;
2468
2469 assert(filename);
2470 assert(lvalue);
2471 assert(rvalue);
2472 assert(c);
2473
2474 if (isempty(rvalue)) {
2475 exec_context_free_log_extra_fields(c);
2476 return 0;
2477 }
2478
063c4b1a 2479 for (;;) {
d3070fbd
LP
2480 _cleanup_free_ char *word = NULL, *k = NULL;
2481 struct iovec *t;
2482 const char *eq;
2483
4ec85141 2484 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
d3070fbd 2485 if (r == 0)
063c4b1a 2486 return 0;
d3070fbd
LP
2487 if (r == -ENOMEM)
2488 return log_oom();
2489 if (r < 0) {
2490 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
2491 return 0;
2492 }
2493
2494 r = unit_full_printf(u, word, &k);
2495 if (r < 0) {
063c4b1a 2496 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word);
d3070fbd
LP
2497 continue;
2498 }
2499
2500 eq = strchr(k, '=');
2501 if (!eq) {
063c4b1a 2502 log_syntax(unit, LOG_ERR, filename, line, 0, "Log field lacks '=' character, ignoring: %s", k);
d3070fbd
LP
2503 continue;
2504 }
2505
2506 if (!journal_field_valid(k, eq-k, false)) {
063c4b1a 2507 log_syntax(unit, LOG_ERR, filename, line, 0, "Log field name is invalid, ignoring: %s", k);
d3070fbd
LP
2508 continue;
2509 }
2510
aa484f35 2511 t = reallocarray(c->log_extra_fields, c->n_log_extra_fields+1, sizeof(struct iovec));
d3070fbd
LP
2512 if (!t)
2513 return log_oom();
2514
2515 c->log_extra_fields = t;
2516 c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE_STRING(k);
2517
2518 k = NULL;
2519 }
d3070fbd
LP
2520}
2521
59fccdc5
LP
2522int config_parse_unit_condition_path(
2523 const char *unit,
2524 const char *filename,
2525 unsigned line,
2526 const char *section,
2527 unsigned section_line,
2528 const char *lvalue,
2529 int ltype,
2530 const char *rvalue,
2531 void *data,
2532 void *userdata) {
52661efd 2533
2fbe635a 2534 _cleanup_free_ char *p = NULL;
59fccdc5
LP
2535 Condition **list = data, *c;
2536 ConditionType t = ltype;
2537 bool trigger, negate;
47538b76 2538 const Unit *u = userdata;
19f6d710 2539 int r;
52661efd
LP
2540
2541 assert(filename);
2542 assert(lvalue);
2543 assert(rvalue);
2544 assert(data);
2545
74051b9b
LP
2546 if (isempty(rvalue)) {
2547 /* Empty assignment resets the list */
447021aa 2548 *list = condition_free_list(*list);
74051b9b
LP
2549 return 0;
2550 }
2551
ab7f148f
LP
2552 trigger = rvalue[0] == '|';
2553 if (trigger)
267632f0
LP
2554 rvalue++;
2555
ab7f148f
LP
2556 negate = rvalue[0] == '!';
2557 if (negate)
52661efd
LP
2558 rvalue++;
2559
19f6d710 2560 r = unit_full_printf(u, rvalue, &p);
59fccdc5 2561 if (r < 0) {
063c4b1a 2562 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
59fccdc5 2563 return 0;
19f6d710 2564 }
095b2d7a 2565
2f4d31c1
YW
2566 r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2567 if (r < 0)
52661efd 2568 return 0;
52661efd 2569
59fccdc5 2570 c = condition_new(t, p, trigger, negate);
ab7f148f 2571 if (!c)
74051b9b 2572 return log_oom();
52661efd 2573
59fccdc5 2574 LIST_PREPEND(conditions, *list, c);
52661efd
LP
2575 return 0;
2576}
2577
59fccdc5
LP
2578int config_parse_unit_condition_string(
2579 const char *unit,
2580 const char *filename,
2581 unsigned line,
2582 const char *section,
2583 unsigned section_line,
2584 const char *lvalue,
2585 int ltype,
2586 const char *rvalue,
2587 void *data,
2588 void *userdata) {
039655a4 2589
2fbe635a 2590 _cleanup_free_ char *s = NULL;
59fccdc5
LP
2591 Condition **list = data, *c;
2592 ConditionType t = ltype;
2593 bool trigger, negate;
47538b76 2594 const Unit *u = userdata;
19f6d710 2595 int r;
039655a4
LP
2596
2597 assert(filename);
2598 assert(lvalue);
2599 assert(rvalue);
2600 assert(data);
2601
74051b9b
LP
2602 if (isempty(rvalue)) {
2603 /* Empty assignment resets the list */
447021aa 2604 *list = condition_free_list(*list);
74051b9b
LP
2605 return 0;
2606 }
2607
9266f31e 2608 trigger = *rvalue == '|';
c0d6e764 2609 if (trigger)
9266f31e 2610 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
267632f0 2611
9266f31e 2612 negate = *rvalue == '!';
c0d6e764 2613 if (negate)
9266f31e 2614 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
039655a4 2615
19f6d710 2616 r = unit_full_printf(u, rvalue, &s);
59fccdc5 2617 if (r < 0) {
cae90de3
ZJS
2618 log_syntax(unit, LOG_ERR, filename, line, r,
2619 "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
59fccdc5 2620 return 0;
19f6d710 2621 }
095b2d7a 2622
59fccdc5 2623 c = condition_new(t, s, trigger, negate);
c0d6e764
LP
2624 if (!c)
2625 return log_oom();
039655a4 2626
59fccdc5 2627 LIST_PREPEND(conditions, *list, c);
039655a4
LP
2628 return 0;
2629}
2630
59fccdc5
LP
2631int config_parse_unit_condition_null(
2632 const char *unit,
2633 const char *filename,
2634 unsigned line,
2635 const char *section,
2636 unsigned section_line,
2637 const char *lvalue,
2638 int ltype,
2639 const char *rvalue,
2640 void *data,
2641 void *userdata) {
d257ddef 2642
59fccdc5 2643 Condition **list = data, *c;
267632f0 2644 bool trigger, negate;
d257ddef
LP
2645 int b;
2646
2647 assert(filename);
2648 assert(lvalue);
2649 assert(rvalue);
2650 assert(data);
2651
55dadc5c
LP
2652 log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is deprecated, please do not use.", lvalue);
2653
74051b9b
LP
2654 if (isempty(rvalue)) {
2655 /* Empty assignment resets the list */
447021aa 2656 *list = condition_free_list(*list);
74051b9b
LP
2657 return 0;
2658 }
2659
2660 trigger = rvalue[0] == '|';
2661 if (trigger)
267632f0
LP
2662 rvalue++;
2663
74051b9b
LP
2664 negate = rvalue[0] == '!';
2665 if (negate)
d257ddef
LP
2666 rvalue++;
2667
74051b9b
LP
2668 b = parse_boolean(rvalue);
2669 if (b < 0) {
12ca818f 2670 log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
d257ddef
LP
2671 return 0;
2672 }
2673
2674 if (!b)
2675 negate = !negate;
2676
74051b9b
LP
2677 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2678 if (!c)
2679 return log_oom();
d257ddef 2680
59fccdc5 2681 LIST_PREPEND(conditions, *list, c);
d257ddef
LP
2682 return 0;
2683}
2684
a57f7e2c
LP
2685int config_parse_unit_requires_mounts_for(
2686 const char *unit,
2687 const char *filename,
2688 unsigned line,
2689 const char *section,
71a61510 2690 unsigned section_line,
a57f7e2c
LP
2691 const char *lvalue,
2692 int ltype,
2693 const char *rvalue,
2694 void *data,
2695 void *userdata) {
7c8fa05c 2696
063c4b1a 2697 const char *p = rvalue;
7c8fa05c 2698 Unit *u = userdata;
035fe294 2699 int r;
7c8fa05c
LP
2700
2701 assert(filename);
2702 assert(lvalue);
2703 assert(rvalue);
2704 assert(data);
2705
063c4b1a 2706 for (;;) {
744bb5b1 2707 _cleanup_free_ char *word = NULL, *resolved = NULL;
a57f7e2c 2708
4ec85141 2709 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294
ZJS
2710 if (r == 0)
2711 return 0;
2712 if (r == -ENOMEM)
a57f7e2c 2713 return log_oom();
035fe294
ZJS
2714 if (r < 0) {
2715 log_syntax(unit, LOG_WARNING, filename, line, r,
2716 "Invalid syntax, ignoring: %s", rvalue);
2717 return 0;
2718 }
7c8fa05c 2719
744bb5b1
LP
2720 r = unit_full_printf(u, word, &resolved);
2721 if (r < 0) {
063c4b1a 2722 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
744bb5b1
LP
2723 continue;
2724 }
2725
2f4d31c1
YW
2726 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2727 if (r < 0)
2728 continue;
2729
eef85c4a 2730 r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE);
a57f7e2c 2731 if (r < 0) {
063c4b1a 2732 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount '%s', ignoring: %m", resolved);
a57f7e2c
LP
2733 continue;
2734 }
2735 }
7c8fa05c 2736}
9e372868 2737
e8e581bf
ZJS
2738int config_parse_documentation(const char *unit,
2739 const char *filename,
2740 unsigned line,
2741 const char *section,
71a61510 2742 unsigned section_line,
e8e581bf
ZJS
2743 const char *lvalue,
2744 int ltype,
2745 const char *rvalue,
2746 void *data,
2747 void *userdata) {
49dbfa7b
LP
2748
2749 Unit *u = userdata;
2750 int r;
2751 char **a, **b;
2752
2753 assert(filename);
2754 assert(lvalue);
2755 assert(rvalue);
2756 assert(u);
2757
74051b9b
LP
2758 if (isempty(rvalue)) {
2759 /* Empty assignment resets the list */
6796073e 2760 u->documentation = strv_free(u->documentation);
74051b9b
LP
2761 return 0;
2762 }
2763
71a61510 2764 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 2765 rvalue, data, userdata);
49dbfa7b
LP
2766 if (r < 0)
2767 return r;
2768
2769 for (a = b = u->documentation; a && *a; a++) {
2770
a2e03378 2771 if (documentation_url_is_valid(*a))
49dbfa7b
LP
2772 *(b++) = *a;
2773 else {
12ca818f 2774 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
2775 free(*a);
2776 }
2777 }
f6d2d421
ZJS
2778 if (b)
2779 *b = NULL;
49dbfa7b
LP
2780
2781 return r;
2782}
2783
349cc4a5 2784#if HAVE_SECCOMP
17df7223
LP
2785int config_parse_syscall_filter(
2786 const char *unit,
2787 const char *filename,
2788 unsigned line,
2789 const char *section,
2790 unsigned section_line,
2791 const char *lvalue,
2792 int ltype,
2793 const char *rvalue,
2794 void *data,
2795 void *userdata) {
2796
8351ceae 2797 ExecContext *c = data;
47538b76 2798 const Unit *u = userdata;
b5fb3789 2799 bool invert = false;
8130926d 2800 const char *p;
17df7223 2801 int r;
8351ceae
LP
2802
2803 assert(filename);
2804 assert(lvalue);
2805 assert(rvalue);
2806 assert(u);
2807
74051b9b
LP
2808 if (isempty(rvalue)) {
2809 /* Empty assignment resets the list */
8cfa775f 2810 c->syscall_filter = hashmap_free(c->syscall_filter);
17df7223 2811 c->syscall_whitelist = false;
74051b9b
LP
2812 return 0;
2813 }
2814
8351ceae
LP
2815 if (rvalue[0] == '~') {
2816 invert = true;
2817 rvalue++;
2818 }
2819
17df7223 2820 if (!c->syscall_filter) {
8cfa775f 2821 c->syscall_filter = hashmap_new(NULL);
17df7223
LP
2822 if (!c->syscall_filter)
2823 return log_oom();
2824
c0467cf3 2825 if (invert)
17df7223
LP
2826 /* Allow everything but the ones listed */
2827 c->syscall_whitelist = false;
c0467cf3 2828 else {
17df7223
LP
2829 /* Allow nothing but the ones listed */
2830 c->syscall_whitelist = true;
8351ceae 2831
17df7223 2832 /* Accept default syscalls if we are on a whitelist */
2f6b9110
LP
2833 r = seccomp_parse_syscall_filter(
2834 "@default", -1, c->syscall_filter,
58f6ab44
ZJS
2835 SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_WHITELIST,
2836 unit,
2837 NULL, 0);
201c1cc2
TM
2838 if (r < 0)
2839 return r;
c0467cf3 2840 }
8351ceae
LP
2841 }
2842
8130926d
LP
2843 p = rvalue;
2844 for (;;) {
8cfa775f
YW
2845 _cleanup_free_ char *word = NULL, *name = NULL;
2846 int num;
8351ceae 2847
8130926d
LP
2848 r = extract_first_word(&p, &word, NULL, 0);
2849 if (r == 0)
063c4b1a 2850 return 0;
8130926d 2851 if (r == -ENOMEM)
74051b9b 2852 return log_oom();
8130926d
LP
2853 if (r < 0) {
2854 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
063c4b1a 2855 return 0;
8130926d 2856 }
8351ceae 2857
8cfa775f
YW
2858 r = parse_syscall_and_errno(word, &name, &num);
2859 if (r < 0) {
2860 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse syscall:errno, ignoring: %s", word);
2861 continue;
2862 }
2863
58f6ab44 2864 r = seccomp_parse_syscall_filter(
acd142af
LP
2865 name, num, c->syscall_filter,
2866 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
2867 (invert ? SECCOMP_PARSE_INVERT : 0)|
2868 (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0),
2869 unit, filename, line);
201c1cc2
TM
2870 if (r < 0)
2871 return r;
c0467cf3 2872 }
17df7223
LP
2873}
2874
57183d11
LP
2875int config_parse_syscall_archs(
2876 const char *unit,
2877 const char *filename,
2878 unsigned line,
2879 const char *section,
2880 unsigned section_line,
2881 const char *lvalue,
2882 int ltype,
2883 const char *rvalue,
2884 void *data,
2885 void *userdata) {
2886
063c4b1a 2887 const char *p = rvalue;
d3b1c508 2888 Set **archs = data;
57183d11
LP
2889 int r;
2890
2891 if (isempty(rvalue)) {
525d3cc7 2892 *archs = set_free(*archs);
57183d11
LP
2893 return 0;
2894 }
2895
d5099efc 2896 r = set_ensure_allocated(archs, NULL);
57183d11
LP
2897 if (r < 0)
2898 return log_oom();
2899
063c4b1a 2900 for (;;) {
035fe294 2901 _cleanup_free_ char *word = NULL;
57183d11
LP
2902 uint32_t a;
2903
4ec85141 2904 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294
ZJS
2905 if (r == 0)
2906 return 0;
2907 if (r == -ENOMEM)
57183d11 2908 return log_oom();
035fe294
ZJS
2909 if (r < 0) {
2910 log_syntax(unit, LOG_WARNING, filename, line, r,
2911 "Invalid syntax, ignoring: %s", rvalue);
2912 return 0;
2913 }
57183d11 2914
035fe294 2915 r = seccomp_arch_from_string(word, &a);
57183d11 2916 if (r < 0) {
035fe294
ZJS
2917 log_syntax(unit, LOG_ERR, filename, line, r,
2918 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
57183d11
LP
2919 continue;
2920 }
2921
d3b1c508 2922 r = set_put(*archs, UINT32_TO_PTR(a + 1));
57183d11
LP
2923 if (r < 0)
2924 return log_oom();
2925 }
57183d11
LP
2926}
2927
17df7223
LP
2928int config_parse_syscall_errno(
2929 const char *unit,
2930 const char *filename,
2931 unsigned line,
2932 const char *section,
2933 unsigned section_line,
2934 const char *lvalue,
2935 int ltype,
2936 const char *rvalue,
2937 void *data,
2938 void *userdata) {
2939
2940 ExecContext *c = data;
2941 int e;
2942
2943 assert(filename);
2944 assert(lvalue);
2945 assert(rvalue);
2946
2947 if (isempty(rvalue)) {
2948 /* Empty assignment resets to KILL */
2949 c->syscall_errno = 0;
2950 return 0;
8351ceae
LP
2951 }
2952
3df90f24
YW
2953 e = parse_errno(rvalue);
2954 if (e <= 0) {
12ca818f 2955 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
17df7223
LP
2956 return 0;
2957 }
8351ceae 2958
17df7223 2959 c->syscall_errno = e;
8351ceae
LP
2960 return 0;
2961}
4298d0b5
LP
2962
2963int config_parse_address_families(
2964 const char *unit,
2965 const char *filename,
2966 unsigned line,
2967 const char *section,
2968 unsigned section_line,
2969 const char *lvalue,
2970 int ltype,
2971 const char *rvalue,
2972 void *data,
2973 void *userdata) {
2974
2975 ExecContext *c = data;
4298d0b5 2976 bool invert = false;
035fe294 2977 const char *p;
4298d0b5
LP
2978 int r;
2979
2980 assert(filename);
2981 assert(lvalue);
2982 assert(rvalue);
4298d0b5
LP
2983
2984 if (isempty(rvalue)) {
2985 /* Empty assignment resets the list */
525d3cc7 2986 c->address_families = set_free(c->address_families);
4298d0b5
LP
2987 c->address_families_whitelist = false;
2988 return 0;
2989 }
2990
2991 if (rvalue[0] == '~') {
2992 invert = true;
2993 rvalue++;
2994 }
2995
2996 if (!c->address_families) {
d5099efc 2997 c->address_families = set_new(NULL);
4298d0b5
LP
2998 if (!c->address_families)
2999 return log_oom();
3000
3001 c->address_families_whitelist = !invert;
3002 }
3003
035fe294
ZJS
3004 for (p = rvalue;;) {
3005 _cleanup_free_ char *word = NULL;
4298d0b5
LP
3006 int af;
3007
4ec85141 3008 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294
ZJS
3009 if (r == 0)
3010 return 0;
3011 if (r == -ENOMEM)
4298d0b5 3012 return log_oom();
035fe294
ZJS
3013 if (r < 0) {
3014 log_syntax(unit, LOG_WARNING, filename, line, r,
3015 "Invalid syntax, ignoring: %s", rvalue);
3016 return 0;
3017 }
4298d0b5 3018
035fe294 3019 af = af_from_name(word);
acf4d158
YW
3020 if (af < 0) {
3021 log_syntax(unit, LOG_ERR, filename, line, af,
063c4b1a 3022 "Failed to parse address family, ignoring: %s", word);
4298d0b5
LP
3023 continue;
3024 }
3025
3026 /* If we previously wanted to forbid an address family and now
035fe294 3027 * we want to allow it, then just remove it from the list.
4298d0b5
LP
3028 */
3029 if (!invert == c->address_families_whitelist) {
3030 r = set_put(c->address_families, INT_TO_PTR(af));
4298d0b5
LP
3031 if (r < 0)
3032 return log_oom();
3033 } else
3034 set_remove(c->address_families, INT_TO_PTR(af));
3035 }
4298d0b5 3036}
add00535
LP
3037
3038int config_parse_restrict_namespaces(
3039 const char *unit,
3040 const char *filename,
3041 unsigned line,
3042 const char *section,
3043 unsigned section_line,
3044 const char *lvalue,
3045 int ltype,
3046 const char *rvalue,
3047 void *data,
3048 void *userdata) {
3049
3050 ExecContext *c = data;
aa9d574d 3051 unsigned long flags;
add00535
LP
3052 bool invert = false;
3053 int r;
3054
3055 if (isempty(rvalue)) {
3056 /* Reset to the default. */
aa9d574d
YW
3057 c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
3058 return 0;
3059 }
3060
3061 /* Boolean parameter ignores the previous settings */
3062 r = parse_boolean(rvalue);
3063 if (r > 0) {
3064 c->restrict_namespaces = 0;
3065 return 0;
3066 } else if (r == 0) {
add00535
LP
3067 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3068 return 0;
3069 }
3070
3071 if (rvalue[0] == '~') {
3072 invert = true;
3073 rvalue++;
3074 }
3075
aa9d574d
YW
3076 /* Not a boolean argument, in this case it's a list of namespace types. */
3077 r = namespace_flags_from_string(rvalue, &flags);
3078 if (r < 0) {
3079 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
3080 return 0;
add00535
LP
3081 }
3082
aa9d574d
YW
3083 if (c->restrict_namespaces == NAMESPACE_FLAGS_INITIAL)
3084 /* Initial assignment. Just set the value. */
3085 c->restrict_namespaces = invert ? (~flags) & NAMESPACE_FLAGS_ALL : flags;
3086 else
3087 /* Merge the value with the previous one. */
3088 SET_FLAG(c->restrict_namespaces, flags, !invert);
add00535
LP
3089
3090 return 0;
3091}
c0467cf3 3092#endif
8351ceae 3093
a016b922
LP
3094int config_parse_unit_slice(
3095 const char *unit,
3096 const char *filename,
3097 unsigned line,
3098 const char *section,
71a61510 3099 unsigned section_line,
a016b922
LP
3100 const char *lvalue,
3101 int ltype,
3102 const char *rvalue,
3103 void *data,
3104 void *userdata) {
3105
063c4b1a 3106 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a016b922 3107 _cleanup_free_ char *k = NULL;
abc9fa1c 3108 Unit *u = userdata, *slice;
a016b922
LP
3109 int r;
3110
3111 assert(filename);
3112 assert(lvalue);
3113 assert(rvalue);
3114 assert(u);
3115
19f6d710 3116 r = unit_name_printf(u, rvalue, &k);
d79200e2 3117 if (r < 0) {
063c4b1a 3118 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
d79200e2 3119 return 0;
19f6d710 3120 }
a016b922 3121
063c4b1a 3122 r = manager_load_unit(u->manager, k, NULL, &error, &slice);
a016b922 3123 if (r < 0) {
063c4b1a 3124 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s, ignoring: %s", k, bus_error_message(&error, r));
a016b922
LP
3125 return 0;
3126 }
3127
d79200e2
LP
3128 r = unit_set_slice(u, slice);
3129 if (r < 0) {
063c4b1a 3130 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
a016b922
LP
3131 return 0;
3132 }
3133
a016b922
LP
3134 return 0;
3135}
3136
b2f8b02e
LP
3137int config_parse_cpu_quota(
3138 const char *unit,
3139 const char *filename,
3140 unsigned line,
3141 const char *section,
3142 unsigned section_line,
3143 const char *lvalue,
3144 int ltype,
3145 const char *rvalue,
3146 void *data,
3147 void *userdata) {
3148
3149 CGroupContext *c = data;
9184ca48 3150 int r;
b2f8b02e
LP
3151
3152 assert(filename);
3153 assert(lvalue);
3154 assert(rvalue);
3155
3156 if (isempty(rvalue)) {
3a43da28 3157 c->cpu_quota_per_sec_usec = USEC_INFINITY;
b2f8b02e
LP
3158 return 0;
3159 }
3160
f806dfd3 3161 r = parse_permille_unbounded(rvalue);
9184ca48 3162 if (r <= 0) {
063c4b1a 3163 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
9a054909 3164 return 0;
b2f8b02e
LP
3165 }
3166
f806dfd3 3167 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 1000U;
b2f8b02e
LP
3168 return 0;
3169}
3170
0b8d3075 3171int config_parse_allowed_cpus(
047f5d63
PH
3172 const char *unit,
3173 const char *filename,
3174 unsigned line,
3175 const char *section,
3176 unsigned section_line,
3177 const char *lvalue,
3178 int ltype,
3179 const char *rvalue,
3180 void *data,
3181 void *userdata) {
3182
3183 CGroupContext *c = data;
3184
3185 (void) parse_cpu_set_extend(rvalue, &c->cpuset_cpus, true, unit, filename, line, lvalue);
3186
3187 return 0;
3188}
3189
0b8d3075 3190int config_parse_allowed_mems(
047f5d63
PH
3191 const char *unit,
3192 const char *filename,
3193 unsigned line,
3194 const char *section,
3195 unsigned section_line,
3196 const char *lvalue,
3197 int ltype,
3198 const char *rvalue,
3199 void *data,
3200 void *userdata) {
3201
3202 CGroupContext *c = data;
3203
3204 (void) parse_cpu_set_extend(rvalue, &c->cpuset_mems, true, unit, filename, line, lvalue);
3205
3206 return 0;
3207}
3208
4ad49000
LP
3209int config_parse_memory_limit(
3210 const char *unit,
3211 const char *filename,
3212 unsigned line,
3213 const char *section,
71a61510 3214 unsigned section_line,
4ad49000
LP
3215 const char *lvalue,
3216 int ltype,
3217 const char *rvalue,
3218 void *data,
3219 void *userdata) {
3220
3221 CGroupContext *c = data;
da4d897e 3222 uint64_t bytes = CGROUP_LIMIT_MAX;
4ad49000
LP
3223 int r;
3224
e57c9ce1 3225 if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
875ae566 3226
f806dfd3 3227 r = parse_permille(rvalue);
875ae566
LP
3228 if (r < 0) {
3229 r = parse_size(rvalue, 1024, &bytes);
3230 if (r < 0) {
063c4b1a 3231 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid memory limit '%s', ignoring: %m", rvalue);
875ae566
LP
3232 return 0;
3233 }
3234 } else
f806dfd3 3235 bytes = physical_memory_scale(r, 1000U);
875ae566 3236
906bdbf5 3237 if (bytes >= UINT64_MAX ||
22bf131b 3238 (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
063c4b1a 3239 log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue);
da4d897e
TH
3240 return 0;
3241 }
4ad49000
LP
3242 }
3243
c52db42b
CD
3244 if (streq(lvalue, "DefaultMemoryLow")) {
3245 c->default_memory_low_set = true;
3246 if (isempty(rvalue))
3247 c->default_memory_low = CGROUP_LIMIT_MIN;
3248 else
3249 c->default_memory_low = bytes;
7ad5439e
CD
3250 } else if (streq(lvalue, "DefaultMemoryMin")) {
3251 c->default_memory_min_set = true;
3252 if (isempty(rvalue))
3253 c->default_memory_min = CGROUP_LIMIT_MIN;
3254 else
3255 c->default_memory_min = bytes;
3256 } else if (streq(lvalue, "MemoryMin")) {
48422635 3257 c->memory_min = bytes;
7ad5439e
CD
3258 c->memory_min_set = true;
3259 } else if (streq(lvalue, "MemoryLow")) {
da4d897e 3260 c->memory_low = bytes;
c52db42b
CD
3261 c->memory_low_set = true;
3262 } else if (streq(lvalue, "MemoryHigh"))
da4d897e
TH
3263 c->memory_high = bytes;
3264 else if (streq(lvalue, "MemoryMax"))
3265 c->memory_max = bytes;
96e131ea
WC
3266 else if (streq(lvalue, "MemorySwapMax"))
3267 c->memory_swap_max = bytes;
3268 else if (streq(lvalue, "MemoryLimit"))
da4d897e 3269 c->memory_limit = bytes;
96e131ea
WC
3270 else
3271 return -EINVAL;
4ad49000 3272
4ad49000
LP
3273 return 0;
3274}
3275
03a7b521
LP
3276int config_parse_tasks_max(
3277 const char *unit,
3278 const char *filename,
3279 unsigned line,
3280 const char *section,
3281 unsigned section_line,
3282 const char *lvalue,
3283 int ltype,
3284 const char *rvalue,
3285 void *data,
3286 void *userdata) {
3287
47538b76 3288 const Unit *u = userdata;
3a0f06c4
ZJS
3289 TasksMax *tasks_max = data;
3290 uint64_t v;
03a7b521
LP
3291 int r;
3292
f5058264 3293 if (isempty(rvalue)) {
3a0f06c4 3294 *tasks_max = u ? u->manager->default_tasks_max : TASKS_MAX_UNSET;
f5058264
TH
3295 return 0;
3296 }
3297
3298 if (streq(rvalue, "infinity")) {
3a0f06c4 3299 *tasks_max = TASKS_MAX_UNSET;
03a7b521
LP
3300 return 0;
3301 }
3302
f806dfd3 3303 r = parse_permille(rvalue);
3a0f06c4
ZJS
3304 if (r >= 0)
3305 *tasks_max = (TasksMax) { r, 1000U }; /* r‰ */
3306 else {
f5058264 3307 r = safe_atou64(rvalue, &v);
83f8e808 3308 if (r < 0) {
063c4b1a 3309 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid maximum tasks value '%s', ignoring: %m", rvalue);
83f8e808
LP
3310 return 0;
3311 }
83f8e808 3312
3a0f06c4
ZJS
3313 if (v <= 0 || v >= UINT64_MAX) {
3314 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
3315 return 0;
3316 }
3317
3318 *tasks_max = (TasksMax) { v };
03a7b521
LP
3319 }
3320
3321 return 0;
3322}
3323
02638280
LP
3324int config_parse_delegate(
3325 const char *unit,
3326 const char *filename,
3327 unsigned line,
3328 const char *section,
3329 unsigned section_line,
3330 const char *lvalue,
3331 int ltype,
3332 const char *rvalue,
3333 void *data,
3334 void *userdata) {
3335
3336 CGroupContext *c = data;
ecae73d7 3337 UnitType t;
02638280
LP
3338 int r;
3339
ecae73d7
ZJS
3340 t = unit_name_to_type(unit);
3341 assert(t != _UNIT_TYPE_INVALID);
3342
3343 if (!unit_vtable[t]->can_delegate) {
3344 log_syntax(unit, LOG_ERR, filename, line, 0, "Delegate= setting not supported for this unit type, ignoring.");
3345 return 0;
3346 }
3347
02638280
LP
3348 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3349 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3350 * mask to delegate. */
3351
1bdfc7b9 3352 if (isempty(rvalue)) {
d48013f8
YW
3353 /* An empty string resets controllers and set Delegate=yes. */
3354 c->delegate = true;
1bdfc7b9
YW
3355 c->delegate_controllers = 0;
3356 return 0;
3357 }
3358
02638280
LP
3359 r = parse_boolean(rvalue);
3360 if (r < 0) {
3361 const char *p = rvalue;
3362 CGroupMask mask = 0;
3363
3364 for (;;) {
3365 _cleanup_free_ char *word = NULL;
3366 CGroupController cc;
3367
4ec85141 3368 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
02638280
LP
3369 if (r == 0)
3370 break;
3371 if (r == -ENOMEM)
3372 return log_oom();
3373 if (r < 0) {
3374 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
ff1b8455 3375 return 0;
02638280
LP
3376 }
3377
3378 cc = cgroup_controller_from_string(word);
3379 if (cc < 0) {
063c4b1a 3380 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid controller name '%s', ignoring", word);
02638280
LP
3381 continue;
3382 }
3383
3384 mask |= CGROUP_CONTROLLER_TO_MASK(cc);
3385 }
3386
3387 c->delegate = true;
3388 c->delegate_controllers |= mask;
3389
3390 } else if (r > 0) {
3391 c->delegate = true;
3392 c->delegate_controllers = _CGROUP_MASK_ALL;
3393 } else {
3394 c->delegate = false;
3395 c->delegate_controllers = 0;
3396 }
3397
3398 return 0;
3399}
3400
4ad49000
LP
3401int config_parse_device_allow(
3402 const char *unit,
3403 const char *filename,
3404 unsigned line,
3405 const char *section,
71a61510 3406 unsigned section_line,
4ad49000
LP
3407 const char *lvalue,
3408 int ltype,
3409 const char *rvalue,
3410 void *data,
3411 void *userdata) {
3412
c9f620bf 3413 _cleanup_free_ char *path = NULL, *resolved = NULL;
4ad49000 3414 CGroupContext *c = data;
c9f620bf 3415 const char *p = rvalue;
1116e14c 3416 int r;
4ad49000
LP
3417
3418 if (isempty(rvalue)) {
3419 while (c->device_allow)
3420 cgroup_context_free_device_allow(c, c->device_allow);
3421
3422 return 0;
3423 }
3424
4ec85141 3425 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
3426 if (r == -ENOMEM)
3427 return log_oom();
508f63b4 3428 if (r < 0) {
1116e14c 3429 log_syntax(unit, LOG_WARNING, filename, line, r,
c9f620bf
YW
3430 "Invalid syntax, ignoring: %s", rvalue);
3431 return 0;
3432 }
3433 if (r == 0) {
3434 log_syntax(unit, LOG_WARNING, filename, line, 0,
3435 "Failed to extract device path and rights from '%s', ignoring.", rvalue);
20d52ab6 3436 return 0;
1116e14c
NBS
3437 }
3438
c9f620bf
YW
3439 r = unit_full_printf(userdata, path, &resolved);
3440 if (r < 0) {
3441 log_syntax(unit, LOG_WARNING, filename, line, r,
3442 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4ad49000
LP
3443 return 0;
3444 }
3445
49fe5c09 3446 if (!STARTSWITH_SET(resolved, "block-", "char-")) {
2f4d31c1 3447
57e84e75
LP
3448 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3449 if (r < 0)
3450 return 0;
3451
3452 if (!valid_device_node_path(resolved)) {
3453 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s', ignoring.", resolved);
3454 return 0;
3455 }
c9f620bf 3456 }
4ad49000 3457
c9f620bf
YW
3458 if (!isempty(p) && !in_charset(p, "rwm")) {
3459 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s', ignoring.", p);
4ad49000
LP
3460 return 0;
3461 }
3462
fd870bac 3463 return cgroup_add_device_allow(c, resolved, p);
4ad49000
LP
3464}
3465
13c31542
TH
3466int config_parse_io_device_weight(
3467 const char *unit,
3468 const char *filename,
3469 unsigned line,
3470 const char *section,
3471 unsigned section_line,
3472 const char *lvalue,
3473 int ltype,
3474 const char *rvalue,
3475 void *data,
3476 void *userdata) {
3477
c9f620bf 3478 _cleanup_free_ char *path = NULL, *resolved = NULL;
13c31542
TH
3479 CGroupIODeviceWeight *w;
3480 CGroupContext *c = data;
c9f620bf 3481 const char *p = rvalue;
13c31542 3482 uint64_t u;
13c31542
TH
3483 int r;
3484
3485 assert(filename);
3486 assert(lvalue);
3487 assert(rvalue);
3488
3489 if (isempty(rvalue)) {
3490 while (c->io_device_weights)
3491 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3492
3493 return 0;
3494 }
3495
4ec85141 3496 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
3497 if (r == -ENOMEM)
3498 return log_oom();
3499 if (r < 0) {
3500 log_syntax(unit, LOG_WARNING, filename, line, r,
3501 "Invalid syntax, ignoring: %s", rvalue);
3502 return 0;
3503 }
3504 if (r == 0 || isempty(p)) {
3505 log_syntax(unit, LOG_WARNING, filename, line, 0,
3506 "Failed to extract device path and weight from '%s', ignoring.", rvalue);
13c31542
TH
3507 return 0;
3508 }
3509
c9f620bf
YW
3510 r = unit_full_printf(userdata, path, &resolved);
3511 if (r < 0) {
3512 log_syntax(unit, LOG_WARNING, filename, line, r,
3513 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3514 return 0;
3515 }
13c31542 3516
2f4d31c1
YW
3517 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3518 if (r < 0)
3519 return 0;
3520
c9f620bf 3521 r = cg_weight_parse(p, &u);
13c31542 3522 if (r < 0) {
c9f620bf 3523 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid, ignoring: %m", p);
13c31542
TH
3524 return 0;
3525 }
3526
3527 assert(u != CGROUP_WEIGHT_INVALID);
3528
3529 w = new0(CGroupIODeviceWeight, 1);
3530 if (!w)
3531 return log_oom();
3532
c9f620bf 3533 w->path = TAKE_PTR(resolved);
13c31542
TH
3534 w->weight = u;
3535
3536 LIST_PREPEND(device_weights, c->io_device_weights, w);
3537 return 0;
3538}
3539
6ae4283c
TH
3540int config_parse_io_device_latency(
3541 const char *unit,
3542 const char *filename,
3543 unsigned line,
3544 const char *section,
3545 unsigned section_line,
3546 const char *lvalue,
3547 int ltype,
3548 const char *rvalue,
3549 void *data,
3550 void *userdata) {
3551
3552 _cleanup_free_ char *path = NULL, *resolved = NULL;
3553 CGroupIODeviceLatency *l;
3554 CGroupContext *c = data;
3555 const char *p = rvalue;
3556 usec_t usec;
3557 int r;
3558
3559 assert(filename);
3560 assert(lvalue);
3561 assert(rvalue);
3562
3563 if (isempty(rvalue)) {
3564 while (c->io_device_latencies)
3565 cgroup_context_free_io_device_latency(c, c->io_device_latencies);
3566
3567 return 0;
3568 }
3569
4ec85141 3570 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
6ae4283c
TH
3571 if (r == -ENOMEM)
3572 return log_oom();
3573 if (r < 0) {
3574 log_syntax(unit, LOG_WARNING, filename, line, r,
3575 "Invalid syntax, ignoring: %s", rvalue);
3576 return 0;
3577 }
3578 if (r == 0 || isempty(p)) {
3579 log_syntax(unit, LOG_WARNING, filename, line, 0,
3580 "Failed to extract device path and latency from '%s', ignoring.", rvalue);
3581 return 0;
3582 }
3583
3584 r = unit_full_printf(userdata, path, &resolved);
3585 if (r < 0) {
3586 log_syntax(unit, LOG_WARNING, filename, line, r,
3587 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3588 return 0;
3589 }
3590
3591 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3592 if (r < 0)
3593 return 0;
3594
3595 if (parse_sec(p, &usec) < 0) {
3596 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", p);
3597 return 0;
3598 }
3599
3600 l = new0(CGroupIODeviceLatency, 1);
3601 if (!l)
3602 return log_oom();
3603
3604 l->path = TAKE_PTR(resolved);
3605 l->target_usec = usec;
3606
3607 LIST_PREPEND(device_latencies, c->io_device_latencies, l);
3608 return 0;
3609}
3610
13c31542
TH
3611int config_parse_io_limit(
3612 const char *unit,
3613 const char *filename,
3614 unsigned line,
3615 const char *section,
3616 unsigned section_line,
3617 const char *lvalue,
3618 int ltype,
3619 const char *rvalue,
3620 void *data,
3621 void *userdata) {
3622
c9f620bf 3623 _cleanup_free_ char *path = NULL, *resolved = NULL;
13c31542
TH
3624 CGroupIODeviceLimit *l = NULL, *t;
3625 CGroupContext *c = data;
9be57249 3626 CGroupIOLimitType type;
c9f620bf 3627 const char *p = rvalue;
13c31542 3628 uint64_t num;
13c31542
TH
3629 int r;
3630
3631 assert(filename);
3632 assert(lvalue);
3633 assert(rvalue);
3634
9be57249
TH
3635 type = cgroup_io_limit_type_from_string(lvalue);
3636 assert(type >= 0);
13c31542
TH
3637
3638 if (isempty(rvalue)) {
3639 LIST_FOREACH(device_limits, l, c->io_device_limits)
9be57249 3640 l->limits[type] = cgroup_io_limit_defaults[type];
13c31542
TH
3641 return 0;
3642 }
3643
4ec85141 3644 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
3645 if (r == -ENOMEM)
3646 return log_oom();
3647 if (r < 0) {
3648 log_syntax(unit, LOG_WARNING, filename, line, r,
3649 "Invalid syntax, ignoring: %s", rvalue);
3650 return 0;
3651 }
3652 if (r == 0 || isempty(p)) {
3653 log_syntax(unit, LOG_WARNING, filename, line, 0,
3654 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
13c31542
TH
3655 return 0;
3656 }
3657
c9f620bf
YW
3658 r = unit_full_printf(userdata, path, &resolved);
3659 if (r < 0) {
3660 log_syntax(unit, LOG_WARNING, filename, line, r,
3661 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3662 return 0;
3663 }
13c31542 3664
2f4d31c1
YW
3665 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3666 if (r < 0)
3667 return 0;
3668
9d5e9b4a 3669 if (streq("infinity", p))
13c31542 3670 num = CGROUP_LIMIT_MAX;
9d5e9b4a 3671 else {
c9f620bf 3672 r = parse_size(p, 1000, &num);
13c31542 3673 if (r < 0 || num <= 0) {
c9f620bf 3674 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid IO limit '%s', ignoring.", p);
13c31542
TH
3675 return 0;
3676 }
3677 }
3678
3679 LIST_FOREACH(device_limits, t, c->io_device_limits) {
c9f620bf 3680 if (path_equal(resolved, t->path)) {
13c31542
TH
3681 l = t;
3682 break;
3683 }
3684 }
3685
3686 if (!l) {
9be57249
TH
3687 CGroupIOLimitType ttype;
3688
13c31542
TH
3689 l = new0(CGroupIODeviceLimit, 1);
3690 if (!l)
3691 return log_oom();
3692
c9f620bf 3693 l->path = TAKE_PTR(resolved);
9be57249
TH
3694 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3695 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
13c31542
TH
3696
3697 LIST_PREPEND(device_limits, c->io_device_limits, l);
3698 }
3699
9be57249 3700 l->limits[type] = num;
13c31542
TH
3701
3702 return 0;
3703}
3704
8e7076ca
LP
3705int config_parse_blockio_device_weight(
3706 const char *unit,
3707 const char *filename,
3708 unsigned line,
3709 const char *section,
71a61510 3710 unsigned section_line,
8e7076ca
LP
3711 const char *lvalue,
3712 int ltype,
3713 const char *rvalue,
3714 void *data,
3715 void *userdata) {
3716
c9f620bf 3717 _cleanup_free_ char *path = NULL, *resolved = NULL;
8e7076ca 3718 CGroupBlockIODeviceWeight *w;
4ad49000 3719 CGroupContext *c = data;
c9f620bf 3720 const char *p = rvalue;
d53d9474 3721 uint64_t u;
4ad49000
LP
3722 int r;
3723
3724 assert(filename);
3725 assert(lvalue);
3726 assert(rvalue);
3727
3728 if (isempty(rvalue)) {
4ad49000
LP
3729 while (c->blockio_device_weights)
3730 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3731
3732 return 0;
3733 }
3734
4ec85141 3735 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
3736 if (r == -ENOMEM)
3737 return log_oom();
3738 if (r < 0) {
3739 log_syntax(unit, LOG_WARNING, filename, line, r,
3740 "Invalid syntax, ignoring: %s", rvalue);
3741 return 0;
3742 }
3743 if (r == 0 || isempty(p)) {
3744 log_syntax(unit, LOG_WARNING, filename, line, 0,
3745 "Failed to extract device node and weight from '%s', ignoring.", rvalue);
8e7076ca
LP
3746 return 0;
3747 }
4ad49000 3748
c9f620bf
YW
3749 r = unit_full_printf(userdata, path, &resolved);
3750 if (r < 0) {
3751 log_syntax(unit, LOG_WARNING, filename, line, r,
3752 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3753 return 0;
3754 }
4ad49000 3755
2f4d31c1
YW
3756 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3757 if (r < 0)
3758 return 0;
3759
c9f620bf 3760 r = cg_blkio_weight_parse(p, &u);
d53d9474 3761 if (r < 0) {
c9f620bf 3762 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid block IO weight '%s', ignoring: %m", p);
4ad49000
LP
3763 return 0;
3764 }
3765
d53d9474
LP
3766 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3767
8e7076ca
LP
3768 w = new0(CGroupBlockIODeviceWeight, 1);
3769 if (!w)
3770 return log_oom();
4ad49000 3771
c9f620bf 3772 w->path = TAKE_PTR(resolved);
d53d9474 3773 w->weight = u;
4ad49000 3774
71fda00f 3775 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
3776 return 0;
3777}
3778
3779int config_parse_blockio_bandwidth(
3780 const char *unit,
3781 const char *filename,
3782 unsigned line,
3783 const char *section,
71a61510 3784 unsigned section_line,
4ad49000
LP
3785 const char *lvalue,
3786 int ltype,
3787 const char *rvalue,
3788 void *data,
3789 void *userdata) {
3790
c9f620bf 3791 _cleanup_free_ char *path = NULL, *resolved = NULL;
979d0311 3792 CGroupBlockIODeviceBandwidth *b = NULL, *t;
4ad49000 3793 CGroupContext *c = data;
c9f620bf 3794 const char *p = rvalue;
59f448cf 3795 uint64_t bytes;
47c0980d 3796 bool read;
4ad49000
LP
3797 int r;
3798
3799 assert(filename);
3800 assert(lvalue);
3801 assert(rvalue);
3802
47c0980d
G
3803 read = streq("BlockIOReadBandwidth", lvalue);
3804
4ad49000 3805 if (isempty(rvalue)) {
979d0311
TH
3806 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3807 b->rbps = CGROUP_LIMIT_MAX;
3808 b->wbps = CGROUP_LIMIT_MAX;
3809 }
4ad49000
LP
3810 return 0;
3811 }
3812
4ec85141 3813 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
3814 if (r == -ENOMEM)
3815 return log_oom();
3816 if (r < 0) {
3817 log_syntax(unit, LOG_WARNING, filename, line, r,
3818 "Invalid syntax, ignoring: %s", rvalue);
3819 return 0;
3820 }
3821 if (r == 0 || isempty(p)) {
3822 log_syntax(unit, LOG_WARNING, filename, line, 0,
3823 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
4ad49000
LP
3824 return 0;
3825 }
3826
c9f620bf
YW
3827 r = unit_full_printf(userdata, path, &resolved);
3828 if (r < 0) {
3829 log_syntax(unit, LOG_WARNING, filename, line, r,
3830 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3831 return 0;
3832 }
4ad49000 3833
2f4d31c1
YW
3834 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3835 if (r < 0)
3836 return 0;
3837
c9f620bf 3838 r = parse_size(p, 1000, &bytes);
4ad49000 3839 if (r < 0 || bytes <= 0) {
c9f620bf 3840 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid Block IO Bandwidth '%s', ignoring.", p);
4ad49000
LP
3841 return 0;
3842 }
3843
979d0311 3844 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
c9f620bf 3845 if (path_equal(resolved, t->path)) {
979d0311
TH
3846 b = t;
3847 break;
3848 }
3849 }
4ad49000 3850
979d0311
TH
3851 if (!t) {
3852 b = new0(CGroupBlockIODeviceBandwidth, 1);
3853 if (!b)
3854 return log_oom();
3855
c9f620bf 3856 b->path = TAKE_PTR(resolved);
979d0311
TH
3857 b->rbps = CGROUP_LIMIT_MAX;
3858 b->wbps = CGROUP_LIMIT_MAX;
3859
3860 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3861 }
4ad49000 3862
979d0311
TH
3863 if (read)
3864 b->rbps = bytes;
3865 else
3866 b->wbps = bytes;
4ad49000
LP
3867
3868 return 0;
3869}
3870
d420282b
LP
3871int config_parse_job_mode_isolate(
3872 const char *unit,
3873 const char *filename,
3874 unsigned line,
3875 const char *section,
3876 unsigned section_line,
3877 const char *lvalue,
3878 int ltype,
3879 const char *rvalue,
3880 void *data,
3881 void *userdata) {
3882
3883 JobMode *m = data;
3884 int r;
3885
3886 assert(filename);
3887 assert(lvalue);
3888 assert(rvalue);
3889
3890 r = parse_boolean(rvalue);
3891 if (r < 0) {
12ca818f 3892 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
3893 return 0;
3894 }
3895
8ab39347
YW
3896 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue);
3897
d420282b
LP
3898 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3899 return 0;
3900}
3901
3536f49e 3902int config_parse_exec_directories(
e66cf1a3
LP
3903 const char *unit,
3904 const char *filename,
3905 unsigned line,
3906 const char *section,
3907 unsigned section_line,
3908 const char *lvalue,
3909 int ltype,
3910 const char *rvalue,
3911 void *data,
3912 void *userdata) {
3913
a2a5291b 3914 char***rt = data;
47538b76 3915 const Unit *u = userdata;
035fe294 3916 const char *p;
e66cf1a3
LP
3917 int r;
3918
3919 assert(filename);
3920 assert(lvalue);
3921 assert(rvalue);
3922 assert(data);
3923
3924 if (isempty(rvalue)) {
3925 /* Empty assignment resets the list */
6796073e 3926 *rt = strv_free(*rt);
e66cf1a3
LP
3927 return 0;
3928 }
3929
035fe294
ZJS
3930 for (p = rvalue;;) {
3931 _cleanup_free_ char *word = NULL, *k = NULL;
e66cf1a3 3932
4ec85141 3933 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294 3934 if (r == -ENOMEM)
e66cf1a3 3935 return log_oom();
035fe294
ZJS
3936 if (r < 0) {
3937 log_syntax(unit, LOG_WARNING, filename, line, r,
3938 "Invalid syntax, ignoring: %s", rvalue);
3939 return 0;
3940 }
091e9efe
LP
3941 if (r == 0)
3942 return 0;
e66cf1a3 3943
18913df9 3944 r = unit_full_printf(u, word, &k);
9b5864d9 3945 if (r < 0) {
035fe294 3946 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 3947 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
9b5864d9
MG
3948 continue;
3949 }
3950
2f4d31c1
YW
3951 r = path_simplify_and_warn(k, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
3952 if (r < 0)
e8865688 3953 continue;
e8865688
LP
3954
3955 if (path_startswith(k, "private")) {
3956 log_syntax(unit, LOG_ERR, filename, line, 0,
4605de11 3957 "%s= path can't be 'private', ignoring assignment: %s", lvalue, word);
e66cf1a3
LP
3958 continue;
3959 }
3960
035fe294 3961 r = strv_push(rt, k);
e66cf1a3
LP
3962 if (r < 0)
3963 return log_oom();
035fe294 3964 k = NULL;
e66cf1a3 3965 }
e66cf1a3
LP
3966}
3967
3af00fb8
LP
3968int config_parse_set_status(
3969 const char *unit,
3970 const char *filename,
3971 unsigned line,
3972 const char *section,
3973 unsigned section_line,
3974 const char *lvalue,
3975 int ltype,
3976 const char *rvalue,
3977 void *data,
3978 void *userdata) {
3979
3af00fb8 3980 size_t l;
a2a5291b 3981 const char *word, *state;
3af00fb8
LP
3982 int r;
3983 ExitStatusSet *status_set = data;
3984
3985 assert(filename);
3986 assert(lvalue);
3987 assert(rvalue);
3988 assert(data);
3989
3e2d435b 3990 /* Empty assignment resets the list */
3af00fb8 3991 if (isempty(rvalue)) {
3e2d435b 3992 exit_status_set_free(status_set);
3af00fb8
LP
3993 return 0;
3994 }
3995
a2a5291b 3996 FOREACH_WORD(word, l, rvalue, state) {
3af00fb8 3997 _cleanup_free_ char *temp;
23d5dd16 3998 Bitmap *bitmap;
3af00fb8 3999
a2a5291b 4000 temp = strndup(word, l);
3af00fb8
LP
4001 if (!temp)
4002 return log_oom();
4003
2e2ed880
ZJS
4004 /* We need to call exit_status_from_string() first, because we want
4005 * to parse numbers as exit statuses, not signals. */
3af00fb8 4006
2e2ed880
ZJS
4007 r = exit_status_from_string(temp);
4008 if (r >= 0) {
4009 assert(r >= 0 && r < 256);
4010 bitmap = &status_set->status;
3af00fb8 4011 } else {
2e2ed880
ZJS
4012 r = signal_from_string(temp);
4013
4014 if (r <= 0) {
4015 log_syntax(unit, LOG_ERR, filename, line, 0,
4016 "Failed to parse value, ignoring: %s", word);
1e2fd62d 4017 continue;
3af00fb8 4018 }
2e2ed880 4019 bitmap = &status_set->signal;
3af00fb8 4020 }
1e2fd62d 4021
2e2ed880 4022 r = bitmap_set(bitmap, r);
063c4b1a 4023 if (r < 0)
2e2ed880 4024 return log_error_errno(r, "Failed to set signal or status %s: %m", word);
3af00fb8 4025 }
b2fadec6 4026 if (!isempty(state))
12ca818f 4027 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3af00fb8
LP
4028
4029 return 0;
4030}
4031
94828d2d
LP
4032int config_parse_namespace_path_strv(
4033 const char *unit,
4034 const char *filename,
4035 unsigned line,
4036 const char *section,
4037 unsigned section_line,
4038 const char *lvalue,
4039 int ltype,
4040 const char *rvalue,
4041 void *data,
4042 void *userdata) {
4043
47538b76 4044 const Unit *u = userdata;
a2a5291b 4045 char*** sv = data;
063c4b1a 4046 const char *p = rvalue;
94828d2d
LP
4047 int r;
4048
4049 assert(filename);
4050 assert(lvalue);
4051 assert(rvalue);
4052 assert(data);
4053
4054 if (isempty(rvalue)) {
4055 /* Empty assignment resets the list */
6796073e 4056 *sv = strv_free(*sv);
94828d2d
LP
4057 return 0;
4058 }
4059
727f76d7 4060 for (;;) {
7b07e993 4061 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
20b7a007
LP
4062 const char *w;
4063 bool ignore_enoent = false, shall_prefix = false;
94828d2d 4064
4ec85141 4065 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
0293a7a8
EV
4066 if (r == 0)
4067 break;
4068 if (r == -ENOMEM)
4069 return log_oom();
727f76d7 4070 if (r < 0) {
7b07e993 4071 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
727f76d7
EV
4072 return 0;
4073 }
94828d2d 4074
20b7a007
LP
4075 w = word;
4076 if (startswith(w, "-")) {
4077 ignore_enoent = true;
4078 w++;
4079 }
4080 if (startswith(w, "+")) {
4081 shall_prefix = true;
4082 w++;
4083 }
7b07e993 4084
20b7a007 4085 r = unit_full_printf(u, w, &resolved);
7b07e993 4086 if (r < 0) {
063c4b1a 4087 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", w);
94828d2d
LP
4088 continue;
4089 }
4090
2f4d31c1
YW
4091 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4092 if (r < 0)
7b07e993 4093 continue;
94828d2d 4094
20b7a007
LP
4095 joined = strjoin(ignore_enoent ? "-" : "",
4096 shall_prefix ? "+" : "",
4097 resolved);
7b07e993
LP
4098
4099 r = strv_push(sv, joined);
94828d2d
LP
4100 if (r < 0)
4101 return log_oom();
4102
7b07e993 4103 joined = NULL;
94828d2d
LP
4104 }
4105
4106 return 0;
4107}
4108
2abd4e38
YW
4109int config_parse_temporary_filesystems(
4110 const char *unit,
4111 const char *filename,
4112 unsigned line,
4113 const char *section,
4114 unsigned section_line,
4115 const char *lvalue,
4116 int ltype,
4117 const char *rvalue,
4118 void *data,
4119 void *userdata) {
4120
47538b76 4121 const Unit *u = userdata;
2abd4e38 4122 ExecContext *c = data;
063c4b1a 4123 const char *p = rvalue;
2abd4e38
YW
4124 int r;
4125
4126 assert(filename);
4127 assert(lvalue);
4128 assert(rvalue);
4129 assert(data);
4130
4131 if (isempty(rvalue)) {
4132 /* Empty assignment resets the list */
4133 temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
4134 c->temporary_filesystems = NULL;
4135 c->n_temporary_filesystems = 0;
4136 return 0;
4137 }
4138
2abd4e38
YW
4139 for (;;) {
4140 _cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL;
4141 const char *w;
4142
4ec85141 4143 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
2abd4e38 4144 if (r == 0)
063c4b1a 4145 return 0;
2abd4e38
YW
4146 if (r == -ENOMEM)
4147 return log_oom();
4148 if (r < 0) {
4149 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
4150 return 0;
4151 }
4152
4153 w = word;
4154 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
063c4b1a
YW
4155 if (r == -ENOMEM)
4156 return log_oom();
4157 if (r < 0) {
4158 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", word);
4159 continue;
4160 }
4161 if (r == 0) {
4162 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring: %s", word);
4163 continue;
4164 }
2abd4e38
YW
4165
4166 r = unit_full_printf(u, path, &resolved);
4167 if (r < 0) {
063c4b1a 4168 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", path);
2abd4e38
YW
4169 continue;
4170 }
4171
2f4d31c1
YW
4172 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4173 if (r < 0)
2abd4e38 4174 continue;
2abd4e38 4175
a26fec24 4176 r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, resolved, w);
6302d1ea 4177 if (r < 0)
2abd4e38 4178 return log_oom();
2abd4e38 4179 }
2abd4e38
YW
4180}
4181
d2d6c096
LP
4182int config_parse_bind_paths(
4183 const char *unit,
4184 const char *filename,
4185 unsigned line,
4186 const char *section,
4187 unsigned section_line,
4188 const char *lvalue,
4189 int ltype,
4190 const char *rvalue,
4191 void *data,
4192 void *userdata) {
4193
4194 ExecContext *c = data;
47538b76 4195 const Unit *u = userdata;
d2d6c096
LP
4196 const char *p;
4197 int r;
4198
4199 assert(filename);
4200 assert(lvalue);
4201 assert(rvalue);
4202 assert(data);
4203
4204 if (isempty(rvalue)) {
4205 /* Empty assignment resets the list */
4206 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
4207 c->bind_mounts = NULL;
4208 c->n_bind_mounts = 0;
4209 return 0;
4210 }
4211
4212 p = rvalue;
4213 for (;;) {
4214 _cleanup_free_ char *source = NULL, *destination = NULL;
42d43f21 4215 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
d2d6c096
LP
4216 char *s = NULL, *d = NULL;
4217 bool rbind = true, ignore_enoent = false;
4218
4ec85141 4219 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
d2d6c096
LP
4220 if (r == 0)
4221 break;
4222 if (r == -ENOMEM)
4223 return log_oom();
4224 if (r < 0) {
2f4d31c1 4225 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
d2d6c096
LP
4226 return 0;
4227 }
4228
42d43f21
DC
4229 r = unit_full_printf(u, source, &sresolved);
4230 if (r < 0) {
4231 log_syntax(unit, LOG_ERR, filename, line, r,
063c4b1a 4232 "Failed to resolved unit specifiers in \"%s\", ignoring: %m", source);
2f4d31c1 4233 continue;
42d43f21
DC
4234 }
4235
4236 s = sresolved;
d2d6c096
LP
4237 if (s[0] == '-') {
4238 ignore_enoent = true;
4239 s++;
4240 }
4241
2f4d31c1
YW
4242 r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4243 if (r < 0)
4244 continue;
d2d6c096
LP
4245
4246 /* Optionally, the destination is specified. */
4247 if (p && p[-1] == ':') {
4ec85141 4248 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
d2d6c096
LP
4249 if (r == -ENOMEM)
4250 return log_oom();
4251 if (r < 0) {
2f4d31c1 4252 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
d2d6c096
LP
4253 return 0;
4254 }
4255 if (r == 0) {
2f4d31c1
YW
4256 log_syntax(unit, LOG_ERR, filename, line, 0, "Missing argument after ':', ignoring: %s", s);
4257 continue;
d2d6c096
LP
4258 }
4259
42d43f21
DC
4260 r = unit_full_printf(u, destination, &dresolved);
4261 if (r < 0) {
4262 log_syntax(unit, LOG_ERR, filename, line, r,
4263 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination);
2f4d31c1 4264 continue;
42d43f21
DC
4265 }
4266
2f4d31c1
YW
4267 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4268 if (r < 0)
4269 continue;
d2d6c096 4270
2f4d31c1 4271 d = dresolved;
d2d6c096
LP
4272
4273 /* Optionally, there's also a short option string specified */
4274 if (p && p[-1] == ':') {
4275 _cleanup_free_ char *options = NULL;
4276
4ec85141 4277 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
d2d6c096
LP
4278 if (r == -ENOMEM)
4279 return log_oom();
4280 if (r < 0) {
4281 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4282 return 0;
4283 }
4284
4285 if (isempty(options) || streq(options, "rbind"))
4286 rbind = true;
4287 else if (streq(options, "norbind"))
4288 rbind = false;
4289 else {
4290 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
2f4d31c1 4291 continue;
d2d6c096
LP
4292 }
4293 }
4294 } else
4295 d = s;
4296
4297 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
4298 &(BindMount) {
4299 .source = s,
4300 .destination = d,
4301 .read_only = !!strstr(lvalue, "ReadOnly"),
4302 .recursive = rbind,
4303 .ignore_enoent = ignore_enoent,
4304 });
4305 if (r < 0)
4306 return log_oom();
4307 }
4308
4309 return 0;
4310}
4311
eae51da3
LP
4312int config_parse_job_timeout_sec(
4313 const char* unit,
4314 const char *filename,
4315 unsigned line,
4316 const char *section,
4317 unsigned section_line,
4318 const char *lvalue,
4319 int ltype,
4320 const char *rvalue,
4321 void *data,
4322 void *userdata) {
4323
4324 Unit *u = data;
4325 usec_t usec;
4326 int r;
4327
4328 assert(filename);
4329 assert(lvalue);
4330 assert(rvalue);
4331 assert(u);
4332
4333 r = parse_sec_fix_0(rvalue, &usec);
4334 if (r < 0) {
4335 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue);
4336 return 0;
4337 }
4338
4339 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
c05f3c8f 4340 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
eae51da3
LP
4341 * count. */
4342
4343 if (!u->job_running_timeout_set)
4344 u->job_running_timeout = usec;
4345
4346 u->job_timeout = usec;
4347
4348 return 0;
4349}
4350
4351int config_parse_job_running_timeout_sec(
4352 const char* unit,
4353 const char *filename,
4354 unsigned line,
4355 const char *section,
4356 unsigned section_line,
4357 const char *lvalue,
4358 int ltype,
4359 const char *rvalue,
4360 void *data,
4361 void *userdata) {
4362
4363 Unit *u = data;
4364 usec_t usec;
4365 int r;
4366
4367 assert(filename);
4368 assert(lvalue);
4369 assert(rvalue);
4370 assert(u);
4371
4372 r = parse_sec_fix_0(rvalue, &usec);
4373 if (r < 0) {
4374 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue);
4375 return 0;
4376 }
4377
4378 u->job_running_timeout = usec;
4379 u->job_running_timeout_set = true;
4380
4381 return 0;
4382}
4383
54fcb619
ZJS
4384int config_parse_emergency_action(
4385 const char* unit,
4386 const char *filename,
4387 unsigned line,
4388 const char *section,
4389 unsigned section_line,
4390 const char *lvalue,
4391 int ltype,
4392 const char *rvalue,
4393 void *data,
4394 void *userdata) {
4395
4396 Manager *m = NULL;
4397 EmergencyAction *x = data;
4398 int r;
4399
4400 assert(filename);
4401 assert(lvalue);
4402 assert(rvalue);
4403 assert(data);
4404
4405 if (unit)
4406 m = ((Unit*) userdata)->manager;
4407 else
4408 m = data;
4409
4410 r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
4411 if (r < 0) {
469f76f1
ZJS
4412 if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
4413 /* Compat mode: remove for systemd 241. */
4414
4415 log_syntax(unit, LOG_INFO, filename, line, r,
4416 "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
4417 lvalue, rvalue);
4418 *x = EMERGENCY_ACTION_EXIT_FORCE;
4419 return 0;
4420 }
4421
54fcb619
ZJS
4422 if (r == -EOPNOTSUPP)
4423 log_syntax(unit, LOG_ERR, filename, line, r,
4424 "%s= specified as %s mode action, ignoring: %s",
4425 lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
4426 else
4427 log_syntax(unit, LOG_ERR, filename, line, r,
4428 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
4429 return 0;
4430 }
4431
4432 return 0;
4433}
4434
a9353a5c
LP
4435int config_parse_pid_file(
4436 const char *unit,
4437 const char *filename,
4438 unsigned line,
4439 const char *section,
4440 unsigned section_line,
4441 const char *lvalue,
4442 int ltype,
4443 const char *rvalue,
4444 void *data,
4445 void *userdata) {
4446
4447 _cleanup_free_ char *k = NULL, *n = NULL;
47538b76 4448 const Unit *u = userdata;
a9353a5c 4449 char **s = data;
a9353a5c
LP
4450 int r;
4451
4452 assert(filename);
4453 assert(lvalue);
4454 assert(rvalue);
4455 assert(u);
4456
b8055c05
YW
4457 if (isempty(rvalue)) {
4458 /* An empty assignment removes already set value. */
4459 *s = mfree(*s);
4460 return 0;
4461 }
4462
a9353a5c
LP
4463 r = unit_full_printf(u, rvalue, &k);
4464 if (r < 0) {
4465 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
4466 return 0;
4467 }
4468
4469 /* If this is a relative path make it absolute by prefixing the /run */
4470 n = path_make_absolute(k, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
4471 if (!n)
4472 return log_oom();
4473
4474 /* Check that the result is a sensible path */
4475 r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4476 if (r < 0)
4477 return r;
4478
4a66b5c9
LP
4479 r = patch_var_run(unit, filename, line, lvalue, &n);
4480 if (r < 0)
4481 return r;
a9353a5c 4482
4a66b5c9 4483 free_and_replace(*s, n);
a9353a5c
LP
4484 return 0;
4485}
4486
7af67e9a
LP
4487int config_parse_exit_status(
4488 const char *unit,
4489 const char *filename,
4490 unsigned line,
4491 const char *section,
4492 unsigned section_line,
4493 const char *lvalue,
4494 int ltype,
4495 const char *rvalue,
4496 void *data,
4497 void *userdata) {
4498
4499 int *exit_status = data, r;
4500 uint8_t u;
4501
4502 assert(filename);
4503 assert(lvalue);
4504 assert(rvalue);
4505 assert(exit_status);
4506
4507 if (isempty(rvalue)) {
4508 *exit_status = -1;
4509 return 0;
4510 }
4511
4512 r = safe_atou8(rvalue, &u);
4513 if (r < 0) {
4514 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse exit status '%s', ignoring: %m", rvalue);
4515 return 0;
4516 }
4517
4518 *exit_status = u;
4519 return 0;
4520}
4521
c72703e2
CD
4522int config_parse_disable_controllers(
4523 const char *unit,
4524 const char *filename,
4525 unsigned line,
4526 const char *section,
4527 unsigned section_line,
4528 const char *lvalue,
4529 int ltype,
4530 const char *rvalue,
4531 void *data,
4532 void *userdata) {
4533
4534 int r;
4535 CGroupContext *c = data;
4536 CGroupMask disabled_mask;
4537
4538 /* 1. If empty, make all controllers eligible for use again.
4539 * 2. If non-empty, merge all listed controllers, space separated. */
4540
4541 if (isempty(rvalue)) {
4542 c->disable_controllers = 0;
4543 return 0;
4544 }
4545
4546 r = cg_mask_from_string(rvalue, &disabled_mask);
4547 if (r < 0 || disabled_mask <= 0) {
4548 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid cgroup string: %s, ignoring", rvalue);
4549 return 0;
4550 }
4551
4552 c->disable_controllers |= disabled_mask;
4553
4554 return 0;
4555}
4556
fab34748
KL
4557int config_parse_ip_filter_bpf_progs(
4558 const char *unit,
4559 const char *filename,
4560 unsigned line,
4561 const char *section,
4562 unsigned section_line,
4563 const char *lvalue,
4564 int ltype,
4565 const char *rvalue,
4566 void *data,
4567 void *userdata) {
4568
4569 _cleanup_free_ char *resolved = NULL;
47538b76 4570 const Unit *u = userdata;
fab34748
KL
4571 char ***paths = data;
4572 int r;
4573
4574 assert(filename);
4575 assert(lvalue);
4576 assert(rvalue);
4577 assert(paths);
4578
4579 if (isempty(rvalue)) {
4580 *paths = strv_free(*paths);
4581 return 0;
4582 }
4583
4584 r = unit_full_printf(u, rvalue, &resolved);
4585 if (r < 0) {
4586 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
4587 return 0;
4588 }
4589
4590 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4591 if (r < 0)
4592 return 0;
4593
4594 if (strv_contains(*paths, resolved))
4595 return 0;
4596
4597 r = strv_extend(paths, resolved);
4598 if (r < 0)
4599 return log_oom();
4600
4601 r = bpf_firewall_supported();
4602 if (r < 0)
4603 return r;
4604 if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
4605 static bool warned = false;
4606
4607 log_full(warned ? LOG_DEBUG : LOG_WARNING,
4608 "File %s:%u configures an IP firewall with BPF programs (%s=%s), but the local system does not support BPF/cgroup based firewalling with multiple filters.\n"
4609 "Starting this unit will fail! (This warning is only shown for the first loaded unit using IP firewalling.)", filename, line, lvalue, rvalue);
4610
4611 warned = true;
4612 }
4613
4614 return 0;
4615}
4616
23a177ef
LP
4617static int merge_by_names(Unit **u, Set *names, const char *id) {
4618 char *k;
4619 int r;
4620
4621 assert(u);
4622 assert(*u);
23a177ef 4623
e8630e69 4624 /* Let's try to add in all names that are aliases of this unit */
23a177ef 4625 while ((k = set_steal_first(names))) {
e8630e69 4626 _cleanup_free_ _unused_ char *free_k = k;
23a177ef 4627
e8630e69 4628 /* First try to merge in the other name into our unit */
9946996c
LP
4629 r = unit_merge_by_name(*u, k);
4630 if (r < 0) {
23a177ef
LP
4631 Unit *other;
4632
e8630e69
ZJS
4633 /* Hmm, we couldn't merge the other unit into ours? Then let's try it the other way
4634 * round. */
036643a2 4635
e8630e69
ZJS
4636 other = manager_get_unit((*u)->manager, k);
4637 if (!other)
4638 return r; /* return previous failure */
036643a2 4639
e8630e69
ZJS
4640 r = unit_merge(other, *u);
4641 if (r < 0)
a837f088 4642 return r;
fe51822e 4643
e8630e69
ZJS
4644 *u = other;
4645 return merge_by_names(u, names, NULL);
036643a2 4646 }
034c6ed7 4647
e8630e69
ZJS
4648 if (streq_ptr(id, k))
4649 unit_choose_id(*u, id);
1b64d026
LP
4650 }
4651
e48614c4 4652 return 0;
0301abf4
LP
4653}
4654
e537352b 4655int unit_load_fragment(Unit *u) {
e8630e69
ZJS
4656 const char *fragment;
4657 _cleanup_set_free_free_ Set *names = NULL;
5a1c1b53 4658 struct stat st;
23a177ef 4659 int r;
0301abf4
LP
4660
4661 assert(u);
ac155bb8
MS
4662 assert(u->load_state == UNIT_STUB);
4663 assert(u->id);
23a177ef 4664
3f5e8115
LP
4665 if (u->transient) {
4666 u->load_state = UNIT_LOADED;
4667 return 0;
4668 }
4669
91e0ee5f
ZJS
4670 /* Possibly rebuild the fragment map to catch new units */
4671 r = unit_file_build_name_map(&u->manager->lookup_paths,
4672 &u->manager->unit_cache_mtime,
4673 &u->manager->unit_id_map,
4674 &u->manager->unit_name_map,
4675 &u->manager->unit_path_cache);
9946996c 4676 if (r < 0)
91e0ee5f
ZJS
4677 log_error_errno(r, "Failed to rebuild name map: %m");
4678
e8630e69
ZJS
4679 r = unit_file_find_fragment(u->manager->unit_id_map,
4680 u->manager->unit_name_map,
4681 u->id,
4682 &fragment,
4683 &names);
4684 if (r < 0 && r != -ENOENT)
294d81f1
LP
4685 return r;
4686
e8630e69
ZJS
4687 if (fragment) {
4688 /* Open the file, check if this is a mask, otherwise read. */
4689 _cleanup_fclose_ FILE *f = NULL;
0301abf4 4690
e8630e69
ZJS
4691 /* Try to open the file name. A symlink is OK, for example for linked files or masks. We
4692 * expect that all symlinks within the lookup paths have been already resolved, but we don't
4693 * verify this here. */
4694 f = fopen(fragment, "re");
4695 if (!f)
4696 return log_unit_notice_errno(u, errno, "Failed to open %s: %m", fragment);
6ccb1b44 4697
e8630e69
ZJS
4698 if (fstat(fileno(f), &st) < 0)
4699 return -errno;
294d81f1 4700
e8630e69 4701 r = free_and_strdup(&u->fragment_path, fragment);
7410616c
LP
4702 if (r < 0)
4703 return r;
294d81f1 4704
e8630e69 4705 if (null_or_empty(&st)) {
88414eed
LP
4706 /* Unit file is masked */
4707
4708 u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
e8630e69
ZJS
4709 u->fragment_mtime = 0;
4710 } else {
4711 u->load_state = UNIT_LOADED;
4712 u->fragment_mtime = timespec_load(&st.st_mtim);
4713
4714 /* Now, parse the file contents */
4715 r = config_parse(u->id, fragment, f,
4716 UNIT_VTABLE(u)->sections,
4717 config_item_perf_lookup, load_fragment_gperf_lookup,
4718 CONFIG_PARSE_ALLOW_INCLUDE, u);
bb28e684 4719 if (r == -ENOEXEC)
e8630e69
ZJS
4720 log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
4721 if (r < 0)
4722 return r;
bb28e684 4723 }
e8630e69 4724 }
890f434c 4725
5a1c1b53
FB
4726 if (u->source_path) {
4727 if (stat(u->source_path, &st) >= 0)
4728 u->source_mtime = timespec_load(&st.st_mtim);
4729 else
4730 u->source_mtime = 0;
4731 }
4732
e8630e69
ZJS
4733 /* We do the merge dance here because for some unit types, the unit might have aliases which are not
4734 * declared in the file system. In particular, this is true (and frequent) for device and swap units.
4735 */
4736 Unit *merged;
4737 const char *id = u->id;
4738 _cleanup_free_ char *free_id = NULL;
294d81f1 4739
e8630e69
ZJS
4740 if (fragment) {
4741 id = basename(fragment);
4742 if (unit_name_is_valid(id, UNIT_NAME_TEMPLATE)) {
4743 assert(u->instance); /* If we're not trying to use a template for non-instanced unit,
4744 * this must be set. */
890f434c 4745
e8630e69
ZJS
4746 r = unit_name_replace_instance(id, u->instance, &free_id);
4747 if (r < 0)
4748 return log_debug_errno(r, "Failed to build id (%s + %s): %m", id, u->instance);
4749 id = free_id;
abc08d4d 4750 }
071830ff
LP
4751 }
4752
e8630e69
ZJS
4753 merged = u;
4754 r = merge_by_names(&merged, names, id);
4755 if (r < 0)
4756 return r;
4757
4758 if (merged != u)
4759 u->load_state = UNIT_MERGED;
4760
23a177ef 4761 return 0;
3efd4195 4762}
e537352b
LP
4763
4764void unit_dump_config_items(FILE *f) {
f975e971
LP
4765 static const struct {
4766 const ConfigParserCallback callback;
4767 const char *rvalue;
4768 } table[] = {
17df7223 4769 { config_parse_warn_compat, "NOTSUPPORTED" },
f975e971
LP
4770 { config_parse_int, "INTEGER" },
4771 { config_parse_unsigned, "UNSIGNED" },
5556b5fe 4772 { config_parse_iec_size, "SIZE" },
59f448cf 4773 { config_parse_iec_uint64, "SIZE" },
5556b5fe 4774 { config_parse_si_size, "SIZE" },
f975e971
LP
4775 { config_parse_bool, "BOOLEAN" },
4776 { config_parse_string, "STRING" },
4777 { config_parse_path, "PATH" },
4778 { config_parse_unit_path_printf, "PATH" },
4779 { config_parse_strv, "STRING [...]" },
4780 { config_parse_exec_nice, "NICE" },
4781 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
4782 { config_parse_exec_io_class, "IOCLASS" },
4783 { config_parse_exec_io_priority, "IOPRIORITY" },
4784 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
4785 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
4786 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
4787 { config_parse_mode, "MODE" },
4788 { config_parse_unit_env_file, "FILE" },
52c239d7
LB
4789 { config_parse_exec_output, "OUTPUT" },
4790 { config_parse_exec_input, "INPUT" },
ca37242e
LP
4791 { config_parse_log_facility, "FACILITY" },
4792 { config_parse_log_level, "LEVEL" },
f975e971 4793 { config_parse_exec_secure_bits, "SECUREBITS" },
a103496c 4794 { config_parse_capability_set, "BOUNDINGSET" },
4f424df7 4795 { config_parse_rlimit, "LIMIT" },
f975e971 4796 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
4797 { config_parse_exec, "PATH [ARGUMENT [...]]" },
4798 { config_parse_service_type, "SERVICETYPE" },
4799 { config_parse_service_restart, "SERVICERESTART" },
f975e971 4800 { config_parse_kill_mode, "KILLMODE" },
f757855e 4801 { config_parse_signal, "SIGNAL" },
f975e971
LP
4802 { config_parse_socket_listen, "SOCKET [...]" },
4803 { config_parse_socket_bind, "SOCKETBIND" },
4804 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 4805 { config_parse_sec, "SECONDS" },
d88a251b 4806 { config_parse_nsec, "NANOSECONDS" },
94828d2d 4807 { config_parse_namespace_path_strv, "PATH [...]" },
d2d6c096 4808 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
7c8fa05c 4809 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
4810 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4811 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 4812 { config_parse_trigger_unit, "UNIT" },
f975e971 4813 { config_parse_timer, "TIMER" },
f975e971 4814 { config_parse_path_spec, "PATH" },
f975e971
LP
4815 { config_parse_notify_access, "ACCESS" },
4816 { config_parse_ip_tos, "TOS" },
4817 { config_parse_unit_condition_path, "CONDITION" },
4818 { config_parse_unit_condition_string, "CONDITION" },
4819 { config_parse_unit_condition_null, "CONDITION" },
a016b922 4820 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
4821 { config_parse_documentation, "URL" },
4822 { config_parse_service_timeout, "SECONDS" },
87a47f99 4823 { config_parse_emergency_action, "ACTION" },
7f0386f6
LP
4824 { config_parse_set_status, "STATUS" },
4825 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 4826 { config_parse_environ, "ENVIRON" },
349cc4a5 4827#if HAVE_SECCOMP
17df7223 4828 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 4829 { config_parse_syscall_archs, "ARCHS" },
17df7223 4830 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 4831 { config_parse_address_families, "FAMILIES" },
add00535 4832 { config_parse_restrict_namespaces, "NAMESPACES" },
c0467cf3 4833#endif
7f0386f6 4834 { config_parse_cpu_shares, "SHARES" },
984faf29 4835 { config_parse_cg_weight, "WEIGHT" },
7f0386f6
LP
4836 { config_parse_memory_limit, "LIMIT" },
4837 { config_parse_device_allow, "DEVICE" },
4838 { config_parse_device_policy, "POLICY" },
13c31542 4839 { config_parse_io_limit, "LIMIT" },
13c31542 4840 { config_parse_io_device_weight, "DEVICEWEIGHT" },
6ae4283c 4841 { config_parse_io_device_latency, "DEVICELATENCY" },
7f0386f6
LP
4842 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4843 { config_parse_blockio_weight, "WEIGHT" },
4844 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4845 { config_parse_long, "LONG" },
4846 { config_parse_socket_service, "SERVICE" },
349cc4a5 4847#if HAVE_SELINUX
6a6751fe
LP
4848 { config_parse_exec_selinux_context, "LABEL" },
4849#endif
4850 { config_parse_job_mode, "MODE" },
4851 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 4852 { config_parse_personality, "PERSONALITY" },
f975e971
LP
4853 };
4854
4855 const char *prev = NULL;
4856 const char *i;
4857
4858 assert(f);
e537352b 4859
f975e971
LP
4860 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4861 const char *rvalue = "OTHER", *lvalue;
313b7856 4862 const ConfigPerfItem *p;
f975e971
LP
4863 size_t prefix_len;
4864 const char *dot;
313b7856 4865 unsigned j;
f975e971
LP
4866
4867 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4868
313b7856
LP
4869 /* Hide legacy settings */
4870 if (p->parse == config_parse_warn_compat &&
4871 p->ltype == DISABLED_LEGACY)
4872 continue;
4873
4874 for (j = 0; j < ELEMENTSOF(table); j++)
4875 if (p->parse == table[j].callback) {
4876 rvalue = table[j].rvalue;
4877 break;
4878 }
4879
f975e971
LP
4880 dot = strchr(i, '.');
4881 lvalue = dot ? dot + 1 : i;
4882 prefix_len = dot-i;
4883
4884 if (dot)
641906e9 4885 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
4886 if (prev)
4887 fputc('\n', f);
4888
4889 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
4890 }
4891
f975e971
LP
4892 fprintf(f, "%s=%s\n", lvalue, rvalue);
4893 prev = i;
4894 }
e537352b 4895}
a07a7324
FS
4896
4897int config_parse_cpu_affinity2(
4898 const char *unit,
4899 const char *filename,
4900 unsigned line,
4901 const char *section,
4902 unsigned section_line,
4903 const char *lvalue,
4904 int ltype,
4905 const char *rvalue,
4906 void *data,
4907 void *userdata) {
4908
4909 CPUSet *affinity = data;
4910
4911 assert(affinity);
4912
4913 (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue);
4914
4915 return 0;
4916}
4917
4918int config_parse_show_status(
4919 const char* unit,
4920 const char *filename,
4921 unsigned line,
4922 const char *section,
4923 unsigned section_line,
4924 const char *lvalue,
4925 int ltype,
4926 const char *rvalue,
4927 void *data,
4928 void *userdata) {
4929
4930 int k;
4931 ShowStatus *b = data;
4932
4933 assert(filename);
4934 assert(lvalue);
4935 assert(rvalue);
4936 assert(data);
4937
4938 k = parse_show_status(rvalue, b);
4939 if (k < 0) {
4940 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
4941 return 0;
4942 }
4943
4944 return 0;
4945}
4946
4947int config_parse_output_restricted(
4948 const char* unit,
4949 const char *filename,
4950 unsigned line,
4951 const char *section,
4952 unsigned section_line,
4953 const char *lvalue,
4954 int ltype,
4955 const char *rvalue,
4956 void *data,
4957 void *userdata) {
4958
4959 ExecOutput t, *eo = data;
4960
4961 assert(filename);
4962 assert(lvalue);
4963 assert(rvalue);
4964 assert(data);
4965
4966 t = exec_output_from_string(rvalue);
4967 if (t < 0) {
4968 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue);
4969 return 0;
4970 }
4971
4972 if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
4973 log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
4974 return 0;
4975 }
4976
4977 *eo = t;
4978 return 0;
4979}
4980
4981int config_parse_crash_chvt(
4982 const char* unit,
4983 const char *filename,
4984 unsigned line,
4985 const char *section,
4986 unsigned section_line,
4987 const char *lvalue,
4988 int ltype,
4989 const char *rvalue,
4990 void *data,
4991 void *userdata) {
4992
4993 int r;
4994
4995 assert(filename);
4996 assert(lvalue);
4997 assert(rvalue);
4998 assert(data);
4999
5000 r = parse_crash_chvt(rvalue, data);
5001 if (r < 0) {
5002 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
5003 return 0;
5004 }
5005
5006 return 0;
5007}
eb34a981
LP
5008
5009int config_parse_swap_priority(
5010 const char *unit,
5011 const char *filename,
5012 unsigned line,
5013 const char *section,
5014 unsigned section_line,
5015 const char *lvalue,
5016 int ltype,
5017 const char *rvalue,
5018 void *data,
5019 void *userdata) {
5020
5021 Swap *s = userdata;
5022 int r, priority;
5023
5024 assert(s);
5025 assert(filename);
5026 assert(lvalue);
5027 assert(rvalue);
5028 assert(data);
5029
5030 if (isempty(rvalue)) {
5031 s->parameters_fragment.priority = -1;
5032 s->parameters_fragment.priority_set = false;
5033 return 0;
5034 }
5035
5036 r = safe_atoi(rvalue, &priority);
5037 if (r < 0) {
5038 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid swap pririty '%s', ignoring.", rvalue);
5039 return 0;
5040 }
5041
5042 if (priority < -1) {
5043 log_syntax(unit, LOG_ERR, filename, line, 0, "Sorry, swap priorities smaller than -1 may only be assigned by the kernel itself, ignoring: %s", rvalue);
5044 return 0;
5045 }
5046
5047 if (priority > 32767) {
5048 log_syntax(unit, LOG_ERR, filename, line, 0, "Swap priority out of range, ignoring: %s", rvalue);
5049 return 0;
5050 }
5051
5052 s->parameters_fragment.priority = priority;
5053 s->parameters_fragment.priority_set = true;
5054 return 0;
5055}