summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--man/systemd.network.xml20
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c6
-rw-r--r--src/libsystemd/sd-netlink/rtnl-message.c28
-rw-r--r--src/network/networkd-address.c48
-rw-r--r--src/network/networkd-address.h1
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-route.c64
-rw-r--r--src/network/networkd-route.h3
-rw-r--r--src/resolve/resolved-dns-transaction.c65
-rw-r--r--src/shared/install.c150
-rw-r--r--src/systemd/sd-netlink.h2
-rw-r--r--src/test/test-install-root.c48
13 files changed, 381 insertions, 58 deletions
diff --git a/TODO b/TODO
index a930583f85..515dfba3c9 100644
--- a/TODO
+++ b/TODO
@@ -51,8 +51,6 @@ Features:
* journald: sigbus API via a signal-handler safe function that people may call
from the SIGBUS handler
-* resolved: cefmz.x.incapdns.net fails to authenticate
-
* when using UTF8, ellipsize with "…" rather than "...", so that we can show more contents before truncating
* move specifier expansion from service_spawn() into load-fragment.c
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 2a20748376..5e287faa6e 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -652,6 +652,18 @@
<para>An address label.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>PreferredLifetime=</varname></term>
+ <listitem>
+ <para>Allows the default "preferred lifetime" of the address to be overridden.
+ Only three settings are accepted: <literal>forever</literal> or <literal>infinity</literal>
+ which is the default and means that the address never expires, and <literal>0</literal> which means
+ that the address is considered immediately "expired" and will not be used,
+ unless explicitly requested. A setting of PreferredLifetime=0 is useful for
+ addresses which are added to be used only by a specific application,
+ which is then configured to use them explicitly.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
@@ -706,6 +718,14 @@
<citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Table=<replaceable>num</replaceable></varname></term>
+ <listitem>
+ <para>The table identifier for the route (a number between 1 and 4294967295, or 0 to unset).
+ The table can be retrieved using <command>ip route show table <replaceable>num</replaceable></command>.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index f56798674c..86d8dee867 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -207,11 +207,11 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
* and gives us too little data (so don't do that)
*/
padding = mempcpy(RTA_DATA(rta), data, data_length);
- else {
+
+ else
/* if no data was passed, make sure we still initialize the padding
note that we can have data_length > 0 (used by some containers) */
padding = RTA_DATA(rta);
- }
/* make sure also the padding at the end of the message is initialized */
padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding;
@@ -343,7 +343,7 @@ int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, c
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = add_rtattr(m, type, &data, len);
+ r = add_rtattr(m, type, data, len);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c
index f251536a89..09240c7b2a 100644
--- a/src/libsystemd/sd-netlink/rtnl-message.c
+++ b/src/libsystemd/sd-netlink/rtnl-message.c
@@ -111,6 +111,20 @@ int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) {
return 0;
}
+int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ rtm->rtm_table = table;
+
+ return 0;
+}
+
int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) {
struct rtmsg *rtm;
@@ -126,6 +140,20 @@ int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) {
return 0;
}
+int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ rtm->rtm_family = family;
+
+ return 0;
+}
+
int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) {
struct rtmsg *rtm;
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 429319da6b..8b52a1f742 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -774,6 +774,54 @@ int config_parse_label(const char *unit,
return 0;
}
+int config_parse_lifetime(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = userdata;
+ _cleanup_address_free_ Address *n = NULL;
+ unsigned k;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = address_new_static(network, section_line, &n);
+ if (r < 0)
+ return r;
+
+ if (STR_IN_SET(rvalue, "forever", "infinity")) {
+ n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
+ n = NULL;
+
+ return 0;
+ }
+
+ r = safe_atou(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (k != 0)
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k);
+ else {
+ n->cinfo.ifa_prefered = k;
+ n = NULL;
+ }
+
+ return 0;
+}
+
bool address_is_ready(const Address *a) {
assert(a);
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 338f6eb9a2..3c81978fb1 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -74,3 +74,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 654d6a0316..550b5e5240 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -65,12 +65,14 @@ Address.Address, config_parse_address,
Address.Peer, config_parse_address, 0, 0
Address.Broadcast, config_parse_broadcast, 0, 0
Address.Label, config_parse_label, 0, 0
+Address.PreferredLifetime, config_parse_lifetime, 0, 0
Route.Gateway, config_parse_gateway, 0, 0
Route.Destination, config_parse_destination, 0, 0
Route.Source, config_parse_destination, 0, 0
Route.Metric, config_parse_route_priority, 0, 0
Route.Scope, config_parse_route_scope, 0, 0
Route.PreferredSource, config_parse_preferred_src, 0, 0
+Route.Table, config_parse_route_table, 0, 0
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index bda2707e6d..01094b20bd 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -451,6 +451,10 @@ int route_configure(Route *route, Link *link,
r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
+
+ r = sd_rtnl_message_route_set_family(req, route->family);
+ if (r < 0)
+ return log_error_errno(r, "Could not set route family: %m");
}
if (route->dst_prefixlen) {
@@ -494,7 +498,26 @@ int route_configure(Route *route, Link *link,
r = sd_rtnl_message_route_set_flags(req, route->flags);
if (r < 0)
- return log_error_errno(r, "Colud not set flags: %m");
+ return log_error_errno(r, "Could not set flags: %m");
+
+ if (route->table != RT_TABLE_DEFAULT) {
+
+ if (route->table < 256) {
+ r = sd_rtnl_message_route_set_table(req, route->table);
+ if (r < 0)
+ return log_error_errno(r, "Could not set route table: %m");
+ } else {
+
+ r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
+ if (r < 0)
+ return log_error_errno(r, "Could not set route table: %m");
+
+ /* Table attribute to allow allow more than 256. */
+ r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
+ if (r < 0)
+ return log_error_errno(r, "Could not append RTA_TABLE attribute: %m");
+ }
+ }
r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
if (r < 0)
@@ -777,3 +800,42 @@ int config_parse_route_scope(const char *unit,
return 0;
}
+
+int config_parse_route_table(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ _cleanup_route_free_ Route *n = NULL;
+ Network *network = userdata;
+ uint32_t k;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_new_static(network, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = safe_atou32(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ n->table = k;
+
+ n = NULL;
+
+ return 0;
+}
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index a4a4bf2653..3ddeea96b7 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -37,7 +37,7 @@ struct Route {
unsigned char protocol; /* RTPROT_* */
unsigned char tos;
uint32_t priority; /* note that ip(8) calls this 'metric' */
- unsigned char table;
+ uint32_t table;
unsigned char pref;
unsigned flags;
@@ -74,3 +74,4 @@ int config_parse_preferred_src(const char *unit, const char *filename, unsigned
int config_parse_destination(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_route_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_route_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 081131ede0..a4a67623e7 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -1804,7 +1804,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
* - For unsigned SOA/NS we get the matching DS
* - For unsigned CNAME/DNAME/DS we get the parent SOA RR
* - For other unsigned RRs we get the matching SOA RR
- * - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
+ * - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR
+ * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
* - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
*/
@@ -2038,32 +2039,42 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
return r;
if (r > 0) {
const char *name;
+ uint16_t type = 0;
name = dns_resource_key_name(t->key);
- /* If this was a SOA or NS request, then this
- * indicates that we are not at a zone apex, hence ask
- * the parent name instead. If this was a DS request,
- * then it's signed when the parent zone is signed,
- * hence ask the parent in that case, too. */
+ /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this
+ * could also be used as indication that we are not at a zone apex, but in real world setups there are
+ * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even
+ * though they have further children. If this was a DS request, then it's signed when the parent zone
+ * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR,
+ * to see if that is signed. */
- if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+ if (t->key->type == DNS_TYPE_DS) {
r = dns_name_parent(&name);
- if (r < 0)
- return r;
- if (r > 0)
- log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).",
+ if (r > 0) {
+ type = DNS_TYPE_SOA;
+ log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).",
t->id, dns_resource_key_name(t->key));
- else
+ } else
name = NULL;
- } else
+
+ } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) {
+
+ type = DNS_TYPE_DS;
+ log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).",
+ t->id, dns_resource_key_name(t->key));
+
+ } else {
+ type = DNS_TYPE_SOA;
log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).",
t->id, dns_resource_key_name(t->key));
+ }
if (name) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
- soa = dns_resource_key_new(t->key->class, DNS_TYPE_SOA, name);
+ soa = dns_resource_key_new(t->key->class, type, name);
if (!soa)
return -ENOMEM;
@@ -2317,11 +2328,12 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe
}
static int dns_transaction_requires_nsec(DnsTransaction *t) {
+ char key_str[DNS_RESOURCE_KEY_STRING_MAX];
DnsTransaction *dt;
const char *name;
+ uint16_t type = 0;
Iterator i;
int r;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
assert(t);
@@ -2355,22 +2367,25 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
name = dns_resource_key_name(t->key);
- if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+ if (t->key->type == DNS_TYPE_DS) {
- /* We got a negative reply for this SOA/NS lookup? If
- * so, then we are not at a zone apex, and thus should
- * look at the result of the parent SOA lookup.
- *
- * We got a negative reply for this DS lookup? DS RRs
- * are signed when their parent zone is signed, hence
- * also check the parent SOA in this case. */
+ /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed,
+ * hence check the parent SOA in this case. */
r = dns_name_parent(&name);
if (r < 0)
return r;
if (r == 0)
return true;
- }
+
+ type = DNS_TYPE_SOA;
+
+ } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS))
+ /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */
+ type = DNS_TYPE_DS;
+ else
+ /* For all other negative replies, check for the SOA lookup */
+ type = DNS_TYPE_SOA;
/* For all other RRs we check the SOA on the same level to see
* if it's signed. */
@@ -2379,7 +2394,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
if (dt->key->class != t->key->class)
continue;
- if (dt->key->type != DNS_TYPE_SOA)
+ if (dt->key->type != type)
continue;
r = dns_name_equal(dns_resource_key_name(dt->key), name);
diff --git a/src/shared/install.c b/src/shared/install.c
index f02d81504f..cc36da1853 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -66,6 +66,35 @@ typedef struct {
OrderedHashmap *have_processed;
} InstallContext;
+typedef enum {
+ PRESET_UNKNOWN,
+ PRESET_ENABLE,
+ PRESET_DISABLE,
+} PresetAction;
+
+typedef struct {
+ char *pattern;
+ PresetAction action;
+} PresetRule;
+
+typedef struct {
+ PresetRule *rules;
+ size_t n_rules;
+} Presets;
+
+static inline void presets_freep(Presets *p) {
+ size_t i;
+
+ if (!p)
+ return;
+
+ for (i = 0; i < p->n_rules; i++)
+ free(p->rules[i].pattern);
+
+ free(p->rules);
+ p->n_rules = 0;
+}
+
static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret);
bool unit_type_may_alias(UnitType type) {
@@ -2434,17 +2463,16 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *
return 1;
}
-int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
+static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
+ _cleanup_(presets_freep) Presets ps = {};
+ size_t n_allocated = 0;
_cleanup_strv_free_ char **files = NULL;
char **p;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- assert(name);
-
- if (!unit_name_is_valid(name, UNIT_NAME_ANY))
- return -EINVAL;
+ assert(presets);
if (scope == UNIT_FILE_SYSTEM)
r = conf_files_list(&files, ".preset", root_dir,
@@ -2461,8 +2489,11 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
"/usr/local/lib/systemd/user-preset",
"/usr/lib/systemd/user-preset",
NULL);
- else
- return 1; /* Default is "enable" */
+ else {
+ *presets = (Presets){};
+
+ return 0;
+ }
if (r < 0)
return r;
@@ -2470,6 +2501,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
STRV_FOREACH(p, files) {
_cleanup_fclose_ FILE *f;
char line[LINE_MAX];
+ int n = 0;
f = fopen(*p, "re");
if (!f) {
@@ -2480,10 +2512,12 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
}
FOREACH_LINE(line, f, return -errno) {
+ PresetRule rule = {};
const char *parameter;
char *l;
l = strstrip(line);
+ n++;
if (isempty(l))
continue;
@@ -2492,31 +2526,87 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
parameter = first_word(l, "enable");
if (parameter) {
- if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
- log_debug("Preset file says enable %s.", name);
- return 1;
- }
+ char *pattern;
- continue;
+ pattern = strdup(parameter);
+ if (!pattern)
+ return -ENOMEM;
+
+ rule = (PresetRule) {
+ .pattern = pattern,
+ .action = PRESET_ENABLE,
+ };
}
parameter = first_word(l, "disable");
if (parameter) {
- if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
- log_debug("Preset file says disable %s.", name);
- return 0;
- }
+ char *pattern;
+ pattern = strdup(parameter);
+ if (!pattern)
+ return -ENOMEM;
+
+ rule = (PresetRule) {
+ .pattern = pattern,
+ .action = PRESET_DISABLE,
+ };
+ }
+
+ if (rule.action) {
+ if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1))
+ return -ENOMEM;
+
+ ps.rules[ps.n_rules++] = rule;
continue;
}
- log_debug("Couldn't parse line '%s'", l);
+ log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
}
}
- /* Default is "enable" */
- log_debug("Preset file doesn't say anything about %s, enabling.", name);
- return 1;
+ *presets = ps;
+ ps = (Presets){};
+
+ return 0;
+}
+
+static int query_presets(const char *name, const Presets presets) {
+ PresetAction action = PRESET_UNKNOWN;
+ size_t i;
+
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ for (i = 0; i < presets.n_rules; i++)
+ if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
+ action = presets.rules[i].action;
+ break;
+ }
+
+ switch (action) {
+ case PRESET_UNKNOWN:
+ log_debug("Preset files don't specify rule for %s. Enabling.", name);
+ return 1;
+ case PRESET_ENABLE:
+ log_debug("Preset files say enable %s.", name);
+ return 1;
+ case PRESET_DISABLE:
+ log_debug("Preset files say disable %s.", name);
+ return 0;
+ default:
+ assert_not_reached("invalid preset action");
+ }
+}
+
+int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
+ _cleanup_(presets_freep) Presets presets = {};
+ int r;
+
+ r = read_presets(scope, root_dir, &presets);
+ if (r < 0)
+ return r;
+
+ return query_presets(name, presets);
}
static int execute_preset(
@@ -2572,6 +2662,7 @@ static int preset_prepare_one(
LookupPaths *paths,
UnitFilePresetMode mode,
const char *name,
+ Presets presets,
UnitFileChange **changes,
unsigned *n_changes) {
@@ -2582,7 +2673,7 @@ static int preset_prepare_one(
install_info_find(minus, name))
return 0;
- r = unit_file_query_preset(scope, paths->root_dir, name);
+ r = query_presets(name, presets);
if (r < 0)
return r;
@@ -2612,6 +2703,7 @@ int unit_file_preset(
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
+ _cleanup_(presets_freep) Presets presets = {};
const char *config_path;
char **i;
int r;
@@ -2626,11 +2718,12 @@ int unit_file_preset(
config_path = runtime ? paths.runtime_config : paths.persistent_config;
- STRV_FOREACH(i, files) {
- if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
- return -EINVAL;
+ r = read_presets(scope, root_dir, &presets);
+ if (r < 0)
+ return r;
- r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, changes, n_changes);
+ STRV_FOREACH(i, files) {
+ r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes);
if (r < 0)
return r;
}
@@ -2649,6 +2742,7 @@ int unit_file_preset_all(
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
+ _cleanup_(presets_freep) Presets presets = {};
const char *config_path = NULL;
char **i;
int r;
@@ -2663,6 +2757,10 @@ int unit_file_preset_all(
config_path = runtime ? paths.runtime_config : paths.persistent_config;
+ r = read_presets(scope, root_dir, &presets);
+ if (r < 0)
+ return r;
+
STRV_FOREACH(i, paths.search_path) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -2686,7 +2784,7 @@ int unit_file_preset_all(
continue;
/* we don't pass changes[] in, because we want to handle errors on our own */
- r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, NULL, 0);
+ r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0);
if (r == -ERFKILL)
r = unit_file_changes_add(changes, n_changes,
UNIT_FILE_IS_MASKED, de->d_name, NULL);
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index af7a797567..3ae110c080 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -137,8 +137,10 @@ int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char
int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope);
int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags);
+int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table);
int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags);
int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family);
+int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family);
int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol);
int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope);
int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos);
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index 4680b0336d..4b9a74fca4 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -681,6 +681,53 @@ static void test_revert(const char *root) {
changes = NULL; n_changes = 0;
}
+static void test_preset_order(const char *root) {
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ const char *p;
+ UnitFileState state;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/prefix-1.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/prefix-2.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 prefix-1.service\n"
+ "disable prefix-*.service\n"
+ "enable prefix-2.service\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/prefix-1.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("prefix-2.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+}
+
int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX";
const char *p;
@@ -709,6 +756,7 @@ int main(int argc, char *argv[]) {
test_template_enable(root);
test_indirect(root);
test_preset_and_list(root);
+ test_preset_order(root);
test_revert(root);
assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);