]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: make WakeOnLan= take multiple features
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 8 Jun 2021 09:09:00 +0000 (18:09 +0900)
committerLennart Poettering <lennart@poettering.net>
Tue, 8 Jun 2021 16:24:11 +0000 (18:24 +0200)
WAKE_XXX are flag, not enum.

man/systemd.link.xml
src/network/test-network-tables.c
src/shared/ethtool-util.c
src/shared/ethtool-util.h
src/udev/net/link-config.c
src/udev/net/link-config.h

index 4650b8f852dbd7548dc0aab95173b2561273ac20..3323b028de5c14e6cda2f2c2542803b2a884320c 100644 (file)
       <varlistentry>
         <term><varname>WakeOnLan=</varname></term>
         <listitem>
-          <para>The Wake-on-LAN policy to set for the device. The
-          supported values are:</para>
+          <para>The Wake-on-LAN policy to set for the device. Takes the special value
+          <literal>off</literal> which disables Wake-on-LAN, or space separated list of the following
+          words:</para>
 
           <variablelist>
             <varlistentry>
                 </para>
               </listitem>
             </varlistentry>
-            <varlistentry>
-              <term><option>off</option></term>
-              <listitem>
-                <para>Never wake.</para>
-              </listitem>
-            </varlistentry>
           </variablelist>
 
-          <para>Defaults to <option>off</option>.</para>
+          <para>Defaults to unset, and the device's default will be used. This setting can be specified
+          multiple times. If an empty string is assigned, then the all previous assignments are
+          cleared.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
index ce34449554f9a6693b53b95548ccac43dd6a9309..f55e524ae99ef2eac7ecbda0275f4374ac6cea1c 100644 (file)
@@ -37,7 +37,6 @@ int main(int argc, char **argv) {
         test_table(netdev_kind, NETDEV_KIND);
         test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA);
         test_table(radv_prefix_delegation, RADV_PREFIX_DELEGATION);
-        test_table(wol, WOL);
         test_table(lldp_event, SD_LLDP_EVENT);
         test_table(ndisc_event, SD_NDISC_EVENT);
         test_table(dhcp_lease_server_type, SD_DHCP_LEASE_SERVER_TYPE);
index 9506af8b8b2542709ab79a0f122349a9e02963b6..f77f6943ca4faf643d3cfcfff4e3feb4fda86298 100644 (file)
@@ -24,19 +24,38 @@ static const char* const duplex_table[_DUP_MAX] = {
 DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
 
-static const char* const wol_table[_WOL_MAX] = {
-        [WOL_PHY]         = "phy",
-        [WOL_UCAST]       = "unicast",
-        [WOL_MCAST]       = "multicast",
-        [WOL_BCAST]       = "broadcast",
-        [WOL_ARP]         = "arp",
-        [WOL_MAGIC]       = "magic",
-        [WOL_MAGICSECURE] = "secureon",
-        [WOL_OFF]         = "off",
+static const struct {
+        uint32_t opt;
+        const char *name;
+} wol_option_map[] = {
+        { WAKE_PHY,         "phy"        },
+        { WAKE_UCAST,       "unicast",   },
+        { WAKE_MCAST,       "multicast", },
+        { WAKE_BCAST,       "broadcast", },
+        { WAKE_ARP,         "arp",       },
+        { WAKE_MAGIC,       "magic",     },
+        { WAKE_MAGICSECURE, "secureon",  },
 };
 
-DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
+int wol_options_to_string_alloc(uint32_t opts, char **ret) {
+        _cleanup_free_ char *str = NULL;
+
+        assert(ret);
+
+        for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++)
+                if (opts & wol_option_map[i].opt &&
+                    !strextend_with_separator(&str, ",", wol_option_map[i].name))
+                        return -ENOMEM;
+
+        if (!str) {
+                str = strdup("off");
+                if (!str)
+                        return -ENOMEM;
+        }
+
+        *ret = TAKE_PTR(str);
+        return 0;
+}
 
 static const char* const port_table[] = {
         [NET_DEV_PORT_TP]     = "tp",
@@ -310,7 +329,7 @@ int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct et
                 dest = _v;                             \
         } while(false)
 
-int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
+int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
         struct ethtool_wolinfo ecmd = {
                 .cmd = ETHTOOL_GWOL,
         };
@@ -323,7 +342,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
         assert(ethtool_fd);
         assert(ifname);
 
-        if (wol == _WOL_INVALID)
+        if (wolopts == UINT32_MAX)
                 return 0;
 
         r = ethtool_connect(ethtool_fd);
@@ -336,66 +355,15 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
         if (r < 0)
                 return -errno;
 
-        switch (wol) {
-        case WOL_PHY:
-                if (ecmd.wolopts != WAKE_PHY) {
-                        ecmd.wolopts = WAKE_PHY;
-                        need_update = true;
-                }
-                break;
-        case WOL_UCAST:
-                if (ecmd.wolopts != WAKE_UCAST) {
-                        ecmd.wolopts = WAKE_UCAST;
-                        need_update = true;
-                }
-                break;
-        case WOL_MCAST:
-                if (ecmd.wolopts != WAKE_MCAST) {
-                        ecmd.wolopts = WAKE_MCAST;
-                        need_update = true;
-                }
-                break;
-        case WOL_BCAST:
-                if (ecmd.wolopts != WAKE_BCAST) {
-                        ecmd.wolopts = WAKE_BCAST;
-                        need_update = true;
-                }
-                break;
-        case WOL_ARP:
-                if (ecmd.wolopts != WAKE_ARP) {
-                        ecmd.wolopts = WAKE_ARP;
-                        need_update = true;
-                }
-                break;
-        case WOL_MAGIC:
-                if (ecmd.wolopts != WAKE_MAGIC) {
-                        ecmd.wolopts = WAKE_MAGIC;
-                        need_update = true;
-                }
-                break;
-        case WOL_MAGICSECURE:
-                if (ecmd.wolopts != WAKE_MAGICSECURE) {
-                        ecmd.wolopts = WAKE_MAGICSECURE;
-                        need_update = true;
-                }
-                break;
-        case WOL_OFF:
-                if (ecmd.wolopts != 0) {
-                        ecmd.wolopts = 0;
-                        need_update = true;
-                }
-                break;
-        default:
-                break;
-        }
+        UPDATE(ecmd.wolopts, wolopts, need_update);
 
-        if (need_update) {
-                ecmd.cmd = ETHTOOL_SWOL;
+        if (!need_update)
+                return 0;
 
-                r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
-                if (r < 0)
-                        return -errno;
-        }
+        ecmd.cmd = ETHTOOL_SWOL;
+        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
+        if (r < 0)
+                return -errno;
 
         return 0;
 }
