wait-online: Add maximum operational state option

This commit is contained in:
Daan De Meyer 2020-01-09 21:31:50 +01:00
parent 8ac7339648
commit 75cd4a5d92
12 changed files with 135 additions and 64 deletions

View File

@ -47,16 +47,16 @@
<variablelist>
<varlistentry>
<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>
<term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
<term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
<listitem><para>Network interface to wait for before deciding if the system is online. This
is useful when a system has several interfaces which will be configured, but a particular
one is necessary to access some network resources. When used, all other interfaces are ignored.
This option may be used more than once to wait for multiple network interfaces. When this
option is specified multiple times, then <command>systemd-networkd-wait-online</command> waits
for all specified interfaces to be online. Optionally, required minimum operational state can be
specified after a colon <literal>:</literal>. Please see
for all specified interfaces to be online. Optionally, required minimum and maximum operational
states 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
@ -74,11 +74,11 @@
</varlistentry>
<varlistentry>
<term><option>-o</option> <replaceable>OPERSTATE</replaceable></term>
<term><option>--operational-state=</option><replaceable>OPERSTATE</replaceable></term>
<term><option>-o</option> <replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
<term><option>--operational-state=</option><replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
<listitem><para>Takes an operational state. Please see
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
<listitem><para>Takes a minimum operational state and an optional maximum 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.

View File

@ -206,13 +206,14 @@
<varlistentry>
<term><varname>RequiredForOnline=</varname></term>
<listitem>
<para>Takes a boolean or operational state. Please see
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
<para>Takes a boolean or a minimum operational state and an optional maximum 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.
when checking for online state. When a minimum operational state and an optional maximum operational
state are set, <literal>yes</literal> is implied, and this controls the minimum and maximum
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

View File

@ -56,3 +56,49 @@ static const char* const link_address_state_table[_LINK_ADDRESS_STATE_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(link_address_state, LinkAddressState);
int parse_operational_state_range(const char *str, LinkOperationalStateRange *out) {
LinkOperationalStateRange range = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
_cleanup_free_ const char *min = NULL;
const char *p;
assert(str);
assert(out);
p = strchr(str, ':');
if (p) {
min = strndup(str, p - str);
if (!isempty(p + 1)) {
range.max = link_operstate_from_string(p + 1);
if (range.max < 0)
return -EINVAL;
}
} else
min = strdup(str);
if (!min)
return -ENOMEM;
if (!isempty(min)) {
range.min = link_operstate_from_string(min);
if (range.min < 0)
return -EINVAL;
}
/* Fail on empty strings. */
if (range.min == _LINK_OPERSTATE_INVALID && range.max == _LINK_OPERSTATE_INVALID)
return -EINVAL;
if (range.min == _LINK_OPERSTATE_INVALID)
range.min = LINK_OPERSTATE_OFF;
if (range.max == _LINK_OPERSTATE_INVALID)
range.max = LINK_OPERSTATE_ROUTABLE;
if (range.min > range.max)
return -EINVAL;
*out = range;
return 0;
}

View File

@ -47,3 +47,13 @@ LinkCarrierState link_carrier_state_from_string(const char *s) _pure_;
const char* link_address_state_to_string(LinkAddressState s) _const_;
LinkAddressState link_address_state_from_string(const char *s) _pure_;
typedef struct LinkOperationalStateRange {
LinkOperationalState min;
LinkOperationalState max;
} LinkOperationalStateRange;
#define LINK_OPERSTATE_RANGE_DEFAULT (LinkOperationalStateRange) { LINK_OPERSTATE_DEGRADED, \
LINK_OPERSTATE_ROUTABLE }
int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);

View File

@ -3879,8 +3879,14 @@ 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)));
fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s",
strempty(link_operstate_to_string(link->network->required_operstate_for_online.min)));
if (link->network->required_operstate_for_online.max != LINK_OPERSTATE_RANGE_DEFAULT.max)
fprintf(f, ":%s",
strempty(link_operstate_to_string(link->network->required_operstate_for_online.max)));
fprintf(f, "\n");
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);

View File

@ -375,7 +375,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.n_ref = 1,
.required_for_online = true,
.required_operstate_for_online = LINK_OPERSTATE_DEGRADED,
.required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
.dhcp = ADDRESS_FAMILY_NO,
.dhcp_critical = -1,
.dhcp_use_ntp = true,
@ -1300,18 +1300,18 @@ int config_parse_required_for_online(
void *userdata) {
Network *network = data;
LinkOperationalState s;
LinkOperationalStateRange range;
bool required = true;
int r;
if (isempty(rvalue)) {
network->required_for_online = true;
network->required_operstate_for_online = LINK_OPERSTATE_DEGRADED;
network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
return 0;
}
s = link_operstate_from_string(rvalue);
if (s < 0) {
r = parse_operational_state_range(rvalue, &range);
if (r < 0) {
r = parse_boolean(rvalue);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
@ -1321,11 +1321,11 @@ int config_parse_required_for_online(
}
required = r;
s = LINK_OPERSTATE_DEGRADED;
range = LINK_OPERSTATE_RANGE_DEFAULT;
}
network->required_for_online = required;
network->required_operstate_for_online = s;
network->required_operstate_for_online = range;
return 0;
}

View File

@ -237,7 +237,7 @@ struct Network {
bool iaid_set;
bool required_for_online; /* Is this network required to be considered online? */
LinkOperationalState required_operstate_for_online;
LinkOperationalStateRange required_operstate_for_online;
LLDPMode lldp_mode; /* LLDP reception */
LLDPEmit lldp_emit; /* LLDP transmission */

View File

@ -36,7 +36,7 @@ int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
.manager = m,
.ifname = TAKE_PTR(n),
.ifindex = ifindex,
.required_operstate = LINK_OPERSTATE_DEGRADED,
.required_operstate = LINK_OPERSTATE_RANGE_DEFAULT,
};
r = hashmap_put(m->links_by_name, l->ifname, l);
@ -105,7 +105,6 @@ 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);
@ -121,19 +120,21 @@ int link_update_monitor(Link *l) {
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 if (isempty(required_operstate))
l->required_operstate = LINK_OPERSTATE_RANGE_DEFAULT;
else {
s = link_operstate_from_string(required_operstate);
if (s < 0)
r = parse_operational_state_range(required_operstate, &l->required_operstate);
if (r < 0)
ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL),
"Failed to parse required operational state, ignoring: %m");
else
l->required_operstate = s;
}
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 {
LinkOperationalState s;
s = link_operstate_from_string(operstate);
if (s < 0)
ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL),

View File

@ -17,7 +17,7 @@ struct Link {
unsigned flags;
bool required_for_online;
LinkOperationalState required_operstate;
LinkOperationalStateRange required_operstate;
LinkOperationalState operational_state;
char *state;
};

View File

