]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
wait-online: Add maximum operational state option
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 9 Jan 2020 20:31:50 +0000 (21:31 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Sat, 18 Jan 2020 17:17:22 +0000 (18:17 +0100)
12 files changed:
man/systemd-networkd-wait-online.service.xml
man/systemd.network.xml
src/libsystemd/sd-network/network-util.c
src/libsystemd/sd-network/network-util.h
src/network/networkd-link.c
src/network/networkd-network.c
src/network/networkd-network.h
src/network/wait-online/link.c
src/network/wait-online/link.h
src/network/wait-online/manager.c
src/network/wait-online/manager.h
src/network/wait-online/wait-online.c

index 96fcb5fb48e3c58b125549a23ddf58f4f2b7793d..e2f1eb0e831b9c61478b078b2e11496b48d8139c 100644 (file)
 
     <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
       </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.
index 36acc4dfeb3712f915cd90e7c278c053980f5687..ca863c980a671044f73a0d51c0cbd255f278324d 100644 (file)
       <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
index 08ed9426389c7fbbd8df1a9b8261b0fb0ab338de..4eb06b91ac93ed0d805a4fd262eb3e0c81e10d2c 100644 (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;
+}
index a19435393df6c3a61d2f716698bc1a4ca6ed0dac..1087a1c664411bea4dd30d133fd9f6e6cdc2d8c0 100644 (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);
index f353833e0de25ea969408a1182ae67673edb0395..0251a48a106e5bd41a177e03a0c1c6a3fa0ff4d8 100644 (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);
index 4fd48be52a023720088fa2312a39cb2e7731ca00..63d5a6847c42a442aa32904ca91b9a435f25e6d0 100644 (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;
 }
index e1c1c17241df4ad937e5bff8be921af79ef6c0e2..efb4da580b6ed2088bae1344ba8e8056f2c60d61 100644 (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 */
index a13373f7d7bec16a14f0057786656ad686a98cb0..69b0057707a52c9e68dc80ab51b862816891cc25 100644 (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),
index d58129dfe872f658fa1ea8d68f0cd24fdc9b9e55..73d9f9cc3ec8d2213da93774f8dbe2e2928cb38c 100644 (file)
@@ -17,7 +17,7 @@ struct Link {
         unsigned flags;
 
         bool required_for_online;
-        LinkOperationalState required_operstate;
+        LinkOperationalStateRange required_operstate;
         LinkOperationalState operational_state;
         char *state;
 };
index 6de3df94e1f2b2334ae4c48805224b6258a2df9c..e4643418a74d9a1dfc5a4224e64b92a81daec4d9 100644 (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;
index dd7d847dd385636e4929c46dfe07091c21041fe0..7398783df713893778270d9db7c1d65cdac321d5 100644 (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);
index 4ce2ac31b2d60ae6f2506d27dc58645e66cd633b..17ed5d38cfe3de6bd53344cc7f391f90e9f61c13 100644 (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;
+
+                        r = parse_operational_state_range(optarg, &range);
+                        if (r < 0)
+                                return log_error_errno(r, "Invalid operational state range '%s'", optarg);
 
-                        s = link_operstate_from_string(optarg);
-                        if (s < 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Invalid operational state '%s'", optarg);
+                        arg_required_operstate = range;
 
-                        arg_required_operstate = s;
                         break;
                 }
                 case ARG_ANY: