<refsect1>
<title>Preset File Format</title>
- <para>The preset files contain a list of directives consisting of
- either the word <literal>enable</literal> or
- <literal>disable</literal> followed by a space and a unit name
- (possibly with shell style wildcards), separated by newlines.
- Empty lines and lines whose first non-whitespace character is <literal>#</literal> or
- <literal>;</literal> are ignored. Multiple instance names for unit
- templates may be specified as a space separated list at the end of
- the line instead of the customary position between <literal>@</literal>
- and the unit suffix.</para>
+ <para>The preset files contain a list of directives, one per line. Empty lines and lines whose first
+ non-whitespace character is <literal>#</literal> or <literal>;</literal> are ignored. Each directive
+ consists of one of the words <literal>enable</literal>, <literal>disable</literal>, or
+ <literal>ignore</literal>, followed by whitespace and a unit name. The unit name may contain shell-style
+ wildcards.</para>
+
+ <para>For the enable directive for template units, one or more instance names may be specified as a
+ space-separated list after the unit name. In this case, those instances will be enabled instead of the
+ instance specified via DefaultInstance= in the unit.</para>
<para>Presets must refer to the "real" unit file, and not to any aliases. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for a description of unit aliasing.</para>
- <para>Two different directives are understood:
- <literal>enable</literal> may be used to enable units by default,
- <literal>disable</literal> to disable units by default.</para>
+ <para>Three different directives are understood: <literal>enable</literal> may be used to enable units by
+ default, <literal>disable</literal> to disable units by default, and <literal>ignore</literal> to ignore
+ units and leave existing configuration intact.</para>
<para>If multiple lines apply to a unit name, the first matching
one takes precedence over all others.</para>
r = unit_get_unit_file_preset(u);
- return sd_bus_message_append(reply, "s",
- r < 0 ? NULL:
- r > 0 ? "enabled" : "disabled");
+ return sd_bus_message_append(reply, "s", preset_action_past_tense_to_string(r));
}
static int property_get_job(
return u->unit_file_state;
}
-int unit_get_unit_file_preset(Unit *u) {
+PresetAction unit_get_unit_file_preset(Unit *u) {
int r;
assert(u);
#include "bpf-program.h"
#include "condition.h"
#include "emergency-action.h"
+#include "install.h"
#include "list.h"
#include "show-status.h"
#include "set.h"
/* Cached unit file state and preset */
UnitFileState unit_file_state;
- int unit_file_preset;
+ PresetAction unit_file_preset;
/* Where the cpu.stat or cpuacct.usage was at the time the unit was started */
nsec_t cpu_usage_base;
void unit_trigger_notify(Unit *u);
UnitFileState unit_get_unit_file_state(Unit *u);
-int unit_get_unit_file_preset(Unit *u);
+PresetAction unit_get_unit_file_preset(Unit *u);
Unit* unit_ref_set(UnitRef *ref, Unit *source, Unit *target);
void unit_ref_unset(UnitRef *ref);
OrderedHashmap *have_processed;
} InstallContext;
-typedef enum {
- PRESET_UNKNOWN,
- PRESET_ENABLE,
- PRESET_DISABLE,
-} PresetAction;
-
struct UnitFilePresetRule {
char *pattern;
PresetAction action;
char **instances;
};
+/* NB! strings use past tense. */
+static const char *const preset_action_past_tense_table[_PRESET_ACTION_MAX] = {
+ [PRESET_UNKNOWN] = "unknown",
+ [PRESET_ENABLE] = "enabled",
+ [PRESET_DISABLE] = "disabled",
+ [PRESET_IGNORE] = "ignored",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(preset_action_past_tense, PresetAction);
+
static bool install_info_has_rules(const InstallInfo *i) {
assert(i);
};
}
+ parameter = first_word(l, "ignore");
+ if (parameter) {
+ char *pattern;
+
+ pattern = strdup(parameter);
+ if (!pattern)
+ return -ENOMEM;
+
+ rule = (UnitFilePresetRule) {
+ .pattern = pattern,
+ .action = PRESET_IGNORE,
+ };
+ }
+
if (rule.action) {
if (!GREEDY_REALLOC(ps.rules, ps.n_rules + 1))
return -ENOMEM;
switch (action) {
case PRESET_UNKNOWN:
log_debug("Preset files don't specify rule for %s. Enabling.", name);
- return 1;
+ return PRESET_ENABLE;
case PRESET_ENABLE:
if (instance_name_list && *instance_name_list)
STRV_FOREACH(s, *instance_name_list)
log_debug("Preset files say enable %s.", *s);
else
log_debug("Preset files say enable %s.", name);
- return 1;
+ return PRESET_ENABLE;
case PRESET_DISABLE:
log_debug("Preset files say disable %s.", name);
- return 0;
+ return PRESET_DISABLE;
+ case PRESET_IGNORE:
+ log_debug("Preset files say ignore %s.", name);
+ return PRESET_IGNORE;
default:
assert_not_reached();
}
}
-int unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) {
+PresetAction unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) {
_cleanup_(unit_file_presets_done) UnitFilePresets tmp = {};
int r;
if (r < 0)
return r;
- if (r > 0) {
+ if (r == PRESET_ENABLE) {
if (instance_name_list)
STRV_FOREACH(s, instance_name_list) {
r = install_info_discover_and_check(plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
return r;
}
- } else
+ } else if (r == PRESET_DISABLE)
r = install_info_discover(minus, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
&info, changes, n_changes);
bool initialized;
} UnitFilePresets;
+typedef enum PresetAction {
+ PRESET_UNKNOWN,
+ PRESET_ENABLE,
+ PRESET_DISABLE,
+ PRESET_IGNORE,
+ _PRESET_ACTION_MAX,
+ _PRESET_ACTION_INVALID = -EINVAL,
+ _PRESET_ACTION_ERRNO_MAX = -ERRNO_MAX, /* Ensure this type covers the whole negative errno range */
+} PresetAction;
+
+const char *preset_action_past_tense_to_string(PresetAction action);
+
void unit_file_presets_done(UnitFilePresets *p);
-int unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached);
+PresetAction unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached);
const char *unit_file_state_to_string(UnitFileState s) _const_;
UnitFileState unit_file_state_from_string(const char *s) _pure_;
return true;
}
+static const char* preset_action_to_color(PresetAction action, bool underline) {
+ assert(action >= 0);
+
+ switch (action) {
+ case PRESET_ENABLE:
+ return underline ? ansi_highlight_green_underline() : ansi_highlight_green();
+ case PRESET_DISABLE:
+ return underline ? ansi_highlight_red_underline() : ansi_highlight_red();
+ case PRESET_IGNORE:
+ return underline ? ansi_highlight_yellow_underline() : ansi_highlight_yellow();
+ default:
+ return NULL;
+ }
+}
+
static int output_unit_file_list(const UnitFileList *units, unsigned c) {
_cleanup_(table_unrefp) Table *table = NULL;
_cleanup_(unit_file_presets_done) UnitFilePresets presets = {};
return table_log_add_error(r);
if (show_preset_for_state(u->state)) {
- const char *unit_preset_str, *on_preset_color;
+ const char *on_preset_color = underline ? on_underline : ansi_normal();
r = unit_file_query_preset(arg_runtime_scope, arg_root, id, &presets);
- if (r < 0) {
- unit_preset_str = "n/a";
- on_preset_color = underline ? on_underline : ansi_normal();
- } else if (r == 0) {
- unit_preset_str = "disabled";
- on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
- } else {
- unit_preset_str = "enabled";
- on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
- }
+ if (r >= 0)
+ on_preset_color = preset_action_to_color(r, underline);
r = table_add_many(table,
- TABLE_STRING, unit_preset_str,
+ TABLE_STRING, strna(preset_action_past_tense_to_string(r)),
TABLE_SET_BOTH_COLORS, strempty(on_preset_color));
} else
r = table_add_many(table,
UnitFileList *fl;
_cleanup_(hashmap_freep) Hashmap *h = NULL;
+ CLEANUP_ARRAY(changes, n_changes, install_changes_free);
+
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) == -ENOENT);
p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
assert_se(write_string_file(p,
"[Install]\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+ p = strjoina(root, "/usr/lib/systemd/system/preset-ignore.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
assert_se(write_string_file(p,
"enable *-yes.*\n"
+ "ignore *-ignore.*\n"
"disable *\n", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
assert_se(n_changes == 0);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_preset_all(RUNTIME_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(h = hashmap_new(&unit_file_list_hash_ops_free));
assert_se(unit_file_get_list(RUNTIME_SCOPE_SYSTEM, root, h, NULL, NULL) >= 0);
}
assert_se(got_yes && got_no);
+
+ assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-ignore.service"), &changes, &n_changes) >= 0);
+ assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-ignore.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+ assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
}
TEST(revert) {