@ -32,7 +32,7 @@ static bool manager_ignore_link(Manager *m, Link *link) {
return strv_fnmatch(m->ignore, link->ifname, 0);
}
static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) {
static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
/* This returns the following:
* -EAGAIN: not processed by udev or networkd
* 0: operstate is not enough
@ -46,13 +46,18 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) {
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 (s.min < 0)
s.min = m->required_operstate.min >= 0 ? m->required_operstate.min
: l->required_operstate.min;
if (l->operational_state < s) {
log_link_debug(l, "Operational state '%s' is below '%s'",
if (s.max < 0)
s.max = m->required_operstate.max >= 0 ? m->required_operstate.max
: l->required_operstate.max;
if (l->operational_state < s.min || l->operational_state > s.max) {
log_link_debug(l, "Operational state '%s' is not in range ['%s':'%s']",
link_operstate_to_string(l->operational_state),
link_operstate_to_string(s));
link_operstate_to_string(s.min), link_operstate_to_string(s.max));
return 0;
}
@ -70,7 +75,7 @@ bool manager_configured(Manager *m) {
if (!hashmap_isempty(m->interfaces)) {
/* 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);
LinkOperationalStateRange *range = p;
l = hashmap_get(m->links_by_name, ifname);
if (!l) {
@ -80,7 +85,7 @@ bool manager_configured(Manager *m) {
continue;
}
if (manager_link_is_online(m, l, s) <= 0) {
if (manager_link_is_online(m, l, *range) <= 0) {
if (!m->any)
return false;
continue;
@ -102,7 +107,9 @@ bool manager_configured(Manager *m) {
continue;
}
r = manager_link_is_online(m, l, _LINK_OPERSTATE_INVALID);
r = manager_link_is_online(m, l,
(LinkOperationalStateRange) { _LINK_OPERSTATE_INVALID,
_LINK_OPERSTATE_INVALID });
if (r < 0 && !m->any)
return false;
if (r > 0)
@ -289,7 +296,7 @@ static int manager_network_monitor_listen(Manager *m) {
}
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
LinkOperationalState required_operstate,
LinkOperationalStateRange required_operstate,
bool any, usec_t timeout) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;

View File

@ -20,7 +20,7 @@ struct Manager {
Hashmap *interfaces;
char **ignore;
LinkOperationalState required_operstate;
LinkOperationalStateRange required_operstate;
bool any;
sd_netlink *rtnl;
@ -34,7 +34,7 @@ struct Manager {
void manager_free(Manager *m);
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
LinkOperationalState required_operstate,
LinkOperationalStateRange required_operstate,
bool any, usec_t timeout);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);

View File

@ -18,10 +18,10 @@ static bool arg_quiet = false;
static usec_t arg_timeout = 120 * USEC_PER_SEC;
static Hashmap *arg_interfaces = NULL;
static char **arg_ignore = NULL;
static LinkOperationalState arg_required_operstate = _LINK_OPERSTATE_INVALID;
static LinkOperationalStateRange arg_required_operstate = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
static bool arg_any = false;
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_keyp);
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
static int help(void) {
@ -37,10 +37,10 @@ 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[:OPERSTATE]\n"
" -i --interface=INTERFACE[:MIN_OPERSTATE[:MAX_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"
" -o --operational-state=MIN_OPERSTATE[:MAX_OPERSTATE]\n"
" Required operational state\n"
" --any Wait until at least one of the interfaces is online\n"
" --timeout=SECS Maximum time to wait for network connectivity\n"
@ -52,28 +52,28 @@ static int help(void) {
return 0;
}
static int parse_interface_with_operstate(const char *str) {
static int parse_interface_with_operstate_range(const char *str) {
_cleanup_free_ char *ifname = NULL;
LinkOperationalState s;
_cleanup_free_ LinkOperationalStateRange *range;
const char *p;
int r;
assert(str);
range = new(LinkOperationalStateRange, 1);
if (!range)
return log_oom();
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);
r = parse_operational_state_range(p + 1, range);
if (r < 0)
log_error_errno(r, "Invalid operational state range '%s'", p + 1);
ifname = strndup(optarg, p - optarg);
} else {
s = _LINK_OPERSTATE_INVALID;
range->min = _LINK_OPERSTATE_INVALID;
range->max = _LINK_OPERSTATE_INVALID;
ifname = strdup(str);
}
if (!ifname)
@ -87,7 +87,7 @@ static int parse_interface_with_operstate(const char *str) {
if (r < 0)
return log_oom();
r = hashmap_put(arg_interfaces, ifname, INT_TO_PTR(s));
r = hashmap_put(arg_interfaces, ifname, TAKE_PTR(range));
if (r < 0)
return log_error_errno(r, "Failed to store interface name: %m");
if (r == 0)
@ -140,7 +140,7 @@ static int parse_argv(int argc, char *argv[]) {
return version();
case 'i':
r = parse_interface_with_operstate(optarg);
r = parse_interface_with_operstate_range(optarg);
if (r < 0)
return r;
break;
@ -152,14 +152,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'o': {
LinkOperationalState s;
LinkOperationalStateRange range;
s = link_operstate_from_string(optarg);
if (s < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid operational state '%s'", optarg);
r = parse_operational_state_range(optarg, &range);
if (r < 0)
return log_error_errno(r, "Invalid operational state range '%s'", optarg);
arg_required_operstate = range;
arg_required_operstate = s;
break;
}
case ARG_ANY: