+ return log_oom();
+
+ LIST_PREPEND(Condition, conditions, u->conditions, c);
+ return 0;
+}
+
+int config_parse_unit_condition_string(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ConditionType cond = ltype;
+ Unit *u = data;
+ bool trigger, negate;
+ Condition *c;
+ _cleanup_free_ char *s = NULL;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
+ trigger = rvalue[0] == '|';
+ if (trigger)
+ rvalue++;
+
+ negate = rvalue[0] == '!';
+ if (negate)
+ rvalue++;
+
+ s = unit_full_printf(u, rvalue);
+ if (!s)
+ return log_oom();
+
+ c = condition_new(cond, s, trigger, negate);
+ if (!c)
+ return log_oom();
+
+ LIST_PREPEND(Condition, conditions, u->conditions, c);
+ return 0;
+}
+
+int config_parse_unit_condition_null(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = data;
+ Condition *c;
+ bool trigger, negate;
+ int b;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
+ trigger = rvalue[0] == '|';
+ if (trigger)
+ rvalue++;
+
+ negate = rvalue[0] == '!';
+ if (negate)
+ rvalue++;
+
+ b = parse_boolean(rvalue);
+ if (b < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -b,
+ "Failed to parse boolean value in condition, ignoring: %s",
+ rvalue);
+ return 0;
+ }
+
+ if (!b)
+ negate = !negate;
+
+ c = condition_new(CONDITION_NULL, NULL, trigger, negate);
+ if (!c)
+ return log_oom();
+
+ LIST_PREPEND(Condition, conditions, u->conditions, c);
+ return 0;
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
+
+int config_parse_unit_requires_mounts_for(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ int r;
+ bool empty_before;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ empty_before = !u->requires_mounts_for;
+
+ r = config_parse_path_strv(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
+
+ /* Make it easy to find units with requires_mounts set */
+ if (empty_before && u->requires_mounts_for)
+ LIST_PREPEND(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
+
+ return r;
+}
+
+int config_parse_documentation(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ int r;
+ char **a, **b;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(u->documentation);
+ u->documentation = NULL;
+ return 0;
+ }
+
+ r = config_parse_unit_strv_printf(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
+ if (r < 0)
+ return r;
+
+ for (a = b = u->documentation; a && *a; a++) {
+
+ if (is_valid_documentation_url(*a))
+ *(b++) = *a;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid URL, ignoring: %s", *a);
+ free(*a);
+ }
+ }
+ *b = NULL;
+
+ return r;
+}
+
+static void syscall_set(uint32_t *p, int nr) {
+ nr = SYSCALL_TO_INDEX(nr);
+ p[nr >> 4] |= 1 << (nr & 31);
+}
+
+static void syscall_unset(uint32_t *p, int nr) {
+ nr = SYSCALL_TO_INDEX(nr);
+ p[nr >> 4] &= ~(1 << (nr & 31));
+}
+
+int config_parse_syscall_filter(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ Unit *u = userdata;
+ bool invert = false;
+ char *w;
+ size_t l;
+ char *state;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ free(c->syscall_filter);
+ c->syscall_filter = NULL;
+ return 0;
+ }
+
+ if (rvalue[0] == '~') {
+ invert = true;
+ rvalue++;
+ }
+
+ if (!c->syscall_filter) {
+ size_t n;
+
+ n = (syscall_max() + 31) >> 4;
+ c->syscall_filter = new(uint32_t, n);
+ if (!c->syscall_filter)
+ return log_oom();
+
+ memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
+
+ /* Add these by default */
+ syscall_set(c->syscall_filter, __NR_execve);
+ syscall_set(c->syscall_filter, __NR_rt_sigreturn);
+#ifdef __NR_sigreturn
+ syscall_set(c->syscall_filter, __NR_sigreturn);
+#endif
+ syscall_set(c->syscall_filter, __NR_exit_group);
+ syscall_set(c->syscall_filter, __NR_exit);
+ }
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ int id;
+ _cleanup_free_ char *t = NULL;
+
+ t = strndup(w, l);
+ if (!t)
+ return log_oom();
+
+ id = syscall_from_name(t);
+ if (id < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse syscall, ignoring: %s", t);
+ continue;
+ }
+
+ if (invert)
+ syscall_unset(c->syscall_filter, id);
+ else
+ syscall_set(c->syscall_filter, id);
+ }
+
+ c->no_new_privileges = true;