@@ -1005,7 +973,6 @@ int config_parse_advertise(
                 void *userdata) {
 
         uint32_t *advertise = data;
-        const char *p;
         int r;
 
         assert(filename);
@@ -1020,7 +987,7 @@ int config_parse_advertise(
                 return 0;
         }
 
-        for (p = rvalue;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL;
                 enum ethtool_link_mode_bit_indices mode;
 
@@ -1098,3 +1065,69 @@ int config_parse_nic_buffer_size(
 
         return 0;
 }
+
+int config_parse_wol(
+                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) {
+
+        uint32_t new_opts = 0, *opts = data;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *opts = UINT32_MAX; /* Do not update WOL option. */
+                return 0;
+        }
+
+        if (streq(rvalue, "off")) {
+                *opts = 0; /* Disable WOL. */
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *w = NULL;
+                bool found = false;
+
+                r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        break;
+
+                for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++)
+                        if (streq(w, wol_option_map[i].name)) {
+                                new_opts |= wol_option_map[i].opt;
+                                found = true;
+                                break;
+                        }
+
+                if (!found)
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Unknown wake-on-lan mode '%s', ignoring.", w);
+        }
+
+        if (*opts == UINT32_MAX)
+                *opts = new_opts;
+        else
+                *opts |= new_opts;
+
+        return 0;
+}
index 3c031cda43960b38a96a43a5539331a5c624b78a..7d287666249ae55fc8ceccf400dfd5a6bf46ffcc 100644 (file)
@@ -18,19 +18,6 @@ typedef enum Duplex {
         _DUP_INVALID = -EINVAL,
 } Duplex;
 
-typedef enum WakeOnLan {
-        WOL_PHY,
-        WOL_UCAST,
-        WOL_MCAST,
-        WOL_BCAST,
-        WOL_ARP,
-        WOL_MAGIC,
-        WOL_MAGICSECURE,
-        WOL_OFF,
-        _WOL_MAX,
-        _WOL_INVALID = -EINVAL,
-} WakeOnLan;
-
 typedef enum NetDevFeature {
         NET_DEV_FEAT_RX,
         NET_DEV_FEAT_TX,
@@ -99,7 +86,7 @@ int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
                           int *ret_autonegotiation, uint64_t *ret_speed,
                           Duplex *ret_duplex, NetDevPort *ret_port);
 int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret);
-int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol);
+int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts);
 int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
 int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features);
 int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
@@ -111,8 +98,7 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
 const char *duplex_to_string(Duplex d) _const_;
 Duplex duplex_from_string(const char *d) _pure_;
 
-const char *wol_to_string(WakeOnLan wol) _const_;
-WakeOnLan wol_from_string(const char *wol) _pure_;
+int wol_options_to_string_alloc(uint32_t opts, char **ret);
 
 const char *port_to_string(NetDevPort port) _const_;
 NetDevPort port_from_string(const char *port) _pure_;
index a9e263a095d38243f68c46a5abf040de1ef31cd5..8dfe23691bc42110212c62dd2deebae4a6bdd222 100644 (file)
@@ -134,7 +134,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
         *link = (LinkConfig) {
                 .filename = TAKE_PTR(name),
                 .mac_address_policy = _MAC_ADDRESS_POLICY_INVALID,
-                .wol = _WOL_INVALID,
+                .wol = UINT32_MAX, /* UINT32_MAX means do not change WOL setting. */
                 .duplex = _DUP_INVALID,
                 .port = _NET_DEV_PORT_INVALID,
                 .autonegotiation = -1,
@@ -329,9 +329,13 @@ static int link_config_apply_ethtool_settings(int *ethtool_fd, const LinkConfig
         }
 
         r = ethtool_set_wol(ethtool_fd, name, config->wol);
-        if (r < 0)
+        if (r < 0) {
+                _cleanup_free_ char *str = NULL;
+
+                (void) wol_options_to_string_alloc(config->wol, &str);
                 log_device_warning_errno(device, r, "Could not set WakeOnLan to %s, ignoring: %m",
-                                         wol_to_string(config->wol));
+                                         strna(str));
+        }
 
         r = ethtool_set_features(ethtool_fd, name, config->features);
         if (r < 0)
index 601f62af8faa149313169a782549d8e98c98d9a1..b505c94f958330db0e3fbadc6046a0caa63ca9cc 100644 (file)
@@ -56,7 +56,7 @@ struct LinkConfig {
         Duplex duplex;
         int autonegotiation;
         uint32_t advertise[N_ADVERTISE];
-        WakeOnLan wol;
+        uint32_t wol;
         NetDevPort port;
         int features[_NET_DEV_FEAT_MAX];
         netdev_channels channels;