Merge pull request #24020 from yuwata/network-rfkill
network: fix wlan interface handling when its RF is killed
This commit is contained in:
@@ -63,6 +63,17 @@ typedef void* (*mfree_func_t)(void *p);
|
||||
#define free_and_replace(a, b) \
|
||||
free_and_replace_full(a, b, free)
|
||||
|
||||
/* This is similar to free_and_replace_full(), but NULL is not assigned to 'b', and its reference counter is
|
||||
* increased. */
|
||||
#define unref_and_replace_full(a, b, ref_func, unref_func) \
|
||||
({ \
|
||||
typeof(a)* _a = &(a); \
|
||||
typeof(b) _b = ref_func(b); \
|
||||
unref_func(*_a); \
|
||||
*_a = _b; \
|
||||
0; \
|
||||
})
|
||||
|
||||
void* memdup(const void *p, size_t l) _alloc_(2);
|
||||
void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, since we return a buffer one byte larger than the specified size */
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ static inline int device_new_from_watch_handle(sd_device **ret, int wd) {
|
||||
}
|
||||
|
||||
int device_get_property_bool(sd_device *device, const char *key);
|
||||
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
|
||||
int device_get_device_id(sd_device *device, const char **ret);
|
||||
int device_get_devlink_priority(sd_device *device, int *ret);
|
||||
int device_get_watch_handle(sd_device *device);
|
||||
@@ -25,6 +26,7 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret);
|
||||
int device_get_devnode_uid(sd_device *device, uid_t *ret);
|
||||
int device_get_devnode_gid(sd_device *device, gid_t *ret);
|
||||
|
||||
void device_clear_sysattr_cache(sd_device *device);
|
||||
int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
|
||||
int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "macro.h"
|
||||
#include "set.h"
|
||||
|
||||
#define device_unref_and_replace(a, b) \
|
||||
unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
|
||||
|
||||
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
|
||||
for (key = sd_device_get_property_first(device, &(value)); \
|
||||
key; \
|
||||
|
||||
@@ -2044,6 +2044,10 @@ _public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_clear_sysattr_cache(sd_device *device) {
|
||||
device->sysattr_values = hashmap_free(device->sysattr_values);
|
||||
}
|
||||
|
||||
int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
|
||||
_unused_ _cleanup_free_ char *old_value = NULL;
|
||||
_cleanup_free_ char *new_key = NULL;
|
||||
@@ -2166,6 +2170,20 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
|
||||
const char *value;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(sysattr);
|
||||
|
||||
r = sd_device_get_sysattr_value(device, sysattr, &value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return parse_boolean(value);
|
||||
}
|
||||
|
||||
static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
|
||||
_cleanup_free_ char *key = NULL;
|
||||
|
||||
|
||||
@@ -1324,7 +1324,7 @@ static bool link_needs_dhcp_broadcast(Link *link) {
|
||||
* If neither is set or a failure occurs, return false, which is the default for this flag.
|
||||
*/
|
||||
r = link->network->dhcp_broadcast;
|
||||
if (r < 0 && link->sd_device && sd_device_get_property_value(link->sd_device, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
|
||||
if (r < 0 && link->dev && sd_device_get_property_value(link->dev, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
|
||||
r = parse_boolean(val);
|
||||
if (r < 0)
|
||||
log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
|
||||
|
||||
@@ -187,8 +187,8 @@ int ipv4ll_configure(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (link->sd_device &&
|
||||
net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) {
|
||||
if (link->dev &&
|
||||
net_get_unique_predictable_data(link->dev, true, &seed) >= 0) {
|
||||
r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1158,7 +1158,7 @@ int link_build_json(Link *link, JsonVariant **ret) {
|
||||
assert(link);
|
||||
assert(ret);
|
||||
|
||||
r = net_get_type_string(link->sd_device, link->iftype, &type);
|
||||
r = net_get_type_string(link->dev, link->iftype, &type);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
|
||||
@@ -1215,7 +1215,7 @@ int link_build_json(Link *link, JsonVariant **ret) {
|
||||
|
||||
w = json_variant_unref(w);
|
||||
|
||||
r = device_build_json(link->sd_device, &w);
|
||||
r = device_build_json(link->dev, &w);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
+24
-35
@@ -229,7 +229,7 @@ static Link *link_free(Link *link) {
|
||||
unlink_and_free(link->lldp_file);
|
||||
unlink_and_free(link->state_file);
|
||||
|
||||
sd_device_unref(link->sd_device);
|
||||
sd_device_unref(link->dev);
|
||||
netdev_unref(link->netdev);
|
||||
|
||||
hashmap_free(link->bound_to_links);
|
||||
@@ -1164,7 +1164,7 @@ static int link_get_network(Link *link, Network **ret) {
|
||||
|
||||
r = net_match_config(
|
||||
&network->match,
|
||||
link->sd_device,
|
||||
link->dev,
|
||||
&link->hw_addr,
|
||||
&link->permanent_hw_addr,
|
||||
link->driver,
|
||||
@@ -1180,11 +1180,11 @@ static int link_get_network(Link *link, Network **ret) {
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
if (network->match.ifname && link->sd_device) {
|
||||
if (network->match.ifname && link->dev) {
|
||||
uint8_t name_assign_type = NET_NAME_UNKNOWN;
|
||||
const char *attr;
|
||||
|
||||
if (sd_device_get_sysattr_value(link->sd_device, "name_assign_type", &attr) >= 0)
|
||||
if (sd_device_get_sysattr_value(link->dev, "name_assign_type", &attr) >= 0)
|
||||
(void) safe_atou8(attr, &name_assign_type);
|
||||
|
||||
warn = name_assign_type == NET_NAME_ENUM;
|
||||
@@ -1433,9 +1433,7 @@ static int link_initialized(Link *link, sd_device *device) {
|
||||
|
||||
/* Always replace with the new sd_device object. As the sysname (and possibly other properties
|
||||
* or sysattrs) may be outdated. */
|
||||
sd_device_ref(device);
|
||||
sd_device_unref(link->sd_device);
|
||||
link->sd_device = device;
|
||||
device_unref_and_replace(link->dev, device);
|
||||
|
||||
/* Do not ignore unamanaged state case here. If an interface is renamed after being once
|
||||
* configured, and the corresponding .network file has Name= in [Match] section, then the
|
||||
@@ -1490,48 +1488,39 @@ static int link_check_initialized(Link *link) {
|
||||
return link_initialized(link, device);
|
||||
}
|
||||
|
||||
int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata) {
|
||||
sd_device_action_t action;
|
||||
Manager *m = userdata;
|
||||
Link *link = NULL;
|
||||
int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action) {
|
||||
int r, ifindex;
|
||||
Link *link;
|
||||
|
||||
assert(m);
|
||||
assert(device);
|
||||
|
||||
r = sd_device_get_action(device, &action);
|
||||
r = sd_device_get_ifindex(device, &ifindex);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "Failed to get ifindex: %m");
|
||||
|
||||
r = link_get_by_index(m, ifindex, &link);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(device, r, "Failed to get udev action, ignoring device: %m");
|
||||
/* This error is not critical, as the corresponding rtnl message may be received later. */
|
||||
log_device_debug_errno(device, r, "Failed to get link from ifindex %i, ignoring: %m", ifindex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ignore the "remove" uevent — let's remove a device only if rtnetlink says so. All other uevents
|
||||
* are "positive" events in some form, i.e. inform us about a changed or new network interface, that
|
||||
* still exists — and we are interested in that. */
|
||||
if (action == SD_DEVICE_REMOVE)
|
||||
return 0;
|
||||
|
||||
r = sd_device_get_ifindex(device, &ifindex);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(device, r, "Ignoring udev %s event for device without ifindex or with invalid ifindex: %m",
|
||||
device_action_to_string(action));
|
||||
/* Let's unref the sd-device object assigned to the corresponding Link object, but keep the Link
|
||||
* object here. It will be removed only when rtnetlink says so. */
|
||||
if (action == SD_DEVICE_REMOVE) {
|
||||
link->dev = sd_device_unref(link->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = device_is_renaming(device);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(device, r, "Failed to determine the device is renamed or not, ignoring '%s' uevent: %m",
|
||||
device_action_to_string(action));
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "Failed to determine if the device is renaming or not: %m");
|
||||
if (r > 0) {
|
||||
log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = link_get_by_index(m, ifindex, &link);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(device, r, "Failed to get link from ifindex %i, ignoring: %m", ifindex);
|
||||
log_device_debug(device, "Device is renaming, waiting for the interface to be renamed.");
|
||||
/* TODO:
|
||||
* What happens when a device is initialized, then soon renamed after that? When we detect
|
||||
* such, maybe we should cancel or postpone all queued requests for the interface. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ typedef struct Link {
|
||||
uint32_t min_mtu;
|
||||
uint32_t max_mtu;
|
||||
uint32_t original_mtu;
|
||||
sd_device *sd_device;
|
||||
sd_device *dev;
|
||||
char *driver;
|
||||
|
||||
/* link-local addressing */
|
||||
@@ -235,7 +235,7 @@ LinkState link_state_from_string(const char *s) _pure_;
|
||||
int link_reconfigure(Link *link, bool force);
|
||||
int link_reconfigure_after_sleep(Link *link);
|
||||
|
||||
int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata);
|
||||
int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action);
|
||||
int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
|
||||
|
||||
int link_flags_to_string_alloc(uint32_t flags, char **ret);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "bus-util.h"
|
||||
#include "conf-parser.h"
|
||||
#include "def.h"
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@@ -168,6 +170,39 @@ static int manager_connect_bus(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_process_uevent(sd_device_monitor *monitor, sd_device *device, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
sd_device_action_t action;
|
||||
const char *s;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = sd_device_get_action(device, &action);
|
||||
if (r < 0)
|
||||
return log_device_warning_errno(device, r, "Failed to get udev action, ignoring: %m");
|
||||
|
||||
r = sd_device_get_subsystem(device, &s);
|
||||
if (r < 0)
|
||||
return log_device_warning_errno(device, r, "Failed to get subsystem, ignoring: %m");
|
||||
|
||||
if (streq(s, "net"))
|
||||
r = manager_udev_process_link(m, device, action);
|
||||
else if (streq(s, "ieee80211"))
|
||||
r = manager_udev_process_wiphy(m, device, action);
|
||||
else if (streq(s, "rfkill"))
|
||||
r = manager_udev_process_rfkill(m, device, action);
|
||||
else {
|
||||
log_device_debug(device, "Received device with unexpected subsystem \"%s\", ignoring.", s);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
log_device_warning_errno(device, r, "Failed to process \"%s\" uevent, ignoring: %m",
|
||||
device_action_to_string(action));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_connect_udev(Manager *m) {
|
||||
int r;
|
||||
|
||||
@@ -186,13 +221,21 @@ static int manager_connect_udev(Manager *m) {
|
||||
|
||||
r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "net", NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not add device monitor filter: %m");
|
||||
return log_error_errno(r, "Could not add device monitor filter for net subsystem: %m");
|
||||
|
||||
r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "ieee80211", NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not add device monitor filter for ieee80211 subsystem: %m");
|
||||
|
||||
r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "rfkill", NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not add device monitor filter for rfkill subsystem: %m");
|
||||
|
||||
r = sd_device_monitor_attach_event(m->device_monitor, m->event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach event to device monitor: %m");
|
||||
|
||||
r = sd_device_monitor_start(m->device_monitor, manager_udev_process_link, m);
|
||||
r = sd_device_monitor_start(m->device_monitor, manager_process_uevent, m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to start device monitor: %m");
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-setlink.h"
|
||||
#include "networkd-wiphy.h"
|
||||
|
||||
static int get_link_default_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
return link_getlink_handler_internal(rtnl, m, link, "Failed to sync link information");
|
||||
@@ -951,17 +952,10 @@ static int link_up_or_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Requ
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r == -ENETDOWN && up && link_up_dsa_slave(link) > 0)
|
||||
log_link_message_debug_errno(link, m, r, "Could not bring up dsa slave, retrying again after dsa master becomes up");
|
||||
else if (r < 0) {
|
||||
const char *error_msg;
|
||||
|
||||
error_msg = up ?
|
||||
(on_activate ? "Could not bring up interface" : "Could not bring up interface, ignoring") :
|
||||
(on_activate ? "Could not bring down interface" : "Could not bring down interface, ignoring");
|
||||
|
||||
log_link_message_warning_errno(link, m, r, error_msg);
|
||||
if (on_activate)
|
||||
return 0;
|
||||
}
|
||||
else if (r < 0)
|
||||
log_link_message_warning_errno(link, m, r, up ?
|
||||
"Could not bring up interface, ignoring" :
|
||||
"Could not bring down interface, ignoring");
|
||||
|
||||
r = link_call_getlink(link, get_link_update_flag_handler);
|
||||
if (r < 0) {
|
||||
@@ -1005,7 +999,7 @@ static int link_up_or_down(Link *link, bool up, Request *req) {
|
||||
return request_call_netlink_async(link->manager->rtnl, m, req);
|
||||
}
|
||||
|
||||
static bool link_is_ready_to_activate(Link *link) {
|
||||
static bool link_is_ready_to_activate(Link *link, bool up) {
|
||||
assert(link);
|
||||
|
||||
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
|
||||
@@ -1014,6 +1008,9 @@ static bool link_is_ready_to_activate(Link *link) {
|
||||
if (link->set_link_messages > 0)
|
||||
return false;
|
||||
|
||||
if (up && link_rfkilled(link) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1024,7 +1021,7 @@ static int link_process_activation(Request *req, Link *link, void *userdata) {
|
||||
assert(req);
|
||||
assert(link);
|
||||
|
||||
if (!link_is_ready_to_activate(link))
|
||||
if (!link_is_ready_to_activate(link, up))
|
||||
return 0;
|
||||
|
||||
r = link_up_or_down(link, up, req);
|
||||
@@ -1104,6 +1101,9 @@ static bool link_is_ready_to_bring_up_or_down(Link *link, bool up) {
|
||||
if (!link->activated)
|
||||
return false;
|
||||
|
||||
if (up && link_rfkilled(link) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+314
-18
@@ -1,11 +1,15 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <net/if_arp.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-wiphy.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "udev-util.h"
|
||||
#include "wifi-util.h"
|
||||
|
||||
Wiphy *wiphy_free(Wiphy *w) {
|
||||
@@ -18,29 +22,49 @@ Wiphy *wiphy_free(Wiphy *w) {
|
||||
hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
|
||||
}
|
||||
|
||||
sd_device_unref(w->dev);
|
||||
sd_device_unref(w->rfkill);
|
||||
|
||||
free(w->name);
|
||||
return mfree(w);
|
||||
}
|
||||
|
||||
static int wiphy_new(Manager *manager, uint32_t index, Wiphy **ret) {
|
||||
static int wiphy_new(Manager *manager, sd_netlink_message *message, Wiphy **ret) {
|
||||
_cleanup_(wiphy_freep) Wiphy *w = NULL;
|
||||
_cleanup_free_ char *name = NULL;
|
||||
uint32_t index;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(message);
|
||||
|
||||
r = sd_netlink_message_read_u32(message, NL80211_ATTR_WIPHY, &index);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_read_string_strdup(message, NL80211_ATTR_WIPHY_NAME, &name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
w = new(Wiphy, 1);
|
||||
if (!w)
|
||||
return -ENOMEM;
|
||||
|
||||
*w = (Wiphy) {
|
||||
.manager = manager,
|
||||
.index = index,
|
||||
.name = TAKE_PTR(name),
|
||||
};
|
||||
|
||||
r = hashmap_ensure_put(&manager->wiphy_by_index, NULL, UINT32_TO_PTR(w->index), w);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
w->manager = manager;
|
||||
r = hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_wiphy_debug(w, "Saved new wiphy: index=%"PRIu32, w->index);
|
||||
|
||||
if (ret)
|
||||
*ret = w;
|
||||
@@ -80,6 +104,129 @@ int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_get_wiphy(Link *link, Wiphy **ret) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *phy = NULL;
|
||||
const char *s;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
if (link->iftype != ARPHRD_ETHER)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!link->dev)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = sd_device_get_devtype(link->dev, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!streq_ptr(s, "wlan"))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = sd_device_get_syspath(link->dev, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
s = strjoina(s, "/phy80211");
|
||||
r = sd_device_new_from_syspath(&phy, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_get_sysname(phy, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* TODO:
|
||||
* Maybe, it is better to cache the found Wiphy object in the Link object.
|
||||
* To support that, we need to investigate what happens when the _phy_ is renamed. */
|
||||
|
||||
return wiphy_get_by_name(link->manager, s, ret);
|
||||
}
|
||||
|
||||
static int rfkill_get_state(sd_device *dev) {
|
||||
int r;
|
||||
|
||||
assert(dev);
|
||||
|
||||
/* The previous values may be outdated. Let's clear cache and re-read the values. */
|
||||
device_clear_sysattr_cache(dev);
|
||||
|
||||
r = device_get_sysattr_bool(dev, "soft");
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return RFKILL_SOFT;
|
||||
|
||||
r = device_get_sysattr_bool(dev, "hard");
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return RFKILL_HARD;
|
||||
|
||||
return RFKILL_UNBLOCKED;
|
||||
}
|
||||
|
||||
static int wiphy_rfkilled(Wiphy *w) {
|
||||
int r;
|
||||
|
||||
assert(w);
|
||||
|
||||
if (!udev_available()) {
|
||||
if (w->rfkill_state != RFKILL_UNBLOCKED) {
|
||||
log_wiphy_debug(w, "Running in container, assuming the radio transmitter is unblocked.");
|
||||
w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!w->rfkill) {
|
||||
if (w->rfkill_state != RFKILL_UNBLOCKED) {
|
||||
log_wiphy_debug(w, "No rfkill device found, assuming the radio transmitter is unblocked.");
|
||||
w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
r = rfkill_get_state(w->rfkill);
|
||||
if (r < 0)
|
||||
return log_wiphy_debug_errno(w, r, "Could not get rfkill state: %m");
|
||||
|
||||
if (w->rfkill_state != r)
|
||||
switch (r) {
|
||||
case RFKILL_UNBLOCKED:
|
||||
log_wiphy_debug(w, "The radio transmitter is unblocked.");
|
||||
break;
|
||||
case RFKILL_SOFT:
|
||||
log_wiphy_debug(w, "The radio transmitter is turned off by software.");
|
||||
break;
|
||||
case RFKILL_HARD:
|
||||
log_wiphy_debug(w, "The radio transmitter is forced off by something outside of the driver's control.");
|
||||
break;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
w->rfkill_state = r; /* Cache the state to suppress the above log messages. */
|
||||
return r != RFKILL_UNBLOCKED;
|
||||
}
|
||||
|
||||
int link_rfkilled(Link *link) {
|
||||
Wiphy *w;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
r = link_get_wiphy(link, &w);
|
||||
if (IN_SET(r, -EOPNOTSUPP, -ENODEV))
|
||||
return false; /* Typically, non-wifi interface or running in container */
|
||||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "Could not get phy: %m");
|
||||
|
||||
return wiphy_rfkilled(w);
|
||||
}
|
||||
|
||||
static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
|
||||
const char *name;
|
||||
int r;
|
||||
@@ -94,28 +241,119 @@ static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (streq_ptr(w->name, name))
|
||||
if (streq(w->name, name))
|
||||
return 0;
|
||||
|
||||
if (w->name)
|
||||
hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
|
||||
log_wiphy_debug(w, "Wiphy name change detected, renamed to %s.", name);
|
||||
|
||||
hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
|
||||
|
||||
r = free_and_strdup(&w->name, name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
|
||||
r = hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1; /* updated */
|
||||
}
|
||||
|
||||
static int wiphy_update(Wiphy *w, sd_netlink_message *message) {
|
||||
static int wiphy_update_device(Wiphy *w) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||
int r;
|
||||
|
||||
assert(w);
|
||||
assert(message);
|
||||
assert(w->name);
|
||||
|
||||
r = wiphy_update_name(w, message);
|
||||
if (!udev_available())
|
||||
return 0;
|
||||
|
||||
w->dev = sd_device_unref(w->dev);
|
||||
|
||||
r = sd_device_new_from_subsystem_sysname(&dev, "ieee80211", w->name);
|
||||
if (r < 0) {
|
||||
/* The corresponding syspath may not exist yet, and may appear later. */
|
||||
log_wiphy_debug_errno(w, r, "Failed to get wiphy device, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
const char *s = NULL;
|
||||
|
||||
(void) sd_device_get_syspath(dev, &s);
|
||||
log_wiphy_debug(w, "Found device: %s", strna(s));
|
||||
}
|
||||
|
||||
w->dev = TAKE_PTR(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wiphy_update_rfkill(Wiphy *w) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
sd_device *rfkill;
|
||||
int r;
|
||||
|
||||
assert(w);
|
||||
|
||||
if (!udev_available())
|
||||
return 0;
|
||||
|
||||
w->rfkill = sd_device_unref(w->rfkill);
|
||||
|
||||
if (!w->dev)
|
||||
return 0;
|
||||
|
||||
r = sd_device_enumerator_new(&e);
|
||||
if (r < 0)
|
||||
return log_wiphy_debug_errno(w, r, "Failed to update wiphy name: %m");
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_allow_uninitialized(e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_subsystem(e, "rfkill", true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_parent(e, w->dev);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rfkill = sd_device_enumerator_get_device_first(e);
|
||||
if (!rfkill) {
|
||||
/* rfkill device may not detected by the kernel yet, and may appear later. */
|
||||
log_wiphy_debug_errno(w, SYNTHETIC_ERRNO(ENODEV), "No rfkill device found, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sd_device_enumerator_get_device_next(e))
|
||||
return log_wiphy_debug_errno(w, SYNTHETIC_ERRNO(EEXIST), "Multiple rfkill devices found.");
|
||||
|
||||
w->rfkill = sd_device_ref(rfkill);
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
const char *s = NULL;
|
||||
|
||||
(void) sd_device_get_syspath(rfkill, &s);
|
||||
log_wiphy_debug(w, "Found rfkill device: %s", strna(s));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wiphy_update(Wiphy *w) {
|
||||
int r;
|
||||
|
||||
assert(w);
|
||||
|
||||
r = wiphy_update_device(w);
|
||||
if (r < 0)
|
||||
return log_wiphy_debug_errno(w, r, "Failed to update wiphy device: %m");
|
||||
|
||||
r = wiphy_update_rfkill(w);
|
||||
if (r < 0)
|
||||
return log_wiphy_debug_errno(w, r, "Failed to update rfkill device: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -166,23 +404,27 @@ int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *mes
|
||||
|
||||
switch (cmd) {
|
||||
case NL80211_CMD_NEW_WIPHY: {
|
||||
bool is_new = !w;
|
||||
|
||||
if (!w) {
|
||||
r = wiphy_new(manager, index, &w);
|
||||
r = wiphy_new(manager, message, &w);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to allocate wiphy, ignoring: %m");
|
||||
log_warning_errno(r, "Failed to save new wiphy, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
r = wiphy_update_name(w, message);
|
||||
if (r < 0) {
|
||||
log_wiphy_warning_errno(w, r, "Failed to update wiphy name, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
if (r == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = wiphy_update(w, message);
|
||||
if (r < 0) {
|
||||
r = wiphy_update(w);
|
||||
if (r < 0)
|
||||
log_wiphy_warning_errno(w, r, "Failed to update wiphy, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_wiphy_debug(w, "Received %s phy.", is_new ? "new" : "updated");
|
||||
break;
|
||||
}
|
||||
case NL80211_CMD_DEL_WIPHY:
|
||||
@@ -203,3 +445,57 @@ int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *mes
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_udev_process_wiphy(Manager *m, sd_device *device, sd_device_action_t action) {
|
||||
const char *name;
|
||||
Wiphy *w;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(device);
|
||||
|
||||
r = sd_device_get_sysname(device, &name);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "Failed to get sysname: %m");
|
||||
|
||||
r = wiphy_get_by_name(m, name, &w);
|
||||
if (r < 0) {
|
||||
/* This error is not critical, as the corresponding genl message may be received later. */
|
||||
log_device_debug_errno(device, r, "Failed to get Wiphy object, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return device_unref_and_replace(w->dev, action == SD_DEVICE_REMOVE ? NULL : device);
|
||||
}
|
||||
|
||||
int manager_udev_process_rfkill(Manager *m, sd_device *device, sd_device_action_t action) {
|
||||
_cleanup_free_ char *parent_path = NULL, *parent_name = NULL;
|
||||
const char *s;
|
||||
Wiphy *w;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(device);
|
||||
|
||||
r = sd_device_get_syspath(device, &s);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "Failed to get syspath: %m");
|
||||
|
||||
/* Do not use sd_device_get_parent() here, as this might be a 'remove' uevent. */
|
||||
r = path_extract_directory(s, &parent_path);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "Failed to get parent syspath: %m");
|
||||
|
||||
r = path_extract_filename(parent_path, &parent_name);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "Failed to get parent name: %m");
|
||||
|
||||
r = wiphy_get_by_name(m, parent_name, &w);
|
||||
if (r < 0) {
|
||||
/* This error is not critical, as the corresponding genl message may be received later. */
|
||||
log_device_debug_errno(device, r, "Failed to get Wiphy object: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return device_unref_and_replace(w->rfkill, action == SD_DEVICE_REMOVE ? NULL : device);
|
||||
}
|
||||
|
||||
@@ -7,13 +7,28 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct Link Link;
|
||||
typedef struct Manager Manager;
|
||||
|
||||
/* The following values are different from the ones defined in linux/rfkill.h. */
|
||||
typedef enum RFKillState {
|
||||
RFKILL_UNKNOWN,
|
||||
RFKILL_UNBLOCKED,
|
||||
RFKILL_SOFT,
|
||||
RFKILL_HARD,
|
||||
_RFKILL_STATE_MAX,
|
||||
_RFKILL_STATE_INVALID = -EINVAL,
|
||||
} RFKillState;
|
||||
|
||||
typedef struct Wiphy {
|
||||
Manager *manager;
|
||||
|
||||
uint32_t index;
|
||||
char *name;
|
||||
|
||||
sd_device *dev;
|
||||
sd_device *rfkill;
|
||||
RFKillState rfkill_state;
|
||||
} Wiphy;
|
||||
|
||||
Wiphy *wiphy_free(Wiphy *w);
|
||||
@@ -22,7 +37,11 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Wiphy*, wiphy_free);
|
||||
int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret);
|
||||
int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret);
|
||||
|
||||
int link_rfkilled(Link *link);
|
||||
|
||||
int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager);
|
||||
int manager_udev_process_wiphy(Manager *m, sd_device *device, sd_device_action_t action);
|
||||
int manager_udev_process_rfkill(Manager *m, sd_device *device, sd_device_action_t action);
|
||||
|
||||
#define log_wiphy_full_errno_zerook(w, level, error, ...) \
|
||||
({ \
|
||||
|
||||
Reference in New Issue
Block a user