#include "unit-name.h"
#include "bus-errors.h"
-#define COMMENTS "#;\n"
+#ifndef HAVE_SYSV_COMPAT
+static int config_parse_warn_compat(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue);
+ return 0;
+}
+#endif
static int config_parse_deps(
const char *filename,
void *data,
void *userdata) {
- int r;
SocketPort *p;
Socket *s;
} else {
p->type = SOCKET_SOCKET;
- if ((r = socket_address_parse(&p->address, rvalue)) < 0) {
+ if (socket_address_parse(&p->address, rvalue) < 0) {
log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
free(p);
return 0;
void *userdata) {
ExecContext *c = data;
- int priority, r;
+ int priority;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((r = safe_atoi(rvalue, &priority)) < 0) {
+ if (safe_atoi(rvalue, &priority) < 0) {
log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue);
return 0;
}
}
c->nice = priority;
- c->nice_set = false;
+ c->nice_set = true;
return 0;
}
void *userdata) {
ExecContext *c = data;
- int oa, r;
+ int oa;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((r = safe_atoi(rvalue, &oa)) < 0) {
+ if (safe_atoi(rvalue, &oa) < 0) {
log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
return 0;
}
k = 0;
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, ";", l) == 0)
+ if (strncmp(w, ";", MAX(l, 1U)) == 0)
break;
k++;
k = 0;
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, ";", l) == 0)
+ if (strncmp(w, ";", MAX(l, 1U)) == 0)
break;
if (honour_argv0 && w == rvalue) {
void *userdata) {
usec_t *usec = data;
- int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((r = parse_usec(rvalue, usec)) < 0) {
+ if (parse_usec(rvalue, usec) < 0) {
log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
return 0;
}
ExecContext *c = data;
unsigned long u;
- int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((r = safe_atolu(rvalue, &u)) < 0) {
+ if (safe_atolu(rvalue, &u) < 0) {
log_error("[%s:%u] Failed to parse time slack value, ignoring: %s", filename, line, rvalue);
return 0;
}
struct rlimit **rl = data;
unsigned long long u;
- int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((r = safe_atollu(rvalue, &u)) < 0) {
+ if (safe_atollu(rvalue, &u) < 0) {
log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
+#ifdef HAVE_SYSV_COMPAT
static int config_parse_sysv_priority(
const char *filename,
unsigned line,
void *userdata) {
int *priority = data;
- int r, i;
+ int i;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((r = safe_atoi(rvalue, &i)) < 0 || i < 0) {
+ if (safe_atoi(rvalue, &i) < 0 || i < 0) {
log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue);
return 0;
}
*priority = (int) i;
return 0;
}
+#endif
+
+static int config_parse_fsck_passno(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int *passno = data;
+ int i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (safe_atoi(rvalue, &i) || i < 0) {
+ log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *passno = (int) i;
+ return 0;
+}
static DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
assert(rvalue);
assert(sig);
- if ((r = signal_from_string(rvalue)) <= 0)
- if (startswith(rvalue, "SIG"))
- r = signal_from_string(rvalue+3);
-
- if (r <= 0) {
+ if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
return 0;
}
assert(data);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, "shared", l) == 0)
+ if (strncmp(w, "shared", MAX(l, 6U)) == 0)
flags |= MS_SHARED;
- else if (strncmp(w, "slave", l) == 0)
+ else if (strncmp(w, "slave", MAX(l, 5U)) == 0)
flags |= MS_SLAVE;
- else if (strncmp(w, "private", l) == 0)
+ else if (strncmp(w, "private", MAX(l, 7U)) == 0)
flags |= MS_PRIVATE;
else {
log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue);
Timer *t = data;
usec_t u;
- int r;
TimerValue *v;
TimerBase b;
return 0;
}
- if ((r = parse_usec(rvalue, &u)) < 0) {
+ if (parse_usec(rvalue, &u) < 0) {
log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
-static int config_parse_env_file(
+static int config_parse_socket_service(
const char *filename,
unsigned line,
const char *section,
void *data,
void *userdata) {
- FILE *f;
+ Socket *s = data;
int r;
- char ***env = data;
+ DBusError error;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (!(f = fopen(rvalue, "re"))) {
- log_error("[%s:%u] Failed to open environment file '%s', ignoring: %m", filename, line, rvalue);
+ dbus_error_init(&error);
+
+ if (!endswith(rvalue, ".service")) {
+ log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
return 0;
}
- while (!feof(f)) {
- char l[LINE_MAX], *p;
- char **t;
+ if ((r = manager_load_unit(s->meta.manager, rvalue, NULL, &error, (Unit**) &s->service)) < 0) {
+ log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+ dbus_error_free(&error);
+ return 0;
+ }
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
+ return 0;
+}
- r = -errno;
- log_error("[%s:%u] Failed to read environment file '%s', ignoring: %m", filename, line, rvalue);
- r = 0;
- goto finish;
- }
+static int config_parse_service_sockets(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Service *s = data;
+ int r;
+ DBusError error;
+ char *state, *w;
+ size_t l;
- p = strstrip(l);
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
- if (!*p)
+ dbus_error_init(&error);
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ char *t;
+ Unit *sock;
+
+ if (!(t = strndup(w, l)))
+ return -ENOMEM;
+
+ if (!endswith(t, ".socket")) {
+ log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
+ free(t);
continue;
+ }
+
+ r = manager_load_unit(s->meta.manager, t, NULL, &error, &sock);
+ free(t);
- if (strchr(COMMENTS, *p))
+ if (r < 0) {
+ log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+ dbus_error_free(&error);
continue;
+ }
- t = strv_env_set(*env, p);
- strv_free(*env);
- *env = t;
+ if ((r = set_ensure_allocated(&s->configured_sockets, trivial_hash_func, trivial_compare_func)) < 0)
+ return r;
+
+ if ((r = set_put(s->configured_sockets, sock)) < 0)
+ return r;
}
- r = 0;
+ return 0;
+}
-finish:
- if (f)
- fclose(f);
+static int config_parse_env_file(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- return r;
+ char ***env = data, **k;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (!path_is_absolute(rvalue[0] == '-' ? rvalue + 1 : rvalue)) {
+ log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!(k = strv_append(*env, rvalue)))
+ return -ENOMEM;
+
+ strv_free(*env);
+ *env = k;
+
+ return 0;
}
static int config_parse_ip_tos(
void *userdata) {
int *ip_tos = data, x;
- int r;
assert(filename);
assert(lvalue);
assert(data);
if ((x = ip_tos_from_string(rvalue)) < 0)
- if ((r = safe_atoi(rvalue, &x)) < 0) {
+ if (safe_atoi(rvalue, &x) < 0) {
log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
+static int config_parse_condition_path(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = data;
+ bool trigger, negate;
+ Condition *c;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((trigger = rvalue[0] == '|'))
+ rvalue++;
+
+ if ((negate = rvalue[0] == '!'))
+ rvalue++;
+
+ if (!path_is_absolute(rvalue)) {
+ log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!(c = condition_new(streq(lvalue, "ConditionPathExists") ? CONDITION_PATH_EXISTS : CONDITION_DIRECTORY_NOT_EMPTY,
+ rvalue, trigger, negate)))
+ return -ENOMEM;
+
+ LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
+ return 0;
+}
+
+static int config_parse_condition_kernel(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = data;
+ bool trigger, negate;
+ Condition *c;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((trigger = rvalue[0] == '|'))
+ rvalue++;
+
+ if ((negate = rvalue[0] == '!'))
+ rvalue++;
+
+ if (!(c = condition_new(CONDITION_KERNEL_COMMAND_LINE, rvalue, trigger, negate)))
+ return -ENOMEM;
+
+ LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
+ return 0;
+}
+
+static int config_parse_condition_virt(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = data;
+ bool trigger, negate;
+ Condition *c;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((trigger = rvalue[0] == '|'))
+ rvalue++;
+
+ if ((negate = rvalue[0] == '!'))
+ rvalue++;
+
+ if (!(c = condition_new(CONDITION_VIRTUALIZATION, rvalue, trigger, negate)))
+ return -ENOMEM;
+
+ LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
+ return 0;
+}
+
+static int config_parse_condition_null(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ 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 ((trigger = rvalue[0] == '|'))
+ rvalue++;
+
+ if ((negate = rvalue[0] == '!'))
+ rvalue++;
+
+ if ((b = parse_boolean(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!b)
+ negate = !negate;
+
+ if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
+ return -ENOMEM;
+
+ LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
+ return 0;
+}
+
static DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
#define FOLLOW_MAX 8
* unit name. */
name = file_name_from_path(*filename);
- if (unit_name_is_valid(name)) {
+ if (unit_name_is_valid(name, false)) {
if (!(id = set_get(names, name))) {
if (!(id = strdup(name)))
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
{ config_parse_service_type, "SERVICETYPE" },
{ config_parse_service_restart, "SERVICERESTART" },
+#ifdef HAVE_SYSV_COMPAT
{ config_parse_sysv_priority, "SYSVPRIORITY" },
+#else
+ { config_parse_warn_compat, "NOTSUPPORTED" },
+#endif
{ config_parse_kill_mode, "KILLMODE" },
{ config_parse_kill_signal, "SIGNAL" },
{ config_parse_listen, "SOCKET [...]" },
{ config_parse_path_unit, "UNIT" },
{ config_parse_notify_access, "ACCESS" },
{ config_parse_ip_tos, "TOS" },
+ { config_parse_condition_path, "CONDITION" },
+ { config_parse_condition_kernel, "CONDITION" },
+ { config_parse_condition_null, "CONDITION" },
+ { config_parse_condition_virt, "CONDITION" },
};
assert(f);
{ "CPUAffinity", config_parse_cpu_affinity, &(context), section }, \
{ "UMask", config_parse_mode, &(context).umask, section }, \
{ "Environment", config_parse_strv, &(context).environment, section }, \
- { "EnvironmentFile", config_parse_env_file, &(context).environment, section }, \
+ { "EnvironmentFile", config_parse_env_file, &(context).environment_files, section }, \
{ "StandardInput", config_parse_input, &(context).std_input, section }, \
{ "StandardOutput", config_parse_output, &(context).std_output, section }, \
{ "StandardError", config_parse_output, &(context).std_error, section }, \
{ "TCPWrapName", config_parse_string_printf, &(context).tcpwrap_name, section }, \
{ "PAMName", config_parse_string_printf, &(context).pam_name, section }, \
{ "KillMode", config_parse_kill_mode, &(context).kill_mode, section }, \
- { "KillSignal", config_parse_kill_signal, &(context).kill_signal, section }
+ { "KillSignal", config_parse_kill_signal, &(context).kill_signal, section }, \
+ { "SendSIGKILL", config_parse_bool, &(context).send_sigkill, section }, \
+ { "UtmpIdentifier", config_parse_string_printf, &(context).utmp_id, section }
const ConfigItem items[] = {
{ "Names", config_parse_names, u, "Unit" },
{ "Requisite", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE), "Unit" },
{ "RequisiteOverridable", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE_OVERRIDABLE), "Unit" },
{ "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Unit" },
+ { "BindTo", config_parse_deps, UINT_TO_PTR(UNIT_BIND_TO), "Unit" },
{ "Conflicts", config_parse_deps, UINT_TO_PTR(UNIT_CONFLICTS), "Unit" },
{ "Before", config_parse_deps, UINT_TO_PTR(UNIT_BEFORE), "Unit" },
{ "After", config_parse_deps, UINT_TO_PTR(UNIT_AFTER), "Unit" },
{ "OnFailure", config_parse_deps, UINT_TO_PTR(UNIT_ON_FAILURE), "Unit" },
- { "RecursiveStop", config_parse_bool, &u->meta.recursive_stop, "Unit" },
{ "StopWhenUnneeded", config_parse_bool, &u->meta.stop_when_unneeded, "Unit" },
{ "RefuseManualStart", config_parse_bool, &u->meta.refuse_manual_start, "Unit" },
{ "RefuseManualStop", config_parse_bool, &u->meta.refuse_manual_stop, "Unit" },
{ "AllowIsolate", config_parse_bool, &u->meta.allow_isolate, "Unit" },
{ "DefaultDependencies", config_parse_bool, &u->meta.default_dependencies, "Unit" },
- { "IgnoreDependencyFailure",config_parse_bool, &u->meta.ignore_dependency_failure, "Unit" },
{ "JobTimeoutSec", config_parse_usec, &u->meta.job_timeout, "Unit" },
+ { "ConditionPathExists", config_parse_condition_path, u, "Unit" },
+ { "ConditionDirectoryNotEmpty", config_parse_condition_path, u, "Unit" },
+ { "ConditionKernelCommandLine", config_parse_condition_kernel, u, "Unit" },
+ { "ConditionVirtualization",config_parse_condition_virt, u, "Unit" },
+ { "ConditionNull", config_parse_condition_null, u, "Unit" },
{ "PIDFile", config_parse_path, &u->service.pid_file, "Service" },
{ "ExecStartPre", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_PRE, "Service" },
{ "PermissionsStartOnly", config_parse_bool, &u->service.permissions_start_only, "Service" },
{ "RootDirectoryStartOnly", config_parse_bool, &u->service.root_directory_start_only, "Service" },
{ "RemainAfterExit", config_parse_bool, &u->service.remain_after_exit, "Service" },
+ { "GuessMainPID", config_parse_bool, &u->service.guess_main_pid, "Service" },
+#ifdef HAVE_SYSV_COMPAT
{ "SysVStartPriority", config_parse_sysv_priority, &u->service.sysv_start_priority, "Service" },
+#else
+ { "SysVStartPriority", config_parse_warn_compat, NULL, "Service" },
+#endif
{ "NonBlocking", config_parse_bool, &u->service.exec_context.non_blocking, "Service" },
{ "BusName", config_parse_string_printf, &u->service.bus_name, "Service" },
{ "NotifyAccess", config_parse_notify_access, &u->service.notify_access, "Service" },
+ { "Sockets", config_parse_service_sockets, &u->service, "Service" },
+ { "FsckPassNo", config_parse_fsck_passno, &u->service.fsck_passno, "Service" },
EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
{ "ListenStream", config_parse_listen, &u->socket, "Socket" },
{ "PipeSize", config_parse_size, &u->socket.pipe_size, "Socket" },
{ "FreeBind", config_parse_bool, &u->socket.free_bind, "Socket" },
{ "TCPCongestion", config_parse_string, &u->socket.tcp_congestion, "Socket" },
+ { "Service", config_parse_socket_service, &u->socket, "Socket" },
EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
{ "What", config_parse_string, &u->mount.parameters_fragment.what, "Mount" },
{ "What", config_parse_path, &u->swap.parameters_fragment.what, "Swap" },
{ "Priority", config_parse_int, &u->swap.parameters_fragment.priority, "Swap" },
+ { "TimeoutSec", config_parse_usec, &u->swap.timeout_usec, "Swap" },
+ EXEC_CONTEXT_CONFIG_ITEMS(u->swap.exec_context, "Swap"),
{ "OnActiveSec", config_parse_timer, &u->timer, "Timer" },
{ "OnBootSec", config_parse_timer, &u->timer, "Timer" },
goto finish;
}
- /* Now, parse the file contents */
- if ((r = config_parse(filename, f, sections, items, false, u)) < 0)
- goto finish;
+ if (null_or_empty(&st))
+ u->meta.load_state = UNIT_MASKED;
+ else {
+ /* Now, parse the file contents */
+ if ((r = config_parse(filename, f, sections, items, false, u)) < 0)
+ goto finish;
+
+ u->meta.load_state = UNIT_LOADED;
+ }
free(u->meta.fragment_path);
u->meta.fragment_path = filename;
u->meta.fragment_mtime = timespec_load(&st.st_mtim);
- u->meta.load_state = UNIT_LOADED;
r = 0;
finish:
}
/* And now, try looking for it under the suggested (originally linked) path */
- if (u->meta.load_state == UNIT_STUB && u->meta.fragment_path)
+ if (u->meta.load_state == UNIT_STUB && u->meta.fragment_path) {
+
if ((r = load_from_path(u, u->meta.fragment_path)) < 0)
return r;
+ if (u->meta.load_state == UNIT_STUB) {
+ /* Hmm, this didn't work? Then let's get rid
+ * of the fragment path stored for us, so that
+ * we don't point to an invalid location. */
+ free(u->meta.fragment_path);
+ u->meta.fragment_path = NULL;
+ }
+ }
+
/* Look for a template */
if (u->meta.load_state == UNIT_STUB && u->meta.instance) {
char *k;