Merge pull request #11893 from yuwata/wait-online-take-operstate

wait-online: optionally take required minimum operational state
This commit is contained in:
Lennart Poettering
2019-03-13 10:10:43 +01:00
committed by GitHub
22 changed files with 431 additions and 157 deletions
+8 -3
View File
@@ -115,6 +115,13 @@
<para>the device has a carrier, but is not yet ready for normal traffic</para>
</listitem>
</varlistentry>
<varlistentry>
<term>degraded-carrier</term>
<listitem>
<para>for bond or bridge master, one of the bonding or bridge slave network interfaces is
in off, no-carrier, or dormant state</para>
</listitem>
</varlistentry>
<varlistentry>
<term>carrier</term>
<listitem>
@@ -125,9 +132,7 @@
<varlistentry>
<term>degraded</term>
<listitem>
<para>the link has carrier and addresses valid on the local link configured, or for bond
or bridge master, one of the bonding or bridge slave network interfaces is in off,
no-carrier, or dormant</para>
<para>the link has carrier and addresses valid on the local link configured</para>
</listitem>
</varlistentry>
<varlistentry>
+40 -6
View File
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
<refentry id="systemd-networkd-wait-online.service" conditional='ENABLE_NETWORKD'>
<refentry id="systemd-networkd-wait-online.service" conditional='ENABLE_NETWORKD'
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-networkd-wait-online.service</title>
@@ -48,8 +49,8 @@
<variablelist>
<varlistentry>
<term><option>-i</option></term>
<term><option>--interface=</option></term>
<term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
<term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
<listitem><para>Network interface to wait for before deciding
if the system is online. This is useful when a system has
@@ -57,21 +58,53 @@
one is necessary to access some network resources. This option
may be used more than once to wait for multiple network
interfaces. When used, all other interfaces are ignored.
Optinally, required minimum operational state can be
specified after a colon <literal>:</literal>. Please see
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for possible operational states. If the operational state is not specified here, then
the value from <varname>RequiredForOnline=</varname> in the corresponding
<filename>.network</filename> file is used if present, and <literal>degraded</literal> otherwise.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--ignore=</option></term>
<term><option>--ignore=</option><replaceable>INTERFACE</replaceable></term>
<listitem><para>Network interfaces to be ignored when deciding
if the system is online. By default, only the loopback
interface is ignored. This option may be used more than once
to ignore multiple network interfaces. </para></listitem>
</varlistentry>
<varlistentry>
<term><option>--timeout=</option></term>
<term><option>-o</option> <replaceable>OPERSTATE</replaceable></term>
<term><option>--operational-state=</option><replaceable>OPERSTATE</replaceable></term>
<listitem><para>Takes an operational state. Please see
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for possible operational states. If set, the specified value overrides
<varname>RequiredForOnline=</varname> settings in <filename>.network</filename> files.
But this does not override operational states specified in <option>--interface</option> option.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--timeout=</option> <replaceable>SECS</replaceable></term>
<listitem><para>Fail the service if the network is not online
by the time the timeout elapses. A timeout of 0 disables the
timeout. Defaults to 120 seconds. </para></listitem>
</varlistentry>
<varlistentry>
<term><option>-q</option></term>
<term><option>--quiet</option></term>
<listitem><para>Suppress log messages.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
@@ -80,7 +113,8 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
+10 -6
View File
@@ -255,15 +255,19 @@
<varlistentry>
<term><varname>RequiredForOnline=</varname></term>
<listitem>
<para>Takes a boolean. When <literal>yes</literal>, the network is deemed
required when determining whether the system is online when running
<literal>systemd-networkd-wait-online</literal>.
When <literal>no</literal>, the network is ignored when checking for
online state. Defaults to <literal>yes</literal>.</para>
<para>Takes a boolean or operational state. Please see
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for possible operational states. When <literal>yes</literal>, the network is deemed required when
determining whether the system is online when running
<command>systemd-networkd-wait-online</command>. When <literal>no</literal>, the network is ignored
when checking for online state. When an operational state is set, <literal>yes</literal> is implied,
and this controls the operational state required for the network interface to be considered online.
Defaults to <literal>yes</literal>.</para>
<para>The network will be brought up normally in all cases, but in
the event that there is no address being assigned by DHCP or the
cable is not plugged in, the link will simply remain offline and be
skipped automatically by <literal>systemd-networkd-wait-online</literal>
skipped automatically by <command>systemd-networkd-wait-online</command>
if <literal>RequiredForOnline=no</literal>.</para>
</listitem>
</varlistentry>
+2
View File
@@ -412,9 +412,11 @@ static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_key);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_key);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
+14
View File
@@ -3,6 +3,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "network-util.h"
#include "string-table.h"
#include "strv.h"
bool network_is_online(void) {
@@ -18,3 +19,16 @@ bool network_is_online(void) {
return false;
}
static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = {
[LINK_OPERSTATE_OFF] = "off",
[LINK_OPERSTATE_NO_CARRIER] = "no-carrier",
[LINK_OPERSTATE_DORMANT] = "dormant",
[LINK_OPERSTATE_DEGRADED_CARRIER] = "degraded-carrier",
[LINK_OPERSTATE_CARRIER] = "carrier",
[LINK_OPERSTATE_DEGRADED] = "degraded",
[LINK_OPERSTATE_ENSLAVED] = "enslaved",
[LINK_OPERSTATE_ROUTABLE] = "routable",
};
DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState);
+18
View File
@@ -3,4 +3,22 @@
#include "sd-network.h"
#include "macro.h"
bool network_is_online(void);
typedef enum LinkOperationalState {
LINK_OPERSTATE_OFF,
LINK_OPERSTATE_NO_CARRIER,
LINK_OPERSTATE_DORMANT,
LINK_OPERSTATE_DEGRADED_CARRIER,
LINK_OPERSTATE_CARRIER,
LINK_OPERSTATE_DEGRADED,
LINK_OPERSTATE_ENSLAVED,
LINK_OPERSTATE_ROUTABLE,
_LINK_OPERSTATE_MAX,
_LINK_OPERSTATE_INVALID = -1
} LinkOperationalState;
const char* link_operstate_to_string(LinkOperationalState s) _const_;
LinkOperationalState link_operstate_from_string(const char *s) _pure_;
+21
View File
@@ -164,6 +164,27 @@ _public_ int sd_network_link_get_required_for_online(int ifindex) {
return parse_boolean(s);
}
_public_ int sd_network_link_get_required_operstate_for_online(int ifindex, char **state) {
_cleanup_free_ char *s = NULL;
int r;
assert_return(state, -EINVAL);
r = network_link_get_string(ifindex, "REQUIRED_OPER_STATE_FOR_ONLINE", &s);
if (r < 0) {
if (r != -ENODATA)
return r;
/* For compatibility, assuming degraded. */
s = strdup("degraded");
if (!s)
return -ENOMEM;
}
*state = TAKE_PTR(s);
return 0;
}
_public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) {
return network_link_get_string(ifindex, "LLMNR", llmnr);
}
+2 -2
View File
@@ -213,7 +213,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
(void) pager_open(arg_pager_flags);
if (arg_legend)
printf("%3s %-16s %-18s %-11s %-10s\n",
printf("%3s %-16s %-18s %-16s %-10s\n",
"IDX",
"LINK",
"TYPE",
@@ -241,7 +241,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
t = link_get_type_string(links[i].iftype, d);
printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
printf("%3i %-16s %-18s %s%-16s%s %s%-10s%s\n",
links[i].ifindex, links[i].name, strna(t),
on_color_operational, strna(operational_state), off_color_operational,
on_color_setup, strna(setup_state), off_color_setup);
+6 -16
View File
@@ -407,15 +407,14 @@ void link_update_operstate(Link *link, bool also_update_master) {
link_is_enslaved(link))
operstate = LINK_OPERSTATE_ENSLAVED;
if (IN_SET(operstate, LINK_OPERSTATE_CARRIER, LINK_OPERSTATE_ENSLAVED, LINK_OPERSTATE_ROUTABLE)) {
if (operstate >= LINK_OPERSTATE_CARRIER) {
Link *slave;
HASHMAP_FOREACH(slave, link->slaves, i) {
link_update_operstate(slave, false);
if (IN_SET(slave->operstate,
LINK_OPERSTATE_OFF, LINK_OPERSTATE_NO_CARRIER, LINK_OPERSTATE_DORMANT))
operstate = LINK_OPERSTATE_DEGRADED;
if (slave->operstate < LINK_OPERSTATE_CARRIER)
operstate = LINK_OPERSTATE_DEGRADED_CARRIER;
}
}
@@ -4004,6 +4003,9 @@ int link_save(Link *link) {
fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
yes_no(link->network->required_for_online));
fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s\n",
strempty(link_operstate_to_string(link->network->required_operstate_for_online)));
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
if (r < 0 && r != -ENOMSG)
@@ -4306,15 +4308,3 @@ static const char* const link_state_table[_LINK_STATE_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState);
static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = {
[LINK_OPERSTATE_OFF] = "off",
[LINK_OPERSTATE_NO_CARRIER] = "no-carrier",
[LINK_OPERSTATE_DORMANT] = "dormant",
[LINK_OPERSTATE_CARRIER] = "carrier",
[LINK_OPERSTATE_DEGRADED] = "degraded",
[LINK_OPERSTATE_ENSLAVED] = "enslaved",
[LINK_OPERSTATE_ROUTABLE] = "routable",
};
DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState);
+2 -39
View File
@@ -15,6 +15,8 @@
#include "sd-netlink.h"
#include "list.h"
#include "log-link.h"
#include "network-util.h"
#include "set.h"
typedef enum LinkState {
@@ -28,18 +30,6 @@ typedef enum LinkState {
_LINK_STATE_INVALID = -1
} LinkState;
typedef enum LinkOperationalState {
LINK_OPERSTATE_OFF,
LINK_OPERSTATE_NO_CARRIER,
LINK_OPERSTATE_DORMANT,
LINK_OPERSTATE_CARRIER,
LINK_OPERSTATE_DEGRADED,
LINK_OPERSTATE_ENSLAVED,
LINK_OPERSTATE_ROUTABLE,
_LINK_OPERSTATE_MAX,
_LINK_OPERSTATE_INVALID = -1
} LinkOperationalState;
typedef struct Manager Manager;
typedef struct Network Network;
typedef struct Address Address;
@@ -180,9 +170,6 @@ int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;
const char* link_operstate_to_string(LinkOperationalState s) _const_;
LinkOperationalState link_operstate_from_string(const char *s) _pure_;
extern const sd_bus_vtable link_vtable[];
int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
@@ -193,30 +180,6 @@ uint32_t link_get_vrf_table(Link *link);
uint32_t link_get_dhcp_route_table(Link *link);
uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
/* Macros which append INTERFACE= to the message */
#define log_link_full(link, level, error, ...) \
({ \
const Link *_l = (link); \
_l ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, NULL, NULL, ##__VA_ARGS__) : \
log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
}) \
#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__)
#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__)
#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__)
#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__)
#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__)
#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__)
#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__)
#define log_link_notice_errno(link, error, ...) log_link_full(link, LOG_NOTICE, error, ##__VA_ARGS__)
#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__)
#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__)
#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__
#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname
#define ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \
(be32toh((address).s_addr) >> 16) & 0xFFu, \
+1 -1
View File
@@ -36,7 +36,7 @@ Link.ARP, config_parse_tristate,
Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast)
Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
Link.RequiredForOnline, config_parse_bool, 0, offsetof(Network, required_for_online)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name)
+44
View File
@@ -366,6 +366,7 @@ int network_load_one(Manager *manager, const char *filename) {
.name = TAKE_PTR(name),
.required_for_online = true,
.required_operstate_for_online = LINK_OPERSTATE_DEGRADED,
.dhcp = ADDRESS_FAMILY_NO,
.dhcp_use_ntp = true,
.dhcp_use_dns = true,
@@ -1671,3 +1672,46 @@ int config_parse_iaid(const char *unit,
return 0;
}
int config_parse_required_for_online(
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 = data;
LinkOperationalState s;
bool required = true;
int r;
if (isempty(rvalue)) {
network->required_for_online = true;
network->required_operstate_for_online = LINK_OPERSTATE_DEGRADED;
return 0;
}
s = link_operstate_from_string(rvalue);
if (s < 0) {
r = parse_boolean(rvalue);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse %s= setting, ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
required = r;
s = LINK_OPERSTATE_DEGRADED;
}
network->required_for_online = required;
network->required_operstate_for_online = s;
return 0;
}
+2
View File
@@ -241,6 +241,7 @@ struct Network {
bool iaid_set;
bool required_for_online; /* Is this network required to be considered online? */
LinkOperationalState required_operstate_for_online;
LLDPMode lldp_mode; /* LLDP reception */
LLDPEmit lldp_emit; /* LLDP transmission */
@@ -324,6 +325,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
/* Legacy IPv4LL support */
CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
+55 -19
View File
@@ -10,6 +10,7 @@
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
_cleanup_(link_freep) Link *l = NULL;
_cleanup_free_ char *n = NULL;
int r;
assert(m);
@@ -23,30 +24,33 @@ int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
if (r < 0)
return r;
l = new0(Link, 1);
n = strdup(ifname);
if (!n)
return -ENOMEM;
l = new(Link, 1);
if (!l)
return -ENOMEM;
l->manager = m;
l->ifname = strdup(ifname);
if (!l->ifname)
return -ENOMEM;
*l = (Link) {
.manager = m,
.ifname = TAKE_PTR(n),
.ifindex = ifindex,
.required_operstate = LINK_OPERSTATE_DEGRADED,
};
r = hashmap_put(m->links_by_name, l->ifname, l);
if (r < 0)
return r;
l->ifindex = ifindex;
r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
if (r < 0)
return r;
if (ret)
*ret = l;
l = NULL;
TAKE_PTR(l);
return 0;
}
@@ -60,6 +64,7 @@ Link *link_free(Link *l) {
hashmap_remove(l->manager->links_by_name, l->ifname);
}
free(l->state);
free(l->ifname);
return mfree(l);
}
@@ -87,9 +92,8 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
if (!new_ifname)
return -ENOMEM;
hashmap_remove(l->manager->links_by_name, l->ifname);
free(l->ifname);
l->ifname = new_ifname;
assert_se(hashmap_remove(l->manager->links_by_name, l->ifname) == l);
free_and_replace(l->ifname, new_ifname);
r = hashmap_put(l->manager->links_by_name, l->ifname, l);
if (r < 0)
@@ -100,17 +104,49 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
}
int link_update_monitor(Link *l) {
_cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *state = NULL;
LinkOperationalState s;
int r, ret = 0;
assert(l);
assert(l->ifname);
l->required_for_online = sd_network_link_get_required_for_online(l->ifindex) != 0;
r = sd_network_link_get_required_for_online(l->ifindex);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to determine whether the link is required for online or not, "
"ignoring: %m");
else
l->required_for_online = r > 0;
l->operational_state = mfree(l->operational_state);
r = sd_network_link_get_required_operstate_for_online(l->ifindex, &required_operstate);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get required operational state, ignoring: %m");
else {
s = link_operstate_from_string(required_operstate);
if (s < 0)
ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL),
"Failed to parse required operational state, ignoring: %m");
else
l->required_operstate = s;
}
sd_network_link_get_operational_state(l->ifindex, &l->operational_state);
r = sd_network_link_get_operational_state(l->ifindex, &operstate);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get operational state, ignoring: %m");
else {
s = link_operstate_from_string(operstate);
if (s < 0)
ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL),
"Failed to parse operational state, ignoring: %m");
else
l->operational_state = s;
}
l->state = mfree(l->state);
r = sd_network_link_get_setup_state(l->ifindex, &state);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get setup state, ignoring: %m");
else
free_and_replace(l->state, state);
sd_network_link_get_setup_state(l->ifindex, &l->state);
return 0;
return ret;
}
+5 -1
View File
@@ -3,6 +3,9 @@
#include "sd-netlink.h"
#include "log-link.h"
#include "network-util.h"
typedef struct Link Link;
typedef struct Manager Manager;
@@ -14,7 +17,8 @@ struct Link {
unsigned flags;
bool required_for_online;
char *operational_state;
LinkOperationalState required_operstate;
LinkOperationalState operational_state;
char *state;
};
+73 -41
View File
@@ -13,7 +13,7 @@
#include "time-util.h"
#include "util.h"
bool manager_ignore_link(Manager *m, Link *link) {
static bool manager_ignore_link(Manager *m, Link *link) {
assert(m);
assert(link);
@@ -22,7 +22,7 @@ bool manager_ignore_link(Manager *m, Link *link) {
return true;
/* if interfaces are given on the command line, ignore all others */
if (m->interfaces && !strv_contains(m->interfaces, link->ifname))
if (m->interfaces && !hashmap_contains(m->interfaces, link->ifname))
return true;
if (!link->required_for_online)
@@ -32,45 +32,73 @@ bool manager_ignore_link(Manager *m, Link *link) {
return strv_fnmatch(m->ignore, link->ifname, 0);
}
bool manager_all_configured(Manager *m) {
Iterator i;
Link *l;
char **ifname;
bool one_ready = false;
static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) {
/* This returns the following:
* -EAGAIN: not processed by udev or networkd
* 0: operstate is not enough
* 1: online */
/* wait for all the links given on the command line to appear */
STRV_FOREACH(ifname, m->interfaces) {
l = hashmap_get(m->links_by_name, *ifname);
if (!l) {
log_debug("still waiting for %s", *ifname);
return false;
}
if (!l->state)
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
"link has not yet been processed by udev");
if (STR_IN_SET(l->state, "configuring", "pending"))
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
"link is being processed by networkd");
if (s < 0)
s = m->required_operstate >= 0 ? m->required_operstate : l->required_operstate;
if (l->operational_state < s) {
log_link_debug(l, "Operational state '%s' is below '%s'",
link_operstate_to_string(l->operational_state),
link_operstate_to_string(s));
return 0;
}
return 1;
}
bool manager_all_configured(Manager *m) {
bool one_ready = false;
Iterator i;
const char *ifname;
void *p;
Link *l;
int r;
/* wait for all the links given on the command line to appear */
HASHMAP_FOREACH_KEY(p, ifname, m->interfaces, i) {
LinkOperationalState s = PTR_TO_INT(p);
l = hashmap_get(m->links_by_name, ifname);
if (!l) {
log_debug("still waiting for %s", ifname);
return false;
}
if (manager_link_is_online(m, l, s) <= 0)
return false;
}
if (!hashmap_isempty(m->interfaces))
/* all interfaces given by the command line are online. */
return true;
/* wait for all links networkd manages to be in admin state 'configured'
and at least one link to gain a carrier */
* and at least one link to gain a carrier */
HASHMAP_FOREACH(l, m->links, i) {
if (manager_ignore_link(m, l)) {
log_info("ignoring: %s", l->ifname);
log_link_info(l, "link is ignored");
continue;
}
if (!l->state) {
log_debug("link %s has not yet been processed by udev",
l->ifname);
r = manager_link_is_online(m, l, _LINK_OPERSTATE_INVALID);
if (r < 0)
return false;
}
if (STR_IN_SET(l->state, "configuring", "pending")) {
log_debug("link %s is being processed by networkd",
l->ifname);
return false;
}
if (l->operational_state &&
STR_IN_SET(l->operational_state, "degraded", "routable"))
if (r > 0)
/* we wait for at least one link to be ready,
regardless of who manages it */
* regardless of who manages it */
one_ready = true;
}
@@ -120,21 +148,21 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *
r = link_new(m, &l, ifindex, ifname);
if (r < 0)
return log_error_errno(r, "Failed to create link object: %m");
r = link_update_monitor(l);
if (r < 0)
return log_error_errno(r, "Failed to initialize link object: %m");
}
r = link_update_rtnl(l, mm);
if (r < 0)
return log_warning_errno(r, "Failed to process RTNL link message: %m");;
log_link_warning_errno(l, r, "Failed to process RTNL link message, ignoring: %m");
r = link_update_monitor(l);
if (r < 0)
log_link_warning_errno(l, r, "Failed to update link state, ignoring: %m");
break;
case RTM_DELLINK:
if (l) {
log_debug("Removing link %i", l->ifindex);
log_link_debug(l, "Removing link");
link_free(l);
}
@@ -217,7 +245,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
HASHMAP_FOREACH(l, m->links, i) {
r = link_update_monitor(l);
if (r < 0)
log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex);
log_link_warning_errno(l, r, "Failed to update monitor information: %m");
}
if (manager_all_configured(m))
@@ -251,18 +279,22 @@ static int manager_network_monitor_listen(Manager *m) {
return 0;
}
int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout) {
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
LinkOperationalState required_operstate, usec_t timeout) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
assert(ret);
m = new0(Manager, 1);
m = new(Manager, 1);
if (!m)
return -ENOMEM;
m->interfaces = interfaces;
m->ignore = ignore;
*m = (Manager) {
.interfaces = interfaces,
.ignore = ignore,
.required_operstate = required_operstate,
};
r = sd_event_default(&m->event);
if (r < 0)
+7 -3
View File
@@ -6,6 +6,7 @@
#include "sd-network.h"
#include "hashmap.h"
#include "network-util.h"
typedef struct Manager Manager;
typedef struct Link Link;
@@ -14,9 +15,12 @@ struct Manager {
Hashmap *links;
Hashmap *links_by_name;
char **interfaces;
/* Do not free the two members below. */
Hashmap *interfaces;
char **ignore;
LinkOperationalState required_operstate;
sd_netlink *rtnl;
sd_event_source *rtnl_event_source;
@@ -27,9 +31,9 @@ struct Manager {
};
void manager_free(Manager *m);
int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout);
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
LinkOperationalState required_operstate, usec_t timeout);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
bool manager_all_configured(Manager *m);
bool manager_ignore_link(Manager *m, Link *link);
+78 -16
View File
@@ -9,14 +9,16 @@
#include "manager.h"
#include "pretty-print.h"
#include "signal-util.h"
#include "socket-util.h"
#include "strv.h"
static bool arg_quiet = false;
static usec_t arg_timeout = 120 * USEC_PER_SEC;
static char **arg_interfaces = NULL;
static Hashmap *arg_interfaces = NULL;
static char **arg_ignore = NULL;
static LinkOperationalState arg_required_operstate = _LINK_OPERSTATE_INVALID;
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_keyp);
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
static int help(void) {
@@ -32,8 +34,11 @@ static int help(void) {
" -h --help Show this help\n"
" --version Print version string\n"
" -q --quiet Do not show status information\n"
" -i --interface=INTERFACE Block until at least these interfaces have appeared\n"
" -i --interface=INTERFACE[:OPERSTATE]\n"
" Block until at least these interfaces have appeared\n"
" --ignore=INTERFACE Don't take these interfaces into account\n"
" -o --operational-state=OPERSTATE\n"
" Required operational state\n"
" --timeout=SECS Maximum time to wait for network connectivity\n"
"\nSee the %s for details.\n"
, program_invocation_short_name
@@ -43,6 +48,52 @@ static int help(void) {
return 0;
}
static int parse_interface_with_operstate(const char *str) {
_cleanup_free_ char *ifname = NULL;
LinkOperationalState s;
const char *p;
int r;
assert(str);
p = strchr(str, ':');
if (p) {
if (isempty(p + 1))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Operational state is empty.");
s = link_operstate_from_string(p + 1);
if (s < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid operational state '%s'", p + 1);
ifname = strndup(optarg, p - optarg);
} else {
s = _LINK_OPERSTATE_INVALID;
ifname = strdup(str);
}
if (!ifname)
return log_oom();
if (!ifname_valid(ifname))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid interface name '%s'", ifname);
r = hashmap_ensure_allocated(&arg_interfaces, &string_hash_ops);
if (r < 0)
return log_oom();
r = hashmap_put(arg_interfaces, ifname, INT_TO_PTR(s));
if (r < 0)
return log_error_errno(r, "Failed to store interface name: %m");
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Interface name %s is already specified", ifname);
TAKE_PTR(ifname);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
@@ -52,12 +103,13 @@ static int parse_argv(int argc, char *argv[]) {
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "quiet", no_argument, NULL, 'q' },
{ "interface", required_argument, NULL, 'i' },
{ "ignore", required_argument, NULL, ARG_IGNORE },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "quiet", no_argument, NULL, 'q' },
{ "interface", required_argument, NULL, 'i' },
{ "ignore", required_argument, NULL, ARG_IGNORE },
{ "operational-state", required_argument, NULL, 'o' },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{}
};
@@ -66,7 +118,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "+hi:q", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "hi:qo:", options, NULL)) >= 0)
switch (c) {
@@ -82,9 +134,9 @@ static int parse_argv(int argc, char *argv[]) {
return version();
case 'i':
if (strv_extend(&arg_interfaces, optarg) < 0)
return log_oom();
r = parse_interface_with_operstate(optarg);
if (r < 0)
return r;
break;
case ARG_IGNORE:
@@ -93,11 +145,21 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'o': {
LinkOperationalState s;
s = link_operstate_from_string(optarg);
if (s < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid operational state '%s'", optarg);
arg_required_operstate = s;
break;
}
case ARG_TIMEOUT:
r = parse_sec(optarg, &arg_timeout);
if (r < 0)
return r;
break;
case '?':
@@ -124,11 +186,11 @@ static int run(int argc, char *argv[]) {
return r;
if (arg_quiet)
log_set_max_level(LOG_WARNING);
log_set_max_level(LOG_ERR);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m, arg_interfaces, arg_ignore, arg_timeout);
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_timeout);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");
+37
View File
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "log.h"
/*
* The following macros append INTERFACE= to the message.
* The macros require a struct named 'Link' which contains 'char *ifname':
*
* typedef struct Link {
* char *ifname;
* } Link;
*
* See, network/networkd-link.h for example.
*/
#define log_link_full(link, level, error, ...) \
({ \
const Link *_l = (link); \
(_l && _l->ifname) ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, NULL, NULL, ##__VA_ARGS__) : \
log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
}) \
#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__)
#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__)
#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__)
#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__)
#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__)
#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__)
#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__)
#define log_link_notice_errno(link, error, ...) log_link_full(link, LOG_NOTICE, error, ##__VA_ARGS__)
#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__)
#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__)
#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__
#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname
+1
View File
@@ -93,6 +93,7 @@ shared_sources = files('''
json.h
lockfile-util.c
lockfile-util.h
log-link.h
logs-show.c
logs-show.h
loop-util.c
+1
View File
@@ -89,6 +89,7 @@ int sd_network_link_get_setup_state(int ifindex, char **state);
* -ENODATA: networkd is not aware of the link
*/
int sd_network_link_get_operational_state(int ifindex, char **state);
int sd_network_link_get_required_operstate_for_online(int ifindex, char **state);
/* Indicates whether the network is relevant to being online.
* Possible return codes:
+4 -4
View File
@@ -1200,7 +1200,7 @@ class NetworkdNetWorkBondTests(unittest.TestCase, Utilities):
output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8')
print(output)
self.assertRegex(output, 'State: degraded \(configured\)')
self.assertRegex(output, 'State: degraded-carrier \(configured\)')
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
time.sleep(2)
@@ -1219,7 +1219,7 @@ class NetworkdNetWorkBondTests(unittest.TestCase, Utilities):
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'down']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'test1', 'down']), 0)
time.sleep(2)
time.sleep(5)
output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
print(output)
@@ -1231,7 +1231,7 @@ class NetworkdNetWorkBondTests(unittest.TestCase, Utilities):
output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8')
print(output)
self.assertRegex(output, 'State: degraded \(configured\)')
self.assertRegex(output, 'State: no-carrier \(configured\)')
class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities):
links = [
@@ -1313,7 +1313,7 @@ class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities):
time.sleep(3)
output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: degraded \(configured\)')
self.assertRegex(output, 'State: degraded-carrier \(configured\)')
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
time.sleep(3)