From 315269fd23c5ce0bb578dc5a64743fb8547cb28f Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Wed, 8 Mar 2017 13:33:19 +0100 Subject: [PATCH] hwsim: Basic DBus interface to radio information In daemon mode start a basic passive DBus interface to expose the information on radios attached to mac80211_hwsim. In this version interfaces have objects of their own. It might be simpler to only show them as an array property on the radio object (array of pairs of string, one string for address, one for name). --- tools/hwsim.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 332 insertions(+), 12 deletions(-) diff --git a/tools/hwsim.c b/tools/hwsim.c index 134bef66..b2cb0f12 100644 --- a/tools/hwsim.c +++ b/tools/hwsim.c @@ -35,6 +35,11 @@ #include "src/util.h" #include "src/storage.h" +#include "src/dbus.h" + +#define HWSIM_RADIO_MANAGER_INTERFACE "net.connman.iwd.hwsim.RadioManager" +#define HWSIM_RADIO_INTERFACE "net.connman.iwd.hwsim.Radio" +#define HWSIM_INTERFACE_INTERFACE "net.connman.iwd.hwsim.Interface" enum { HWSIM_CMD_UNSPEC, @@ -91,6 +96,7 @@ static bool keep_radios_attr; static bool no_vif_attr; static bool p2p_attr; static const char *radio_name_attr; +static struct l_dbus *dbus; static void do_debug(const char *str, void *user_data) { @@ -245,7 +251,8 @@ struct radio_info_rec { uint32_t wiphy_id; char alpha2[2]; bool p2p; - uint32_t regdom; + bool custom_regdom; + uint32_t regdom_idx; int channels; uint8_t addrs[ETH_ALEN * 2]; char *name; @@ -309,6 +316,23 @@ static bool interface_info_match_id(const void *a, const void *b) return rec->id == id; } +static const char *radio_get_path(const struct radio_info_rec *rec) +{ + static char path[15]; + + snprintf(path, sizeof(path), "/radio%u", rec->id); + return path; +} + +static const char *interface_get_path(const struct interface_info_rec *rec) +{ + static char path[25]; + + snprintf(path, sizeof(path), "%s/%u", + radio_get_path(rec->radio_rec), rec->id); + return path; +} + static bool parse_addresses(const uint8_t *buf, size_t len, struct radio_info_rec *rec) { @@ -373,6 +397,9 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data) int bytes, consumed; unsigned int uintval; bool old; + struct radio_info_rec prev_rec; + bool name_change = false; + const char *path; if (!l_genl_attr_init(&attr, msg)) return; @@ -400,6 +427,12 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data) if (rec) { old = true; + memcpy(&prev_rec, rec, sizeof(prev_rec)); + + if (strlen(rec->name) != name_len || + memcmp(rec->name, name, name_len)) + name_change = true; + l_free(rec->name); } else { old = false; @@ -438,7 +471,8 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data) if (len != 4) break; - rec->regdom = *(uint32_t *) data; + rec->custom_regdom = true; + rec->regdom_idx = *(uint32_t *) data; break; } } @@ -456,14 +490,14 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data) "/sys/class/ieee80211/%s/index", rec->name); if (bytes < 0) { l_error("Error reading index for %s from sysfs", rec->name); - goto free_radio; + goto err_free_radio; } file_buffer[bytes] = '\0'; if (sscanf((char *) file_buffer, "%u %n", &uintval, &consumed) != 1 || consumed != bytes) { l_error("Error parsing index for %s from sysfs", rec->name); - goto free_radio; + goto err_free_radio; } rec->wiphy_id = uintval; @@ -472,11 +506,11 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data) "/sys/class/ieee80211/%s/addresses", rec->name); if (bytes < 0) { l_error("Error reading addresses for %s from sysfs", rec->name); - goto free_radio; + goto err_free_radio; } if (!parse_addresses(file_buffer, bytes, rec)) - goto free_radio; + goto err_free_radio; if (!radio_info) radio_info = l_queue_new(); @@ -484,11 +518,37 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data) if (!old) l_queue_push_tail(radio_info, rec); - /* TODO: Create DBus object */ + path = radio_get_path(rec); + + if (!old) { + /* Create Dbus object */ + + if (!l_dbus_object_add_interface(dbus, path, + HWSIM_RADIO_INTERFACE, rec)) + l_info("Unable to add the %s interface to %s", + HWSIM_RADIO_INTERFACE, path); + + if (!l_dbus_object_add_interface(dbus, path, + L_DBUS_INTERFACE_PROPERTIES, + NULL)) + l_info("Unable to add the %s interface to %s", + L_DBUS_INTERFACE_PROPERTIES, path); + } else { + /* Emit property change events */ + + if (memcmp(prev_rec.addrs, rec->addrs, sizeof(rec->addrs))) + l_dbus_property_changed(dbus, path, + HWSIM_RADIO_INTERFACE, + "Addresses"); + + if (name_change) + l_dbus_property_changed(dbus, path, + HWSIM_RADIO_INTERFACE, "Name"); + } return; -free_radio: +err_free_radio: if (!old) radio_free(rec); } @@ -532,6 +592,9 @@ static void get_wiphy_callback(struct l_genl_msg *msg, void *user_data) l_free(rec->name); rec->name = l_strndup(name, name_len); + + l_dbus_property_changed(dbus, radio_get_path(rec), + HWSIM_RADIO_INTERFACE, "Name"); } static void get_interface_callback(struct l_genl_msg *msg, void *user_data) @@ -547,6 +610,9 @@ static void get_interface_callback(struct l_genl_msg *msg, void *user_data) struct interface_info_rec *rec; struct radio_info_rec *radio_rec; bool old; + const char *path; + struct interface_info_rec prev_rec; + bool name_change = false; if (!l_genl_attr_init(&attr, msg)) return; @@ -595,6 +661,12 @@ static void get_interface_callback(struct l_genl_msg *msg, void *user_data) if (rec) { old = true; + memcpy(&prev_rec, rec, sizeof(prev_rec)); + + if (strlen(rec->name) != ifname_len || + memcmp(rec->name, ifname, ifname_len)) + name_change = true; + l_free(rec->name); } else { old = false; @@ -614,7 +686,34 @@ static void get_interface_callback(struct l_genl_msg *msg, void *user_data) if (!old) l_queue_push_tail(interface_info, rec); - /* TODO: Create DBus object */ + path = interface_get_path(rec); + + if (!old) { + /* Create Dbus object */ + + if (!l_dbus_object_add_interface(dbus, path, + HWSIM_INTERFACE_INTERFACE, rec)) + l_info("Unable to add the %s interface to %s", + HWSIM_INTERFACE_INTERFACE, path); + + if (!l_dbus_object_add_interface(dbus, path, + L_DBUS_INTERFACE_PROPERTIES, + NULL)) + l_info("Unable to add the %s interface to %s", + L_DBUS_INTERFACE_PROPERTIES, path); + } else { + /* Emit property change events */ + + if (memcmp(prev_rec.addr, rec->addr, ETH_ALEN)) + l_dbus_property_changed(dbus, path, + HWSIM_INTERFACE_INTERFACE, + "Addresses"); + + if (name_change) + l_dbus_property_changed(dbus, path, + HWSIM_INTERFACE_INTERFACE, + "Name"); + } } static bool interface_info_destroy_by_radio(void *data, void *user_data) @@ -625,7 +724,7 @@ static bool interface_info_destroy_by_radio(void *data, void *user_data) if (rec->radio_rec != radio_rec) return false; - /* TODO: Drop DBus object */ + l_dbus_unregister_object(dbus, interface_get_path(rec)); interface_free(rec); @@ -666,7 +765,7 @@ static void del_radio_event(struct l_genl_msg *msg) l_queue_foreach_remove(interface_info, interface_info_destroy_by_radio, radio); - /* TODO: Drop DBus object */ + l_dbus_unregister_object(dbus, radio_get_path(radio)); radio_free(radio); l_queue_remove(radio_info, radio); @@ -702,7 +801,7 @@ static void del_interface_event(struct l_genl_msg *msg) if (!interface) return; - /* TODO: Drop DBus object */ + l_dbus_unregister_object(dbus, interface_get_path(interface)); interface_free(interface); l_queue_remove(interface_info, interface); @@ -756,6 +855,218 @@ static void nl80211_config_notify(struct l_genl_msg *msg, void *user_data) } } +static void setup_radio_manager_interface(struct l_dbus_interface *interface) +{ +} + +static bool radio_property_get_name(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct radio_info_rec *rec = user_data; + + l_dbus_message_builder_append_basic(builder, 's', rec->name); + + return true; +} + +static bool radio_property_get_addresses(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct radio_info_rec *rec = user_data; + unsigned int i; + + l_dbus_message_builder_enter_array(builder, "s"); + + for (i = 0; i < sizeof(rec->addrs); i += ETH_ALEN) { + const char *str = util_address_to_string(rec->addrs + i); + + l_dbus_message_builder_append_basic(builder, 's', str); + } + + l_dbus_message_builder_leave_array(builder); + + return true; +} + +static bool radio_property_get_channels(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct radio_info_rec *rec = user_data; + uint16_t val = rec->channels; + + l_dbus_message_builder_append_basic(builder, 'q', &val); + + return true; +} + +static bool radio_property_get_alpha2(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct radio_info_rec *rec = user_data; + + if (rec->alpha2[0] == 0 || rec->alpha2[1] == 0) + return false; + + l_dbus_message_builder_enter_struct(builder, "yy"); + l_dbus_message_builder_append_basic(builder, 'y', &rec->alpha2[0]); + l_dbus_message_builder_append_basic(builder, 'y', &rec->alpha2[1]); + l_dbus_message_builder_leave_struct(builder); + + return true; +} + +static bool radio_property_get_p2p(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct radio_info_rec *rec = user_data; + bool val = rec->p2p; + + l_dbus_message_builder_append_basic(builder, 'b', &val); + + return true; +} + +static bool radio_property_get_regdom(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct radio_info_rec *rec = user_data; + + if (!rec->custom_regdom) + return false; + + l_dbus_message_builder_append_basic(builder, 'u', &rec->regdom_idx); + + return true; +} + +static void setup_radio_interface(struct l_dbus_interface *interface) +{ + l_dbus_interface_property(interface, "Name", 0, "s", + radio_property_get_name, NULL); + l_dbus_interface_property(interface, "Addresses", 0, "s", + radio_property_get_addresses, NULL); + l_dbus_interface_property(interface, "Channels", 0, "q", + radio_property_get_channels, NULL); + l_dbus_interface_property(interface, "Alpha2", 0, "(yy)", + radio_property_get_alpha2, NULL); + l_dbus_interface_property(interface, "P2PDevice", 0, "b", + radio_property_get_p2p, NULL); + l_dbus_interface_property(interface, "RegulatoryDomainIndex", 0, "u", + radio_property_get_regdom, NULL); +} + +static bool interface_property_get_name(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct interface_info_rec *rec = user_data; + + l_dbus_message_builder_append_basic(builder, 's', rec->name); + + return true; +} + +static bool interface_property_get_address(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + const struct interface_info_rec *rec = user_data; + const char *str = util_address_to_string(rec->addr); + + l_dbus_message_builder_append_basic(builder, 's', str); + + return true; +} + +static void setup_interface_interface(struct l_dbus_interface *interface) +{ + l_dbus_interface_property(interface, "Name", 0, "s", + interface_property_get_name, NULL); + l_dbus_interface_property(interface, "Address", 0, "s", + interface_property_get_address, NULL); +} + +static void request_name_callback(struct l_dbus *dbus, bool success, + bool queued, void *user_data) +{ + if (!success) + l_error("Name request failed"); +} + +static void ready_callback(void *user_data) +{ + l_dbus_name_acquire(dbus, "net.connman.iwd.hwsim", false, false, true, + request_name_callback, NULL); + + if (!l_dbus_object_manager_enable(dbus)) + l_info("Unable to register the ObjectManager"); +} + +static void disconnect_callback(void *user_data) +{ + l_info("D-Bus disconnected, quitting..."); + l_main_quit(); +} + +static bool setup_dbus_hwsim(void) +{ + dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS); + if (!dbus) { + l_error("Unable to connect to Dbus"); + return false; + } + + if (!l_dbus_register_interface(dbus, HWSIM_RADIO_MANAGER_INTERFACE, + setup_radio_manager_interface, + NULL, false)) { + l_error("Unable to register the %s interface", + HWSIM_RADIO_MANAGER_INTERFACE); + return false; + } + + if (!l_dbus_register_interface(dbus, HWSIM_RADIO_INTERFACE, + setup_radio_interface, NULL, false)) { + l_error("Unable to register the %s interface", + HWSIM_RADIO_INTERFACE); + return false; + } + + if (!l_dbus_register_interface(dbus, HWSIM_INTERFACE_INTERFACE, + setup_interface_interface, + NULL, false)) { + l_error("Unable to register the %s interface", + HWSIM_INTERFACE_INTERFACE); + return false; + } + + if (!l_dbus_object_add_interface(dbus, "/", + HWSIM_RADIO_MANAGER_INTERFACE, + NULL)) { + l_info("Unable to add the %s interface to /", + HWSIM_RADIO_MANAGER_INTERFACE); + return false; + } + + l_dbus_set_ready_handler(dbus, ready_callback, dbus, NULL); + l_dbus_set_disconnect_handler(dbus, disconnect_callback, NULL, NULL); + + return true; +} + static void get_radio_done_initial(void *user_data) { struct l_genl_msg *msg; @@ -888,6 +1199,9 @@ static void hwsim_ready(void *user_data) break; case ACTION_NONE: + if (!setup_dbus_hwsim()) + goto error; + l_genl_family_set_watches(nl80211, nl80211_ready, NULL, NULL, NULL); @@ -895,6 +1209,10 @@ static void hwsim_ready(void *user_data) } return; + +error: + exit_status = EXIT_FAILURE; + l_main_quit(); } static void hwsim_disappeared(void *user_data) @@ -1068,6 +1386,8 @@ int main(int argc, char *argv[]) l_genl_family_unref(nl80211); l_genl_unref(genl); + l_dbus_destroy(dbus); + hwsim_radio_cache_cleanup(); done: