1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-26 14:04:03 +03:00

Merge pull request #34318 from YHNdnzj/networkctl-edit-if-netdev

networkctl: support editing netdev files by link and cat ":all"
This commit is contained in:
Yu Watanabe 2024-09-10 14:32:28 +09:00 committed by GitHub
commit 6d449bc71f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 325 additions and 147 deletions

View File

@ -434,8 +434,9 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
<filename>/run/</filename>, depending on whether <option>--runtime</option> is specified.
Specially, if the name is prefixed by <literal>@</literal>, it will be treated as
a network interface, and editing will be performed on the network config files associated
with it. Additionally, the interface name can be suffixed with <literal>:network</literal> (default)
or <literal>:link</literal>, in order to choose the type of network config to operate on.</para>
with it. Additionally, the interface name can be suffixed with <literal>:network</literal> (default),
<literal>:link</literal>, or <literal>:netdev</literal>, in order to choose the type of network config
to operate on.</para>
<para>If <option>--drop-in=</option> is specified, edit the drop-in file instead of
the main configuration file. Unless <option>--no-reload</option> is specified,
@ -460,9 +461,10 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
<optional><replaceable>FILE</replaceable>|<replaceable>@DEVICE</replaceable></optional>
</term>
<listitem>
<para>Show network configuration files. This command honors the <literal>@</literal> prefix in the
same way as <command>edit</command>. When no argument is specified,
<citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
<para>Show network configuration files. This command honors the <literal>@</literal> prefix in a
similar way as <command>edit</command>, with support for an additional suffix <literal>:all</literal>
for showing all types of configuration files associated with the interface at once. When no argument
is specified, <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
and its drop-in files will be shown.</para>
<xi:include href="version-info.xml" xpointer="v254"/>

View File

@ -159,25 +159,37 @@ int sd_network_link_get_network_file(int ifindex, char **ret) {
return network_link_get_string(ifindex, "NETWORK_FILE", ret);
}
int sd_network_link_get_network_file_dropins(int ifindex, char ***ret) {
_cleanup_free_ char **sv = NULL, *joined = NULL;
int sd_network_link_get_netdev_file(int ifindex, char **ret) {
return network_link_get_string(ifindex, "NETDEV_FILE", ret);
}
static int link_get_config_file_dropins_internal(int ifindex, const char *field, char ***ret) {
_cleanup_free_ char *s = NULL;
int r;
assert(field);
assert_return(ifindex > 0, -EINVAL);
assert_return(ret, -EINVAL);
r = network_link_get_string(ifindex, "NETWORK_FILE_DROPINS", &joined);
r = network_link_get_string(ifindex, field, &s);
if (r < 0)
return r;
r = strv_split_full(&sv, joined, ":", EXTRACT_CUNESCAPE);
r = strv_split_full(ret, s, ":", EXTRACT_CUNESCAPE);
if (r < 0)
return r;
*ret = TAKE_PTR(sv);
return 0;
}
int sd_network_link_get_network_file_dropins(int ifindex, char ***ret) {
return link_get_config_file_dropins_internal(ifindex, "NETWORK_FILE_DROPINS", ret);
}
int sd_network_link_get_netdev_file_dropins(int ifindex, char ***ret) {
return link_get_config_file_dropins_internal(ifindex, "NETDEV_FILE_DROPINS", ret);
}
int sd_network_link_get_operational_state(int ifindex, char **ret) {
return network_link_get_string(ifindex, "OPER_STATE", ret);
}

View File

@ -215,6 +215,7 @@ static NetDev *netdev_free(NetDev *netdev) {
condition_free_list(netdev->conditions);
free(netdev->filename);
strv_free(netdev->dropins);
free(netdev->description);
free(netdev->ifname);
@ -832,7 +833,9 @@ int netdev_load_one(Manager *manager, const char *filename) {
NETDEV_VTABLE(netdev)->sections,
config_item_perf_lookup, network_netdev_gperf_lookup,
CONFIG_PARSE_WARN,
netdev, NULL, NULL);
netdev,
NULL,
&netdev->dropins);
if (r < 0)
return r; /* config_parse_many() logs internally. */

View File

@ -116,6 +116,7 @@ typedef struct NetDev {
unsigned n_ref;
char *filename;
char **dropins;
LIST_HEAD(Condition, conditions);

View File

@ -23,6 +23,7 @@
#include "path-util.h"
#include "pretty-print.h"
#include "selinux-util.h"
#include "string-table.h"
#include "strv.h"
#include "virt.h"
@ -31,6 +32,22 @@ typedef enum ReloadFlags {
RELOAD_UDEVD = 1 << 1,
} ReloadFlags;
typedef enum LinkConfigType {
CONFIG_NETWORK,
CONFIG_LINK,
CONFIG_NETDEV,
_CONFIG_MAX,
_CONFIG_INVALID = -EINVAL,
} LinkConfigType;
static const char* const link_config_type_table[_CONFIG_MAX] = {
[CONFIG_NETWORK] = "network",
[CONFIG_LINK] = "link",
[CONFIG_NETDEV] = "netdev",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(link_config_type, LinkConfigType);
static int get_config_files_by_name(
const char *name,
bool allow_masked,
@ -115,28 +132,25 @@ static int get_dropin_by_name(
}
static int get_network_files_by_link(
sd_netlink **rtnl,
const char *link,
int ifindex,
bool ignore_missing,
char **ret_path,
char ***ret_dropins) {
_cleanup_strv_free_ char **dropins = NULL;
_cleanup_free_ char *path = NULL;
int r, ifindex;
int r;
assert(rtnl);
assert(link);
assert(ifindex > 0);
assert(ret_path);
assert(ret_dropins);
ifindex = rtnl_resolve_interface_or_warn(rtnl, link);
if (ifindex < 0)
return ifindex;
r = sd_network_link_get_network_file(ifindex, &path);
if (r == -ENODATA)
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"Link '%s' has no associated network file.", link);
return log_full_errno(ignore_missing ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(ENOENT),
"Link '%s' has no associated network file.", link);
if (r < 0)
return log_error_errno(r, "Failed to get network file for link '%s': %m", link);
@ -150,7 +164,40 @@ static int get_network_files_by_link(
return 0;
}
static int get_link_files_by_link(const char *link, char **ret_path, char ***ret_dropins) {
static int get_netdev_files_by_link(
const char *link,
int ifindex,
bool ignore_missing,
char **ret_path,
char ***ret_dropins) {
_cleanup_strv_free_ char **dropins = NULL;
_cleanup_free_ char *path = NULL;
int r;
assert(link);
assert(ifindex > 0);
assert(ret_path);
assert(ret_dropins);
r = sd_network_link_get_netdev_file(ifindex, &path);
if (r == -ENODATA)
return log_full_errno(ignore_missing ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(ENOENT),
"Link '%s' has no associated netdev file.", link);
if (r < 0)
return log_error_errno(r, "Failed to get netdev file for link '%s': %m", link);
r = sd_network_link_get_netdev_file_dropins(ifindex, &dropins);
if (r < 0 && r != -ENODATA)
return log_error_errno(r, "Failed to get netdev drop-ins for link '%s': %m", link);
*ret_path = TAKE_PTR(path);
*ret_dropins = TAKE_PTR(dropins);
return 0;
}
static int get_link_files_by_link(const char *link, bool ignore_missing, char **ret_path, char ***ret_dropins) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
_cleanup_strv_free_ char **dropins_split = NULL;
_cleanup_free_ char *p = NULL;
@ -167,7 +214,8 @@ static int get_link_files_by_link(const char *link, char **ret_path, char ***ret
r = sd_device_get_property_value(device, "ID_NET_LINK_FILE", &path);
if (r == -ENOENT)
return log_error_errno(r, "Link '%s' has no associated link file.", link);
return log_full_errno(ignore_missing ? LOG_DEBUG : LOG_ERR, r,
"Link '%s' has no associated link file.", link);
if (r < 0)
return log_error_errno(r, "Failed to get link file for link '%s': %m", link);
@ -191,62 +239,72 @@ static int get_link_files_by_link(const char *link, char **ret_path, char ***ret
}
static int get_config_files_by_link_config(
const char *link_config,
const char *ifname,
LinkConfigType type,
bool ignore_missing,
sd_netlink **rtnl,
char **ret_path,
char ***ret_dropins,
ReloadFlags *ret_reload) {
char ***ret_dropins) {
_cleanup_strv_free_ char **dropins = NULL, **link_config_split = NULL;
_cleanup_free_ char *path = NULL;
const char *ifname, *type;
ReloadFlags reload;
size_t n;
int r;
assert(link_config);
assert(ifname);
assert(type >= 0 && type < _CONFIG_MAX);
assert(rtnl);
assert(ret_path);
assert(ret_dropins);
link_config_split = strv_split(link_config, ":");
if (!link_config_split)
return log_oom();
if (type == CONFIG_LINK)
return get_link_files_by_link(ifname, ignore_missing, ret_path, ret_dropins);
n = strv_length(link_config_split);
if (n == 0 || isempty(link_config_split[0]))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No link name is given.");
if (n > 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid link config '%s'.", link_config);
if (!networkd_is_running())
return log_full_errno(ignore_missing ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(ESRCH),
"Cannot get network/netdev file for link if systemd-networkd is not running.");
ifname = link_config_split[0];
type = n == 2 ? link_config_split[1] : "network";
int ifindex = rtnl_resolve_interface_or_warn(rtnl, ifname);
if (ifindex < 0)
return ifindex;
if (streq(type, "network")) {
if (!networkd_is_running())
return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
"Cannot get network file for link if systemd-networkd is not running.");
if (type == CONFIG_NETWORK)
r = get_network_files_by_link(ifname, ifindex, ignore_missing, ret_path, ret_dropins);
else if (type == CONFIG_NETDEV)
r = get_netdev_files_by_link(ifname, ifindex, ignore_missing, ret_path, ret_dropins);
else
assert_not_reached();
r = get_network_files_by_link(rtnl, ifname, &path, &dropins);
if (r < 0)
return r;
return r;
}
reload = RELOAD_NETWORKD;
} else if (streq(type, "link")) {
r = get_link_files_by_link(ifname, &path, &dropins);
if (r < 0)
return r;
static int parse_link_config(const char *link_config, char **ret_ifname, LinkConfigType *ret_type) {
const char *p = ASSERT_PTR(link_config);
_cleanup_free_ char *ifname = NULL;
int r;
reload = RELOAD_UDEVD;
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid config type '%s' for link '%s'.", type, ifname);
assert(ret_ifname);
assert(ret_type);
*ret_path = TAKE_PTR(path);
*ret_dropins = TAKE_PTR(dropins);
r = extract_first_word(&p, &ifname, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r <= 0)
return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL),
"Failed to extract link name from '%s': %m", link_config);
if (ret_reload)
*ret_reload = reload;
if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid link name: %s", ifname);
LinkConfigType t;
if (isempty(p))
t = CONFIG_NETWORK;
else if (streq(p, "all"))
t = _CONFIG_MAX;
else {
t = link_config_type_from_string(p);
if (t < 0)
return log_error_errno(t, "Invalid config type '%s' for link '%s'.", p, ifname);
}
*ret_ifname = TAKE_PTR(ifname);
*ret_type = t;
return 0;
}
@ -427,18 +485,29 @@ int verb_edit(int argc, char *argv[], void *userdata) {
link_config = startswith(*name, "@");
if (link_config) {
ReloadFlags flags;
_cleanup_free_ char *ifname = NULL;
LinkConfigType type;
r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, &flags);
r = parse_link_config(link_config, &ifname, &type);
if (r < 0)
return r;
if (type == _CONFIG_MAX)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Config type 'all' cannot be used with 'edit'.");
reload |= flags;
r = get_config_files_by_link_config(ifname, type,
/* ignore_missing = */ false,
&rtnl,
&path, &dropins);
if (r < 0)
return r;
r = add_config_to_edit(&context, path, dropins);
if (r < 0)
return r;
reload |= type == CONFIG_LINK ? RELOAD_UDEVD : RELOAD_NETWORKD;
continue;
}
@ -482,6 +551,66 @@ int verb_edit(int argc, char *argv[], void *userdata) {
return reload_daemons(reload);
}
static int cat_files_by_link_one(
const char *ifname,
LinkConfigType type,
sd_netlink **rtnl,
bool ignore_missing,
bool *first) {
_cleanup_strv_free_ char **dropins = NULL;
_cleanup_free_ char *path = NULL;
int r;
assert(ifname);
assert(type >= 0 && type < _CONFIG_MAX);
assert(rtnl);
assert(first);
r = get_config_files_by_link_config(ifname, type, ignore_missing, rtnl, &path, &dropins);
if (ignore_missing && IN_SET(r, -ENOENT, -ESRCH))
return 0;
if (r < 0)
return r;
if (!*first)
putchar('\n');
r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
if (r < 0)
return r;
*first = false;
return 0;
}
static int cat_files_by_link_config(const char *link_config, sd_netlink **rtnl, bool *first) {
_cleanup_free_ char *ifname = NULL;
LinkConfigType type;
int r;
assert(link_config);
assert(rtnl);
assert(first);
r = parse_link_config(link_config, &ifname, &type);
if (r < 0)
return r;
if (type == _CONFIG_MAX) {
for (LinkConfigType i = 0; i < _CONFIG_MAX; i++) {
r = cat_files_by_link_one(ifname, i, rtnl, /* ignore_missing = */ true, first);
if (r < 0)
return r;
}
return 0;
}
return cat_files_by_link_one(ifname, type, rtnl, /* ignore_missing = */ false, first);
}
int verb_cat(int argc, char *argv[], void *userdata) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **args = strv_skip(argv, 1);
@ -494,37 +623,37 @@ int verb_cat(int argc, char *argv[], void *userdata) {
bool first = true;
STRV_FOREACH(name, args) {
_cleanup_strv_free_ char **dropins = NULL;
_cleanup_free_ char *path = NULL;
const char *link_config;
link_config = startswith(*name, "@");
if (link_config) {
r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, /* ret_reload = */ NULL);
r = cat_files_by_link_config(link_config, &rtnl, &first);
if (r < 0)
return RET_GATHER(ret, r);
} else {
r = get_config_files_by_name(*name, /* allow_masked = */ false, &path, &dropins);
if (r == -ENOENT) {
RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name));
continue;
}
if (r == -ERFKILL) {
RET_GATHER(ret, log_debug_errno(r, "Network config '%s' is masked, ignoring.", *name));
continue;
}
if (r < 0) {
log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
return RET_GATHER(ret, r);
}
return r;
continue;
}
_cleanup_strv_free_ char **dropins = NULL;
_cleanup_free_ char *path = NULL;
r = get_config_files_by_name(*name, /* allow_masked = */ false, &path, &dropins);
if (r == -ENOENT) {
RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name));
continue;
}
if (r == -ERFKILL) {
RET_GATHER(ret, log_debug_errno(r, "Network config '%s' is masked, ignoring.", *name));
continue;
}
if (r < 0)
return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
if (!first)
putchar('\n');
r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
if (r < 0)
return RET_GATHER(ret, r);
return r;
first = false;
}

View File

@ -227,8 +227,10 @@ static int table_add_string_line(Table *table, const char *key, const char *valu
return 0;
}
static int format_dropins(char **dropins) {
STRV_FOREACH(d, dropins) {
static int format_config_files(char ***files, const char *main_config) {
assert(files);
STRV_FOREACH(d, *files) {
_cleanup_free_ char *s = NULL;
int glyph = *(d + 1) == NULL ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH;
@ -239,6 +241,9 @@ static int format_dropins(char **dropins) {
free_and_replace(*d, s);
}
if (strv_prepend(files, main_config) < 0)
return log_oom();
return 0;
}
@ -250,8 +255,8 @@ static int link_status_one(
const LinkInfo *info) {
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL,
**route_domains = NULL, **link_dropins = NULL, **network_dropins = NULL;
_cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL, *captive_portal = NULL,
**route_domains = NULL, **link_dropins = NULL, **network_dropins = NULL, **netdev_dropins = NULL;
_cleanup_free_ char *t = NULL, *network = NULL, *netdev = NULL, *iaid = NULL, *duid = NULL, *captive_portal = NULL,
*setup_state = NULL, *operational_state = NULL, *online_state = NULL, *activation_policy = NULL;
const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL,
*on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup, *on_color_online;
@ -282,6 +287,8 @@ static int link_status_one(
(void) sd_network_link_get_captive_portal(info->ifindex, &captive_portal);
(void) sd_network_link_get_network_file(info->ifindex, &network);
(void) sd_network_link_get_network_file_dropins(info->ifindex, &network_dropins);
(void) sd_network_link_get_netdev_file(info->ifindex, &netdev);
(void) sd_network_link_get_netdev_file_dropins(info->ifindex, &netdev_dropins);
(void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
(void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
(void) sd_network_link_get_activation_policy(info->ifindex, &activation_policy);
@ -312,19 +319,17 @@ static int link_status_one(
(void) dhcp_lease_load(&lease, lease_file);
r = format_dropins(network_dropins);
r = format_config_files(&network_dropins, network);
if (r < 0)
return r;
if (strv_prepend(&network_dropins, network) < 0)
return log_oom();
r = format_dropins(link_dropins);
r = format_config_files(&link_dropins, link);
if (r < 0)
return r;
if (strv_prepend(&link_dropins, link) < 0)
return log_oom();
r = format_config_files(&netdev_dropins, netdev);
if (r < 0)
return r;
table = table_new_vertical();
if (!table)
@ -333,7 +338,15 @@ static int link_status_one(
if (arg_full)
table_set_width(table, 0);
/* unit files and basic states. */
/* Config files and basic states. */
if (netdev_dropins) {
r = table_add_many(table,
TABLE_FIELD, "NetDev File",
TABLE_STRV, netdev_dropins);
if (r < 0)
return table_log_add_error(r);
}
r = table_add_many(table,
TABLE_FIELD, "Link File",
TABLE_STRV, link_dropins ?: STRV_MAKE("n/a"),

View File

@ -277,10 +277,6 @@ int manager_save(Manager *m) {
address_state = LINK_ADDRESS_STATE_OFF;
LinkOnlineState online_state;
size_t links_offline = 0, links_online = 0;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_strv_free_ char **p = NULL;
_cleanup_fclose_ FILE *f = NULL;
Link *link;
int r;
assert(m);
@ -288,6 +284,7 @@ int manager_save(Manager *m) {
if (isempty(m->state_file))
return 0; /* Do not update state file when running in test mode. */
Link *link;
HASHMAP_FOREACH(link, m->links_by_index) {
if (link->flags & IFF_LOOPBACK)
continue;
@ -336,20 +333,14 @@ int manager_save(Manager *m) {
(links_offline > 0 ? LINK_ONLINE_STATE_PARTIAL : LINK_ONLINE_STATE_ONLINE) :
(links_offline > 0 ? LINK_ONLINE_STATE_OFFLINE : _LINK_ONLINE_STATE_INVALID);
operstate_str = link_operstate_to_string(operstate);
assert(operstate_str);
operstate_str = ASSERT_PTR(link_operstate_to_string(operstate));
carrier_state_str = ASSERT_PTR(link_carrier_state_to_string(carrier_state));
address_state_str = ASSERT_PTR(link_address_state_to_string(address_state));
ipv4_address_state_str = ASSERT_PTR(link_address_state_to_string(ipv4_address_state));
ipv6_address_state_str = ASSERT_PTR(link_address_state_to_string(ipv6_address_state));
carrier_state_str = link_carrier_state_to_string(carrier_state);
assert(carrier_state_str);
address_state_str = link_address_state_to_string(address_state);
assert(address_state_str);
ipv4_address_state_str = link_address_state_to_string(ipv4_address_state);
assert(ipv4_address_state_str);
ipv6_address_state_str = link_address_state_to_string(ipv6_address_state);
assert(ipv6_address_state_str);
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
r = fopen_temporary(m->state_file, &f, &temp_path);
if (r < 0)
@ -386,6 +377,8 @@ int manager_save(Manager *m) {
temp_path = mfree(temp_path);
_cleanup_strv_free_ char **p = NULL;
if (m->operational_state != operstate) {
m->operational_state = operstate;
if (strv_extend(&p, "OperationalState") < 0)
@ -568,9 +561,32 @@ static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, U
}
}
static int serialize_config_files(FILE *f, const char *prefix, const char *main_config, char * const *dropins) {
assert(f);
assert(prefix);
assert(main_config);
fprintf(f, "%s_FILE=%s\n", prefix, main_config);
bool space = false;
fprintf(f, "%s_FILE_DROPINS=\"", prefix);
STRV_FOREACH(d, dropins) {
_cleanup_free_ char *escaped = NULL;
escaped = xescape(*d, ":");
if (!escaped)
return -ENOMEM;
fputs_with_separator(f, escaped, ":", &space);
}
fputs("\"\n", f);
return 0;
}
static int link_save(Link *link) {
const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state,
*captive_portal;
const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@ -584,23 +600,12 @@ static int link_save(Link *link) {
if (link->state == LINK_STATE_LINGER)
return 0;
admin_state = link_state_to_string(link->state);
assert(admin_state);
oper_state = link_operstate_to_string(link->operstate);
assert(oper_state);
carrier_state = link_carrier_state_to_string(link->carrier_state);
assert(carrier_state);
address_state = link_address_state_to_string(link->address_state);
assert(address_state);
ipv4_address_state = link_address_state_to_string(link->ipv4_address_state);
assert(ipv4_address_state);
ipv6_address_state = link_address_state_to_string(link->ipv6_address_state);
assert(ipv6_address_state);
admin_state = ASSERT_PTR(link_state_to_string(link->state));
oper_state = ASSERT_PTR(link_operstate_to_string(link->operstate));
carrier_state = ASSERT_PTR(link_carrier_state_to_string(link->carrier_state));
address_state = ASSERT_PTR(link_address_state_to_string(link->address_state));
ipv4_address_state = ASSERT_PTR(link_address_state_to_string(link->ipv4_address_state));
ipv6_address_state = ASSERT_PTR(link_address_state_to_string(link->ipv6_address_state));
r = fopen_temporary(link->state_file, &f, &temp_path);
if (r < 0)
@ -619,7 +624,7 @@ static int link_save(Link *link) {
admin_state, oper_state, carrier_state, address_state, ipv4_address_state, ipv6_address_state);
if (link->network) {
const char *online_state;
const char *online_state, *captive_portal;
bool space = false;
online_state = link_online_state_to_string(link->online_state);
@ -641,19 +646,15 @@ static int link_save(Link *link) {
fprintf(f, "ACTIVATION_POLICY=%s\n",
activation_policy_to_string(link->network->activation_policy));
fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
r = serialize_config_files(f, "NETWORK", link->network->filename, link->network->dropins);
if (r < 0)
return r;
fputs("NETWORK_FILE_DROPINS=\"", f);
STRV_FOREACH(d, link->network->dropins) {
_cleanup_free_ char *escaped = NULL;
escaped = xescape(*d, ":");
if (!escaped)
return -ENOMEM;
fputs_with_separator(f, escaped, ":", &space);
if (link->netdev) {
r = serialize_config_files(f, "NETDEV", link->netdev->filename, link->netdev->dropins);
if (r < 0)
return r;
}
fputs("\"\n", f);
/************************************************************/
@ -778,8 +779,9 @@ static int link_save(Link *link) {
if (!set_isempty(nta_anchors)) {
const char *n;
fputs("DNSSEC_NTA=", f);
space = false;
fputs("DNSSEC_NTA=", f);
SET_FOREACH(n, nta_anchors)
fputs_with_separator(f, n, NULL, &space);
fputc('\n', f);
@ -794,9 +796,7 @@ static int link_save(Link *link) {
if (r < 0)
return r;
fprintf(f,
"DHCP_LEASE=%s\n",
link->lease_file);
fprintf(f, "DHCP_LEASE=%s\n", link->lease_file);
} else
(void) unlink(link->lease_file);

View File

@ -122,6 +122,12 @@ int sd_network_link_get_network_file(int ifindex, char **ret);
/* Get paths to .network file dropins applied to link */
int sd_network_link_get_network_file_dropins(int ifindex, char ***ret);
/* Get path to .netdev file associated with link */
int sd_network_link_get_netdev_file(int ifindex, char **ret);
/* Get path to .netdev file dropins associated with link */
int sd_network_link_get_netdev_file_dropins(int ifindex, char ***ret);
/* Get DNS entries for a given link. These are string representations of
* IP addresses */
int sd_network_link_get_dns(int ifindex, char ***ret);

View File

@ -1027,6 +1027,7 @@ DNS=127.0.0.1
subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', 'dummy0', '--timeout=10'])
out = subprocess.check_output(['networkctl', 'status', 'dummy0'])
self.assertIn(b'50-test.netdev', out)
self.assertIn(b'50-test.network.d/dns.conf', out)
for _ in range(50):

View File

@ -99,10 +99,18 @@ cmp "/usr/lib/systemd/network/$LINK_NAME" "/etc/systemd/network/$LINK_NAME"
systemctl unmask systemd-networkd
systemctl stop systemd-networkd
(! networkctl cat @test2)
(! networkctl cat @test2:netdev)
systemctl start systemd-networkd
SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
networkctl cat @test2:network | cmp - <(networkctl cat "$NETWORK_NAME")
networkctl cat @test2:netdev | cmp - <(networkctl cat "$NETDEV_NAME")
for c in "$NETWORK_NAME" "$NETDEV_NAME"; do
assert_in "$(networkctl cat "$c" | head -n1)" "$(networkctl cat @test2:all)"
done
(! networkctl edit @test2:all)
EDITOR='cp' script -ec 'networkctl edit @test2 --drop-in test2.conf' /dev/null
cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test2.conf"
@ -112,6 +120,9 @@ SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:c
ip_link="$(ip link show test2)"
if systemctl --quiet is-active systemd-udevd; then
networkctl cat @test2:link | cmp - <(networkctl cat "$LINK_NAME")
assert_in "$(networkctl cat "$LINK_NAME" | head -n1)" "$(networkctl cat @test2:all)"
assert_in 'alias test_alias' "$ip_link"
fi