diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 78f0e25a6f..c8b5a057f8 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -58,31 +58,38 @@
systemd-networkd8.
- Virtual Network Device files must have the extension
- .netdev; other extensions are ignored.
- Virtual network devices are created as soon as networkd is
- started. If a netdev with the specified name already exists,
- networkd will use that as-is rather than create its own. Note that
- the settings of the pre-existing netdev will not be changed by
+ The main Virtual Network Device file must have the extension .netdev;
+ other extensions are ignored. Virtual network devices are created as soon as networkd is
+ started. If a netdev with the specified name already exists, networkd will use that as-is rather
+ than create its own. Note that the settings of the pre-existing netdev will not be changed by
networkd.
- The .netdev files are read from the
- files located in the system network directory
- /usr/lib/systemd/network, the volatile
- runtime network directory
- /run/systemd/network and the local
- administration network directory
- /etc/systemd/network. All configuration files
- are collectively sorted and processed in lexical order, regardless
- of the directories in which they live. However, files with
- identical filenames replace each other. Files in
- /etc have the highest priority, files in
- /run take precedence over files with the same
- name in /usr/lib. This can be used to
- override a system-supplied configuration file with a local file if
- needed. As a special case, an empty file (file size 0) or symlink
- with the same name pointing to /dev/null
- disables the configuration file entirely (it is "masked").
+ The .netdev files are read from the files located in the system
+ network directory /usr/lib/systemd/network, the volatile runtime network
+ directory /run/systemd/network and the local administration network
+ directory /etc/systemd/network. All configuration files are collectively
+ sorted and processed in lexical order, regardless of the directories in which they live.
+ However, files with identical filenames replace each other. Files in /etc
+ have the highest priority, files in /run take precedence over files with
+ the same name in /usr/lib. This can be used to override a system-supplied
+ configuration file with a local file if needed. As a special case, an empty file (file size 0)
+ or symlink with the same name pointing to /dev/null disables the
+ configuration file entirely (it is "masked").
+
+ Along with the netdev file foo.netdev, a "drop-in" directory
+ foo.netdev.d/ may exist. All files with the suffix .conf
+ from this directory will be parsed after the file itself is parsed. This is useful to alter or
+ add configuration settings, without having to modify the main configuration file. Each drop-in
+ file must have appropriate section headers.
+
+ In addition to /etc/systemd/network, drop-in .d
+ directories can be placed in /usr/lib/systemd/network or
+ /run/systemd/network directories. Drop-in files in
+ /etc take precedence over those in /run which in turn
+ take precedence over those in /usr/lib. Drop-in files under any of these
+ directories take precedence over the main netdev file wherever located. (Of course, since
+ /run is temporary and /usr/lib is for vendors, it is
+ unlikely drop-ins should be used in either of those places.)
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index c332cd7bdc..eb7d441842 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -58,31 +58,40 @@
systemd-networkd8.
- Network files must have the extension
- .network; other extensions are ignored.
- Networks are applied to links whenever the links appear.
+ The main network file must have the extension .network; other
+ extensions are ignored. Networks are applied to links whenever the links appear.
- The .network files are read from the
- files located in the system network directory
- /usr/lib/systemd/network, the volatile
- runtime network directory
- /run/systemd/network and the local
- administration network directory
- /etc/systemd/network. All configuration files
- are collectively sorted and processed in lexical order, regardless
- of the directories in which they live. However, files with
- identical filenames replace each other. Files in
- /etc have the highest priority, files in
- /run take precedence over files with the same
- name in /usr/lib. This can be used to
- override a system-supplied configuration file with a local file if
- needed. As a special case, an empty file (file size 0) or symlink
- with the same name pointing to /dev/null
- disables the configuration file entirely (it is "masked").
+ The .network files are read from the files located in the system
+ network directory /usr/lib/systemd/network, the volatile runtime network
+ directory /run/systemd/network and the local administration network
+ directory /etc/systemd/network. All configuration files are collectively
+ sorted and processed in lexical order, regardless of the directories in which they live.
+ However, files with identical filenames replace each other. Files in /etc
+ have the highest priority, files in /run take precedence over files with
+ the same name in /usr/lib. This can be used to override a system-supplied
+ configuration file with a local file if needed. As a special case, an empty file (file size 0)
+ or symlink with the same name pointing to /dev/null disables the
+ configuration file entirely (it is "masked").
- Note that an interface without any static IPv6 addresses configured, and neither DHCPv6 nor IPv6LL enabled,
- shall be considered to have no IPv6 support. IPv6 will be automatically disabled for that interface by writing "1"
- to /proc/sys/net/ipv6/conf/ifname/disable_ipv6.
+ Along with the network file foo.network, a "drop-in" directory
+ foo.network.d/ may exist. All files with the suffix
+ .conf from this directory will be parsed after the file itself is
+ parsed. This is useful to alter or add configuration settings, without having to modify the main
+ configuration file. Each drop-in file must have appropriate section headers.
+
+ In addition to /etc/systemd/network, drop-in .d
+ directories can be placed in /usr/lib/systemd/network or
+ /run/systemd/network directories. Drop-in files in
+ /etc take precedence over those in /run which in turn
+ take precedence over those in /usr/lib. Drop-in files under any of these
+ directories take precedence over the main netdev file wherever located. (Of course, since
+ /run is temporary and /usr/lib is for vendors, it is
+ unlikely drop-ins should be used in either of those places.)
+
+ Note that an interface without any static IPv6 addresses configured, and neither DHCPv6
+ nor IPv6LL enabled, shall be considered to have no IPv6 support. IPv6 will be automatically
+ disabled for that interface by writing "1" to
+ /proc/sys/net/ipv6/conf/ifname/disable_ipv6.
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index f818e772a9..9778283fec 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -144,71 +144,71 @@
and are
equivalent.
- Time span values encoded in unit files can be written in various formats. A stand-alone number specifies a
- time in seconds. If suffixed with a time unit, the unit is honored. A concatenation of multiple values with units
- is supported, in which case the values are added up. Example: 50 refers to 50 seconds;
- 2min 200ms refers to 2 minutes and 200 milliseconds, i.e. 120200 ms. The following time units
- are understood: s, min, h, d,
+ Time span values encoded in unit files can be written in various formats. A stand-alone
+ number specifies a time in seconds. If suffixed with a time unit, the unit is honored. A
+ concatenation of multiple values with units is supported, in which case the values are added
+ up. Example: 50 refers to 50 seconds; 2min 200ms refers to
+ 2 minutes and 200 milliseconds, i.e. 120200 ms. The following time units are understood:
+ s, min, h, d,
w, ms, us. For details see
systemd.time7.
- Empty lines and lines starting with # or ; are ignored. This may be
- used for commenting. Lines ending in a backslash are concatenated with the following line while reading and the
- backslash is replaced by a space character. This may be used to wrap long lines.
+ Empty lines and lines starting with # or ; are
+ ignored. This may be used for commenting. Lines ending in a backslash are concatenated with the
+ following line while reading and the backslash is replaced by a space character. This may be
+ used to wrap long lines.
- Units can be aliased (have an alternative name), by creating a symlink from the new name to the existing name
- in one of the unit search paths. For example, systemd-networkd.service has the alias
- dbus-org.freedesktop.network1.service, created during installation as the symlink
- /usr/lib/systemd/system/dbus-org.freedesktop.network1.service. In addition, unit files may
- specify aliases through the Alias= directive in the [Install] section; those aliases are only
- effective when the unit is enabled. When the unit is enabled, symlinks will be created for those names, and removed
- when the unit is disabled. For example, reboot.target specifies
- Alias=ctrl-alt-del.target, so when enabled it will be invoked whenever CTRL+ALT+DEL is
- pressed. Alias names may be used in commands like enable, disable,
- start, stop, status, …, and in unit dependency directives
- Wants=, Requires=, Before=, After=, …,
- with the limitation that aliases specified through Alias= are only effective when the unit is
- enabled. Aliases cannot be used with the preset command.
+ Units can be aliased (have an alternative name), by creating a symlink from the new name
+ to the existing name in one of the unit search paths. For example,
+ systemd-networkd.service has the alias
+ dbus-org.freedesktop.network1.service, created during installation as the
+ symlink /usr/lib/systemd/system/dbus-org.freedesktop.network1.service. In
+ addition, unit files may specify aliases through the Alias= directive in the
+ [Install] section; those aliases are only effective when the unit is enabled. When the unit is
+ enabled, symlinks will be created for those names, and removed when the unit is disabled. For
+ example, reboot.target specifies
+ Alias=ctrl-alt-del.target, so when enabled it will be invoked whenever
+ CTRL+ALT+DEL is pressed. Alias names may be used in commands like enable,
+ disable, start, stop,
+ status, …, and in unit dependency directives Wants=,
+ Requires=, Before=, After=, …, with the
+ limitation that aliases specified through Alias= are only effective when the
+ unit is enabled. Aliases cannot be used with the preset command.
- Along with a unit file foo.service, the
- directory foo.service.wants/ may exist. All
- unit files symlinked from such a directory are implicitly added as
- dependencies of type Wants= to the unit. This
- is useful to hook units into the start-up of other units, without
- having to modify their unit files. For details about the semantics
- of Wants=, see below. The preferred way to
- create symlinks in the .wants/ directory of a
- unit file is with the enable command of the
+ Along with a unit file foo.service, the directory
+ foo.service.wants/ may exist. All unit files symlinked from such a
+ directory are implicitly added as dependencies of type Wants= to the unit.
+ This is useful to hook units into the start-up of other units, without having to modify their
+ unit files. For details about the semantics of Wants=, see below. The
+ preferred way to create symlinks in the .wants/ directory of a unit file is
+ with the enable command of the
systemctl1
- tool which reads information from the [Install] section of unit
- files (see below). A similar functionality exists for
- Requires= type dependencies as well, the
- directory suffix is .requires/ in this
- case.
+ tool which reads information from the [Install] section of unit files (see below). A similar
+ functionality exists for Requires= type dependencies as well, the directory
+ suffix is .requires/ in this case.Along with a unit file foo.service, a "drop-in" directory
- foo.service.d/ may exist. All files with the suffix .conf from this
- directory will be parsed after the file itself is parsed. This is useful to alter or add configuration settings for
- a unit, without having to modify unit files. Each drop-in file must have appropriate section headers. Note that for
- instantiated units, this logic will first look for the instance .d/ subdirectory and read its
- .conf files, followed by the template .d/ subdirectory and the
- .conf files there. Also note that settings from the [Install] section are not
- honoured in drop-in unit files, and have no effect.
+ foo.service.d/ may exist. All files with the suffix
+ .conf from this directory will be parsed after the file itself is
+ parsed. This is useful to alter or add configuration settings for a unit, without having to
+ modify unit files. Each drop-in file must have appropriate section headers. Note that for
+ instantiated units, this logic will first look for the instance .d/
+ subdirectory and read its .conf files, followed by the template
+ .d/ subdirectory and the .conf files there. Also note that
+ settings from the [Install] section are not honoured in drop-in unit files,
+ and have no effect.
- In addition to /etc/systemd/system,
- the drop-in .conf files for system services
- can be placed in /usr/lib/systemd/system or
- /run/systemd/system directories. Drop-in
- files in /etc take precedence over those in
- /run which in turn take precedence over
- those in /usr/lib. Drop-in files under any of
- these directories take precedence over unit files wherever located.
- (Of course, since /run is temporary and
- /usr/lib is for vendors, it is unlikely
- drop-ins should be used in either of those places.)
-
+ In addition to /etc/systemd/system, the drop-in .d
+ directories for system services can be placed in /usr/lib/systemd/system or
+ /run/systemd/system directories. Drop-in files in /etc
+ take precedence over those in /run which in turn take precedence over those
+ in /usr/lib. Drop-in files under any of these directories take precedence
+ over unit files wherever located. (Of course, since /run is temporary and
+ /usr/lib is for vendors, it is unlikely drop-ins should be used in either
+ of those places.)
+
+
Some unit names reflect paths existing in the file system
namespace. Example: a device unit
diff --git a/src/basic/def.h b/src/basic/def.h
index 1a7a0f4928..2266eff650 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -79,7 +79,7 @@
#endif
/* Return a nulstr for a standard cascade of configuration paths,
- * suitable to pass to conf_files_list_nulstr() or config_parse_many()
+ * suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr()
* to implement drop-in directories for extending configuration
* files. */
#define CONF_PATHS_NULSTR(n) \
diff --git a/src/core/main.c b/src/core/main.c
index 7d8322ebd8..803307c9d5 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -715,7 +715,7 @@ static int parse_config_file(void) {
CONF_PATHS_NULSTR("systemd/system.conf.d") :
CONF_PATHS_NULSTR("systemd/user.conf.d");
- config_parse_many(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL);
+ config_parse_many_nulstr(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL);
/* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY
* like everywhere else. */
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index be724aed4e..9dea10b3e1 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -128,7 +128,7 @@ static int parse_config(void) {
{}
};
- return config_parse_many(PKGSYSCONFDIR "/coredump.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/coredump.conf",
CONF_PATHS_NULSTR("systemd/coredump.conf.d"),
"Coredump\0",
config_item_table_lookup, items,
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index aebc4cafb4..a9009cfefe 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -1194,7 +1194,7 @@ static int parse_config(void) {
{ "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
{}};
- return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf",
CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
"Remote\0", config_item_table_lookup, items,
false, NULL);
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 4647cfdeb3..c0f967ab94 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -542,7 +542,7 @@ static int parse_config(void) {
{ "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
{}};
- return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf",
CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"),
"Upload\0", config_item_table_lookup, items,
false, NULL);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 3507910919..cc352dbae2 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -1474,7 +1474,7 @@ static int server_parse_proc_cmdline(Server *s) {
static int server_parse_config_file(Server *s) {
assert(s);
- return config_parse_many(PKGSYSCONFDIR "/journald.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf",
CONF_PATHS_NULSTR("systemd/journald.conf.d"),
"Journal\0",
config_item_perf_lookup, journald_gperf_lookup,
diff --git a/src/login/logind.c b/src/login/logind.c
index bbbf4aef57..a9a06f5e28 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -1002,7 +1002,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
static int manager_parse_config_file(Manager *m) {
assert(m);
- return config_parse_many(PKGSYSCONFDIR "/logind.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf",
CONF_PATHS_NULSTR("systemd/logind.conf.d"),
"Login\0",
config_item_perf_lookup, logind_gperf_lookup,
diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c
index c03e2b2ebf..49bb8c18f6 100644
--- a/src/network/networkd-conf.c
+++ b/src/network/networkd-conf.c
@@ -29,7 +29,7 @@
int manager_parse_config_file(Manager *m) {
assert(m);
- return config_parse_many(PKGSYSCONFDIR "/networkd.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf",
CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
"DHCP\0",
config_item_perf_lookup, networkd_gperf_lookup,
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index 897de9bde5..a210ba1242 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -576,6 +576,7 @@ static int netdev_load_one(Manager *manager, const char *filename) {
_cleanup_netdev_unref_ NetDev *netdev = NULL;
_cleanup_free_ NetDev *netdev_raw = NULL;
_cleanup_fclose_ FILE *file = NULL;
+ const char *dropin_dirname;
int r;
assert(manager);
@@ -599,11 +600,12 @@ static int netdev_load_one(Manager *manager, const char *filename) {
return log_oom();
netdev_raw->kind = _NETDEV_KIND_INVALID;
+ dropin_dirname = strjoina(basename(filename), ".d");
- r = config_parse(NULL, filename, file,
- "Match\0NetDev\0",
- config_item_perf_lookup, network_netdev_gperf_lookup,
- true, false, true, netdev_raw);
+ r = config_parse_many(filename, network_dirs, dropin_dirname,
+ "Match\0NetDev\0",
+ config_item_perf_lookup, network_netdev_gperf_lookup,
+ true, netdev_raw);
if (r < 0)
return r;
@@ -619,7 +621,7 @@ static int netdev_load_one(Manager *manager, const char *filename) {
return 0;
if (netdev_raw->kind == _NETDEV_KIND_INVALID) {
- log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename);
+ log_warning("NetDev has no Kind configured in %s. Ignoring", filename);
return 0;
}
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 91e51e9124..313abca762 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -40,6 +40,7 @@ static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
_cleanup_fclose_ FILE *file = NULL;
char *d;
+ const char *dropin_dirname;
Route *route;
Address *address;
int r;
@@ -137,21 +138,23 @@ static int network_load_one(Manager *manager, const char *filename) {
network->arp = -1;
network->ipv6_accept_ra_use_dns = true;
- r = config_parse(NULL, filename, file,
- "Match\0"
- "Link\0"
- "Network\0"
- "Address\0"
- "Route\0"
- "DHCP\0"
- "DHCPv4\0" /* compat */
- "DHCPServer\0"
- "IPv6AcceptRA\0"
- "Bridge\0"
- "BridgeFDB\0"
- "BridgeVLAN\0",
- config_item_perf_lookup, network_network_gperf_lookup,
- false, false, true, network);
+ dropin_dirname = strjoina(network->name, ".network.d");
+
+ r = config_parse_many(filename, network_dirs, dropin_dirname,
+ "Match\0"
+ "Link\0"
+ "Network\0"
+ "Address\0"
+ "Route\0"
+ "DHCP\0"
+ "DHCPv4\0" /* compat */
+ "DHCPServer\0"
+ "IPv6AcceptRA\0"
+ "Bridge\0"
+ "BridgeFDB\0"
+ "BridgeVLAN\0",
+ config_item_perf_lookup, network_network_gperf_lookup,
+ false, network);
if (r < 0)
return r;
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index dd233e7c4a..83f7596ac8 100644
--- a/src/resolve/resolved-conf.c
+++ b/src/resolve/resolved-conf.c
@@ -221,7 +221,7 @@ int manager_parse_config_file(Manager *m) {
assert(m);
- r = config_parse_many(PKGSYSCONFDIR "/resolved.conf",
+ r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf",
CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
"Resolve\0",
config_item_perf_lookup, resolved_gperf_lookup,
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index f31d219418..2ec0155b71 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -396,22 +396,18 @@ int config_parse(const char *unit,
return 0;
}
-/* Parse each config file in the specified directories. */
-int config_parse_many(const char *conf_file,
- const char *conf_file_dirs,
- const char *sections,
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- void *userdata) {
- _cleanup_strv_free_ char **files = NULL;
+static int config_parse_many_files(
+ const char *conf_file,
+ char **files,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
char **fn;
int r;
- r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
- if (r < 0)
- return r;
-
if (conf_file) {
r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
if (r < 0)
@@ -427,6 +423,56 @@ int config_parse_many(const char *conf_file,
return 0;
}
+/* Parse each config file in the directories specified as nulstr. */
+int config_parse_many_nulstr(
+ const char *conf_file,
+ const char *conf_file_dirs,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
+ _cleanup_strv_free_ char **files = NULL;
+ int r;
+
+ r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ if (r < 0)
+ return r;
+
+ return config_parse_many_files(conf_file, files,
+ sections, lookup, table, relaxed, userdata);
+}
+
+/* Parse each config file in the directories specified as strv. */
+int config_parse_many(
+ const char *conf_file,
+ const char* const* conf_file_dirs,
+ const char *dropin_dirname,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
+ _cleanup_strv_free_ char **dropin_dirs = NULL;
+ _cleanup_strv_free_ char **files = NULL;
+ const char *suffix;
+ int r;
+
+ suffix = strjoina("/", dropin_dirname);
+ r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
+ if (r < 0)
+ return r;
+
+ r = conf_files_list_strv(&files, ".conf", NULL, (const char* const*) dropin_dirs);
+ if (r < 0)
+ return r;
+
+ return config_parse_many_files(conf_file, files,
+ sections, lookup, table, relaxed, userdata);
+}
+
#define DEFINE_PARSER(type, vartype, conv_func) \
int config_parse_##type( \
const char *unit, \
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 3298dc0cea..26ff3df16f 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -84,24 +84,36 @@ int config_item_table_lookup(const void *table, const char *section, const char
* ConfigPerfItem tables */
int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
-int config_parse(const char *unit,
- const char *filename,
- FILE *f,
- const char *sections, /* nulstr */
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- bool allow_include,
- bool warn,
- void *userdata);
+int config_parse(
+ const char *unit,
+ const char *filename,
+ FILE *f,
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ bool allow_include,
+ bool warn,
+ void *userdata);
-int config_parse_many(const char *conf_file, /* possibly NULL */
- const char *conf_file_dirs, /* nulstr */
- const char *sections, /* nulstr */
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- void *userdata);
+int config_parse_many_nulstr(
+ const char *conf_file, /* possibly NULL */
+ const char *conf_file_dirs, /* nulstr */
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata);
+
+int config_parse_many(
+ const char *conf_file, /* possibly NULL */
+ const char* const* conf_file_dirs,
+ const char *dropin_dirname,
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata);
/* Generic parsers */
int config_parse_int(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/shared/sleep-config.c b/src/shared/sleep-config.c
index f00624d0f2..ed31a80c8d 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -58,7 +58,7 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
{}
};
- config_parse_many(PKGSYSCONFDIR "/sleep.conf",
+ config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf",
CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
"Sleep\0", config_item_table_lookup, items,
false, NULL);
diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c
index 20c64a3354..bf25b112e1 100644
--- a/src/timesync/timesyncd-conf.c
+++ b/src/timesync/timesyncd-conf.c
@@ -98,7 +98,7 @@ int config_parse_servers(
int manager_parse_config_file(Manager *m) {
assert(m);
- return config_parse_many(PKGSYSCONFDIR "/timesyncd.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/timesyncd.conf",
CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"),
"Time\0",
config_item_perf_lookup, timesyncd_gperf_lookup,
diff --git a/test/networkd-test.py b/test/networkd-test.py
index bfa1bf3580..baa1dc2a47 100755
--- a/test/networkd-test.py
+++ b/test/networkd-test.py
@@ -54,7 +54,6 @@ class ClientTestBase:
self.workdir_obj = tempfile.TemporaryDirectory()
self.workdir = self.workdir_obj.name
self.config = '/run/systemd/network/test_eth42.network'
- os.makedirs(os.path.dirname(self.config), exist_ok=True)
# avoid "Failed to open /dev/tty" errors in containers
os.environ['SYSTEMD_LOG_TARGET'] = 'journal'
@@ -77,10 +76,14 @@ class ClientTestBase:
def tearDown(self):
self.shutdown_iface()
- if os.path.exists(self.config):
- os.unlink(self.config)
subprocess.call(['systemctl', 'stop', 'systemd-networkd'])
+ def writeConfig(self, fname, contents):
+ os.makedirs(os.path.dirname(fname), exist_ok=True)
+ with open(fname, 'w') as f:
+ f.write(contents)
+ self.addCleanup(os.remove, fname)
+
def show_journal(self, unit):
'''Show journal of given unit since start of the test'''
@@ -107,8 +110,8 @@ class ClientTestBase:
def do_test(self, coldplug=True, ipv6=False, extra_opts='',
online_timeout=10, dhcp_mode='yes'):
subprocess.check_call(['systemctl', 'start', 'systemd-resolved'])
- with open(self.config, 'w') as f:
- f.write('''[Match]
+ self.writeConfig(self.config, '''\
+[Match]
Name=%s
[Network]
DHCP=%s
@@ -225,20 +228,18 @@ DHCP=%s
self.do_test(coldplug=False, ipv6=True)
def test_route_only_dns(self):
- with open('/run/systemd/network/myvpn.netdev', 'w') as f:
- f.write('''[NetDev]
+ self.writeConfig('/run/systemd/network/myvpn.netdev', '''\
+[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- with open('/run/systemd/network/myvpn.network', 'w') as f:
- f.write('''[Match]
+ self.writeConfig('/run/systemd/network/myvpn.network', '''\
+[Match]
Name=dummy0
[Network]
Address=192.168.42.100
DNS=192.168.42.1
Domains= ~company''')
- self.addCleanup(os.remove, '/run/systemd/network/myvpn.netdev')
- self.addCleanup(os.remove, '/run/systemd/network/myvpn.network')
self.do_test(coldplug=True, ipv6=False,
extra_opts='IPv6AcceptRouterAdvertisements=False')
@@ -320,7 +321,8 @@ class NetworkdClientTest(ClientTestBase, unittest.TestCase):
(fd, script) = tempfile.mkstemp(prefix='networkd-router.sh')
self.addCleanup(os.remove, script)
with os.fdopen(fd, 'w+') as f:
- f.write('''#!/bin/sh -eu
+ f.write('''\
+#!/bin/sh -eu
mkdir -p /run/systemd/network
mkdir -p /run/systemd/netif
mount -t tmpfs none /run/systemd/network
@@ -398,20 +400,18 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ { s/^.*=//
# we don't use this interface for this test
self.if_router = None
- with open('/run/systemd/network/test.netdev', 'w') as f:
- f.write('''[NetDev]
+ self.writeConfig('/run/systemd/network/test.netdev', '''\
+[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- with open('/run/systemd/network/test.network', 'w') as f:
- f.write('''[Match]
+ self.writeConfig('/run/systemd/network/test.network', '''\
+[Match]
Name=dummy0
[Network]
Address=192.168.42.100
DNS=192.168.42.1
Domains= one two three four five six seven eight nine ten''')
- self.addCleanup(os.remove, '/run/systemd/network/test.netdev')
- self.addCleanup(os.remove, '/run/systemd/network/test.network')
subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
@@ -432,23 +432,18 @@ Domains= one two three four five six seven eight nine ten''')
name_prefix = 'a' * 60
- with open('/run/systemd/network/test.netdev', 'w') as f:
- f.write('''[NetDev]
+ self.writeConfig('/run/systemd/network/test.netdev', '''\
+[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- with open('/run/systemd/network/test.network', 'w') as f:
- f.write('''[Match]
+ self.writeConfig('/run/systemd/network/test.network', '''\
+[Match]
Name=dummy0
[Network]
Address=192.168.42.100
DNS=192.168.42.1
-Domains=''')
- for i in range(5):
- f.write('%s%i ' % (name_prefix, i))
-
- self.addCleanup(os.remove, '/run/systemd/network/test.netdev')
- self.addCleanup(os.remove, '/run/systemd/network/test.network')
+Domains={p}0 {p}1 {p}2 {p}3 {p}4'''.format(p=name_prefix))
subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
@@ -458,9 +453,38 @@ Domains=''')
if ' one' in contents:
break
time.sleep(0.1)
- self.assertRegex(contents, 'search .*%(p)s0 %(p)s1 %(p)s2' % {'p': name_prefix})
+ self.assertRegex(contents, 'search .*{p}0 {p}1 {p}2'.format(p=name_prefix))
self.assertIn('# Total length of all search domains is too long, remaining ones ignored.', contents)
+ def test_dropin(self):
+ # we don't use this interface for this test
+ self.if_router = None
+
+ self.writeConfig('/run/systemd/network/test.netdev', '''\
+[NetDev]
+Name=dummy0
+Kind=dummy
+MACAddress=12:34:56:78:9a:bc''')
+ self.writeConfig('/run/systemd/network/test.network', '''\
+[Match]
+Name=dummy0
+[Network]
+Address=192.168.42.100
+DNS=192.168.42.1''')
+ self.writeConfig('/run/systemd/network/test.network.d/dns.conf', '''\
+[Network]
+DNS=127.0.0.1''')
+
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+
+ for timeout in range(50):
+ with open(RESOLV_CONF) as f:
+ contents = f.read()
+ if ' 127.0.0.1' in contents:
+ break
+ time.sleep(0.1)
+ self.assertIn('nameserver 192.168.42.1\n', contents)
+ self.assertIn('nameserver 127.0.0.1\n', contents)
if __name__ == '__main__':
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,