summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2010-10-13 02:15:41 +0200
committerLennart Poettering <lennart@poettering.net>2010-10-13 02:15:41 +0200
commit52661efd21608dc7e0ac26b714a9254ed6180ddb (patch)
tree5f64764566711e6a38a664cfc0ee9f9a997e4857
parente04aad61bb5eff117e51631727a3ef2807c75d6b (diff)
unit: add minimal condition checker for unit startup
-rw-r--r--Makefile.am3
-rw-r--r--TODO4
-rw-r--r--man/systemd.unit.xml42
-rw-r--r--src/condition.c158
-rw-r--r--src/condition.h57
-rw-r--r--src/execute.c1
-rw-r--r--src/load-fragment.c65
-rw-r--r--src/main.c4
-rw-r--r--src/unit.c10
-rw-r--r--src/unit.h4
10 files changed, 341 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am
index c4d4d2773d..a028553cd3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -402,7 +402,8 @@ libsystemd_core_la_SOURCES = \
src/fdset.c \
src/namespace.c \
src/tcpwrap.c \
- src/cgroup-util.c
+ src/cgroup-util.c \
+ src/condition.c
libsystemd_core_la_CFLAGS = \
$(AM_CFLAGS) \
diff --git a/TODO b/TODO
index 4052945897..6d905dc68c 100644
--- a/TODO
+++ b/TODO
@@ -42,8 +42,6 @@
* systemctl list-jobs - show dependencies
-* ConditionFileExists=, ConditionKernelCommandLine=, ConditionEnvironment= with !
-
* accountsservice is borked
* auditd service files
@@ -84,6 +82,8 @@
* fix plymouth socket, when plymouth started to use a clean one
+* parse early boot time env var from dracut
+
External:
* patch kernel to add /proc/swaps change notifications
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index e59c1a16c4..e54cafaabc 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -585,6 +585,48 @@
change.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ConditionPathExists=</varname></term>
+ <term><varname>ConditionKernelCommandLine=</varname></term>
+
+ <listitem><para>Before starting a unit
+ verify that the specified condition is
+ true. With
+ <varname>ConditionPathExists=</varname>
+ a file existance condition can be
+ checked before a unit is started. If
+ the specified absolute path name does
+ not exist startup of a unit will not
+ actually happen, however the unit is
+ still useful for ordering purposes in
+ this case. The condition is checked at
+ the time the queued start job is to be
+ executed. If the absolute path name
+ passed to
+ <varname>ConditionPathExists=</varname>
+ is prefixed with an exclamation mark
+ (!), the test is negated, and the unit
+ only started if the path does not
+ exist. Similarly
+ <varname>ConditionKernelCommandLine=</varname>
+ may be used to check whether a
+ specific kernel command line option is
+ set (or if prefixed with the
+ exclamation mark unset). The argument
+ must either be a single word, or an
+ assignment (i.e. two words, seperated
+ by the equality sign). In the former
+ case the kernel command line is search
+ for the word appearing as is, or as
+ left hand side of an assignment. In
+ the latter case the exact assignment
+ is looked for with right and left hand
+ side matching. If multiple conditions
+ are specified the unit will be
+ executed iff at least one of them
+ apply (i.e. a logical OR is
+ applied).</para></listitem>
+ </varlistentry>
</variablelist>
<para>Unit file may include a [Install] section, which
diff --git a/src/condition.c b/src/condition.c
new file mode 100644
index 0000000000..8c2db2d3e9
--- /dev/null
+++ b/src/condition.c
@@ -0,0 +1,158 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 ProFUSION embedded systems
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "condition.h"
+
+Condition* condition_new(ConditionType type, const char *parameter, bool negate) {
+ Condition *c;
+
+ c = new0(Condition, 1);
+ c->type = type;
+ c->negate = negate;
+
+ if (!(c->parameter = strdup(parameter))) {
+ free(c);
+ return NULL;
+ }
+
+ return c;
+}
+
+void condition_free(Condition *c) {
+ assert(c);
+
+ free(c->parameter);
+ free(c);
+}
+
+void condition_free_list(Condition *first) {
+ Condition *c, *n;
+
+ LIST_FOREACH_SAFE(conditions, c, n, first)
+ condition_free(c);
+}
+
+static bool test_kernel_command_line(const char *parameter) {
+ char *line, *w, *state, *word = NULL;
+ bool equal;
+ int r;
+ size_t l, pl;
+ bool found = false;
+
+ if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
+ log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+ return false;
+ }
+
+ equal = !!strchr(parameter, '=');
+ pl = strlen(parameter);
+
+ FOREACH_WORD_QUOTED(w, l, line, state) {
+
+ free(word);
+ if (!(word = strndup(w, l)))
+ break;
+
+ if (equal) {
+ if (streq(word, parameter)) {
+ found = true;
+ break;
+ }
+ } else {
+ if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
+ found = true;
+ break;
+ }
+ }
+
+ }
+
+ free(word);
+ free(line);
+
+ return found;
+}
+
+bool condition_test(Condition *c) {
+ assert(c);
+
+ switch(c->type) {
+
+ case CONDITION_PATH_EXISTS:
+ return (access(c->parameter, F_OK) >= 0) == !c->negate;
+
+ case CONDITION_KERNEL_COMMAND_LINE:
+ return !!test_kernel_command_line(c->parameter) == !c->negate;
+
+ default:
+ assert_not_reached("Invalid condition type.");
+ }
+}
+
+bool condition_test_list(Condition *first) {
+ Condition *c;
+
+ /* If the condition list is empty, then it is true */
+ if (!first)
+ return true;
+
+ /* Otherwise, if any of the conditions apply we return true */
+ LIST_FOREACH(conditions, c, first)
+ if (condition_test(c))
+ return true;
+
+ return false;
+}
+
+void condition_dump(Condition *c, FILE *f, const char *prefix) {
+ assert(c);
+ assert(f);
+
+ if (!prefix)
+ prefix = "";
+
+ fprintf(f,
+ "%s%s: %s%s\n",
+ prefix,
+ condition_type_to_string(c->type),
+ c->negate ? "!" : "",
+ c->parameter);
+}
+
+void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
+ Condition *c;
+
+ LIST_FOREACH(conditions, c, first)
+ condition_dump(c, f, prefix);
+}
+
+static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
+ [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
+ [CONDITION_PATH_EXISTS] = "ConditionPathExists"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
diff --git a/src/condition.h b/src/condition.h
new file mode 100644
index 0000000000..4e0d63cd51
--- /dev/null
+++ b/src/condition.h
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooconditionhfoo
+#define fooconditionhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "list.h"
+
+typedef enum ConditionType {
+ CONDITION_PATH_EXISTS,
+ CONDITION_KERNEL_COMMAND_LINE,
+ _CONDITION_TYPE_MAX,
+ _CONDITION_TYPE_INVALID = -1
+} ConditionType;
+
+typedef struct Condition {
+ ConditionType type;
+ char *parameter;
+ bool negate;
+
+ LIST_FIELDS(struct Condition, conditions);
+} Condition;
+
+Condition* condition_new(ConditionType type, const char *parameter, bool negate);
+void condition_free(Condition *c);
+void condition_free_list(Condition *c);
+
+bool condition_test(Condition *c);
+bool condition_test_list(Condition *c);
+
+void condition_dump(Condition *c, FILE *f, const char *prefix);
+void condition_dump_list(Condition *c, FILE *f, const char *prefix);
+
+const char* condition_type_to_string(ConditionType t);
+int condition_type_from_string(const char *s);
+
+#endif
diff --git a/src/execute.c b/src/execute.c
index 9c7e0d6b70..b5afa68108 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -1613,7 +1613,6 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
fprintf(f,
"%sUtmpIdentifier: %s\n",
prefix, c->utmp_id);
-
}
void exec_status_start(ExecStatus *s, pid_t pid) {
diff --git a/src/load-fragment.c b/src/load-fragment.c
index eb9861802b..2b5c8e70dd 100644
--- a/src/load-fragment.c
+++ b/src/load-fragment.c
@@ -1400,6 +1400,67 @@ static int config_parse_ip_tos(
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 negate;
+ Condition *c;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((negate = rvalue[0] == '!'))
+ rvalue++;
+
+ if (!path_is_absolute(rvalue)) {
+ log_error("[%s:%u] Path in condition not absolute: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!(c = condition_new(CONDITION_PATH_EXISTS, rvalue, 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 negate;
+ Condition *c;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((negate = rvalue[0] == '!'))
+ rvalue++;
+
+ if (!(c = condition_new(CONDITION_KERNEL_COMMAND_LINE, rvalue, 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
@@ -1571,6 +1632,8 @@ static void dump_items(FILE *f, const ConfigItem *items) {
{ 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" },
};
assert(f);
@@ -1692,6 +1755,8 @@ static int load_from_path(Unit *u, const char *path) {
{ "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" },
+ { "ConditionKernelCommandLine", config_parse_condition_kernel, u, "Unit" },
{ "PIDFile", config_parse_path, &u->service.pid_file, "Service" },
{ "ExecStartPre", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_PRE, "Service" },
diff --git a/src/main.c b/src/main.c
index fa306d6aad..15bd2e4d15 100644
--- a/src/main.c
+++ b/src/main.c
@@ -545,11 +545,9 @@ static int parse_config_file(void) {
}
static int parse_proc_cmdline(void) {
- char *line;
+ char *line, *w, *state;
int r;
- char *w;
size_t l;
- char *state;
if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
diff --git a/src/unit.c b/src/unit.c
index 9fc9be5c79..07978134de 100644
--- a/src/unit.c
+++ b/src/unit.c
@@ -375,6 +375,8 @@ void unit_free(Unit *u) {
set_free_free(u->meta.names);
+ condition_free_list(u->meta.conditions);
+
free(u->meta.instance);
free(u);
}
@@ -639,6 +641,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
if (u->meta.job_timeout > 0)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->meta.job_timeout));
+ condition_dump_list(u->meta.conditions, f, prefix);
+
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
Unit *other;
@@ -840,6 +844,12 @@ int unit_start(Unit *u) {
if (!UNIT_VTABLE(u)->start)
return -EBADR;
+ /* If the conditions failed, don't do anything at all */
+ if (!condition_test_list(u->meta.conditions)) {
+ log_debug("Starting of %s requested but condition failed. Ignoring.", u->meta.id);
+ return -EALREADY;
+ }
+
/* We don't suppress calls to ->start() here when we are
* already starting, to allow this request to be used as a
* "hurry up" call, for example when the unit is in some "auto
diff --git a/src/unit.h b/src/unit.h
index 605fa3774d..fa869ece8f 100644
--- a/src/unit.h
+++ b/src/unit.h
@@ -38,6 +38,7 @@ typedef enum UnitDependency UnitDependency;
#include "list.h"
#include "socket-util.h"
#include "execute.h"
+#include "condition.h"
#define DEFAULT_TIMEOUT_USEC (60*USEC_PER_SEC)
#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC)
@@ -154,6 +155,9 @@ struct Meta {
usec_t job_timeout;
+ /* Conditions to check */
+ LIST_HEAD(Condition, conditions);
+
dual_timestamp inactive_exit_timestamp;
dual_timestamp active_enter_timestamp;
dual_timestamp active_exit_timestamp;