From 35779c6675728fa6f0fd0a21cefb904408509c23 Mon Sep 17 00:00:00 2001 From: Sebastien Bacher Date: Mon, 5 Jul 2021 20:35:03 +0200 Subject: New upstream version 1.32.2 --- src/libnm-client-impl/libnm.pc.in | 14 + src/libnm-client-impl/libnm.ver | 1795 ++++ src/libnm-client-impl/meson.build | 231 + src/libnm-client-impl/nm-access-point.c | 654 ++ src/libnm-client-impl/nm-active-connection.c | 839 ++ src/libnm-client-impl/nm-checkpoint.c | 202 + src/libnm-client-impl/nm-client.c | 8834 ++++++++++++++++++++ src/libnm-client-impl/nm-dbus-helpers.c | 110 + src/libnm-client-impl/nm-dbus-helpers.h | 23 + src/libnm-client-impl/nm-default-libnm.h | 23 + src/libnm-client-impl/nm-device-6lowpan.c | 135 + src/libnm-client-impl/nm-device-adsl.c | 130 + src/libnm-client-impl/nm-device-bond.c | 211 + src/libnm-client-impl/nm-device-bridge.c | 204 + src/libnm-client-impl/nm-device-bt.c | 273 + src/libnm-client-impl/nm-device-dummy.c | 110 + src/libnm-client-impl/nm-device-ethernet.c | 404 + src/libnm-client-impl/nm-device-generic.c | 175 + src/libnm-client-impl/nm-device-infiniband.c | 180 + src/libnm-client-impl/nm-device-ip-tunnel.c | 578 ++ src/libnm-client-impl/nm-device-macsec.c | 631 ++ src/libnm-client-impl/nm-device-macvlan.c | 299 + src/libnm-client-impl/nm-device-modem.c | 360 + src/libnm-client-impl/nm-device-olpc-mesh.c | 201 + src/libnm-client-impl/nm-device-ovs-bridge.c | 169 + src/libnm-client-impl/nm-device-ovs-interface.c | 89 + src/libnm-client-impl/nm-device-ovs-port.c | 169 + src/libnm-client-impl/nm-device-ppp.c | 37 + src/libnm-client-impl/nm-device-private.h | 0 src/libnm-client-impl/nm-device-team.c | 247 + src/libnm-client-impl/nm-device-tun.c | 394 + src/libnm-client-impl/nm-device-veth.c | 124 + src/libnm-client-impl/nm-device-vlan.c | 257 + src/libnm-client-impl/nm-device-vrf.c | 145 + src/libnm-client-impl/nm-device-vxlan.c | 787 ++ src/libnm-client-impl/nm-device-wifi-p2p.c | 414 + src/libnm-client-impl/nm-device-wifi.c | 789 ++ src/libnm-client-impl/nm-device-wimax.c | 389 + src/libnm-client-impl/nm-device-wireguard.c | 202 + src/libnm-client-impl/nm-device-wpan.c | 93 + src/libnm-client-impl/nm-device.c | 3080 +++++++ src/libnm-client-impl/nm-dhcp-config.c | 215 + src/libnm-client-impl/nm-dhcp4-config.c | 30 + src/libnm-client-impl/nm-dhcp4-config.h | 33 + src/libnm-client-impl/nm-dhcp6-config.c | 30 + src/libnm-client-impl/nm-dhcp6-config.h | 33 + src/libnm-client-impl/nm-dns-manager.c | 217 + src/libnm-client-impl/nm-dns-manager.h | 22 + src/libnm-client-impl/nm-ip-config.c | 644 ++ src/libnm-client-impl/nm-ip4-config.c | 30 + src/libnm-client-impl/nm-ip4-config.h | 33 + src/libnm-client-impl/nm-ip6-config.c | 30 + src/libnm-client-impl/nm-ip6-config.h | 33 + src/libnm-client-impl/nm-libnm-utils.c | 913 ++ src/libnm-client-impl/nm-libnm-utils.h | 1030 +++ src/libnm-client-impl/nm-object-private.h | 15 + src/libnm-client-impl/nm-object.c | 311 + src/libnm-client-impl/nm-property-infos-dbus.xml | 125 + .../nm-property-infos-ifcfg-rh.xml | 335 + .../nm-property-infos-keyfile.xml | 126 + src/libnm-client-impl/nm-property-infos-nmcli.xml | 106 + .../nm-remote-connection-private.h | 22 + src/libnm-client-impl/nm-remote-connection.c | 839 ++ src/libnm-client-impl/nm-secret-agent-old.c | 1995 +++++ src/libnm-client-impl/nm-settings-docs-gir.xml | 530 ++ src/libnm-client-impl/nm-vpn-connection.c | 223 + src/libnm-client-impl/nm-vpn-editor.c | 55 + src/libnm-client-impl/nm-vpn-plugin-old.c | 1135 +++ src/libnm-client-impl/nm-vpn-service-plugin.c | 1384 +++ src/libnm-client-impl/nm-wifi-p2p-peer.c | 575 ++ src/libnm-client-impl/nm-wimax-nsp.c | 184 + src/libnm-client-impl/tests/meson.build | 46 + src/libnm-client-impl/tests/test-libnm.c | 3502 ++++++++ src/libnm-client-impl/tests/test-nm-client.c | 1300 +++ .../tests/test-remote-settings-client.c | 498 ++ src/libnm-client-impl/tests/test-secret-agent.c | 746 ++ 76 files changed, 41346 insertions(+) create mode 100644 src/libnm-client-impl/libnm.pc.in create mode 100644 src/libnm-client-impl/libnm.ver create mode 100644 src/libnm-client-impl/meson.build create mode 100644 src/libnm-client-impl/nm-access-point.c create mode 100644 src/libnm-client-impl/nm-active-connection.c create mode 100644 src/libnm-client-impl/nm-checkpoint.c create mode 100644 src/libnm-client-impl/nm-client.c create mode 100644 src/libnm-client-impl/nm-dbus-helpers.c create mode 100644 src/libnm-client-impl/nm-dbus-helpers.h create mode 100644 src/libnm-client-impl/nm-default-libnm.h create mode 100644 src/libnm-client-impl/nm-device-6lowpan.c create mode 100644 src/libnm-client-impl/nm-device-adsl.c create mode 100644 src/libnm-client-impl/nm-device-bond.c create mode 100644 src/libnm-client-impl/nm-device-bridge.c create mode 100644 src/libnm-client-impl/nm-device-bt.c create mode 100644 src/libnm-client-impl/nm-device-dummy.c create mode 100644 src/libnm-client-impl/nm-device-ethernet.c create mode 100644 src/libnm-client-impl/nm-device-generic.c create mode 100644 src/libnm-client-impl/nm-device-infiniband.c create mode 100644 src/libnm-client-impl/nm-device-ip-tunnel.c create mode 100644 src/libnm-client-impl/nm-device-macsec.c create mode 100644 src/libnm-client-impl/nm-device-macvlan.c create mode 100644 src/libnm-client-impl/nm-device-modem.c create mode 100644 src/libnm-client-impl/nm-device-olpc-mesh.c create mode 100644 src/libnm-client-impl/nm-device-ovs-bridge.c create mode 100644 src/libnm-client-impl/nm-device-ovs-interface.c create mode 100644 src/libnm-client-impl/nm-device-ovs-port.c create mode 100644 src/libnm-client-impl/nm-device-ppp.c create mode 100644 src/libnm-client-impl/nm-device-private.h create mode 100644 src/libnm-client-impl/nm-device-team.c create mode 100644 src/libnm-client-impl/nm-device-tun.c create mode 100644 src/libnm-client-impl/nm-device-veth.c create mode 100644 src/libnm-client-impl/nm-device-vlan.c create mode 100644 src/libnm-client-impl/nm-device-vrf.c create mode 100644 src/libnm-client-impl/nm-device-vxlan.c create mode 100644 src/libnm-client-impl/nm-device-wifi-p2p.c create mode 100644 src/libnm-client-impl/nm-device-wifi.c create mode 100644 src/libnm-client-impl/nm-device-wimax.c create mode 100644 src/libnm-client-impl/nm-device-wireguard.c create mode 100644 src/libnm-client-impl/nm-device-wpan.c create mode 100644 src/libnm-client-impl/nm-device.c create mode 100644 src/libnm-client-impl/nm-dhcp-config.c create mode 100644 src/libnm-client-impl/nm-dhcp4-config.c create mode 100644 src/libnm-client-impl/nm-dhcp4-config.h create mode 100644 src/libnm-client-impl/nm-dhcp6-config.c create mode 100644 src/libnm-client-impl/nm-dhcp6-config.h create mode 100644 src/libnm-client-impl/nm-dns-manager.c create mode 100644 src/libnm-client-impl/nm-dns-manager.h create mode 100644 src/libnm-client-impl/nm-ip-config.c create mode 100644 src/libnm-client-impl/nm-ip4-config.c create mode 100644 src/libnm-client-impl/nm-ip4-config.h create mode 100644 src/libnm-client-impl/nm-ip6-config.c create mode 100644 src/libnm-client-impl/nm-ip6-config.h create mode 100644 src/libnm-client-impl/nm-libnm-utils.c create mode 100644 src/libnm-client-impl/nm-libnm-utils.h create mode 100644 src/libnm-client-impl/nm-object-private.h create mode 100644 src/libnm-client-impl/nm-object.c create mode 100644 src/libnm-client-impl/nm-property-infos-dbus.xml create mode 100644 src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml create mode 100644 src/libnm-client-impl/nm-property-infos-keyfile.xml create mode 100644 src/libnm-client-impl/nm-property-infos-nmcli.xml create mode 100644 src/libnm-client-impl/nm-remote-connection-private.h create mode 100644 src/libnm-client-impl/nm-remote-connection.c create mode 100644 src/libnm-client-impl/nm-secret-agent-old.c create mode 100644 src/libnm-client-impl/nm-settings-docs-gir.xml create mode 100644 src/libnm-client-impl/nm-vpn-connection.c create mode 100644 src/libnm-client-impl/nm-vpn-editor.c create mode 100644 src/libnm-client-impl/nm-vpn-plugin-old.c create mode 100644 src/libnm-client-impl/nm-vpn-service-plugin.c create mode 100644 src/libnm-client-impl/nm-wifi-p2p-peer.c create mode 100644 src/libnm-client-impl/nm-wimax-nsp.c create mode 100644 src/libnm-client-impl/tests/meson.build create mode 100644 src/libnm-client-impl/tests/test-libnm.c create mode 100644 src/libnm-client-impl/tests/test-nm-client.c create mode 100644 src/libnm-client-impl/tests/test-remote-settings-client.c create mode 100644 src/libnm-client-impl/tests/test-secret-agent.c (limited to 'src/libnm-client-impl') diff --git a/src/libnm-client-impl/libnm.pc.in b/src/libnm-client-impl/libnm.pc.in new file mode 100644 index 00000000..afc5e4d9 --- /dev/null +++ b/src/libnm-client-impl/libnm.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +vpnservicedir=@nmlibdir@/VPN + +Name: libnm +Description: Convenience library for clients of NetworkManager +Version: @VERSION@ +Requires: gio-2.0 +Cflags: -I${includedir}/libnm +Libs: -L${libdir} -lnm + + diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver new file mode 100644 index 00000000..65d52b1a --- /dev/null +++ b/src/libnm-client-impl/libnm.ver @@ -0,0 +1,1795 @@ +libnm_1_0_0 { +global: + nm_802_11_ap_flags_get_type; + nm_802_11_ap_security_flags_get_type; + nm_802_11_mode_get_type; + nm_access_point_connection_valid; + nm_access_point_filter_connections; + nm_access_point_get_bssid; + nm_access_point_get_flags; + nm_access_point_get_frequency; + nm_access_point_get_max_bitrate; + nm_access_point_get_mode; + nm_access_point_get_rsn_flags; + nm_access_point_get_ssid; + nm_access_point_get_strength; + nm_access_point_get_type; + nm_access_point_get_wpa_flags; + nm_active_connection_get_connection; + nm_active_connection_get_connection_type; + nm_active_connection_get_default6; + nm_active_connection_get_default; + nm_active_connection_get_devices; + nm_active_connection_get_dhcp4_config; + nm_active_connection_get_dhcp6_config; + nm_active_connection_get_id; + nm_active_connection_get_ip4_config; + nm_active_connection_get_ip6_config; + nm_active_connection_get_master; + nm_active_connection_get_specific_object_path; + nm_active_connection_get_state; + nm_active_connection_get_type; + nm_active_connection_get_uuid; + nm_active_connection_get_vpn; + nm_active_connection_state_get_type; + nm_agent_manager_error_get_type; + nm_agent_manager_error_quark; + nm_bluetooth_capabilities_get_type; + nm_client_activate_connection_async; + nm_client_activate_connection_finish; + nm_client_add_and_activate_connection_async; + nm_client_add_and_activate_connection_finish; + nm_client_add_connection_async; + nm_client_add_connection_finish; + nm_client_check_connectivity; + nm_client_check_connectivity_async; + nm_client_check_connectivity_finish; + nm_client_deactivate_connection; + nm_client_deactivate_connection_async; + nm_client_deactivate_connection_finish; + nm_client_error_get_type; + nm_client_error_quark; + nm_client_get_activating_connection; + nm_client_get_active_connections; + nm_client_get_connection_by_id; + nm_client_get_connection_by_path; + nm_client_get_connection_by_uuid; + nm_client_get_connections; + nm_client_get_connectivity; + nm_client_get_device_by_iface; + nm_client_get_device_by_path; + nm_client_get_devices; + nm_client_get_logging; + nm_client_get_nm_running; + nm_client_get_permission_result; + nm_client_get_primary_connection; + nm_client_get_startup; + nm_client_get_state; + nm_client_get_type; + nm_client_get_version; + nm_client_load_connections; + nm_client_load_connections_async; + nm_client_load_connections_finish; + nm_client_networking_get_enabled; + nm_client_networking_set_enabled; + nm_client_new; + nm_client_new_async; + nm_client_new_finish; + nm_client_permission_get_type; + nm_client_permission_result_get_type; + nm_client_reload_connections; + nm_client_reload_connections_async; + nm_client_reload_connections_finish; + nm_client_save_hostname; + nm_client_save_hostname_async; + nm_client_save_hostname_finish; + nm_client_set_logging; + nm_client_wimax_get_enabled; + nm_client_wimax_hardware_get_enabled; + nm_client_wimax_set_enabled; + nm_client_wireless_get_enabled; + nm_client_wireless_hardware_get_enabled; + nm_client_wireless_set_enabled; + nm_client_wwan_get_enabled; + nm_client_wwan_hardware_get_enabled; + nm_client_wwan_set_enabled; + nm_connection_add_setting; + nm_connection_clear_secrets; + nm_connection_clear_secrets_with_flags; + nm_connection_clear_settings; + nm_connection_compare; + nm_connection_diff; + nm_connection_dump; + nm_connection_error_get_type; + nm_connection_error_quark; + nm_connection_for_each_setting_value; + nm_connection_get_connection_type; + nm_connection_get_id; + nm_connection_get_interface_name; + nm_connection_get_path; + nm_connection_get_setting; + nm_connection_get_setting_802_1x; + nm_connection_get_setting_adsl; + nm_connection_get_setting_bluetooth; + nm_connection_get_setting_bond; + nm_connection_get_setting_bridge; + nm_connection_get_setting_bridge_port; + nm_connection_get_setting_by_name; + nm_connection_get_setting_cdma; + nm_connection_get_setting_connection; + nm_connection_get_setting_dcb; + nm_connection_get_setting_generic; + nm_connection_get_setting_gsm; + nm_connection_get_setting_infiniband; + nm_connection_get_setting_ip4_config; + nm_connection_get_setting_ip6_config; + nm_connection_get_setting_olpc_mesh; + nm_connection_get_setting_ppp; + nm_connection_get_setting_pppoe; + nm_connection_get_setting_serial; + nm_connection_get_setting_team; + nm_connection_get_setting_team_port; + nm_connection_get_setting_vlan; + nm_connection_get_setting_vpn; + nm_connection_get_setting_wimax; + nm_connection_get_setting_wired; + nm_connection_get_setting_wireless; + nm_connection_get_setting_wireless_security; + nm_connection_get_type; + nm_connection_get_uuid; + nm_connection_get_virtual_device_description; + nm_connection_is_type; + nm_connection_is_virtual; + nm_connection_need_secrets; + nm_connection_normalize; + nm_connection_remove_setting; + nm_connection_replace_settings; + nm_connection_replace_settings_from_connection; + nm_connection_serialization_flags_get_type; + nm_connection_set_path; + nm_connection_to_dbus; + nm_connection_update_secrets; + nm_connection_verify; + nm_connectivity_state_get_type; + nm_crypto_error_get_type; + nm_crypto_error_quark; + nm_device_adsl_get_carrier; + nm_device_adsl_get_type; + nm_device_bond_get_carrier; + nm_device_bond_get_hw_address; + nm_device_bond_get_slaves; + nm_device_bond_get_type; + nm_device_bridge_get_carrier; + nm_device_bridge_get_hw_address; + nm_device_bridge_get_slaves; + nm_device_bridge_get_type; + nm_device_bt_get_capabilities; + nm_device_bt_get_hw_address; + nm_device_bt_get_name; + nm_device_bt_get_type; + nm_device_capabilities_get_type; + nm_device_connection_compatible; + nm_device_connection_valid; + nm_device_delete; + nm_device_delete_async; + nm_device_delete_finish; + nm_device_disambiguate_names; + nm_device_disconnect; + nm_device_disconnect_async; + nm_device_disconnect_finish; + nm_device_error_get_type; + nm_device_error_quark; + nm_device_ethernet_get_carrier; + nm_device_ethernet_get_hw_address; + nm_device_ethernet_get_permanent_hw_address; + nm_device_ethernet_get_speed; + nm_device_ethernet_get_type; + nm_device_filter_connections; + nm_device_generic_get_hw_address; + nm_device_generic_get_type; + nm_device_get_active_connection; + nm_device_get_autoconnect; + nm_device_get_available_connections; + nm_device_get_capabilities; + nm_device_get_description; + nm_device_get_device_type; + nm_device_get_dhcp4_config; + nm_device_get_dhcp6_config; + nm_device_get_driver; + nm_device_get_driver_version; + nm_device_get_firmware_missing; + nm_device_get_firmware_version; + nm_device_get_hw_address; + nm_device_get_iface; + nm_device_get_ip4_config; + nm_device_get_ip6_config; + nm_device_get_ip_iface; + nm_device_get_managed; + nm_device_get_mtu; + nm_device_get_physical_port_id; + nm_device_get_product; + nm_device_get_setting_type; + nm_device_get_state; + nm_device_get_state_reason; + nm_device_get_type; + nm_device_get_type_description; + nm_device_get_udi; + nm_device_get_vendor; + nm_device_infiniband_get_carrier; + nm_device_infiniband_get_hw_address; + nm_device_infiniband_get_type; + nm_device_is_software; + nm_device_modem_capabilities_get_type; + nm_device_modem_get_current_capabilities; + nm_device_modem_get_modem_capabilities; + nm_device_modem_get_type; + nm_device_olpc_mesh_get_active_channel; + nm_device_olpc_mesh_get_companion; + nm_device_olpc_mesh_get_hw_address; + nm_device_olpc_mesh_get_type; + nm_device_set_autoconnect; + nm_device_state_get_type; + nm_device_state_reason_get_type; + nm_device_team_get_carrier; + nm_device_team_get_hw_address; + nm_device_team_get_slaves; + nm_device_team_get_type; + nm_device_type_get_type; + nm_device_vlan_get_carrier; + nm_device_vlan_get_hw_address; + nm_device_vlan_get_parent; + nm_device_vlan_get_type; + nm_device_vlan_get_vlan_id; + nm_device_wifi_capabilities_get_type; + nm_device_wifi_get_access_point_by_path; + nm_device_wifi_get_access_points; + nm_device_wifi_get_active_access_point; + nm_device_wifi_get_bitrate; + nm_device_wifi_get_capabilities; + nm_device_wifi_get_hw_address; + nm_device_wifi_get_mode; + nm_device_wifi_get_permanent_hw_address; + nm_device_wifi_get_type; + nm_device_wifi_request_scan; + nm_device_wifi_request_scan_async; + nm_device_wifi_request_scan_finish; + nm_device_wimax_get_active_nsp; + nm_device_wimax_get_bsid; + nm_device_wimax_get_center_frequency; + nm_device_wimax_get_cinr; + nm_device_wimax_get_hw_address; + nm_device_wimax_get_nsp_by_path; + nm_device_wimax_get_nsps; + nm_device_wimax_get_rssi; + nm_device_wimax_get_tx_power; + nm_device_wimax_get_type; + nm_dhcp_config_get_family; + nm_dhcp_config_get_one_option; + nm_dhcp_config_get_options; + nm_dhcp_config_get_type; + nm_ip_address_equal; + nm_ip_address_get_address; + nm_ip_address_get_address_binary; + nm_ip_address_get_attribute; + nm_ip_address_get_attribute_names; + nm_ip_address_get_family; + nm_ip_address_get_prefix; + nm_ip_address_get_type; + nm_ip_address_new; + nm_ip_address_new_binary; + nm_ip_address_ref; + nm_ip_address_set_address; + nm_ip_address_set_address_binary; + nm_ip_address_set_attribute; + nm_ip_address_set_prefix; + nm_ip_address_unref; + nm_ip_config_get_addresses; + nm_ip_config_get_domains; + nm_ip_config_get_family; + nm_ip_config_get_gateway; + nm_ip_config_get_nameservers; + nm_ip_config_get_routes; + nm_ip_config_get_searches; + nm_ip_config_get_type; + nm_ip_config_get_wins_servers; + nm_ip_route_equal; + nm_ip_route_get_attribute; + nm_ip_route_get_attribute_names; + nm_ip_route_get_dest; + nm_ip_route_get_dest_binary; + nm_ip_route_get_family; + nm_ip_route_get_metric; + nm_ip_route_get_next_hop; + nm_ip_route_get_next_hop_binary; + nm_ip_route_get_prefix; + nm_ip_route_get_type; + nm_ip_route_new; + nm_ip_route_new_binary; + nm_ip_route_ref; + nm_ip_route_set_attribute; + nm_ip_route_set_dest; + nm_ip_route_set_dest_binary; + nm_ip_route_set_metric; + nm_ip_route_set_next_hop; + nm_ip_route_set_next_hop_binary; + nm_ip_route_set_prefix; + nm_ip_route_unref; + nm_manager_error_get_type; + nm_manager_error_quark; + nm_object_get_path; + nm_object_get_type; + nm_remote_connection_commit_changes; + nm_remote_connection_commit_changes_async; + nm_remote_connection_commit_changes_finish; + nm_remote_connection_delete; + nm_remote_connection_delete_async; + nm_remote_connection_delete_finish; + nm_remote_connection_get_secrets; + nm_remote_connection_get_secrets_async; + nm_remote_connection_get_secrets_finish; + nm_remote_connection_get_type; + nm_remote_connection_get_unsaved; + nm_remote_connection_get_visible; + nm_remote_connection_save; + nm_remote_connection_save_async; + nm_remote_connection_save_finish; + nm_secret_agent_capabilities_get_type; + nm_secret_agent_error_get_type; + nm_secret_agent_error_quark; + nm_secret_agent_get_secrets_flags_get_type; + nm_secret_agent_old_delete_secrets; + nm_secret_agent_old_get_registered; + nm_secret_agent_old_get_secrets; + nm_secret_agent_old_get_type; + nm_secret_agent_old_register; + nm_secret_agent_old_register_async; + nm_secret_agent_old_register_finish; + nm_secret_agent_old_save_secrets; + nm_secret_agent_old_unregister; + nm_secret_agent_old_unregister_async; + nm_secret_agent_old_unregister_finish; + nm_setting_802_1x_add_altsubject_match; + nm_setting_802_1x_add_eap_method; + nm_setting_802_1x_add_phase2_altsubject_match; + nm_setting_802_1x_ck_format_get_type; + nm_setting_802_1x_ck_scheme_get_type; + nm_setting_802_1x_clear_altsubject_matches; + nm_setting_802_1x_clear_eap_methods; + nm_setting_802_1x_clear_phase2_altsubject_matches; + nm_setting_802_1x_get_altsubject_match; + nm_setting_802_1x_get_anonymous_identity; + nm_setting_802_1x_get_ca_cert_blob; + nm_setting_802_1x_get_ca_cert_path; + nm_setting_802_1x_get_ca_cert_scheme; + nm_setting_802_1x_get_ca_path; + nm_setting_802_1x_get_client_cert_blob; + nm_setting_802_1x_get_client_cert_path; + nm_setting_802_1x_get_client_cert_scheme; + nm_setting_802_1x_get_eap_method; + nm_setting_802_1x_get_identity; + nm_setting_802_1x_get_num_altsubject_matches; + nm_setting_802_1x_get_num_eap_methods; + nm_setting_802_1x_get_num_phase2_altsubject_matches; + nm_setting_802_1x_get_pac_file; + nm_setting_802_1x_get_password; + nm_setting_802_1x_get_password_flags; + nm_setting_802_1x_get_password_raw; + nm_setting_802_1x_get_password_raw_flags; + nm_setting_802_1x_get_phase1_fast_provisioning; + nm_setting_802_1x_get_phase1_peaplabel; + nm_setting_802_1x_get_phase1_peapver; + nm_setting_802_1x_get_phase2_altsubject_match; + nm_setting_802_1x_get_phase2_auth; + nm_setting_802_1x_get_phase2_autheap; + nm_setting_802_1x_get_phase2_ca_cert_blob; + nm_setting_802_1x_get_phase2_ca_cert_path; + nm_setting_802_1x_get_phase2_ca_cert_scheme; + nm_setting_802_1x_get_phase2_ca_path; + nm_setting_802_1x_get_phase2_client_cert_blob; + nm_setting_802_1x_get_phase2_client_cert_path; + nm_setting_802_1x_get_phase2_client_cert_scheme; + nm_setting_802_1x_get_phase2_private_key_blob; + nm_setting_802_1x_get_phase2_private_key_format; + nm_setting_802_1x_get_phase2_private_key_password; + nm_setting_802_1x_get_phase2_private_key_password_flags; + nm_setting_802_1x_get_phase2_private_key_path; + nm_setting_802_1x_get_phase2_private_key_scheme; + nm_setting_802_1x_get_phase2_subject_match; + nm_setting_802_1x_get_pin; + nm_setting_802_1x_get_pin_flags; + nm_setting_802_1x_get_private_key_blob; + nm_setting_802_1x_get_private_key_format; + nm_setting_802_1x_get_private_key_password; + nm_setting_802_1x_get_private_key_password_flags; + nm_setting_802_1x_get_private_key_path; + nm_setting_802_1x_get_private_key_scheme; + nm_setting_802_1x_get_subject_match; + nm_setting_802_1x_get_system_ca_certs; + nm_setting_802_1x_get_type; + nm_setting_802_1x_new; + nm_setting_802_1x_remove_altsubject_match; + nm_setting_802_1x_remove_altsubject_match_by_value; + nm_setting_802_1x_remove_eap_method; + nm_setting_802_1x_remove_eap_method_by_value; + nm_setting_802_1x_remove_phase2_altsubject_match; + nm_setting_802_1x_remove_phase2_altsubject_match_by_value; + nm_setting_802_1x_set_ca_cert; + nm_setting_802_1x_set_client_cert; + nm_setting_802_1x_set_phase2_ca_cert; + nm_setting_802_1x_set_phase2_client_cert; + nm_setting_802_1x_set_phase2_private_key; + nm_setting_802_1x_set_private_key; + nm_setting_adsl_get_encapsulation; + nm_setting_adsl_get_password; + nm_setting_adsl_get_password_flags; + nm_setting_adsl_get_protocol; + nm_setting_adsl_get_type; + nm_setting_adsl_get_username; + nm_setting_adsl_get_vci; + nm_setting_adsl_get_vpi; + nm_setting_adsl_new; + nm_setting_bluetooth_get_bdaddr; + nm_setting_bluetooth_get_connection_type; + nm_setting_bluetooth_get_type; + nm_setting_bluetooth_new; + nm_setting_bond_add_option; + nm_setting_bond_get_num_options; + nm_setting_bond_get_option; + nm_setting_bond_get_option_by_name; + nm_setting_bond_get_option_default; + nm_setting_bond_get_type; + nm_setting_bond_get_valid_options; + nm_setting_bond_new; + nm_setting_bond_remove_option; + nm_setting_bond_validate_option; + nm_setting_bridge_get_ageing_time; + nm_setting_bridge_get_forward_delay; + nm_setting_bridge_get_hello_time; + nm_setting_bridge_get_mac_address; + nm_setting_bridge_get_max_age; + nm_setting_bridge_get_priority; + nm_setting_bridge_get_stp; + nm_setting_bridge_get_type; + nm_setting_bridge_new; + nm_setting_bridge_port_get_hairpin_mode; + nm_setting_bridge_port_get_path_cost; + nm_setting_bridge_port_get_priority; + nm_setting_bridge_port_get_type; + nm_setting_bridge_port_new; + nm_setting_cdma_get_number; + nm_setting_cdma_get_password; + nm_setting_cdma_get_password_flags; + nm_setting_cdma_get_type; + nm_setting_cdma_get_username; + nm_setting_cdma_new; + nm_setting_compare; + nm_setting_compare_flags_get_type; + nm_setting_connection_add_permission; + nm_setting_connection_add_secondary; + nm_setting_connection_get_autoconnect; + nm_setting_connection_get_autoconnect_priority; + nm_setting_connection_get_connection_type; + nm_setting_connection_get_gateway_ping_timeout; + nm_setting_connection_get_id; + nm_setting_connection_get_interface_name; + nm_setting_connection_get_master; + nm_setting_connection_get_num_permissions; + nm_setting_connection_get_num_secondaries; + nm_setting_connection_get_permission; + nm_setting_connection_get_read_only; + nm_setting_connection_get_secondary; + nm_setting_connection_get_slave_type; + nm_setting_connection_get_timestamp; + nm_setting_connection_get_type; + nm_setting_connection_get_uuid; + nm_setting_connection_get_zone; + nm_setting_connection_is_slave_type; + nm_setting_connection_new; + nm_setting_connection_permissions_user_allowed; + nm_setting_connection_remove_permission; + nm_setting_connection_remove_permission_by_value; + nm_setting_connection_remove_secondary; + nm_setting_connection_remove_secondary_by_value; + nm_setting_dcb_flags_get_type; + nm_setting_dcb_get_app_fcoe_flags; + nm_setting_dcb_get_app_fcoe_mode; + nm_setting_dcb_get_app_fcoe_priority; + nm_setting_dcb_get_app_fip_flags; + nm_setting_dcb_get_app_fip_priority; + nm_setting_dcb_get_app_iscsi_flags; + nm_setting_dcb_get_app_iscsi_priority; + nm_setting_dcb_get_priority_bandwidth; + nm_setting_dcb_get_priority_flow_control; + nm_setting_dcb_get_priority_flow_control_flags; + nm_setting_dcb_get_priority_group_bandwidth; + nm_setting_dcb_get_priority_group_flags; + nm_setting_dcb_get_priority_group_id; + nm_setting_dcb_get_priority_strict_bandwidth; + nm_setting_dcb_get_priority_traffic_class; + nm_setting_dcb_get_type; + nm_setting_dcb_new; + nm_setting_dcb_set_priority_bandwidth; + nm_setting_dcb_set_priority_flow_control; + nm_setting_dcb_set_priority_group_bandwidth; + nm_setting_dcb_set_priority_group_id; + nm_setting_dcb_set_priority_strict_bandwidth; + nm_setting_dcb_set_priority_traffic_class; + nm_setting_diff; + nm_setting_diff_result_get_type; + nm_setting_duplicate; + nm_setting_enumerate_values; + nm_setting_generic_get_type; + nm_setting_generic_new; + nm_setting_get_dbus_property_type; + nm_setting_get_name; + nm_setting_get_secret_flags; + nm_setting_get_type; + nm_setting_gsm_get_apn; + nm_setting_gsm_get_home_only; + nm_setting_gsm_get_network_id; + nm_setting_gsm_get_number; + nm_setting_gsm_get_password; + nm_setting_gsm_get_password_flags; + nm_setting_gsm_get_pin; + nm_setting_gsm_get_pin_flags; + nm_setting_gsm_get_type; + nm_setting_gsm_get_username; + nm_setting_gsm_new; + nm_setting_infiniband_get_mac_address; + nm_setting_infiniband_get_mtu; + nm_setting_infiniband_get_p_key; + nm_setting_infiniband_get_parent; + nm_setting_infiniband_get_transport_mode; + nm_setting_infiniband_get_type; + nm_setting_infiniband_get_virtual_interface_name; + nm_setting_infiniband_new; + nm_setting_ip4_config_get_dhcp_client_id; + nm_setting_ip4_config_get_type; + nm_setting_ip4_config_new; + nm_setting_ip6_config_get_ip6_privacy; + nm_setting_ip6_config_get_type; + nm_setting_ip6_config_new; + nm_setting_ip6_config_privacy_get_type; + nm_setting_ip_config_add_address; + nm_setting_ip_config_add_dns; + nm_setting_ip_config_add_dns_search; + nm_setting_ip_config_add_route; + nm_setting_ip_config_clear_addresses; + nm_setting_ip_config_clear_dns; + nm_setting_ip_config_clear_dns_searches; + nm_setting_ip_config_clear_routes; + nm_setting_ip_config_get_address; + nm_setting_ip_config_get_dhcp_hostname; + nm_setting_ip_config_get_dhcp_send_hostname; + nm_setting_ip_config_get_dns; + nm_setting_ip_config_get_dns_search; + nm_setting_ip_config_get_gateway; + nm_setting_ip_config_get_ignore_auto_dns; + nm_setting_ip_config_get_ignore_auto_routes; + nm_setting_ip_config_get_may_fail; + nm_setting_ip_config_get_method; + nm_setting_ip_config_get_never_default; + nm_setting_ip_config_get_num_addresses; + nm_setting_ip_config_get_num_dns; + nm_setting_ip_config_get_num_dns_searches; + nm_setting_ip_config_get_num_routes; + nm_setting_ip_config_get_route; + nm_setting_ip_config_get_route_metric; + nm_setting_ip_config_get_type; + nm_setting_ip_config_remove_address; + nm_setting_ip_config_remove_address_by_value; + nm_setting_ip_config_remove_dns; + nm_setting_ip_config_remove_dns_by_value; + nm_setting_ip_config_remove_dns_search; + nm_setting_ip_config_remove_dns_search_by_value; + nm_setting_ip_config_remove_route; + nm_setting_ip_config_remove_route_by_value; + nm_setting_lookup_type; + nm_setting_olpc_mesh_get_channel; + nm_setting_olpc_mesh_get_dhcp_anycast_address; + nm_setting_olpc_mesh_get_ssid; + nm_setting_olpc_mesh_get_type; + nm_setting_olpc_mesh_new; + nm_setting_ppp_get_baud; + nm_setting_ppp_get_crtscts; + nm_setting_ppp_get_lcp_echo_failure; + nm_setting_ppp_get_lcp_echo_interval; + nm_setting_ppp_get_mppe_stateful; + nm_setting_ppp_get_mru; + nm_setting_ppp_get_mtu; + nm_setting_ppp_get_no_vj_comp; + nm_setting_ppp_get_noauth; + nm_setting_ppp_get_nobsdcomp; + nm_setting_ppp_get_nodeflate; + nm_setting_ppp_get_refuse_chap; + nm_setting_ppp_get_refuse_eap; + nm_setting_ppp_get_refuse_mschap; + nm_setting_ppp_get_refuse_mschapv2; + nm_setting_ppp_get_refuse_pap; + nm_setting_ppp_get_require_mppe; + nm_setting_ppp_get_require_mppe_128; + nm_setting_ppp_get_type; + nm_setting_ppp_new; + nm_setting_pppoe_get_password; + nm_setting_pppoe_get_password_flags; + nm_setting_pppoe_get_service; + nm_setting_pppoe_get_type; + nm_setting_pppoe_get_username; + nm_setting_pppoe_new; + nm_setting_secret_flags_get_type; + nm_setting_serial_get_baud; + nm_setting_serial_get_bits; + nm_setting_serial_get_parity; + nm_setting_serial_get_send_delay; + nm_setting_serial_get_stopbits; + nm_setting_serial_get_type; + nm_setting_serial_new; + nm_setting_serial_parity_get_type; + nm_setting_set_secret_flags; + nm_setting_team_get_config; + nm_setting_team_get_type; + nm_setting_team_new; + nm_setting_team_port_get_config; + nm_setting_team_port_get_type; + nm_setting_team_port_new; + nm_setting_to_string; + nm_setting_verify; + nm_setting_vlan_add_priority; + nm_setting_vlan_add_priority_str; + nm_setting_vlan_clear_priorities; + nm_setting_vlan_get_flags; + nm_setting_vlan_get_id; + nm_setting_vlan_get_num_priorities; + nm_setting_vlan_get_parent; + nm_setting_vlan_get_priority; + nm_setting_vlan_get_type; + nm_setting_vlan_new; + nm_setting_vlan_remove_priority; + nm_setting_vlan_remove_priority_by_value; + nm_setting_vlan_remove_priority_str_by_value; + nm_setting_vpn_add_data_item; + nm_setting_vpn_add_secret; + nm_setting_vpn_foreach_data_item; + nm_setting_vpn_foreach_secret; + nm_setting_vpn_get_data_item; + nm_setting_vpn_get_num_data_items; + nm_setting_vpn_get_num_secrets; + nm_setting_vpn_get_secret; + nm_setting_vpn_get_service_type; + nm_setting_vpn_get_type; + nm_setting_vpn_get_user_name; + nm_setting_vpn_new; + nm_setting_vpn_remove_data_item; + nm_setting_vpn_remove_secret; + nm_setting_wimax_get_mac_address; + nm_setting_wimax_get_network_name; + nm_setting_wimax_get_type; + nm_setting_wimax_new; + nm_setting_wired_add_mac_blacklist_item; + nm_setting_wired_add_s390_option; + nm_setting_wired_clear_mac_blacklist_items; + nm_setting_wired_get_auto_negotiate; + nm_setting_wired_get_cloned_mac_address; + nm_setting_wired_get_duplex; + nm_setting_wired_get_mac_address; + nm_setting_wired_get_mac_address_blacklist; + nm_setting_wired_get_mac_blacklist_item; + nm_setting_wired_get_mtu; + nm_setting_wired_get_num_mac_blacklist_items; + nm_setting_wired_get_num_s390_options; + nm_setting_wired_get_port; + nm_setting_wired_get_s390_nettype; + nm_setting_wired_get_s390_option; + nm_setting_wired_get_s390_option_by_key; + nm_setting_wired_get_s390_subchannels; + nm_setting_wired_get_speed; + nm_setting_wired_get_type; + nm_setting_wired_get_valid_s390_options; + nm_setting_wired_new; + nm_setting_wired_remove_mac_blacklist_item; + nm_setting_wired_remove_mac_blacklist_item_by_value; + nm_setting_wired_remove_s390_option; + nm_setting_wireless_add_mac_blacklist_item; + nm_setting_wireless_add_seen_bssid; + nm_setting_wireless_ap_security_compatible; + nm_setting_wireless_clear_mac_blacklist_items; + nm_setting_wireless_get_band; + nm_setting_wireless_get_bssid; + nm_setting_wireless_get_channel; + nm_setting_wireless_get_cloned_mac_address; + nm_setting_wireless_get_hidden; + nm_setting_wireless_get_mac_address; + nm_setting_wireless_get_mac_address_blacklist; + nm_setting_wireless_get_mac_blacklist_item; + nm_setting_wireless_get_mode; + nm_setting_wireless_get_mtu; + nm_setting_wireless_get_num_mac_blacklist_items; + nm_setting_wireless_get_num_seen_bssids; + nm_setting_wireless_get_rate; + nm_setting_wireless_get_seen_bssid; + nm_setting_wireless_get_ssid; + nm_setting_wireless_get_tx_power; + nm_setting_wireless_get_type; + nm_setting_wireless_new; + nm_setting_wireless_remove_mac_blacklist_item; + nm_setting_wireless_remove_mac_blacklist_item_by_value; + nm_setting_wireless_security_add_group; + nm_setting_wireless_security_add_pairwise; + nm_setting_wireless_security_add_proto; + nm_setting_wireless_security_clear_groups; + nm_setting_wireless_security_clear_pairwise; + nm_setting_wireless_security_clear_protos; + nm_setting_wireless_security_get_auth_alg; + nm_setting_wireless_security_get_group; + nm_setting_wireless_security_get_key_mgmt; + nm_setting_wireless_security_get_leap_password; + nm_setting_wireless_security_get_leap_password_flags; + nm_setting_wireless_security_get_leap_username; + nm_setting_wireless_security_get_num_groups; + nm_setting_wireless_security_get_num_pairwise; + nm_setting_wireless_security_get_num_protos; + nm_setting_wireless_security_get_pairwise; + nm_setting_wireless_security_get_proto; + nm_setting_wireless_security_get_psk; + nm_setting_wireless_security_get_psk_flags; + nm_setting_wireless_security_get_type; + nm_setting_wireless_security_get_wep_key; + nm_setting_wireless_security_get_wep_key_flags; + nm_setting_wireless_security_get_wep_key_type; + nm_setting_wireless_security_get_wep_tx_keyidx; + nm_setting_wireless_security_new; + nm_setting_wireless_security_remove_group; + nm_setting_wireless_security_remove_group_by_value; + nm_setting_wireless_security_remove_pairwise; + nm_setting_wireless_security_remove_pairwise_by_value; + nm_setting_wireless_security_remove_proto; + nm_setting_wireless_security_remove_proto_by_value; + nm_setting_wireless_security_set_wep_key; + nm_settings_error_get_type; + nm_settings_error_quark; + nm_simple_connection_get_type; + nm_simple_connection_new; + nm_simple_connection_new_clone; + nm_simple_connection_new_from_dbus; + nm_state_get_type; + nm_utils_ap_mode_security_valid; + nm_utils_bin2hexstr; + nm_utils_check_virtual_device_compatibility; + nm_utils_escape_ssid; + nm_utils_file_is_certificate; + nm_utils_file_is_pkcs12; + nm_utils_file_is_private_key; + nm_utils_file_search_in_paths; + nm_utils_hexstr2bin; + nm_utils_hwaddr_atoba; + nm_utils_hwaddr_aton; + nm_utils_hwaddr_canonical; + nm_utils_hwaddr_len; + nm_utils_hwaddr_matches; + nm_utils_hwaddr_ntoa; + nm_utils_hwaddr_valid; + nm_utils_iface_valid_name; + nm_utils_inet4_ntop; + nm_utils_inet6_ntop; + nm_utils_ip4_addresses_from_variant; + nm_utils_ip4_addresses_to_variant; + nm_utils_ip4_dns_from_variant; + nm_utils_ip4_dns_to_variant; + nm_utils_ip4_get_default_prefix; + nm_utils_ip4_netmask_to_prefix; + nm_utils_ip4_prefix_to_netmask; + nm_utils_ip4_routes_from_variant; + nm_utils_ip4_routes_to_variant; + nm_utils_ip6_addresses_from_variant; + nm_utils_ip6_addresses_to_variant; + nm_utils_ip6_dns_from_variant; + nm_utils_ip6_dns_to_variant; + nm_utils_ip6_routes_from_variant; + nm_utils_ip6_routes_to_variant; + nm_utils_ipaddr_valid; + nm_utils_is_empty_ssid; + nm_utils_is_uuid; + nm_utils_same_ssid; + nm_utils_security_type_get_type; + nm_utils_security_valid; + nm_utils_ssid_to_utf8; + nm_utils_uuid_generate; + nm_utils_wep_key_valid; + nm_utils_wifi_channel_to_freq; + nm_utils_wifi_find_next_channel; + nm_utils_wifi_freq_to_channel; + nm_utils_wifi_is_channel_valid; + nm_utils_wifi_strength_bars; + nm_utils_wpa_psk_valid; + nm_vlan_flags_get_type; + nm_vlan_priority_map_get_type; + nm_vpn_connection_get_banner; + nm_vpn_connection_get_type; + nm_vpn_connection_get_vpn_state; + nm_vpn_connection_state_get_type; + nm_vpn_connection_state_reason_get_type; + nm_vpn_editor_get_type; + nm_vpn_editor_get_widget; + nm_vpn_editor_plugin_capability_get_type; + nm_vpn_editor_plugin_export; + nm_vpn_editor_plugin_get_capabilities; + nm_vpn_editor_plugin_get_editor; + nm_vpn_editor_plugin_get_suggested_filename; + nm_vpn_editor_plugin_get_type; + nm_vpn_editor_plugin_import; + nm_vpn_editor_update_connection; + nm_vpn_plugin_error_get_type; + nm_vpn_plugin_error_quark; + nm_vpn_plugin_failure_get_type; + nm_vpn_plugin_old_disconnect; + nm_vpn_plugin_old_failure; + nm_vpn_plugin_old_get_connection; + nm_vpn_plugin_old_get_secret_flags; + nm_vpn_plugin_old_get_state; + nm_vpn_plugin_old_get_type; + nm_vpn_plugin_old_read_vpn_details; + nm_vpn_plugin_old_secrets_required; + nm_vpn_plugin_old_set_ip4_config; + nm_vpn_plugin_old_set_login_banner; + nm_vpn_plugin_old_set_state; + nm_vpn_service_state_get_type; + nm_wep_key_type_get_type; + nm_wimax_nsp_connection_valid; + nm_wimax_nsp_filter_connections; + nm_wimax_nsp_get_name; + nm_wimax_nsp_get_network_type; + nm_wimax_nsp_get_signal_quality; + nm_wimax_nsp_get_type; + nm_wimax_nsp_network_type_get_type; +local: + *; +}; + +libnm_1_0_4 { + #nm_setting_connection_autoconnect_slaves_get_type@libnm_1_0_4; + #nm_setting_connection_get_autoconnect_slaves@libnm_1_0_4; +} libnm_1_0_0; + +libnm_1_0_6 { + #nm_access_point_get_last_seen@libnm_1_0_6; + #nm_device_get_metered@libnm_1_0_6; + #nm_device_wifi_request_scan_options@libnm_1_0_6; + #nm_device_wifi_request_scan_options_async@libnm_1_0_6; + #nm_metered_get_type@libnm_1_0_6; + #nm_setting_connection_get_metered@libnm_1_0_6; + #nm_setting_wired_get_wake_on_lan@libnm_1_0_6; + #nm_setting_wired_get_wake_on_lan_password@libnm_1_0_6; + #nm_setting_wired_wake_on_lan_get_type@libnm_1_0_6; + #nm_utils_enum_from_str@libnm_1_0_6; + #nm_utils_enum_to_str@libnm_1_0_6; + #nm_utils_wifi_2ghz_freqs@libnm_1_0_6; + #nm_utils_wifi_5ghz_freqs@libnm_1_0_6; +} libnm_1_0_4; + +libnm_1_2_0 { +global: + nm_access_point_get_last_seen; + nm_client_get_all_devices; + nm_connection_get_setting_ip_tunnel; + nm_connection_get_setting_macvlan; + nm_connection_get_setting_vxlan; + nm_connection_verify_secrets; + nm_device_ethernet_get_s390_subchannels; + nm_device_get_applied_connection; + nm_device_get_applied_connection_async; + nm_device_get_applied_connection_finish; + nm_device_get_lldp_neighbors; + nm_device_get_metered; + nm_device_get_nm_plugin_missing; + nm_device_ip_tunnel_get_encapsulation_limit; + nm_device_ip_tunnel_get_flow_label; + nm_device_ip_tunnel_get_input_key; + nm_device_ip_tunnel_get_local; + nm_device_ip_tunnel_get_mode; + nm_device_ip_tunnel_get_output_key; + nm_device_ip_tunnel_get_parent; + nm_device_ip_tunnel_get_path_mtu_discovery; + nm_device_ip_tunnel_get_remote; + nm_device_ip_tunnel_get_tos; + nm_device_ip_tunnel_get_ttl; + nm_device_ip_tunnel_get_type; + nm_device_is_real; + nm_device_macvlan_get_hw_address; + nm_device_macvlan_get_mode; + nm_device_macvlan_get_no_promisc; + nm_device_macvlan_get_parent; + nm_device_macvlan_get_tap; + nm_device_macvlan_get_type; + nm_device_reapply; + nm_device_reapply_async; + nm_device_reapply_finish; + nm_device_set_managed; + nm_device_tun_get_group; + nm_device_tun_get_hw_address; + nm_device_tun_get_mode; + nm_device_tun_get_multi_queue; + nm_device_tun_get_owner; + nm_device_tun_get_no_pi; + nm_device_tun_get_type; + nm_device_tun_get_vnet_hdr; + nm_device_vxlan_get_ageing; + nm_device_vxlan_get_dst_port; + nm_device_vxlan_get_group; + nm_device_vxlan_get_hw_address; + nm_device_vxlan_get_id; + nm_device_vxlan_get_l2miss; + nm_device_vxlan_get_l3miss; + nm_device_vxlan_get_learning; + nm_device_vxlan_get_limit; + nm_device_vxlan_get_local; + nm_device_vxlan_get_parent; + nm_device_vxlan_get_proxy; + nm_device_vxlan_get_src_port_max; + nm_device_vxlan_get_src_port_min; + nm_device_vxlan_get_tos; + nm_device_vxlan_get_ttl; + nm_device_vxlan_get_type; + nm_device_wifi_request_scan_options; + nm_device_wifi_request_scan_options_async; + nm_ip_tunnel_mode_get_type; + nm_lldp_neighbor_get_attr_names; + nm_lldp_neighbor_get_attr_string_value; + nm_lldp_neighbor_get_attr_type; + nm_lldp_neighbor_get_attr_uint_value; + nm_lldp_neighbor_get_type; + nm_lldp_neighbor_new; + nm_lldp_neighbor_ref; + nm_lldp_neighbor_unref; + nm_metered_get_type; + nm_setting_802_1x_check_cert_scheme; + nm_setting_802_1x_get_domain_suffix_match; + nm_setting_802_1x_get_phase2_domain_suffix_match; + nm_setting_bridge_get_multicast_snooping; + nm_setting_connection_autoconnect_slaves_get_type; + nm_setting_connection_get_autoconnect_slaves; + nm_setting_connection_get_lldp; + nm_setting_connection_get_metered; + nm_setting_connection_lldp_get_type; + nm_setting_gsm_get_device_id; + nm_setting_gsm_get_sim_id; + nm_setting_gsm_get_sim_operator_id; + nm_setting_ip4_config_get_dhcp_fqdn; + nm_setting_ip6_config_addr_gen_mode_get_type; + nm_setting_ip6_config_get_addr_gen_mode; + nm_setting_ip_config_add_dns_option; + nm_setting_ip_config_clear_dns_options; + nm_setting_ip_config_get_dad_timeout; + nm_setting_ip_config_get_dhcp_timeout; + nm_setting_ip_config_get_dns_option; + nm_setting_ip_config_get_num_dns_options; + nm_setting_ip_config_has_dns_options; + nm_setting_ip_config_remove_dns_option; + nm_setting_ip_config_remove_dns_option_by_value; + nm_setting_ip_tunnel_get_input_key; + nm_setting_ip_tunnel_get_local; + nm_setting_ip_tunnel_get_mode; + nm_setting_ip_tunnel_get_mtu; + nm_setting_ip_tunnel_get_output_key; + nm_setting_ip_tunnel_get_parent; + nm_setting_ip_tunnel_get_path_mtu_discovery; + nm_setting_ip_tunnel_get_remote; + nm_setting_ip_tunnel_get_tos; + nm_setting_ip_tunnel_get_type; + nm_setting_ip_tunnel_get_ttl; + nm_setting_ip_tunnel_new; + nm_setting_mac_randomization_get_type; + nm_setting_tun_get_group; + nm_setting_tun_get_mode; + nm_setting_tun_get_multi_queue; + nm_setting_tun_get_owner; + nm_setting_tun_get_pi; + nm_setting_tun_get_type; + nm_setting_tun_get_vnet_hdr; + nm_setting_tun_mode_get_type; + nm_setting_tun_new; + nm_setting_verify_secrets; + nm_setting_vpn_get_timeout; + nm_setting_macvlan_get_mode; + nm_setting_macvlan_get_parent; + nm_setting_macvlan_get_promiscuous; + nm_setting_macvlan_get_tap; + nm_setting_macvlan_get_type; + nm_setting_macvlan_mode_get_type; + nm_setting_macvlan_new; + nm_setting_vxlan_get_ageing; + nm_setting_vxlan_get_destination_port; + nm_setting_vxlan_get_id; + nm_setting_vxlan_get_l2_miss; + nm_setting_vxlan_get_l3_miss; + nm_setting_vxlan_get_learning; + nm_setting_vxlan_get_limit; + nm_setting_vxlan_get_local; + nm_setting_vxlan_get_parent; + nm_setting_vxlan_get_proxy; + nm_setting_vxlan_get_remote; + nm_setting_vxlan_get_rsc; + nm_setting_vxlan_get_source_port_max; + nm_setting_vxlan_get_source_port_min; + nm_setting_vxlan_get_tos; + nm_setting_vxlan_get_ttl; + nm_setting_vxlan_get_type; + nm_setting_vxlan_new; + nm_setting_wired_get_wake_on_lan; + nm_setting_wired_get_wake_on_lan_password; + nm_setting_wired_wake_on_lan_get_type; + nm_setting_wireless_get_powersave; + nm_setting_wireless_get_mac_address_randomization; + nm_setting_wireless_powersave_get_type; + nm_utils_bond_mode_int_to_string; + nm_utils_bond_mode_string_to_int; + nm_utils_enum_from_str; + nm_utils_enum_to_str; + nm_utils_enum_get_values; + nm_utils_wifi_2ghz_freqs; + nm_utils_wifi_5ghz_freqs; + nm_vpn_editor_plugin_load_from_file; + nm_vpn_plugin_info_get_filename; + nm_vpn_plugin_info_get_editor_plugin; + nm_vpn_plugin_info_get_name; + nm_vpn_plugin_info_get_plugin; + nm_vpn_plugin_info_get_program; + nm_vpn_plugin_info_get_type; + nm_vpn_plugin_info_load_editor_plugin; + nm_vpn_plugin_info_lookup_property; + nm_vpn_plugin_info_new_from_file; + nm_vpn_plugin_info_new_with_data; + nm_vpn_plugin_info_set_editor_plugin; + nm_vpn_plugin_info_validate_filename; + nm_vpn_plugin_info_list_add; + nm_vpn_plugin_info_list_find_by_filename; + nm_vpn_plugin_info_list_find_by_name; + nm_vpn_plugin_info_list_find_by_service; + nm_vpn_plugin_info_list_load; + nm_vpn_plugin_info_list_remove; + nm_vpn_service_plugin_disconnect; + nm_vpn_service_plugin_failure; + nm_vpn_service_plugin_get_connection; + nm_vpn_service_plugin_get_secret_flags; + nm_vpn_service_plugin_get_type; + nm_vpn_service_plugin_read_vpn_details; + nm_vpn_service_plugin_secrets_required; + nm_vpn_service_plugin_set_config; + nm_vpn_service_plugin_set_ip4_config; + nm_vpn_service_plugin_set_ip6_config; + nm_vpn_service_plugin_set_login_banner; +} libnm_1_0_0; + +libnm_1_2_4 { + #nm_setting_ip_config_get_dns_priority@libnm_1_2_4; +} libnm_1_2_0; + +libnm_1_4_0 { +global: + nm_device_team_get_config; + nm_setting_connection_get_stable_id; + nm_setting_ip6_config_get_token; + nm_setting_ip_config_get_dns_priority; + nm_setting_wired_get_generate_mac_address_mask; + nm_setting_wireless_get_generate_mac_address_mask; + nm_vpn_editor_plugin_get_plugin_info; + nm_vpn_editor_plugin_get_vt; + nm_vpn_editor_plugin_load; + nm_vpn_editor_plugin_set_plugin_info; + nm_vpn_plugin_info_get_aliases; + nm_vpn_plugin_info_get_auth_dialog; + nm_vpn_plugin_info_get_service; + nm_vpn_plugin_info_list_get_service_types; + nm_vpn_plugin_info_list_find_service_type; + nm_vpn_plugin_info_new_search_file; + nm_vpn_plugin_info_supports_hints; +} libnm_1_2_0; + +libnm_1_6_0 { +global: + nm_capability_get_type; + nm_client_get_dns_configuration; + nm_client_get_dns_mode; + nm_client_get_dns_rc_manager; + nm_connection_get_setting_macsec; + nm_connection_get_setting_proxy; + nm_device_macsec_get_cipher_suite; + nm_device_macsec_get_encoding_sa; + nm_device_macsec_get_encrypt; + nm_device_macsec_get_es; + nm_device_macsec_get_hw_address; + nm_device_macsec_get_icv_length; + nm_device_macsec_get_include_sci; + nm_device_macsec_get_protect; + nm_device_macsec_get_replay_protect; + nm_device_macsec_get_scb; + nm_device_macsec_get_sci; + nm_device_macsec_get_type; + nm_device_macsec_get_validation; + nm_device_macsec_get_window; + nm_dns_entry_get_domains; + nm_dns_entry_get_interface; + nm_dns_entry_get_nameservers; + nm_dns_entry_get_priority; + nm_dns_entry_get_type; + nm_dns_entry_get_vpn; + nm_dns_entry_unref; + nm_setting_connection_get_autoconnect_retries; + nm_setting_macsec_get_encrypt; + nm_setting_macsec_get_mka_cak; + nm_setting_macsec_get_mka_cak_flags; + nm_setting_macsec_get_mka_ckn; + nm_setting_macsec_get_mode; + nm_setting_macsec_get_parent; + nm_setting_macsec_get_port; + nm_setting_macsec_get_type; + nm_setting_macsec_get_validation; + nm_setting_macsec_mode_get_type; + nm_setting_macsec_new; + nm_setting_macsec_validation_get_type; + nm_setting_proxy_get_type; + nm_setting_proxy_new; + nm_setting_proxy_get_method; + nm_setting_proxy_method_get_type; + nm_setting_proxy_get_browser_only; + nm_setting_proxy_get_pac_url; + nm_setting_proxy_get_pac_script; + nm_setting_802_1x_get_ca_cert_uri; + nm_setting_802_1x_get_phase2_ca_cert_uri; + nm_setting_802_1x_get_client_cert_uri; + nm_setting_802_1x_get_phase2_client_cert_uri; + nm_setting_802_1x_get_private_key_uri; + nm_setting_802_1x_get_phase2_private_key_uri; + nm_utils_is_json_object; + nm_utils_version; + nm_utils_is_valid_iface_name; +} libnm_1_4_0; + +libnm_1_8_0 { +global: + nm_active_connection_state_reason_get_type; + nm_active_connection_get_state_reason; + nm_connection_get_setting_dummy; + nm_device_dummy_get_type; + nm_ip_route_get_variant_attribute_spec; + nm_ip_route_attribute_validate; + nm_setting_802_1x_auth_flags_get_type; + nm_setting_802_1x_get_auth_timeout; + nm_setting_802_1x_get_ca_cert_password; + nm_setting_802_1x_get_ca_cert_password_flags; + nm_setting_802_1x_get_client_cert_password; + nm_setting_802_1x_get_client_cert_password_flags; + nm_setting_802_1x_get_phase1_auth_flags; + nm_setting_802_1x_get_phase2_ca_cert_password; + nm_setting_802_1x_get_phase2_ca_cert_password_flags; + nm_setting_802_1x_get_phase2_client_cert_password; + nm_setting_802_1x_get_phase2_client_cert_password_flags; + nm_setting_cdma_get_mtu; + nm_setting_dummy_get_type; + nm_setting_dummy_new; + nm_setting_gsm_get_mtu; + nm_setting_user_check_key; + nm_setting_user_check_val; + nm_setting_user_get_data; + nm_setting_user_get_keys; + nm_setting_user_get_type; + nm_setting_user_new; + nm_setting_user_set_data; + nm_utils_format_variant_attributes; + nm_utils_parse_variant_attributes; +} libnm_1_6_0; + +libnm_1_10_0 { +global: + nm_activation_state_flags_get_type; + nm_active_connection_get_state_flags; + nm_client_connectivity_check_get_available; + nm_client_connectivity_check_get_enabled; + nm_client_connectivity_check_set_enabled; + nm_connection_get_settings; + nm_device_dummy_get_hw_address; + nm_device_ovs_bridge_get_type; + nm_device_ovs_interface_get_type; + nm_device_ovs_port_get_type; + nm_device_ppp_get_type; + nm_ip_route_equal_full; + nm_setting_bridge_get_group_forward_mask; + nm_setting_connection_get_auth_retries; + nm_setting_ip_config_get_route_table; + nm_setting_ovs_bridge_get_fail_mode; + nm_setting_ovs_bridge_get_mcast_snooping_enable; + nm_setting_ovs_bridge_get_rstp_enable; + nm_setting_ovs_bridge_get_stp_enable; + nm_setting_ovs_bridge_get_type; + nm_setting_ovs_bridge_new; + nm_setting_ovs_interface_get_interface_type; + nm_setting_ovs_interface_get_type; + nm_setting_ovs_interface_new; + nm_setting_ovs_patch_get_peer; + nm_setting_ovs_patch_get_type; + nm_setting_ovs_patch_new; + nm_setting_ovs_port_get_bond_downdelay; + nm_setting_ovs_port_get_bond_mode; + nm_setting_ovs_port_get_bond_updelay; + nm_setting_ovs_port_get_lacp; + nm_setting_ovs_port_get_tag; + nm_setting_ovs_port_get_type; + nm_setting_ovs_port_get_vlan_mode; + nm_setting_ovs_port_new; + nm_setting_pppoe_get_parent; + nm_setting_wireless_security_get_pmf; + nm_setting_wireless_security_get_wps_method; + nm_setting_wireless_security_pmf_get_type; + nm_setting_wireless_security_wps_method_get_type; +} libnm_1_8_0; + +libnm_1_10_2 { +global: + nm_remote_connection_update2; + nm_remote_connection_update2_finish; + nm_settings_update2_flags_get_type; + nm_setting_tc_config_add_qdisc; + nm_setting_tc_config_add_tfilter; + nm_setting_tc_config_clear_qdiscs; + nm_setting_tc_config_clear_tfilters; + nm_setting_tc_config_get_num_qdiscs; + nm_setting_tc_config_get_num_tfilters; + nm_setting_tc_config_get_qdisc; + nm_setting_tc_config_get_tfilter; + nm_setting_tc_config_get_type; + nm_setting_tc_config_new; + nm_setting_tc_config_remove_qdisc; + nm_setting_tc_config_remove_qdisc_by_value; + nm_setting_tc_config_remove_tfilter; + nm_setting_tc_config_remove_tfilter_by_value; + nm_setting_team_add_link_watcher; + nm_setting_team_add_runner_tx_hash; + nm_setting_team_clear_link_watchers; + nm_setting_team_get_link_watcher; + nm_setting_team_get_mcast_rejoin_count; + nm_setting_team_get_mcast_rejoin_interval; + nm_setting_team_get_notify_peers_count; + nm_setting_team_get_notify_peers_interval; + nm_setting_team_get_num_link_watchers; + nm_setting_team_get_num_runner_tx_hash; + nm_setting_team_get_runner; + nm_setting_team_get_runner_active; + nm_setting_team_get_runner_agg_select_policy; + nm_setting_team_get_runner_fast_rate; + nm_setting_team_get_runner_hwaddr_policy; + nm_setting_team_get_runner_min_ports; + nm_setting_team_get_runner_sys_prio; + nm_setting_team_get_runner_tx_balancer; + nm_setting_team_get_runner_tx_balancer_interval; + nm_setting_team_get_runner_tx_hash; + nm_setting_team_port_add_link_watcher; + nm_setting_team_port_clear_link_watchers; + nm_setting_team_port_get_lacp_key; + nm_setting_team_port_get_lacp_prio; + nm_setting_team_port_get_link_watcher; + nm_setting_team_port_get_num_link_watchers; + nm_setting_team_port_get_prio; + nm_setting_team_port_get_queue_id; + nm_setting_team_port_get_sticky; + nm_setting_team_port_remove_link_watcher; + nm_setting_team_port_remove_link_watcher_by_value; + nm_setting_team_remove_link_watcher; + nm_setting_team_remove_link_watcher_by_value; + nm_setting_team_remove_runner_tx_hash; + nm_setting_team_remove_runner_tx_hash_by_value; + nm_tc_action_dup; + nm_tc_action_equal; + nm_tc_action_get_attribute; + nm_tc_action_get_attribute_names; + nm_tc_action_get_kind; + nm_tc_action_get_type; + nm_tc_action_new; + nm_tc_action_ref; + nm_tc_action_set_attribute; + nm_tc_action_unref; + nm_tc_qdisc_dup; + nm_tc_qdisc_equal; + nm_tc_qdisc_get_handle; + nm_tc_qdisc_get_kind; + nm_tc_qdisc_get_parent; + nm_tc_qdisc_get_type; + nm_tc_qdisc_new; + nm_tc_qdisc_ref; + nm_tc_qdisc_set_handle; + nm_tc_qdisc_unref; + nm_tc_tfilter_dup; + nm_tc_tfilter_equal; + nm_tc_tfilter_get_handle; + nm_tc_tfilter_get_kind; + nm_tc_tfilter_get_parent; + nm_tc_tfilter_get_type; + nm_tc_tfilter_new; + nm_tc_tfilter_ref; + nm_tc_tfilter_set_handle; + nm_tc_tfilter_unref; + nm_team_link_watcher_arp_ping_flags_get_type; + nm_team_link_watcher_dup; + nm_team_link_watcher_equal; + nm_team_link_watcher_get_delay_down; + nm_team_link_watcher_get_delay_up; + nm_team_link_watcher_get_flags; + nm_team_link_watcher_get_init_wait; + nm_team_link_watcher_get_interval; + nm_team_link_watcher_get_missed_max; + nm_team_link_watcher_get_name; + nm_team_link_watcher_get_source_host; + nm_team_link_watcher_get_target_host; + nm_team_link_watcher_get_type; + nm_team_link_watcher_new_arp_ping; + nm_team_link_watcher_new_ethtool; + nm_team_link_watcher_new_nsna_ping; + nm_team_link_watcher_ref; + nm_team_link_watcher_unref; + nm_utils_tc_action_from_str; + nm_utils_tc_action_to_str; + nm_utils_tc_qdisc_from_str; + nm_utils_tc_qdisc_to_str; + nm_utils_tc_tfilter_from_str; + nm_utils_tc_tfilter_to_str; +} libnm_1_10_0; + +libnm_1_10_14 { + #nm_setting_connection_get_mdns@libnm_1_10_14; + #nm_setting_connection_mdns_get_type@libnm_1_10_14; +} libnm_1_10_2; + +libnm_1_12_0 { +global: + nm_checkpoint_create_flags_get_type; + nm_checkpoint_get_created; + nm_checkpoint_get_devices; + nm_checkpoint_get_rollback_timeout; + nm_checkpoint_get_type; + nm_client_checkpoint_adjust_rollback_timeout; + nm_client_checkpoint_adjust_rollback_timeout_finish; + nm_client_checkpoint_create; + nm_client_checkpoint_create_finish; + nm_client_checkpoint_destroy; + nm_client_checkpoint_destroy_finish; + nm_client_checkpoint_rollback; + nm_client_checkpoint_rollback_finish; + nm_client_get_checkpoints; + nm_connection_get_setting_tc_config; + nm_device_ip_tunnel_get_flags; + nm_device_wifi_get_last_scan; + nm_ip_tunnel_flags_get_type; + nm_remote_connection_get_filename; + nm_remote_connection_get_flags; + nm_setting_connection_get_mdns; + nm_setting_connection_mdns_get_type; + nm_setting_ip_tunnel_get_flags; + nm_setting_ip6_config_get_dhcp_duid; + nm_setting_macsec_get_send_sci; + nm_setting_vpn_get_data_keys; + nm_setting_vpn_get_secret_keys; + nm_setting_wireless_security_get_fils; + nm_setting_wireless_security_fils_get_type; + nm_setting_wireless_get_wake_on_wlan; + nm_setting_wireless_wake_on_wlan_get_type; + nm_settings_connection_flags_get_type; + nm_utils_get_timestamp_msec; + nm_vpn_service_plugin_shutdown; +} libnm_1_10_0; + +libnm_1_12_2 { + nm_connection_get_setting_ovs_bridge; + nm_connection_get_setting_ovs_interface; + nm_connection_get_setting_ovs_patch; + nm_connection_get_setting_ovs_port; + nm_connection_get_setting_tun; + nm_device_ovs_bridge_get_slaves; + nm_device_ovs_port_get_slaves; +} libnm_1_12_0; + +libnm_1_14_0 { +global: + nm_connection_multi_connect_get_type; + nm_device_6lowpan_get_type; + nm_device_wireguard_get_fwmark; + nm_device_wireguard_get_listen_port; + nm_device_wireguard_get_public_key; + nm_device_wireguard_get_type; + nm_device_wpan_get_type; + nm_setting_6lowpan_get_type; + nm_setting_connection_get_llmnr; + nm_setting_connection_get_multi_connect; + nm_setting_connection_llmnr_get_type; + nm_setting_ethtool_clear_features; + nm_setting_ethtool_get_feature; + nm_setting_ethtool_get_type; + nm_setting_ethtool_new; + nm_setting_ethtool_set_feature; + nm_setting_match_add_interface_name; + nm_setting_match_clear_interface_names; + nm_setting_match_get_interface_name; + nm_setting_match_get_interface_names; + nm_setting_match_get_num_interface_names; + nm_setting_match_get_type; + nm_setting_match_remove_interface_name; + nm_setting_match_remove_interface_name_by_value; + nm_setting_sriov_add_vf; + nm_setting_sriov_clear_vfs; + nm_setting_sriov_get_autoprobe_drivers; + nm_setting_sriov_get_num_vfs; + nm_setting_sriov_get_total_vfs; + nm_setting_sriov_get_type; + nm_setting_sriov_get_vf; + nm_setting_sriov_new; + nm_setting_sriov_remove_vf; + nm_setting_sriov_remove_vf_by_index; + nm_setting_wpan_get_type; + nm_sriov_vf_add_vlan; + nm_sriov_vf_dup; + nm_sriov_vf_equal; + nm_sriov_vf_get_attribute; + nm_sriov_vf_get_attribute_names; + nm_sriov_vf_get_index; + nm_sriov_vf_get_type; + nm_sriov_vf_get_vlan_ids; + nm_sriov_vf_get_vlan_protocol; + nm_sriov_vf_get_vlan_qos; + nm_sriov_vf_new; + nm_sriov_vf_ref; + nm_sriov_vf_remove_vlan; + nm_sriov_vf_set_attribute; + nm_sriov_vf_set_vlan_protocol; + nm_sriov_vf_set_vlan_qos; + nm_sriov_vf_unref; + nm_sriov_vf_vlan_protocol_get_type; + nm_ternary_get_type; + nm_utils_sriov_vf_from_str; + nm_utils_sriov_vf_to_str; +} libnm_1_12_0; + +libnm_1_14_8 { + nm_ethtool_optname_is_feature; +} libnm_1_14_0; + +libnm_1_16_0 { +global: + nm_client_add_and_activate_connection2; + nm_client_add_and_activate_connection2_finish; + nm_device_get_connectivity; + nm_device_wifi_p2p_get_hw_address; + nm_device_wifi_p2p_get_peers; + nm_device_wifi_p2p_get_type; + nm_device_wifi_p2p_start_find; + nm_device_wifi_p2p_start_find_finish; + nm_device_wifi_p2p_stop_find; + nm_device_wifi_p2p_stop_find_finish; + nm_setting_wifi_p2p_get_peer; + nm_setting_wifi_p2p_get_type; + nm_setting_wifi_p2p_get_wfd_ies; + nm_setting_wifi_p2p_get_wps_method; + nm_setting_wifi_p2p_new; + nm_setting_wireguard_append_peer; + nm_setting_wireguard_clear_peers; + nm_setting_wireguard_get_fwmark; + nm_setting_wireguard_get_listen_port; + nm_setting_wireguard_get_mtu; + nm_setting_wireguard_get_peer; + nm_setting_wireguard_get_peer_by_public_key; + nm_setting_wireguard_get_peer_routes; + nm_setting_wireguard_get_peers_len; + nm_setting_wireguard_get_private_key; + nm_setting_wireguard_get_private_key_flags; + nm_setting_wireguard_get_type; + nm_setting_wireguard_new; + nm_setting_wireguard_remove_peer; + nm_setting_wireguard_set_peer; + nm_team_link_watcher_get_vlanid; + nm_team_link_watcher_new_arp_ping2; + nm_utils_base64secret_decode; + nm_wifi_p2p_peer_connection_valid; + nm_wifi_p2p_peer_filter_connections; + nm_wifi_p2p_peer_get_flags; + nm_wifi_p2p_peer_get_hw_address; + nm_wifi_p2p_peer_get_last_seen; + nm_wifi_p2p_peer_get_manufacturer; + nm_wifi_p2p_peer_get_model; + nm_wifi_p2p_peer_get_model_number; + nm_wifi_p2p_peer_get_name; + nm_wifi_p2p_peer_get_serial; + nm_wifi_p2p_peer_get_strength; + nm_wifi_p2p_peer_get_type; + nm_wifi_p2p_peer_get_wfd_ies; + nm_wireguard_peer_append_allowed_ip; + nm_wireguard_peer_clear_allowed_ips; + nm_wireguard_peer_cmp; + nm_wireguard_peer_get_allowed_ip; + nm_wireguard_peer_get_allowed_ips_len; + nm_wireguard_peer_get_endpoint; + nm_wireguard_peer_get_persistent_keepalive; + nm_wireguard_peer_get_preshared_key; + nm_wireguard_peer_get_preshared_key_flags; + nm_wireguard_peer_get_public_key; + nm_wireguard_peer_get_type; + nm_wireguard_peer_is_sealed; + nm_wireguard_peer_is_valid; + nm_wireguard_peer_new; + nm_wireguard_peer_new_clone; + nm_wireguard_peer_ref; + nm_wireguard_peer_remove_allowed_ip; + nm_wireguard_peer_seal; + nm_wireguard_peer_set_endpoint; + nm_wireguard_peer_set_persistent_keepalive; + nm_wireguard_peer_set_preshared_key; + nm_wireguard_peer_set_preshared_key_flags; + nm_wireguard_peer_set_public_key; + nm_wireguard_peer_unref; +} libnm_1_14_0; + +libnm_1_18_0 { +global: + nm_bridge_vlan_cmp; + nm_bridge_vlan_from_str; + nm_bridge_vlan_get_type; + nm_bridge_vlan_get_vid_range; + nm_bridge_vlan_is_pvid; + nm_bridge_vlan_is_sealed; + nm_bridge_vlan_is_untagged; + nm_bridge_vlan_new; + nm_bridge_vlan_new_clone; + nm_bridge_vlan_ref; + nm_bridge_vlan_seal; + nm_bridge_vlan_set_pvid; + nm_bridge_vlan_set_untagged; + nm_bridge_vlan_to_str; + nm_bridge_vlan_unref; + nm_ip_routing_rule_as_string_flags_get_type; + nm_ip_routing_rule_cmp; + nm_ip_routing_rule_from_string; + nm_ip_routing_rule_get_action; + nm_ip_routing_rule_get_addr_family; + nm_ip_routing_rule_get_destination_port_end; + nm_ip_routing_rule_get_destination_port_start; + nm_ip_routing_rule_get_from; + nm_ip_routing_rule_get_from_len; + nm_ip_routing_rule_get_fwmark; + nm_ip_routing_rule_get_fwmask; + nm_ip_routing_rule_get_iifname; + nm_ip_routing_rule_get_invert; + nm_ip_routing_rule_get_ipproto; + nm_ip_routing_rule_get_oifname; + nm_ip_routing_rule_get_priority; + nm_ip_routing_rule_get_source_port_end; + nm_ip_routing_rule_get_source_port_start; + nm_ip_routing_rule_get_table; + nm_ip_routing_rule_get_to; + nm_ip_routing_rule_get_to_len; + nm_ip_routing_rule_get_tos; + nm_ip_routing_rule_get_type; + nm_ip_routing_rule_is_sealed; + nm_ip_routing_rule_new; + nm_ip_routing_rule_new_clone; + nm_ip_routing_rule_ref; + nm_ip_routing_rule_seal; + nm_ip_routing_rule_set_action; + nm_ip_routing_rule_set_destination_port; + nm_ip_routing_rule_set_from; + nm_ip_routing_rule_set_fwmark; + nm_ip_routing_rule_set_iifname; + nm_ip_routing_rule_set_invert; + nm_ip_routing_rule_set_ipproto; + nm_ip_routing_rule_set_oifname; + nm_ip_routing_rule_set_priority; + nm_ip_routing_rule_set_source_port; + nm_ip_routing_rule_set_table; + nm_ip_routing_rule_set_to; + nm_ip_routing_rule_set_tos; + nm_ip_routing_rule_to_string; + nm_ip_routing_rule_unref; + nm_ip_routing_rule_validate; + nm_lldp_neighbor_get_attr_value; + nm_setting_bridge_add_vlan; + nm_setting_bridge_clear_vlans; + nm_setting_bridge_get_num_vlans; + nm_setting_bridge_get_vlan; + nm_setting_bridge_get_vlan_default_pvid; + nm_setting_bridge_get_vlan_filtering; + nm_setting_bridge_port_add_vlan; + nm_setting_bridge_port_clear_vlans; + nm_setting_bridge_port_get_num_vlans; + nm_setting_bridge_port_get_vlan; + nm_setting_bridge_port_remove_vlan; + nm_setting_bridge_port_remove_vlan_by_vid; + nm_setting_bridge_remove_vlan; + nm_setting_bridge_remove_vlan_by_vid; + nm_setting_ip_config_add_routing_rule; + nm_setting_ip_config_clear_routing_rules; + nm_setting_ip_config_get_num_routing_rules; + nm_setting_ip_config_get_routing_rule; + nm_setting_ip_config_remove_routing_rule; + nm_tc_qdisc_get_attribute; + nm_tc_qdisc_get_attribute_names; + nm_tc_qdisc_set_attribute; +} libnm_1_16_0; + +libnm_1_20_0 { +global: + nm_client_add_connection2; + nm_client_add_connection2_finish; + nm_client_connectivity_check_get_uri; + nm_device_modem_get_apn; + nm_device_modem_get_device_id; + nm_device_modem_get_operator_code; + nm_ip_routing_rule_get_suppress_prefixlength; + nm_ip_routing_rule_set_suppress_prefixlength; + nm_setting_connection_get_wait_device_timeout; + nm_setting_ethtool_get_optnames; + nm_setting_ovs_dpdk_get_devargs; + nm_setting_ovs_dpdk_get_type; + nm_setting_ovs_dpdk_new; + nm_setting_wireguard_get_ip4_auto_default_route; + nm_setting_wireguard_get_ip6_auto_default_route; + nm_settings_add_connection2_flags_get_type; +} libnm_1_18_0; + +libnm_1_20_6 { +global: + nm_setting_802_1x_get_optional; +} libnm_1_20_0; + +libnm_1_22_0 { +global: + nm_client_get_context_busy_watcher; + nm_client_get_dbus_connection; + nm_client_get_dbus_name_owner; + nm_client_get_main_context; + nm_client_get_metered; + nm_client_reload; + nm_client_reload_finish; + nm_device_get_interface_flags; + nm_device_interface_flags_get_type; + nm_dhcp_hostname_flags_get_type; + nm_ip_address_cmp_flags_get_type; + nm_ip_address_cmp_full; + nm_manager_reload_flags_get_type; + nm_setting_gsm_get_auto_config; + nm_setting_ip_config_get_dhcp_hostname_flags; +} libnm_1_20_0; + +libnm_1_22_2 { +global: + nm_client_get_capabilities; +} libnm_1_22_0; + +libnm_1_22_8 { +global: + nm_setting_ip6_config_get_ra_timeout; +} libnm_1_22_2; + +libnm_1_24_0 { +global: + nm_client_dbus_call; + nm_client_dbus_call_finish; + nm_client_dbus_set_property; + nm_client_dbus_set_property_finish; + nm_client_get_instance_flags; + nm_client_get_object_by_path; + nm_client_get_permissions_state; + nm_client_instance_flags_get_type; + nm_device_vrf_get_table; + nm_device_vrf_get_type; + nm_object_get_client; + nm_secret_agent_old_destroy; + nm_secret_agent_old_enable; + nm_secret_agent_old_get_context_busy_watcher; + nm_secret_agent_old_get_dbus_connection; + nm_secret_agent_old_get_dbus_name_owner; + nm_secret_agent_old_get_main_context; + nm_setting_802_1x_get_domain_match; + nm_setting_802_1x_get_phase2_domain_match; + nm_setting_bond_get_option_normalized; + nm_setting_bridge_get_group_address; + nm_setting_bridge_get_multicast_querier; + nm_setting_bridge_get_multicast_query_use_ifaddr; + nm_setting_bridge_get_multicast_router; + nm_setting_bridge_get_vlan_protocol; + nm_setting_bridge_get_vlan_stats_enabled; + nm_setting_vrf_get_table; + nm_setting_vrf_get_type; + nm_setting_vrf_new; +} libnm_1_22_8; + +libnm_1_26_0 { +global: + nm_device_get_path; + nm_ethtool_optname_is_coalesce; + nm_ethtool_optname_is_ring; + nm_setting_bridge_get_multicast_hash_max; + nm_setting_bridge_get_multicast_last_member_count; + nm_setting_bridge_get_multicast_last_member_interval; + nm_setting_bridge_get_multicast_membership_interval; + nm_setting_bridge_get_multicast_querier_interval; + nm_setting_bridge_get_multicast_query_interval; + nm_setting_bridge_get_multicast_query_response_interval; + nm_setting_bridge_get_multicast_startup_query_count; + nm_setting_bridge_get_multicast_startup_query_interval; + nm_setting_connection_get_mud_url; + nm_setting_match_add_driver; + nm_setting_match_add_kernel_command_line; + nm_setting_match_add_path; + nm_setting_match_clear_drivers; + nm_setting_match_clear_kernel_command_lines; + nm_setting_match_clear_paths; + nm_setting_match_get_driver; + nm_setting_match_get_drivers; + nm_setting_match_get_kernel_command_line; + nm_setting_match_get_kernel_command_lines; + nm_setting_match_get_num_drivers; + nm_setting_match_get_num_kernel_command_lines; + nm_setting_match_get_num_paths; + nm_setting_match_get_path; + nm_setting_match_get_paths; + nm_setting_match_remove_driver; + nm_setting_match_remove_driver_by_value; + nm_setting_match_remove_kernel_command_line; + nm_setting_match_remove_kernel_command_line_by_value; + nm_setting_match_remove_path; + nm_setting_match_remove_path_by_value; + nm_setting_option_clear_by_name; + nm_setting_option_get; + nm_setting_option_get_all_names; + nm_setting_option_get_boolean; + nm_setting_option_get_uint32; + nm_setting_option_set; + nm_setting_option_set_boolean; + nm_setting_option_set_uint32; +} libnm_1_24_0; + +libnm_1_26_4 { +global: + nm_setting_ip4_config_get_dhcp_vendor_class_identifier; +} libnm_1_26_0; + +libnm_1_28_0 { +global: + nm_setting_ip_config_add_dhcp_reject_server; + nm_setting_ip_config_clear_dhcp_reject_servers; + nm_setting_ip_config_get_dhcp_reject_servers; + nm_setting_ip_config_remove_dhcp_reject_server; + nm_setting_wireless_get_ap_isolation; +} libnm_1_26_4; + +libnm_1_30_0 { +global: + nm_device_veth_get_type; + nm_keyfile_handler_data_fail_with_error; + nm_keyfile_handler_data_get_context; + nm_keyfile_handler_data_warn_get; + nm_keyfile_handler_flags_get_type; + nm_keyfile_handler_type_get_type; + nm_keyfile_read; + nm_keyfile_warn_severity_get_type; + nm_keyfile_write; + nm_setting_hostname_get_from_dhcp; + nm_setting_hostname_get_from_dns_lookup; + nm_setting_hostname_get_only_from_default; + nm_setting_hostname_get_priority; + nm_setting_hostname_get_type; + nm_setting_ovs_external_ids_check_key; + nm_setting_ovs_external_ids_check_val; + nm_setting_ovs_external_ids_get_data; + nm_setting_ovs_external_ids_get_data_keys; + nm_setting_ovs_external_ids_get_type; + nm_setting_ovs_external_ids_new; + nm_setting_ovs_external_ids_set_data; + nm_setting_veth_get_peer; + nm_setting_veth_get_type; + nm_setting_veth_new; + nm_utils_print; +} libnm_1_28_0; + +libnm_1_32_0 { +global: + nm_ethtool_optname_is_pause; + nm_ip_address_dup; + nm_ip_route_dup; + nm_setting_match_new; + nm_setting_wired_get_accept_all_mac_addresses; +} libnm_1_30_0; diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build new file mode 100644 index 00000000..21a01e0b --- /dev/null +++ b/src/libnm-client-impl/meson.build @@ -0,0 +1,231 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +libnm_client_impl_sources = files( + 'nm-access-point.c', + 'nm-active-connection.c', + 'nm-checkpoint.c', + 'nm-client.c', + 'nm-dbus-helpers.c', + 'nm-device-6lowpan.c', + 'nm-device-adsl.c', + 'nm-device-bond.c', + 'nm-device-bridge.c', + 'nm-device-bt.c', + 'nm-device-dummy.c', + 'nm-device-ethernet.c', + 'nm-device-generic.c', + 'nm-device-infiniband.c', + 'nm-device-ip-tunnel.c', + 'nm-device-macsec.c', + 'nm-device-macvlan.c', + 'nm-device-modem.c', + 'nm-device-olpc-mesh.c', + 'nm-device-ovs-bridge.c', + 'nm-device-ovs-interface.c', + 'nm-device-ovs-port.c', + 'nm-device-ppp.c', + 'nm-device-team.c', + 'nm-device-tun.c', + 'nm-device-veth.c', + 'nm-device-vlan.c', + 'nm-device-vrf.c', + 'nm-device-vxlan.c', + 'nm-device-wifi-p2p.c', + 'nm-device-wifi.c', + 'nm-device-wimax.c', + 'nm-device-wireguard.c', + 'nm-device-wpan.c', + 'nm-device.c', + 'nm-dhcp-config.c', + 'nm-dhcp4-config.c', + 'nm-dhcp6-config.c', + 'nm-dns-manager.c', + 'nm-ip-config.c', + 'nm-ip4-config.c', + 'nm-ip6-config.c', + 'nm-libnm-utils.c', + 'nm-object.c', + 'nm-remote-connection.c', + 'nm-secret-agent-old.c', + 'nm-vpn-connection.c', + 'nm-vpn-editor.c', + 'nm-vpn-plugin-old.c', + 'nm-vpn-service-plugin.c', + 'nm-wifi-p2p-peer.c', + 'nm-wimax-nsp.c', +) + +libnm_client_impl = static_library( + 'nm-client-impl', + sources: libnm_client_impl_sources + libnm_client_public_enum_sources, + dependencies: [ + libnmdbus_dep, + libnm_core_public_dep, + libnm_client_public_dep, + libudev_dep, + glib_dep, + ], +) + +linker_script = join_paths(meson.current_source_dir(), 'libnm.ver') + +libnm = shared_library( + 'nm', + version: libnm_version, + dependencies: [ + libnm_core_public_dep, + libnm_client_public_dep, + libudev_dep, + glib_dep, + ], + link_whole: [ + libnm_client_impl, + libnm_core_aux_intern, + libnm_core_impl, + libnm_crypto, + libnm_base, + libnm_udev_aux, + libnm_systemd_shared, + libnm_log_null, + libnm_glib_aux, + libnm_std_aux, + libnmdbus, + libc_siphash, + ], + link_args: '-Wl,--version-script,@0@'.format(linker_script), + link_depends: linker_script, + install: true, +) + +libnm_dep = declare_dependency( + dependencies: [ + libnm_core_public_dep, + libnm_client_public_dep, + glib_dep, + ], + link_with: libnm, +) + +pkg.generate( + libraries: libnm, + version: nm_version, + name: libnm_name, + description: 'Convenience library for clients of NetworkManager', + filebase: libnm_name, + subdirs: libnm_name, + requires: 'gio-2.0', + variables: [ + 'exec_prefix=${prefix}', + 'vpnservicedir=' + join_paths('${prefix}', 'lib', nm_name, 'VPN'), + ], +) + +if enable_tests + test( + 'check-local-exports-' + libnm_name, + check_exports, + args: [libnm.full_path(), linker_script], + ) +endif + +if enable_introspection + + libnm_gir = gnome.generate_gir( + libnm, + sources: libnm_core_settings_sources + libnm_core_impl_sources + libnm_core_public_enum_sources + libnm_core_headers + libnm_client_impl_sources + libnm_client_headers + libnm_client_public_enum_sources + [nm_version_macro_header], + includes: 'Gio-2.0', + include_directories: [ + libnm_core_public_inc, + libnm_client_public_inc, + src_inc, + top_inc, + ], + nsversion: nm_gir_version, + namespace: 'NM', + identifier_prefix: nm_id_prefix, + symbol_prefix: nm_id_prefix.to_lower(), + header: 'NetworkManager.h', + export_packages: libnm_name, + extra_args: [ + '-DNETWORKMANAGER_COMPILATION', + ], + install: true, + ) + + infos = [ 'dbus', 'nmcli', 'keyfile' ] + if enable_ifcfg_rh + infos += [ 'ifcfg-rh' ] + endif + foreach info: infos + t = custom_target( + 'nm-propery-infos-' + info + '.xml', + input: libnm_core_settings_sources, + output: 'nm-propery-infos-' + info + '.xml', + command: [ + python.path(), + join_paths(meson.source_root(), 'tools', 'generate-docs-nm-property-infos.py'), + info, + '@OUTPUT@', + '@INPUT@' + ], + ) + + # meson 0.47 doesn't support non-static keys for dicts + # nor extending dicts incrementally. Workaround. + if info == 'dbus' + nm_property_infos_xml_dbus = t + elif info == 'keyfile' + nm_property_infos_xml_keyfile = t + elif info == 'ifcfg-rh' + nm_property_infos_xml_ifcfg_rh = t + elif info == 'nmcli' + nm_property_infos_xml_nmcli = t + else + assert(false) + endif + endforeach + if enable_ifcfg_rh + nm_property_infos_xml = { + 'dbus': nm_property_infos_xml_dbus, + 'keyfile': nm_property_infos_xml_keyfile, + 'nmcli': nm_property_infos_xml_nmcli, + 'ifcfg-rh': nm_property_infos_xml_ifcfg_rh, + } + else + nm_property_infos_xml = { + 'dbus': nm_property_infos_xml_dbus, + 'keyfile': nm_property_infos_xml_keyfile, + 'nmcli': nm_property_infos_xml_nmcli, + } + endif + + gi_typelib_path = run_command('printenv', 'GI_TYPELIB_PATH').stdout() + if gi_typelib_path != '' + gi_typelib_path = ':' + gi_typelib_path + endif + gi_typelib_path = meson.current_build_dir() + gi_typelib_path + + ld_library_path = run_command('printenv', 'LD_LIBRARY_PATH').stdout() + if ld_library_path != '' + ld_library_path = ':' + ld_library_path + endif + ld_library_path = meson.current_build_dir() + ld_library_path + + nm_settings_docs_xml_gir = custom_target( + 'nm-settings-docs-gir.xml', + input: libnm_gir[0], + output: 'nm-settings-docs-gir.xml', + command: [ + 'env', + 'GI_TYPELIB_PATH=' + gi_typelib_path, + 'LD_LIBRARY_PATH=' + ld_library_path, + python.path(), + join_paths(meson.source_root(), 'tools', 'generate-docs-nm-settings-docs-gir.py'), + '--lib-path', meson.current_build_dir(), + '--gir', '@INPUT@', + '--output', '@OUTPUT@' + ], + depends: libnm_gir, + ) + +endif diff --git a/src/libnm-client-impl/nm-access-point.c b/src/libnm-client-impl/nm-access-point.c new file mode 100644 index 00000000..3d12aa45 --- /dev/null +++ b/src/libnm-client-impl/nm-access-point.c @@ -0,0 +1,654 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-access-point.h" + +#include + +#include "nm-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-utils.h" + +#include "nm-dbus-interface.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMAccessPoint, + PROP_FLAGS, + PROP_WPA_FLAGS, + PROP_RSN_FLAGS, + PROP_SSID, + PROP_FREQUENCY, + PROP_HW_ADDRESS, + PROP_MODE, + PROP_MAX_BITRATE, + PROP_STRENGTH, + PROP_BSSID, + PROP_LAST_SEEN, ); + +typedef struct { + GBytes *ssid; + char * bssid; + guint32 flags; + guint32 wpa_flags; + guint32 rsn_flags; + guint32 frequency; + guint32 mode; + guint32 max_bitrate; + gint32 last_seen; + guint8 strength; +} NMAccessPointPrivate; + +struct _NMAccessPoint { + NMObject parent; + NMAccessPointPrivate _priv; +}; + +struct _NMAccessPointClass { + NMObjectClass parent; +}; + +G_DEFINE_TYPE(NMAccessPoint, nm_access_point, NM_TYPE_OBJECT) + +#define NM_ACCESS_POINT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMAccessPoint, NM_IS_ACCESS_POINT, NMObject) + +/*****************************************************************************/ + +/** + * nm_access_point_get_flags: + * @ap: a #NMAccessPoint + * + * Gets the flags of the access point. + * + * Returns: the flags + **/ +NM80211ApFlags +nm_access_point_get_flags(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NM_802_11_AP_FLAGS_NONE); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->flags; +} + +/** + * nm_access_point_get_wpa_flags: + * @ap: a #NMAccessPoint + * + * Gets the WPA (version 1) flags of the access point. + * + * Returns: the WPA flags + **/ +NM80211ApSecurityFlags +nm_access_point_get_wpa_flags(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NM_802_11_AP_SEC_NONE); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->wpa_flags; +} + +/** + * nm_access_point_get_rsn_flags: + * @ap: a #NMAccessPoint + * + * Gets the RSN (Robust Secure Network, ie WPA version 2) flags of the access + * point. + * + * Returns: the RSN flags + **/ +NM80211ApSecurityFlags +nm_access_point_get_rsn_flags(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NM_802_11_AP_SEC_NONE); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->rsn_flags; +} + +/** + * nm_access_point_get_ssid: + * @ap: a #NMAccessPoint + * + * Gets the SSID of the access point. + * + * Returns: (transfer none): the #GBytes containing the SSID, or %NULL if the + * SSID is unknown. + **/ +GBytes * +nm_access_point_get_ssid(NMAccessPoint *ap) +{ + NMAccessPointPrivate *priv; + + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NULL); + + priv = NM_ACCESS_POINT_GET_PRIVATE(ap); + nm_assert(!priv->ssid || g_bytes_get_size(priv->ssid) > 0); + return priv->ssid; +} + +/** + * nm_access_point_get_frequency: + * @ap: a #NMAccessPoint + * + * Gets the frequency of the access point in MHz. + * + * Returns: the frequency in MHz + **/ +guint32 +nm_access_point_get_frequency(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->frequency; +} + +/** + * nm_access_point_get_bssid: + * @ap: a #NMAccessPoint + * + * Gets the Basic Service Set ID (BSSID) of the Wi-Fi access point. + * + * Returns: the BSSID of the access point. This is an internal string and must + * not be modified or freed. + **/ +const char * +nm_access_point_get_bssid(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NULL); + + return _nml_coerce_property_str_not_empty(NM_ACCESS_POINT_GET_PRIVATE(ap)->bssid); +} + +/** + * nm_access_point_get_mode: + * @ap: a #NMAccessPoint + * + * Gets the mode of the access point. + * + * Returns: the mode + **/ +NM80211Mode +nm_access_point_get_mode(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->mode; +} + +/** + * nm_access_point_get_max_bitrate: + * @ap: a #NMAccessPoint + * + * Gets the maximum bit rate of the access point in kbit/s. + * + * Returns: the maximum bit rate (kbit/s) + **/ +guint32 +nm_access_point_get_max_bitrate(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->max_bitrate; +} + +/** + * nm_access_point_get_strength: + * @ap: a #NMAccessPoint + * + * Gets the current signal strength of the access point as a percentage. + * + * Returns: the signal strength (0 to 100) + **/ +guint8 +nm_access_point_get_strength(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->strength; +} + +/** + * nm_access_point_get_last_seen: + * @ap: a #NMAccessPoint + * + * Returns the timestamp (in CLOCK_BOOTTIME seconds) for the last time the + * access point was found in scan results. A value of -1 means the access + * point has not been found in a scan. + * + * Returns: the last seen time in seconds + * + * Since: 1.2 + **/ +int +nm_access_point_get_last_seen(NMAccessPoint *ap) +{ + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), -1); + + return NM_ACCESS_POINT_GET_PRIVATE(ap)->last_seen; +} +NM_BACKPORT_SYMBOL(libnm_1_0_6, int, nm_access_point_get_last_seen, (NMAccessPoint * ap), (ap)); + +/** + * nm_access_point_connection_valid: + * @ap: an #NMAccessPoint to validate @connection against + * @connection: an #NMConnection to validate against @ap + * + * Validates a given connection against a given Wi-Fi access point to ensure that + * the connection may be activated with that AP. The connection must match the + * @ap's SSID, (if given) BSSID, and other attributes like security settings, + * channel, band, etc. + * + * Returns: %TRUE if the connection may be activated with this Wi-Fi AP, + * %FALSE if it cannot be. + **/ +gboolean +nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection) +{ + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity *s_wsec; + const char * ctype, *ap_bssid; + GBytes * setting_ssid; + GBytes * ap_ssid; + const char * setting_bssid; + const char * setting_mode; + NM80211Mode ap_mode; + const char * setting_band; + guint32 ap_freq, setting_chan, ap_chan; + + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), FALSE); + g_return_val_if_fail(NM_IS_CONNECTION(connection), FALSE); + + s_con = nm_connection_get_setting_connection(connection); + if (!s_con) + return FALSE; + + ctype = nm_setting_connection_get_connection_type(s_con); + if (!ctype || !nm_streq(ctype, NM_SETTING_WIRELESS_SETTING_NAME)) + return FALSE; + + s_wifi = nm_connection_get_setting_wireless(connection); + if (!s_wifi) + return FALSE; + + /* SSID checks */ + ap_ssid = nm_access_point_get_ssid(ap); + if (!ap_ssid) + return FALSE; + setting_ssid = nm_setting_wireless_get_ssid(s_wifi); + if (!setting_ssid || !g_bytes_equal(ap_ssid, setting_ssid)) + return FALSE; + + /* BSSID checks */ + ap_bssid = nm_access_point_get_bssid(ap); + if (!ap_bssid) + return FALSE; + setting_bssid = nm_setting_wireless_get_bssid(s_wifi); + if (setting_bssid) { + guint8 c[ETH_ALEN]; + + if (!nm_utils_hwaddr_aton(ap_bssid, c, ETH_ALEN) + || !nm_utils_hwaddr_matches(c, ETH_ALEN, setting_bssid, -1)) + return FALSE; + } + + /* Mode */ + ap_mode = nm_access_point_get_mode(ap); + if (ap_mode == NM_802_11_MODE_UNKNOWN) + return FALSE; + setting_mode = nm_setting_wireless_get_mode(s_wifi); + if (setting_mode && ap_mode) { + if (!strcmp(setting_mode, "infrastructure") && (ap_mode != NM_802_11_MODE_INFRA)) + return FALSE; + if (!strcmp(setting_mode, "adhoc") && (ap_mode != NM_802_11_MODE_ADHOC)) + return FALSE; + /* Hotspot never matches against APs as it's a device-specific mode. */ + if (!strcmp(setting_mode, "ap")) + return FALSE; + } + + /* Band and Channel/Frequency */ + ap_freq = nm_access_point_get_frequency(ap); + if (ap_freq) { + setting_band = nm_setting_wireless_get_band(s_wifi); + if (g_strcmp0(setting_band, "a") == 0) { + if (ap_freq < 4915 || ap_freq > 5825) + return FALSE; + } else if (g_strcmp0(setting_band, "bg") == 0) { + if (ap_freq < 2412 || ap_freq > 2484) + return FALSE; + } + + setting_chan = nm_setting_wireless_get_channel(s_wifi); + if (setting_chan) { + ap_chan = nm_utils_wifi_freq_to_channel(ap_freq); + if (setting_chan != ap_chan) + return FALSE; + } + } + + s_wsec = nm_connection_get_setting_wireless_security(connection); + if (!nm_setting_wireless_ap_security_compatible(s_wifi, + s_wsec, + nm_access_point_get_flags(ap), + nm_access_point_get_wpa_flags(ap), + nm_access_point_get_rsn_flags(ap), + ap_mode)) + return FALSE; + + return TRUE; +} + +/** + * nm_access_point_filter_connections: + * @ap: an #NMAccessPoint to filter connections for + * @connections: (element-type NMConnection): an array of #NMConnections to + * filter + * + * Filters a given array of connections for a given #NMAccessPoint object and + * returns connections which may be activated with the access point. Any + * returned connections will match the @ap's SSID and (if given) BSSID and + * other attributes like security settings, channel, etc. + * + * To obtain the list of connections that are compatible with this access point, + * use nm_client_get_connections() and then filter the returned list for a given + * #NMDevice using nm_device_filter_connections() and finally filter that list + * with this function. + * + * Returns: (transfer full) (element-type NMConnection): an array of + * #NMConnections that could be activated with the given @ap. The array should + * be freed with g_ptr_array_unref() when it is no longer required. + * + * WARNING: the transfer annotation for this function may not work correctly + * with bindings. See https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/305. + * You can filter the list yourself with nm_access_point_connection_valid(). + **/ +GPtrArray * +nm_access_point_filter_connections(NMAccessPoint *ap, const GPtrArray *connections) +{ + GPtrArray *filtered; + guint i; + + g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NULL); + + if (!connections) + return NULL; + + filtered = g_ptr_array_new_with_free_func(g_object_unref); + for (i = 0; i < connections->len; i++) { + NMConnection *candidate = connections->pdata[i]; + + if (nm_access_point_connection_valid(ap, candidate)) + g_ptr_array_add(filtered, g_object_ref(candidate)); + } + + return filtered; +} + +/*****************************************************************************/ + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_hw_address(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMAccessPoint * self = NM_ACCESS_POINT(dbobj->nmobj); + NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE(self); + + g_free(priv->bssid); + priv->bssid = value ? g_variant_dup_string(value, NULL) : 0u; + _notify(self, PROP_HW_ADDRESS); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +nm_access_point_init(NMAccessPoint *ap) +{ + NM_ACCESS_POINT_GET_PRIVATE(ap)->last_seen = -1; +} + +static void +finalize(GObject *object) +{ + NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE(object); + + if (priv->ssid) + g_bytes_unref(priv->ssid); + g_free(priv->bssid); + + G_OBJECT_CLASS(nm_access_point_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMAccessPoint *ap = NM_ACCESS_POINT(object); + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_flags(value, nm_access_point_get_flags(ap)); + break; + case PROP_WPA_FLAGS: + g_value_set_flags(value, nm_access_point_get_wpa_flags(ap)); + break; + case PROP_RSN_FLAGS: + g_value_set_flags(value, nm_access_point_get_rsn_flags(ap)); + break; + case PROP_SSID: + g_value_set_boxed(value, nm_access_point_get_ssid(ap)); + break; + case PROP_FREQUENCY: + g_value_set_uint(value, nm_access_point_get_frequency(ap)); + break; + case PROP_HW_ADDRESS: + g_value_set_string(value, nm_access_point_get_bssid(ap)); + break; + case PROP_BSSID: + g_value_set_string(value, nm_access_point_get_bssid(ap)); + break; + case PROP_MODE: + g_value_set_enum(value, nm_access_point_get_mode(ap)); + break; + case PROP_MAX_BITRATE: + g_value_set_uint(value, nm_access_point_get_max_bitrate(ap)); + break; + case PROP_STRENGTH: + g_value_set_uchar(value, nm_access_point_get_strength(ap)); + break; + case PROP_LAST_SEEN: + g_value_set_int(value, nm_access_point_get_last_seen(ap)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_ACCESS_POINT, + nm_access_point_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMAccessPoint, _priv.flags), + NML_DBUS_META_PROPERTY_INIT_U("Frequency", PROP_FREQUENCY, NMAccessPoint, _priv.frequency), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + PROP_BSSID, + "s", + _notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_I("LastSeen", PROP_LAST_SEEN, NMAccessPoint, _priv.last_seen), + NML_DBUS_META_PROPERTY_INIT_U("MaxBitrate", + PROP_MAX_BITRATE, + NMAccessPoint, + _priv.max_bitrate), + NML_DBUS_META_PROPERTY_INIT_U("Mode", PROP_MODE, NMAccessPoint, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_U("RsnFlags", PROP_RSN_FLAGS, NMAccessPoint, _priv.rsn_flags), + NML_DBUS_META_PROPERTY_INIT_AY("Ssid", PROP_SSID, NMAccessPoint, _priv.ssid), + NML_DBUS_META_PROPERTY_INIT_Y("Strength", PROP_STRENGTH, NMAccessPoint, _priv.strength), + NML_DBUS_META_PROPERTY_INIT_U("WpaFlags", + PROP_WPA_FLAGS, + NMAccessPoint, + _priv.wpa_flags), ), ); + +static void +nm_access_point_class_init(NMAccessPointClass *ap_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(ap_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMAccessPoint:flags: + * + * The flags of the access point. + **/ + obj_properties[PROP_FLAGS] = g_param_spec_flags(NM_ACCESS_POINT_FLAGS, + "", + "", + NM_TYPE_802_11_AP_FLAGS, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:wpa-flags: + * + * The WPA flags of the access point. + **/ + obj_properties[PROP_WPA_FLAGS] = g_param_spec_flags(NM_ACCESS_POINT_WPA_FLAGS, + "", + "", + NM_TYPE_802_11_AP_SECURITY_FLAGS, + NM_802_11_AP_SEC_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:rsn-flags: + * + * The RSN flags of the access point. + **/ + obj_properties[PROP_RSN_FLAGS] = g_param_spec_flags(NM_ACCESS_POINT_RSN_FLAGS, + "", + "", + NM_TYPE_802_11_AP_SECURITY_FLAGS, + NM_802_11_AP_SEC_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:ssid: + * + * The SSID of the access point, or %NULL if it is not known. + **/ + obj_properties[PROP_SSID] = g_param_spec_boxed(NM_ACCESS_POINT_SSID, + "", + "", + G_TYPE_BYTES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:frequency: + * + * The frequency of the access point. + **/ + obj_properties[PROP_FREQUENCY] = g_param_spec_uint(NM_ACCESS_POINT_FREQUENCY, + "", + "", + 0, + 10000, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:bssid: + * + * The BSSID of the access point. + **/ + obj_properties[PROP_BSSID] = g_param_spec_string(NM_ACCESS_POINT_BSSID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:hw-address: + * + * Alias for #NMAccessPoint:bssid. + * + * Deprecated: 1.0: Use #NMAccessPoint:bssid. + **/ + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string(NM_ACCESS_POINT_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:mode: + * + * The mode of the access point; either "infrastructure" (a central + * coordinator of the wireless network allowing clients to connect) or + * "ad-hoc" (a network with no central controller). + **/ + obj_properties[PROP_MODE] = g_param_spec_enum(NM_ACCESS_POINT_MODE, + "", + "", + NM_TYPE_802_11_MODE, + NM_802_11_MODE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:max-bitrate: + * + * The maximum bit rate of the access point in kbit/s. + **/ + obj_properties[PROP_MAX_BITRATE] = g_param_spec_uint(NM_ACCESS_POINT_MAX_BITRATE, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:strength: + * + * The current signal strength of the access point. + **/ + obj_properties[PROP_STRENGTH] = g_param_spec_uchar(NM_ACCESS_POINT_STRENGTH, + "", + "", + 0, + G_MAXUINT8, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMAccessPoint:last-seen: + * + * The timestamp (in CLOCK_BOOTTIME seconds) for the last time the + * access point was found in scan results. A value of -1 means the + * access point has not been found in a scan. + * + * Since: 1.2 + **/ + obj_properties[PROP_LAST_SEEN] = g_param_spec_int(NM_ACCESS_POINT_LAST_SEEN, + "", + "", + -1, + G_MAXINT, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_accesspoint); +} diff --git a/src/libnm-client-impl/nm-active-connection.c b/src/libnm-client-impl/nm-active-connection.c new file mode 100644 index 00000000..aa96219c --- /dev/null +++ b/src/libnm-client-impl/nm-active-connection.c @@ -0,0 +1,839 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2014 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-active-connection.h" + +#include "nm-dbus-interface.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-device.h" +#include "nm-connection.h" +#include "nm-vpn-connection.h" +#include "nm-dbus-helpers.h" +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-remote-connection.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMActiveConnection, + PROP_CONNECTION, + PROP_ID, + PROP_UUID, + PROP_TYPE, + PROP_SPECIFIC_OBJECT_PATH, + PROP_DEVICES, + PROP_STATE, + PROP_STATE_FLAGS, + PROP_DEFAULT, + PROP_IP4_CONFIG, + PROP_DHCP4_CONFIG, + PROP_DEFAULT6, + PROP_IP6_CONFIG, + PROP_DHCP6_CONFIG, + PROP_VPN, + PROP_MASTER, ); + +enum { + STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +enum { + PROPERTY_O_IDX_CONNECTION, + PROPERTY_O_IDX_MASTER, + PROPERTY_O_IDX_IP4_CONFIG, + PROPERTY_O_IDX_IP6_CONFIG, + PROPERTY_O_IDX_DHCP4_CONFIG, + PROPERTY_O_IDX_DHCP6_CONFIG, + _PROPERTY_O_IDX_NUM, +}; + +typedef struct _NMActiveConnectionPrivate { + NMLDBusPropertyO property_o[_PROPERTY_O_IDX_NUM]; + NMLDBusPropertyAO devices; + NMRefString * specific_object_path; + char * id; + char * uuid; + char * type; + + guint32 state; + guint32 state_flags; + + bool is_default; + bool is_default6; + bool is_vpn; + + guint32 reason; +} NMActiveConnectionPrivate; + +G_DEFINE_TYPE(NMActiveConnection, nm_active_connection, NM_TYPE_OBJECT); + +#define NM_ACTIVE_CONNECTION_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMActiveConnection, NM_IS_ACTIVE_CONNECTION, NMObject) + +/*****************************************************************************/ + +/** + * nm_active_connection_get_connection: + * @connection: a #NMActiveConnection + * + * Gets the #NMRemoteConnection associated with @connection. + * + * Returns: (transfer none): the #NMRemoteConnection which this + * #NMActiveConnection is an active instance of. + **/ +NMRemoteConnection * +nm_active_connection_get_connection(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_CONNECTION]); +} + +/** + * nm_active_connection_get_id: + * @connection: a #NMActiveConnection + * + * Gets the #NMConnection's ID. + * + * Returns: the ID of the #NMConnection that backs the #NMActiveConnection. + * This is the internal string used by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_id(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return _nml_coerce_property_str_not_empty(NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->id); +} + +/** + * nm_active_connection_get_uuid: + * @connection: a #NMActiveConnection + * + * Gets the #NMConnection's UUID. + * + * Returns: the UUID of the #NMConnection that backs the #NMActiveConnection. + * This is the internal string used by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_uuid(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return _nml_coerce_property_str_not_empty(NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->uuid); +} + +/** + * nm_active_connection_get_connection_type: + * @connection: a #NMActiveConnection + * + * Gets the #NMConnection's type. + * + * Returns: the type of the #NMConnection that backs the #NMActiveConnection. + * This is the internal string used by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_connection_type(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return _nml_coerce_property_str_not_empty(NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->type); +} + +/** + * nm_active_connection_get_specific_object_path: + * @connection: a #NMActiveConnection + * + * Gets the path of the "specific object" used at activation. + * + * Currently, there is no single method that will allow you to automatically turn + * this into an appropriate #NMObject; you need to know what kind of object it + * is based on other information. (Eg, if @connection corresponds to a Wi-Fi + * connection, then the specific object will be an #NMAccessPoint, and you can + * resolve it with nm_device_wifi_get_access_point_by_path().) + * + * Returns: the specific object's D-Bus path. This is the internal string used + * by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_specific_object_path(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return _nml_coerce_property_object_path( + NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->specific_object_path); +} + +/** + * nm_active_connection_get_devices: + * @connection: a #NMActiveConnection + * + * Gets the #NMDevices used for the active connections. + * + * Returns: (element-type NMDevice): the #GPtrArray containing #NMDevices. + * This is the internal copy used by the connection, and must not be modified. + **/ +const GPtrArray * +nm_active_connection_get_devices(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->devices); +} + +/** + * nm_active_connection_get_state: + * @connection: a #NMActiveConnection + * + * Gets the active connection's state. + * + * Returns: the state + **/ +NMActiveConnectionState +nm_active_connection_get_state(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NM_ACTIVE_CONNECTION_STATE_UNKNOWN); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->state; +} + +/** + * nm_active_connection_get_state_flags: + * @connection: a #NMActiveConnection + * + * Gets the active connection's state flags. + * + * Returns: the state flags + * + * Since: 1.10 + **/ +NMActivationStateFlags +nm_active_connection_get_state_flags(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NM_ACTIVATION_STATE_FLAG_NONE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->state_flags; +} + +/** + * nm_active_connection_get_state_reason: + * @connection: a #NMActiveConnection + * + * Gets the reason for active connection's state. + * + * Returns: the reason + * + * Since: 1.8 + **/ +NMActiveConnectionStateReason +nm_active_connection_get_state_reason(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), + NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->reason; +} + +/** + * nm_active_connection_get_default: + * @connection: a #NMActiveConnection + * + * Whether the active connection is the default IPv4 one (that is, is used for + * the default IPv4 route and DNS information). + * + * Returns: %TRUE if the active connection is the default IPv4 connection + **/ +gboolean +nm_active_connection_get_default(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), FALSE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->is_default; +} + +/** + * nm_active_connection_get_ip4_config: + * @connection: an #NMActiveConnection + * + * Gets the current IPv4 #NMIPConfig associated with the #NMActiveConnection. + * + * Returns: (transfer none): the IPv4 #NMIPConfig, or %NULL if the connection is + * not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED state. + **/ +NMIPConfig * +nm_active_connection_get_ip4_config(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_IP4_CONFIG]); +} + +/** + * nm_active_connection_get_dhcp4_config: + * @connection: an #NMActiveConnection + * + * Gets the current IPv4 #NMDhcpConfig (if any) associated with the + * #NMActiveConnection. + * + * Returns: (transfer none): the IPv4 #NMDhcpConfig, or %NULL if the connection + * does not use DHCP, or is not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED + * state. + **/ +NMDhcpConfig * +nm_active_connection_get_dhcp4_config(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_DHCP4_CONFIG]); +} + +/** + * nm_active_connection_get_default6: + * @connection: a #NMActiveConnection + * + * Whether the active connection is the default IPv6 one (that is, is used for + * the default IPv6 route and DNS information). + * + * Returns: %TRUE if the active connection is the default IPv6 connection + **/ +gboolean +nm_active_connection_get_default6(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), FALSE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->is_default6; +} + +/** + * nm_active_connection_get_ip6_config: + * @connection: an #NMActiveConnection + * + * Gets the current IPv6 #NMIPConfig associated with the #NMActiveConnection. + * + * Returns: (transfer none): the IPv6 #NMIPConfig, or %NULL if the connection is + * not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED state. + **/ +NMIPConfig * +nm_active_connection_get_ip6_config(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_IP6_CONFIG]); +} + +/** + * nm_active_connection_get_dhcp6_config: + * @connection: an #NMActiveConnection + * + * Gets the current IPv6 #NMDhcpConfig (if any) associated with the + * #NMActiveConnection. + * + * Returns: (transfer none): the IPv6 #NMDhcpConfig, or %NULL if the connection + * does not use DHCPv6, or is not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED + * state. + **/ +NMDhcpConfig * +nm_active_connection_get_dhcp6_config(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_DHCP6_CONFIG]); +} + +/** + * nm_active_connection_get_vpn: + * @connection: a #NMActiveConnection + * + * Whether the active connection is a VPN connection. + * + * Returns: %TRUE if the active connection is a VPN connection + **/ +gboolean +nm_active_connection_get_vpn(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), FALSE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->is_vpn; +} + +/** + * nm_active_connection_get_master: + * @connection: a #NMActiveConnection + * + * Gets the master #NMDevice of the connection. + * + * Returns: (transfer none): the master #NMDevice of the #NMActiveConnection. + **/ +NMDevice * +nm_active_connection_get_master(NMActiveConnection *connection) +{ + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(connection), NULL); + + return nml_dbus_property_o_get_obj( + &NM_ACTIVE_CONNECTION_GET_PRIVATE(connection)->property_o[PROPERTY_O_IDX_MASTER]); +} + +/*****************************************************************************/ + +static void +_notify_event_state_changed(NMClient *client, NMClientNotifyEventWithPtr *notify_event) +{ + gs_unref_object NMActiveConnection *self = notify_event->user_data; + NMActiveConnectionPrivate * priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); + + /* we expose here the value cache in @priv. In practice, this is the same + * value as we received from the signal. In the unexpected case where they + * differ, the cached value of the current instance would still be more correct. */ + g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) priv->state, (guint) priv->reason); +} + +void +_nm_active_connection_state_changed_commit(NMActiveConnection *self, guint32 state, guint32 reason) +{ + NMClient * client; + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); + + client = _nm_object_get_client(self); + + if (priv->state != state) { + priv->state = state; + _nm_client_queue_notify_object(client, self, obj_properties[PROP_STATE]); + } + + priv->reason = reason; + + _nm_client_notify_event_queue_with_ptr(client, + NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP + 1, + _notify_event_state_changed, + g_object_ref(self)); +} +/*****************************************************************************/ + +static gboolean +is_ready(NMObject *nmobj) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(nmobj); + + /* Usually, we don't want to expose our NMObject instances until they are fully initialized. + * For NMRemoteSetting this means to wait until GetSettings() returns. + * + * Note that most object types reference each other (directly or indirectly). E.g. the + * NMActiveConnection refers to the NMRemoteConnection and the NMDevice instance. So, + * we don't want to hide them too long, otherwise basically the entire set of objects + * will be hidden until they are all initialized. So, usually, when a NMObject references + * objects that are not yet initialized, that reference will just be NULL but the object + * will be considered ready already. + * + * For NMActiveConnection referencing a NMRemoteConnection don't do that. Here we wait for the + * NMRemoteConnection to be ready as well. This is somewhat arbitrary special casing, but + * the effect is that when nm_client_add_and_activate*() returns, the NMActiveConnection already + * references a initialized NMRemoteConnection. + */ + if (!nml_dbus_property_o_is_ready_fully(&priv->property_o[PROPERTY_O_IDX_CONNECTION])) + return FALSE; + + return NM_OBJECT_CLASS(nm_active_connection_parent_class)->is_ready(nmobj); +} + +/*****************************************************************************/ + +static void +nm_active_connection_init(NMActiveConnection *self) +{ + NMActiveConnectionPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionPrivate); + + self->_priv = priv; +} + +static void +finalize(GObject *object) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(object); + + g_free(priv->id); + g_free(priv->uuid); + g_free(priv->type); + nm_ref_string_unref(priv->specific_object_path); + + G_OBJECT_CLASS(nm_active_connection_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMActiveConnection *self = NM_ACTIVE_CONNECTION(object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object(value, nm_active_connection_get_connection(self)); + break; + case PROP_ID: + g_value_set_string(value, nm_active_connection_get_id(self)); + break; + case PROP_UUID: + g_value_set_string(value, nm_active_connection_get_uuid(self)); + break; + case PROP_TYPE: + g_value_set_string(value, nm_active_connection_get_connection_type(self)); + break; + case PROP_SPECIFIC_OBJECT_PATH: + g_value_set_string(value, nm_active_connection_get_specific_object_path(self)); + break; + case PROP_DEVICES: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_active_connection_get_devices(self))); + break; + case PROP_STATE: + g_value_set_enum(value, nm_active_connection_get_state(self)); + break; + case PROP_STATE_FLAGS: + g_value_set_uint(value, nm_active_connection_get_state_flags(self)); + break; + case PROP_DEFAULT: + g_value_set_boolean(value, nm_active_connection_get_default(self)); + break; + case PROP_IP4_CONFIG: + g_value_set_object(value, nm_active_connection_get_ip4_config(self)); + break; + case PROP_DHCP4_CONFIG: + g_value_set_object(value, nm_active_connection_get_dhcp4_config(self)); + break; + case PROP_DEFAULT6: + g_value_set_boolean(value, nm_active_connection_get_default6(self)); + break; + case PROP_IP6_CONFIG: + g_value_set_object(value, nm_active_connection_get_ip6_config(self)); + break; + case PROP_DHCP6_CONFIG: + g_value_set_object(value, nm_active_connection_get_dhcp6_config(self)); + break; + case PROP_VPN: + g_value_set_boolean(value, nm_active_connection_get_vpn(self)); + break; + case PROP_MASTER: + g_value_set_object(value, nm_active_connection_get_master(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_connection_active = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + nm_active_connection_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_O_PROP("Connection", + PROP_CONNECTION, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_CONNECTION], + nm_remote_connection_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Default", + PROP_DEFAULT, + NMActiveConnectionPrivate, + is_default), + NML_DBUS_META_PROPERTY_INIT_B("Default6", + PROP_DEFAULT6, + NMActiveConnectionPrivate, + is_default6), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Devices", + PROP_DEVICES, + NMActiveConnectionPrivate, + devices, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Dhcp4Config", + PROP_DHCP4_CONFIG, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_DHCP4_CONFIG], + nm_dhcp4_config_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Dhcp6Config", + PROP_DHCP6_CONFIG, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_DHCP6_CONFIG], + nm_dhcp6_config_get_type), + NML_DBUS_META_PROPERTY_INIT_S("Id", PROP_ID, NMActiveConnectionPrivate, id), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Ip4Config", + PROP_IP4_CONFIG, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_IP4_CONFIG], + nm_ip4_config_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Ip6Config", + PROP_IP6_CONFIG, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_IP6_CONFIG], + nm_ip6_config_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Master", + PROP_MASTER, + NMActiveConnectionPrivate, + property_o[PROPERTY_O_IDX_MASTER], + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_O("SpecificObject", + PROP_SPECIFIC_OBJECT_PATH, + NMActiveConnectionPrivate, + specific_object_path), + NML_DBUS_META_PROPERTY_INIT_U("State", PROP_STATE, NMActiveConnectionPrivate, state), + NML_DBUS_META_PROPERTY_INIT_U("StateFlags", + PROP_STATE_FLAGS, + NMActiveConnectionPrivate, + state_flags), + NML_DBUS_META_PROPERTY_INIT_S("Type", PROP_TYPE, NMActiveConnectionPrivate, type), + NML_DBUS_META_PROPERTY_INIT_S("Uuid", PROP_UUID, NMActiveConnectionPrivate, uuid), + NML_DBUS_META_PROPERTY_INIT_B("Vpn", PROP_VPN, NMActiveConnectionPrivate, is_vpn), ), + .base_struct_offset = G_STRUCT_OFFSET(NMActiveConnection, _priv), ); + +static void +nm_active_connection_class_init(NMActiveConnectionClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(NMActiveConnectionPrivate)); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + nm_object_class->is_ready = is_ready; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_INDIRECT(nm_object_class, NMActiveConnection); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_N(nm_object_class, + NMActiveConnectionPrivate, + property_o); + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMActiveConnectionPrivate, devices); + + /** + * NMActiveConnection:connection: + * + * The connection that this is an active instance of. + **/ + obj_properties[PROP_CONNECTION] = + g_param_spec_object(NM_ACTIVE_CONNECTION_CONNECTION, + "", + "", + NM_TYPE_REMOTE_CONNECTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:id: + * + * The active connection's ID + **/ + obj_properties[PROP_ID] = g_param_spec_string(NM_ACTIVE_CONNECTION_ID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:uuid: + * + * The active connection's UUID + **/ + obj_properties[PROP_UUID] = g_param_spec_string(NM_ACTIVE_CONNECTION_UUID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:type: + * + * The active connection's type + **/ + obj_properties[PROP_TYPE] = g_param_spec_string(NM_ACTIVE_CONNECTION_TYPE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:specific-object-path: + * + * The path to the "specific object" of the active connection; see + * nm_active_connection_get_specific_object_path() for more details. + **/ + obj_properties[PROP_SPECIFIC_OBJECT_PATH] = + g_param_spec_string(NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT_PATH, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:devices: (type GPtrArray(NMDevice)) + * + * The devices of the active connection. + **/ + obj_properties[PROP_DEVICES] = g_param_spec_boxed(NM_ACTIVE_CONNECTION_DEVICES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:state: + * + * The state of the active connection. + **/ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_ACTIVE_CONNECTION_STATE, + "", + "", + NM_TYPE_ACTIVE_CONNECTION_STATE, + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:state-flags: + * + * The state flags of the active connection. + * + * Since: 1.10 + **/ + obj_properties[PROP_STATE_FLAGS] = g_param_spec_uint(NM_ACTIVE_CONNECTION_STATE_FLAGS, + "", + "", + 0, + G_MAXUINT32, + NM_ACTIVATION_STATE_FLAG_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:default: + * + * Whether the active connection is the default IPv4 one. + **/ + obj_properties[PROP_DEFAULT] = g_param_spec_boolean(NM_ACTIVE_CONNECTION_DEFAULT, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:ip4-config: + * + * The IPv4 #NMIPConfig of the connection. + **/ + obj_properties[PROP_IP4_CONFIG] = + g_param_spec_object(NM_ACTIVE_CONNECTION_IP4_CONFIG, + "", + "", + NM_TYPE_IP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:dhcp4-config: + * + * The IPv4 #NMDhcpConfig of the connection. + **/ + obj_properties[PROP_DHCP4_CONFIG] = + g_param_spec_object(NM_ACTIVE_CONNECTION_DHCP4_CONFIG, + "", + "", + NM_TYPE_DHCP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:default6: + * + * Whether the active connection is the default IPv6 one. + **/ + obj_properties[PROP_DEFAULT6] = g_param_spec_boolean(NM_ACTIVE_CONNECTION_DEFAULT6, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:ip6-config: + * + * The IPv6 #NMIPConfig of the connection. + **/ + obj_properties[PROP_IP6_CONFIG] = + g_param_spec_object(NM_ACTIVE_CONNECTION_IP6_CONFIG, + "", + "", + NM_TYPE_IP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:dhcp6-config: + * + * The IPv6 #NMDhcpConfig of the connection. + **/ + obj_properties[PROP_DHCP6_CONFIG] = + g_param_spec_object(NM_ACTIVE_CONNECTION_DHCP6_CONFIG, + "", + "", + NM_TYPE_DHCP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:vpn: + * + * Whether the active connection is a VPN connection. + **/ + obj_properties[PROP_VPN] = g_param_spec_boolean(NM_ACTIVE_CONNECTION_VPN, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMActiveConnection:master: + * + * The master device if one exists. + **/ + obj_properties[PROP_MASTER] = g_param_spec_object(NM_ACTIVE_CONNECTION_MASTER, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_connection_active); + + /* TODO: the state reason should also be exposed as a property in libnm's NMActiveConnection, + * like done for NMDevice's state reason. */ + + /* TODO: the D-Bus API should also expose the state-reason as a property instead of + * a "StateChanged" signal. Like done for Device's "StateReason". */ + + /** + * NMActiveConnection::state-changed: + * @active_connection: the source #NMActiveConnection + * @state: the new state number (#NMActiveConnectionState) + * @reason: the state change reason (#NMActiveConnectionStateReason) + */ + signals[STATE_CHANGED] = g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_UINT, + G_TYPE_UINT); +} diff --git a/src/libnm-client-impl/nm-checkpoint.c b/src/libnm-client-impl/nm-checkpoint.c new file mode 100644 index 00000000..c1f6b092 --- /dev/null +++ b/src/libnm-client-impl/nm-checkpoint.c @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-checkpoint.h" + +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-interface.h" +#include "nm-device.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DEVICES, PROP_CREATED, PROP_ROLLBACK_TIMEOUT, ); + +typedef struct { + NMLDBusPropertyAO devices; + gint64 created; + guint32 rollback_timeout; +} NMCheckpointPrivate; + +struct _NMCheckpoint { + NMObject parent; + NMCheckpointPrivate _priv; +}; + +struct _NMCheckpointClass { + NMObjectClass parent; +}; + +G_DEFINE_TYPE(NMCheckpoint, nm_checkpoint, NM_TYPE_OBJECT) + +#define NM_CHECKPOINT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMCheckpoint, NM_IS_CHECKPOINT, NMObject) + +/*****************************************************************************/ + +/** + * nm_checkpoint_get_devices: + * @checkpoint: a #NMCheckpoint + * + * The devices that are part of this checkpoint. + * + * Returns: (element-type NMDevice): the devices list. + * + * Since: 1.12 + **/ +const GPtrArray * +nm_checkpoint_get_devices(NMCheckpoint *checkpoint) +{ + g_return_val_if_fail(NM_IS_CHECKPOINT(checkpoint), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CHECKPOINT_GET_PRIVATE(checkpoint)->devices); +} + +/** + * nm_checkpoint_get_created: + * @checkpoint: a #NMCheckpoint + * + * Gets the timestamp (in CLOCK_BOOTTIME milliseconds) of checkpoint creation. + * + * Use nm_utils_get_timestamp_msec() to obtain current time value suitable for + * comparing to this value. + * + * Returns: the timestamp of checkpoint creation. + * + * Since: 1.12 + **/ +gint64 +nm_checkpoint_get_created(NMCheckpoint *checkpoint) +{ + g_return_val_if_fail(NM_IS_CHECKPOINT(checkpoint), 0); + + return NM_CHECKPOINT_GET_PRIVATE(checkpoint)->created; +} + +/** + * nm_checkpoint_get_rollback_timeout: + * @checkpoint: a #NMCheckpoint + * + * Gets the timeout in seconds for automatic rollback. + * + * Returns: the rollback timeout. + * + * Since: 1.12 + **/ +guint32 +nm_checkpoint_get_rollback_timeout(NMCheckpoint *checkpoint) +{ + g_return_val_if_fail(NM_IS_CHECKPOINT(checkpoint), 0); + + return NM_CHECKPOINT_GET_PRIVATE(checkpoint)->rollback_timeout; +} + +/*****************************************************************************/ + +static void +nm_checkpoint_init(NMCheckpoint *checkpoint) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMCheckpoint * checkpoint = NM_CHECKPOINT(object); + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE(checkpoint); + + switch (prop_id) { + case PROP_DEVICES: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_checkpoint_get_devices(checkpoint))); + break; + case PROP_CREATED: + g_value_set_int64(value, priv->created); + break; + case PROP_ROLLBACK_TIMEOUT: + g_value_set_uint(value, priv->rollback_timeout); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_checkpoint = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_CHECKPOINT, + nm_checkpoint_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_X("Created", PROP_CREATED, NMCheckpoint, _priv.created), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Devices", + PROP_DEVICES, + NMCheckpoint, + _priv.devices, + nm_device_get_type, + .is_always_ready = TRUE), + NML_DBUS_META_PROPERTY_INIT_U("RollbackTimeout", + PROP_ROLLBACK_TIMEOUT, + NMCheckpoint, + _priv.rollback_timeout), ), ); + +static void +nm_checkpoint_class_init(NMCheckpointClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMCheckpoint); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMCheckpointPrivate, devices); + + /** + * NMCheckpoint:devices: (type GPtrArray(NMDevice)) + * + * The devices that are part of this checkpoint. + * + * Since: 1.12 + **/ + obj_properties[PROP_DEVICES] = g_param_spec_boxed(NM_CHECKPOINT_DEVICES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMCheckpoint:created: + * + * The timestamp (in CLOCK_BOOTTIME milliseconds) of checkpoint creation. + * + * Since: 1.12 + **/ + obj_properties[PROP_CREATED] = g_param_spec_int64(NM_CHECKPOINT_CREATED, + "", + "", + G_MININT64, + G_MAXINT64, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMCheckpoint:rollback-timeout: + * + * Timeout in seconds for automatic rollback, or zero. + * + * Since: 1.12 + **/ + obj_properties[PROP_ROLLBACK_TIMEOUT] = + g_param_spec_uint(NM_CHECKPOINT_ROLLBACK_TIMEOUT, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_checkpoint); +} diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c new file mode 100644 index 00000000..c17dc4f7 --- /dev/null +++ b/src/libnm-client-impl/nm-client.c @@ -0,0 +1,8834 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-client.h" + +#include + +#include "libnm-std-aux/c-list-util.h" +#include "libnm-glib-aux/nm-c-list.h" +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "libnm-core-aux-intern/nm-common-macros.h" + +#include "nm-access-point.h" +#include "nm-active-connection.h" +#include "nm-checkpoint.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-helpers.h" +#include "nm-device-6lowpan.h" +#include "nm-device-adsl.h" +#include "nm-device-bond.h" +#include "nm-device-bridge.h" +#include "nm-device-bt.h" +#include "nm-device-dummy.h" +#include "nm-device-ethernet.h" +#include "nm-device-generic.h" +#include "nm-device-infiniband.h" +#include "nm-device-ip-tunnel.h" +#include "nm-device-macsec.h" +#include "nm-device-macvlan.h" +#include "nm-device-modem.h" +#include "nm-device-olpc-mesh.h" +#include "nm-device-ovs-bridge.h" +#include "nm-device-ovs-interface.h" +#include "nm-device-ovs-port.h" +#include "nm-device-ppp.h" +#include "nm-device-team.h" +#include "nm-device-tun.h" +#include "nm-device-vlan.h" +#include "nm-device-vxlan.h" +#include "nm-device-wifi-p2p.h" +#include "nm-device-wifi.h" +#include "nm-device-wireguard.h" +#include "nm-device-wpan.h" +#include "nm-dhcp-config.h" +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-dns-manager.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-object-private.h" +#include "nm-remote-connection.h" +#include "nm-utils.h" +#include "nm-vpn-connection.h" +#include "nm-wifi-p2p-peer.h" + +/*****************************************************************************/ + +NM_CACHED_QUARK_FCN("nm-context-busy-watcher", nm_context_busy_watcher_quark); + +static void +_context_busy_watcher_attach_integration_source_cb(gpointer data, GObject *where_the_object_was) +{ + nm_g_source_destroy_and_unref(data); +} + +void +nm_context_busy_watcher_integrate_source(GMainContext *outer_context, + GMainContext *inner_context, + GObject * context_busy_watcher) +{ + GSource *source; + + nm_assert(outer_context); + nm_assert(inner_context); + nm_assert(outer_context != inner_context); + nm_assert(G_IS_OBJECT(context_busy_watcher)); + + source = nm_utils_g_main_context_create_integrate_source(inner_context); + g_source_attach(source, outer_context); + + /* The problem is... + * + * NMClient is associated with a GMainContext, just like its underlying GDBusConnection + * also queues signals and callbacks on that main context. During operation, NMClient + * will schedule async operations which will return asynchronously on that GMainContext. + * + * Note that depending on whether NMClient got initialized synchronously or asynchronously, + * it has an internal priv->dbus_context that is different from the outer priv->main_context. + * However, the problem is in both cases. + * + * So, as long as there are pending D-Bus calls, the GMainContext is referenced and kept alive. + * When NMClient gets destroyed, the pending calls get cancelled, but the async callback are still + * scheduled to return. + * That means, the main context stays alive until it gets iterated long enough so that all pending + * operations are completed. + * + * Note that pending operations don't keep NMClient alive, so NMClient can already be gone by + * then, but the user still should iterate the main context long enough to process the (cancelled) + * callbacks... at least, if the user cares about whether the remaining memory and file descriptors + * of the GMainContext can be reclaimed. + * + * In hindsight, maybe pending references should kept NMClient alive. But then NMClient would + * need a special "shutdown()" API that the user must invoke, because unrefing would no longer + * be enough to ensure a shutdown (imagine a situation where NMClient receives a constant flow + * of "CheckPermissions" signals, which keeps retriggering an async request). Anyway, we cannot + * add such a shutdown API now, as it would break client's expectations that they can just unref + * the NMClient to destroy it. + * + * So, we allow NMClient to unref, but the user is advised to keep iterating the main context. + * But for how long? Here comes nm_client_get_context_busy_watcher() into play. The user may + * subscribe a weak pointer to that instance and should keep iterating as long as the object + * exists. + * + * Now, back to synchronous initialization: here we have the internal priv->dbus_context context. + * We also cannot remove that context right away, instead we need to keep it integrated in the + * caller's priv->main_context as long as we have pending calls: that is, as long as the + * context-busy-watcher is alive. + */ + + g_object_weak_ref(context_busy_watcher, + _context_busy_watcher_attach_integration_source_cb, + source); +} + +/*****************************************************************************/ + +typedef struct { + /* It is quite wasteful to require 2 pointers per property (of an instance) only to track whether + * the property got changed. But it's convenient! */ + CList changed_prop_lst; + + GVariant *prop_data_value; +} NMLDBusObjPropData; + +typedef struct { + CList iface_lst; + union { + const NMLDBusMetaIface *meta; + NMRefString * name; + } dbus_iface; + + CList changed_prop_lst_head; + + /* We also keep track of non-well known interfaces. The presence of a D-Bus interface + * is what makes a D-Bus alive or not. As we should track all D-Bus objects, we also + * need to track whether there are any interfaces on it -- even if we otherwise don't + * care about the interface. */ + bool dbus_iface_is_wellknown : 1; + + /* if TRUE, the interface is about to be removed. */ + bool iface_removed : 1; + + bool nmobj_checked : 1; + bool nmobj_compatible : 1; + + NMLDBusObjPropData prop_datas[]; +} NMLDBusObjIfaceData; + +/* The dbus_path must be the first element, so when we hash the object by the dbus_path, + * we also can lookup the object by only having a NMRefString at hand + * using nm_pdirect_hash()/nm_pdirect_equal(). */ +G_STATIC_ASSERT(G_STRUCT_OFFSET(NMLDBusObject, dbus_path) == 0); + +typedef void (*NMLDBusObjWatchNotifyFcn)(NMClient *client, gpointer obj_watcher); + +struct _NMLDBusObjWatcher { + NMLDBusObject *dbobj; + struct { + CList watcher_lst; + NMLDBusObjWatchNotifyFcn notify_fcn; + } _priv; +}; + +typedef struct { + NMLDBusObjWatcher parent; + gpointer user_data; +} NMLDBusObjWatcherWithPtr; + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMClient, + PROP_DBUS_CONNECTION, + PROP_DBUS_NAME_OWNER, + PROP_VERSION, + PROP_INSTANCE_FLAGS, + PROP_STATE, + PROP_STARTUP, + PROP_NM_RUNNING, + PROP_NETWORKING_ENABLED, + PROP_WIRELESS_ENABLED, + PROP_WIRELESS_HARDWARE_ENABLED, + PROP_WWAN_ENABLED, + PROP_WWAN_HARDWARE_ENABLED, + PROP_WIMAX_ENABLED, + PROP_WIMAX_HARDWARE_ENABLED, + PROP_ACTIVE_CONNECTIONS, + PROP_CONNECTIVITY, + PROP_CONNECTIVITY_CHECK_URI, + PROP_CONNECTIVITY_CHECK_AVAILABLE, + PROP_CONNECTIVITY_CHECK_ENABLED, + PROP_PRIMARY_CONNECTION, + PROP_ACTIVATING_CONNECTION, + PROP_DEVICES, + PROP_ALL_DEVICES, + PROP_CONNECTIONS, + PROP_HOSTNAME, + PROP_CAN_MODIFY, + PROP_METERED, + PROP_DNS_MODE, + PROP_DNS_RC_MANAGER, + PROP_DNS_CONFIGURATION, + PROP_CHECKPOINTS, + PROP_CAPABILITIES, + PROP_PERMISSIONS_STATE, ); + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + ANY_DEVICE_ADDED, + ANY_DEVICE_REMOVED, + PERMISSION_CHANGED, + CONNECTION_ADDED, + CONNECTION_REMOVED, + ACTIVE_CONNECTION_ADDED, + ACTIVE_CONNECTION_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +enum { + PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION = 0, + PROPERTY_O_IDX_NM_PRIMAY_CONNECTION, + _PROPERTY_O_IDX_NM_NUM, +}; + +enum { + PROPERTY_AO_IDX_DEVICES = 0, + PROPERTY_AO_IDX_ALL_DEVICES, + PROPERTY_AO_IDX_ACTIVE_CONNECTIONS, + PROPERTY_AO_IDX_CHECKPOINTS, + _PROPERTY_AO_IDX_NM_NUM, +}; + +typedef struct { + struct udev * udev; + GMainContext * main_context; + GMainContext * dbus_context; + GObject * context_busy_watcher; + GDBusConnection *dbus_connection; + NMLInitData * init_data; + GHashTable * dbus_objects; + CList obj_changed_lst_head; + GCancellable * name_owner_get_cancellable; + GCancellable * get_managed_objects_cancellable; + + CList queue_notify_lst_head; + CList notify_event_lst_head; + + CList dbus_objects_lst_head_watched_only; + CList dbus_objects_lst_head_on_dbus; + CList dbus_objects_lst_head_with_nmobj_not_ready; + CList dbus_objects_lst_head_with_nmobj_ready; + + NMLDBusObject *dbobj_nm; + NMLDBusObject *dbobj_settings; + NMLDBusObject *dbobj_dns_manager; + + gsize log_call_counter; + + guint8 * permissions; + GCancellable *permissions_cancellable; + + char *name_owner; + guint name_owner_changed_id; + guint dbsid_nm_object_manager; + guint dbsid_dbus_properties_properties_changed; + guint dbsid_nm_settings_connection_updated; + guint dbsid_nm_connection_active_state_changed; + guint dbsid_nm_vpn_connection_state_changed; + guint dbsid_nm_check_permissions; + + NMClientInstanceFlags instance_flags : 3; + + NMTernary permissions_state : 3; + + bool instance_flags_constructed : 1; + + bool udev_inited : 1; + bool notify_event_lst_changed : 1; + bool check_dbobj_visible_all : 1; + bool nm_running : 1; + + struct { + NMLDBusPropertyO property_o[_PROPERTY_O_IDX_NM_NUM]; + NMLDBusPropertyAO property_ao[_PROPERTY_AO_IDX_NM_NUM]; + char * connectivity_check_uri; + char * version; + guint32 * capabilities_arr; + gsize capabilities_len; + guint32 connectivity; + guint32 state; + guint32 metered; + bool connectivity_check_available; + bool connectivity_check_enabled; + bool networking_enabled; + bool startup; + bool wireless_enabled; + bool wireless_hardware_enabled; + bool wwan_enabled; + bool wwan_hardware_enabled; + } nm; + + struct { + NMLDBusPropertyAO connections; + char * hostname; + bool can_modify; + } settings; + + struct { + GPtrArray *configuration; + char * mode; + char * rc_manager; + } dns_manager; + +} NMClientPrivate; + +struct _NMClient { + union { + GObject parent; + NMObjectBase obj_base; + }; + NMClientPrivate _priv; +}; + +struct _NMClientClass { + union { + GObjectClass parent; + NMObjectBaseClass obj_base; + }; +}; + +static void nm_client_initable_iface_init(GInitableIface *iface); +static void nm_client_async_initable_iface_init(GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE(NMClient, + nm_client, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, nm_client_initable_iface_init); + G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, + nm_client_async_initable_iface_init);) + +#define NM_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMClient, NM_IS_CLIENT) + +/*****************************************************************************/ + +static void _init_start_check_complete(NMClient *self); + +static void name_owner_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data); + +static void name_owner_get_call(NMClient *self); + +static void _set_nm_running(NMClient *self); + +/*****************************************************************************/ + +static NMRefString *_dbus_path_nm = NULL; +static NMRefString *_dbus_path_settings = NULL; +static NMRefString *_dbus_path_dns_manager = NULL; + +/*****************************************************************************/ + +static NM_UTILS_LOOKUP_STR_DEFINE( + nml_dbus_obj_state_to_string, + NMLDBusObjState, + NM_UTILS_LOOKUP_DEFAULT_WARN("???"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_UNLINKED, "unlinked"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_WATCHED_ONLY, "watched-only"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_ON_DBUS, "on-dbus"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, "not-ready"), + NM_UTILS_LOOKUP_ITEM(NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, "ready"), ); + +/*****************************************************************************/ + +/** + * nm_client_error_quark: + * + * Registers an error quark for #NMClient if necessary. + * + * Returns: the error quark used for #NMClient errors. + **/ +NM_CACHED_QUARK_FCN("nm-client-error-quark", nm_client_error_quark); + +/*****************************************************************************/ + +NMLInitData * +nml_init_data_new_sync(GCancellable *cancellable, GMainLoop *main_loop, GError **error_location) +{ + NMLInitData *init_data; + + init_data = g_slice_new(NMLInitData); + *init_data = (NMLInitData){ + .cancellable = nm_g_object_ref(cancellable), + .is_sync = TRUE, + .data.sync = + { + .main_loop = main_loop, + .error_location = error_location, + }, + }; + return init_data; +} + +NMLInitData * +nml_init_data_new_async(GCancellable *cancellable, GTask *task_take) +{ + NMLInitData *init_data; + + init_data = g_slice_new(NMLInitData); + *init_data = (NMLInitData){ + .cancellable = nm_g_object_ref(cancellable), + .is_sync = FALSE, + .data.async = + { + .task = g_steal_pointer(&task_take), + }, + }; + return init_data; +} + +void +nml_init_data_return(NMLInitData *init_data, GError *error_take) +{ + nm_assert(init_data); + + nm_clear_pointer(&init_data->cancel_on_idle_source, nm_g_source_destroy_and_unref); + nm_clear_g_signal_handler(init_data->cancellable, &init_data->cancelled_id); + + if (init_data->is_sync) { + if (error_take) + g_propagate_error(init_data->data.sync.error_location, error_take); + g_main_loop_quit(init_data->data.sync.main_loop); + } else { + if (error_take) + g_task_return_error(init_data->data.async.task, error_take); + else + g_task_return_boolean(init_data->data.async.task, TRUE); + g_object_unref(init_data->data.async.task); + } + nm_g_object_unref(init_data->cancellable); + nm_g_slice_free(init_data); +} + +/*****************************************************************************/ + +GError * +_nm_client_new_error_nm_not_running(void) +{ + return g_error_new_literal(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, + "NetworkManager is not running"); +} + +GError * +_nm_client_new_error_nm_not_cached(void) +{ + return g_error_new_literal(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_FAILED, + "Object is no longer in the client cache"); +} + +static void +_nm_client_dbus_call_simple_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + GAsyncReadyCallback callback; + gpointer callback_user_data; + gs_unref_object GObject *context_busy_watcher = NULL; + gpointer obfuscated_self_ptr; + gpointer log_call_counter_ptr; + + nm_utils_user_data_unpack(user_data, + &callback, + &callback_user_data, + &context_busy_watcher, + &obfuscated_self_ptr, + &log_call_counter_ptr); + + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(NML_DBUS_LOG_LEVEL_TRACE), + "nmclient[" NM_HASH_OBFUSCATE_PTR_FMT "]: call[%" G_GSIZE_FORMAT "] completed", + (guint64) GPOINTER_TO_SIZE(obfuscated_self_ptr), + GPOINTER_TO_SIZE(log_call_counter_ptr)); + + callback(source, result, callback_user_data); +} + +void +_nm_client_dbus_call_simple(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + gs_free char * log_str = NULL; + gsize log_call_counter; + + nm_assert(priv->name_owner); + nm_assert(!cancellable || G_IS_CANCELLABLE(cancellable)); + nm_assert(callback); + nm_assert(object_path); + nm_assert(interface_name); + nm_assert(method_name); + nm_assert(parameters); + nm_assert(reply_type); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + log_call_counter = ++priv->log_call_counter; + + NML_NMCLIENT_LOG_T(self, + "call[%" G_GSIZE_FORMAT "] D-Bus method on %s: %s, %s.%s -> %s (%s)", + log_call_counter, + priv->name_owner, + object_path, + interface_name, + method_name, + (const char *) reply_type ?: "???", + parameters ? (log_str = g_variant_print(parameters, TRUE)) : "NULL"); + + g_dbus_connection_call(priv->dbus_connection, + priv->name_owner, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + _nm_client_dbus_call_simple_cb, + nm_utils_user_data_pack(callback, + user_data, + g_object_ref(priv->context_busy_watcher), + GSIZE_TO_POINTER(NM_HASH_OBFUSCATE_PTR(self)), + GSIZE_TO_POINTER(log_call_counter))); +} + +void +_nm_client_dbus_call(NMClient * self, + gpointer source_obj, + gpointer source_tag, + GCancellable * cancellable, + GAsyncReadyCallback user_callback, + gpointer user_callback_data, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback internal_callback) +{ + NMClientPrivate *priv; + gs_unref_object GTask *task = NULL; + + nm_assert(!source_obj || G_IS_OBJECT(source_obj)); + nm_assert(source_tag); + nm_assert(!cancellable || G_IS_CANCELLABLE(cancellable)); + nm_assert(internal_callback); + nm_assert(object_path); + nm_assert(interface_name); + nm_assert(method_name); + nm_assert(parameters); + nm_assert(reply_type); + + task = nm_g_task_new(source_obj, cancellable, source_tag, user_callback, user_callback_data); + + if (!self) { + nm_g_variant_unref_floating(parameters); + g_task_return_error(task, _nm_client_new_error_nm_not_cached()); + return; + } + + priv = NM_CLIENT_GET_PRIVATE(self); + if (!priv->name_owner) { + nm_g_variant_unref_floating(parameters); + g_task_return_error(task, _nm_client_new_error_nm_not_running()); + return; + } + + _nm_client_dbus_call_simple(self, + cancellable, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + internal_callback, + g_steal_pointer(&task)); +} + +GVariant * +_nm_client_dbus_call_sync(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError ** error) +{ + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + + nm_assert(!cancellable || G_IS_CANCELLABLE(cancellable)); + nm_assert(!error || !*error); + nm_assert(object_path); + nm_assert(interface_name); + nm_assert(method_name); + nm_assert(parameters); + nm_assert(reply_type); + + if (!self) { + nm_g_variant_unref_floating(parameters); + nm_g_set_error_take_lazy(error, _nm_client_new_error_nm_not_cached()); + return NULL; + } + + priv = NM_CLIENT_GET_PRIVATE(self); + if (!priv->name_owner) { + nm_g_variant_unref_floating(parameters); + nm_g_set_error_take_lazy(error, _nm_client_new_error_nm_not_running()); + return NULL; + } + + ret = g_dbus_connection_call_sync(priv->dbus_connection, + priv->name_owner, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + error); + if (!ret) { + if (error && strip_dbus_error) + g_dbus_error_strip_remote_error(*error); + return NULL; + } + + return g_steal_pointer(&ret); +} + +gboolean +_nm_client_dbus_call_sync_void(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + + ret = _nm_client_dbus_call_sync(self, + cancellable, + object_path, + interface_name, + method_name, + parameters, + G_VARIANT_TYPE("()"), + flags, + timeout_msec, + strip_dbus_error, + error); + return !!ret; +} + +void +_nm_client_set_property_sync_legacy(NMClient * self, + const char *object_path, + const char *interface_name, + const char *property_name, + const char *format_string, + ...) +{ + NMClientPrivate *priv; + GVariant * val; + gs_unref_variant GVariant *ret = NULL; + va_list ap; + + nm_assert(!self || NM_IS_CLIENT(self)); + nm_assert(interface_name); + nm_assert(property_name); + nm_assert(format_string); + + if (!self) + return; + + priv = NM_CLIENT_GET_PRIVATE(self); + if (!priv->name_owner) + return; + + va_start(ap, format_string); + val = g_variant_new_va(format_string, NULL, &ap); + va_end(ap); + + nm_assert(val); + + /* A synchronous D-Bus call that is not cancellable an ignores the return value. + * This function only exists for backward compatibility. */ + ret = g_dbus_connection_call_sync(priv->dbus_connection, + priv->name_owner, + object_path, + DBUS_INTERFACE_PROPERTIES, + "Set", + g_variant_new("(ssv)", interface_name, property_name, val), + NULL, + G_DBUS_CALL_FLAGS_NONE, + 2000, + NULL, + NULL); +} + +/*****************************************************************************/ + +#define _assert_main_context_is_current_source(self, x_context) \ + G_STMT_START \ + { \ + if (NM_MORE_ASSERTS > 0) { \ + GSource *_source = g_main_current_source(); \ + \ + if (_source) { \ + NMClientPrivate *_priv = NM_CLIENT_GET_PRIVATE(self); \ + \ + nm_assert(g_source_get_context(_source) == _priv->x_context); \ + nm_assert(g_main_context_is_owner(_priv->x_context)); \ + } \ + } \ + } \ + G_STMT_END + +#define _assert_main_context_is_current_thread_default(self, x_context) \ + G_STMT_START \ + { \ + if (NM_MORE_ASSERTS > 0) { \ + NMClientPrivate *_priv = NM_CLIENT_GET_PRIVATE(self); \ + \ + nm_assert((g_main_context_get_thread_default() ?: g_main_context_default()) \ + == _priv->x_context); \ + nm_assert(g_main_context_is_owner(_priv->x_context)); \ + } \ + } \ + G_STMT_END + +/*****************************************************************************/ + +void +_nm_client_queue_notify_object(NMClient *self, gpointer nmobj, const GParamSpec *pspec) +{ + NMObjectBase *base; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NM_IS_OBJECT(nmobj) || NM_IS_CLIENT(nmobj)); + + base = (NMObjectBase *) nmobj; + + if (base->is_disposing) { + /* Don't emit property changed signals once the NMClient + * instance is about to shut down. */ + nm_assert(nmobj == self); + return; + } + + if (c_list_is_empty(&base->queue_notify_lst)) { + c_list_link_tail(&NM_CLIENT_GET_PRIVATE(self)->queue_notify_lst_head, + &base->queue_notify_lst); + g_object_ref(nmobj); + g_object_freeze_notify(nmobj); + } + + if (pspec) + g_object_notify_by_pspec(nmobj, (GParamSpec *) pspec); +} + +/*****************************************************************************/ + +gpointer +_nm_client_notify_event_queue(NMClient * self, + int priority, + NMClientNotifyEventCb callback, + gsize event_size) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + NMClientNotifyEvent *notify_event; + + nm_assert(callback); + nm_assert(event_size > sizeof(NMClientNotifyEvent)); + + notify_event = g_malloc(event_size); + notify_event->priority = priority; + notify_event->callback = callback; + c_list_link_tail(&priv->notify_event_lst_head, ¬ify_event->lst); + priv->notify_event_lst_changed = TRUE; + return notify_event; +} + +NMClientNotifyEventWithPtr * +_nm_client_notify_event_queue_with_ptr(NMClient * self, + int priority, + NMClientNotifyEventWithPtrCb callback, + gpointer user_data) +{ + NMClientNotifyEventWithPtr *notify_event; + + notify_event = _nm_client_notify_event_queue(self, + priority, + (NMClientNotifyEventCb) callback, + sizeof(NMClientNotifyEventWithPtr)); + notify_event->user_data = user_data; + return notify_event; +} + +/*****************************************************************************/ + +typedef struct { + NMClientNotifyEvent parent; + GObject * source; + NMObject * obj; + guint signal_id; +} NMClientNotifyEventObjAddedRemove; + +static void +_nm_client_notify_event_queue_emit_obj_signal_cb(NMClient *self, gpointer notify_event_base) +{ + NMClientNotifyEventObjAddedRemove *notify_event = notify_event_base; + + NML_NMCLIENT_LOG_T( + self, + "[%s] emit \"%s\" signal for %s", + NM_IS_CLIENT(notify_event->source) ? "nmclient" : _nm_object_get_path(notify_event->source), + g_signal_name(notify_event->signal_id), + _nm_object_get_path(notify_event->obj)); + + nm_assert(NM_IS_OBJECT(notify_event->source) || NM_IS_CLIENT(notify_event->source)); + + g_signal_emit(notify_event->source, notify_event->signal_id, 0, notify_event->obj); + + g_object_unref(notify_event->obj); + g_object_unref(notify_event->source); +} + +void +_nm_client_notify_event_queue_emit_obj_signal(NMClient *self, + GObject * source, + NMObject *nmobj, + gboolean is_added /* or else removed */, + int prio_offset, + guint signal_id) +{ + NMClientNotifyEventObjAddedRemove *notify_event; + + nm_assert(prio_offset >= 0); + nm_assert(prio_offset < 20); + nm_assert(NM_IS_OBJECT(source) || NM_IS_CLIENT(source)); + nm_assert(NM_IS_OBJECT(nmobj)); + + if (((NMObjectBase *) source)->is_disposing) { + nm_assert(NM_IS_CLIENT(source)); + return; + } + + notify_event = _nm_client_notify_event_queue( + self, + is_added ? NM_CLIENT_NOTIFY_EVENT_PRIO_AFTER - 20 + prio_offset + : NM_CLIENT_NOTIFY_EVENT_PRIO_BEFORE + 20 - prio_offset, + _nm_client_notify_event_queue_emit_obj_signal_cb, + sizeof(NMClientNotifyEventObjAddedRemove)); + notify_event->source = g_object_ref(source); + notify_event->obj = g_object_ref(nmobj); + notify_event->signal_id = signal_id; +} + +/*****************************************************************************/ + +static int +_nm_client_notify_event_cmp(const CList *a, const CList *b, const void *user_data) +{ + NM_CMP_DIRECT(c_list_entry(a, NMClientNotifyEvent, lst)->priority, + c_list_entry(b, NMClientNotifyEvent, lst)->priority); + return 0; +} + +static void +_nm_client_notify_event_emit_parts(NMClient *self, int max_priority /* included! */) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + NMClientNotifyEvent *notify_event; + + while (TRUE) { + if (priv->notify_event_lst_changed) { + priv->notify_event_lst_changed = FALSE; + c_list_sort(&priv->notify_event_lst_head, _nm_client_notify_event_cmp, NULL); + } + notify_event = c_list_first_entry(&priv->notify_event_lst_head, NMClientNotifyEvent, lst); + if (!notify_event) + return; + if (notify_event->priority > max_priority) + return; + c_list_unlink_stale(¬ify_event->lst); + notify_event->callback(self, notify_event); + g_free(notify_event); + } +} + +static void +_nm_client_notify_event_emit(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + NMObjectBase * base; + + _nm_client_notify_event_emit_parts(self, NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP); + + while ( + (base = c_list_first_entry(&priv->queue_notify_lst_head, NMObjectBase, queue_notify_lst))) { + c_list_unlink(&base->queue_notify_lst); + g_object_thaw_notify(G_OBJECT(base)); + g_object_unref(base); + } + + _nm_client_notify_event_emit_parts(self, G_MAXINT); +} + +/*****************************************************************************/ + +GDBusConnection * +_nm_client_get_dbus_connection(NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE(self)->dbus_connection; +} + +const char * +_nm_client_get_dbus_name_owner(NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE(self)->name_owner; +} + +GMainContext * +_nm_client_get_context_main(NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE(self)->main_context; +} + +GMainContext * +_nm_client_get_context_dbus(NMClient *self) +{ + return NM_CLIENT_GET_PRIVATE(self)->dbus_context; +} + +/** + * nm_client_get_main_context: + * @self: the #NMClient instance + * + * The #NMClient instance is permanently associated with the current + * thread default #GMainContext, referenced the time when the instance + * was created. To receive events, the user must iterate this context + * and can use it to synchronize access to the client. + * + * Note that even after #NMClient instance got destroyed, there might + * still be pending sources registered in the context. That means, to fully + * clean up, the user must continue iterating the context as long as + * the nm_client_get_context_busy_watcher() object is alive. + * + * Returns: (transfer none): the #GMainContext of the client. + * + * Since: 1.22 + */ +GMainContext * +nm_client_get_main_context(NMClient *self) +{ + g_return_val_if_fail(NM_IS_CLIENT(self), NULL); + + return _nm_client_get_context_main(self); +} + +/** + * nm_client_get_context_busy_watcher: + * @self: the NMClient instance. + * + * Returns: (transfer none): a GObject that stays alive as long as there are pending + * D-Bus operations. + * + * NMClient will schedule asynchronous D-Bus requests which will complete on + * the GMainContext associated with the instance. When destroying the NMClient + * instance, those requests are cancelled right away, however their pending requests are + * still outstanding and queued in the GMainContext. These outstanding callbacks + * keep the GMainContext alive. In order to fully release all resources, + * the user must keep iterating the main context until all these callbacks + * are handled. Of course, at this point no more actual callbacks will be invoked + * for the user, those are all cancelled internally. + * + * This just leaves one problem: how long does the user need to keep the + * GMainContext running to ensure everything is cleaned up? The answer is + * this GObject. Subscribe a weak reference to the returned object and keep + * iterating the main context until the object got unreferenced. + * + * Note that after the NMClient instance gets destroyed, all outstanding operations + * will be cancelled right away. That means, the user needs to iterate the #GMainContext + * a bit longer, but it is guaranteed that the cleanup happens soon after. + * + * The way of using the context-busy-watch, is by registering a weak pointer to + * see when it gets destroyed. That means, user code should not take additional + * references on this object to not keep it alive longer. + * + * If you plan to exit the program after releasing the NMClient instance + * you may not need to worry about these "leaks". Also, if you anyway plan to continue + * iterating the #GMainContext afterwards, then you don't need to care when exactly + * NMClient is gone completely. + * + * Since: 1.22 + */ +GObject * +nm_client_get_context_busy_watcher(NMClient *self) +{ + GObject *w; + + g_return_val_if_fail(NM_IS_CLIENT(self), NULL); + + w = NM_CLIENT_GET_PRIVATE(self)->context_busy_watcher; + return g_object_get_qdata(w, nm_context_busy_watcher_quark()) ?: w; +} + +struct udev * +_nm_client_get_udev(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + if (G_UNLIKELY(!priv->udev_inited)) { + priv->udev_inited = TRUE; + /* for testing, we don't want to use udev in libnm. */ + if (!nm_streq0(g_getenv("LIBNM_USE_NO_UDEV"), "1")) + priv->udev = udev_new(); + } + + return priv->udev; +} + +/*****************************************************************************/ + +static void +_ASSERT_dbobj(NMLDBusObject *dbobj, NMClient *self) +{ +#if NM_MORE_ASSERTS > 5 + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(dbobj == g_hash_table_lookup(NM_CLIENT_GET_PRIVATE(self)->dbus_objects, dbobj)); +#endif +} + +static NMLDBusObject * +nml_dbus_object_new(NMRefString *dbus_path_take) +{ + NMLDBusObject *dbobj; + + nm_assert(NM_IS_REF_STRING(dbus_path_take)); + + dbobj = g_slice_new(NMLDBusObject); + *dbobj = (NMLDBusObject){ + .dbus_path = g_steal_pointer(&dbus_path_take), + .ref_count = 1, + .dbus_objects_lst = C_LIST_INIT(dbobj->dbus_objects_lst), + .iface_lst_head = C_LIST_INIT(dbobj->iface_lst_head), + .watcher_lst_head = C_LIST_INIT(dbobj->watcher_lst_head), + .obj_changed_lst = C_LIST_INIT(dbobj->obj_changed_lst), + .obj_state = NML_DBUS_OBJ_STATE_UNLINKED, + }; + return dbobj; +} + +NMLDBusObject * +nml_dbus_object_ref(NMLDBusObject *dbobj) +{ + nm_assert(dbobj); + nm_assert(dbobj->ref_count > 0); + + dbobj->ref_count++; + return dbobj; +} + +void +nml_dbus_object_unref(NMLDBusObject *dbobj) +{ + nm_assert(dbobj); + nm_assert(dbobj->ref_count > 0); + + if (--dbobj->ref_count > 0) + return; + + nm_assert(c_list_is_empty(&dbobj->obj_changed_lst)); + nm_assert(c_list_is_empty(&dbobj->iface_lst_head)); + nm_assert(c_list_is_empty(&dbobj->watcher_lst_head)); + nm_assert(!dbobj->nmobj); + + nm_ref_string_unref(dbobj->dbus_path); + nm_g_slice_free(dbobj); +} + +static NMLDBusObjIfaceData * +nml_dbus_object_iface_data_get(NMLDBusObject *dbobj, + const char * dbus_iface_name, + gboolean allow_create) +{ + const NMLDBusMetaIface *meta_iface; + NMLDBusObjIfaceData * db_iface_data; + NMLDBusObjPropData * db_prop_data; + guint count = 0; + guint i; + + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(dbus_iface_name); + +#if NM_MORE_ASSERTS > 10 + { + gboolean expect_well_known = TRUE; + + /* all well-known interfaces must come first in the list. */ + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (db_iface_data->dbus_iface_is_wellknown == expect_well_known) + continue; + nm_assert(expect_well_known); + expect_well_known = FALSE; + } + } +#endif + + meta_iface = nml_dbus_meta_iface_get(dbus_iface_name); + if (meta_iface) { + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->iface_removed) + continue; + if (db_iface_data->dbus_iface.meta == meta_iface) + return db_iface_data; + count++; + } + } else { + c_list_for_each_entry_prev (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + if (db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->iface_removed) + continue; + if (nm_streq(db_iface_data->dbus_iface.name->str, dbus_iface_name)) + return db_iface_data; + count++; + } + } + + if (!allow_create) + return NULL; + + if (count > 20) { + /* We track the list of interfaces that an object has in a linked list. + * That is efficient and convenient, if we assume that each object only has a small + * number of interfaces (which very much should be the case). Here, something is very + * odd, maybe there is a bug or the server side is misbehaving. Anyway, error out. */ + return NULL; + } + + db_iface_data = g_malloc( + G_STRUCT_OFFSET(NMLDBusObjIfaceData, prop_datas) + + (meta_iface ? (sizeof(NMLDBusObjPropData) * meta_iface->n_dbus_properties) : 0u)); + if (meta_iface) { + *db_iface_data = (NMLDBusObjIfaceData){ + .dbus_iface.meta = meta_iface, + .dbus_iface_is_wellknown = TRUE, + .changed_prop_lst_head = C_LIST_INIT(db_iface_data->changed_prop_lst_head), + .iface_removed = FALSE, + }; + db_prop_data = &db_iface_data->prop_datas[0]; + for (i = 0; i < meta_iface->n_dbus_properties; i++, db_prop_data++) { + *db_prop_data = (NMLDBusObjPropData){ + .prop_data_value = NULL, + .changed_prop_lst = C_LIST_INIT(db_prop_data->changed_prop_lst), + }; + } + c_list_link_front(&dbobj->iface_lst_head, &db_iface_data->iface_lst); + } else { + /* Intentionally don't initialize the other fields. We are not supposed + * to touch them, and a valgrind warning would be preferable. */ + db_iface_data->dbus_iface.name = nm_ref_string_new(dbus_iface_name); + db_iface_data->dbus_iface_is_wellknown = FALSE; + db_iface_data->iface_removed = FALSE; + c_list_link_tail(&dbobj->iface_lst_head, &db_iface_data->iface_lst); + } + + return db_iface_data; +} + +static void +nml_dbus_obj_iface_data_destroy(NMLDBusObjIfaceData *db_iface_data) +{ + guint i; + + nm_assert(db_iface_data); + nm_assert(c_list_is_empty(&db_iface_data->iface_lst)); + + if (db_iface_data->dbus_iface_is_wellknown) { + for (i = 0; i < db_iface_data->dbus_iface.meta->n_dbus_properties; i++) + nm_g_variant_unref(db_iface_data->prop_datas[i].prop_data_value); + } else + nm_ref_string_unref(db_iface_data->dbus_iface.name); + + g_free(db_iface_data); +} + +gpointer +nml_dbus_object_get_property_location(NMLDBusObject * dbobj, + const NMLDBusMetaIface * meta_iface, + const NMLDBusMetaProperty *meta_property) +{ + char *target_c; + + target_c = (char *) dbobj->nmobj; + if (meta_iface->base_struct_offset > 0) + target_c = *((gpointer *) (&target_c[meta_iface->base_struct_offset])); + return &target_c[meta_property->prop_struct_offset]; +} + +static void +nml_dbus_object_set_obj_state(NMLDBusObject *dbobj, NMLDBusObjState obj_state, NMClient *self) +{ + NMClientPrivate *priv; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + +#if NM_MORE_ASSERTS > 10 + priv = NM_CLIENT_GET_PRIVATE(self); + switch (dbobj->obj_state) { + case NML_DBUS_OBJ_STATE_UNLINKED: + nm_assert(c_list_is_empty(&dbobj->dbus_objects_lst)); + break; + case NML_DBUS_OBJ_STATE_WATCHED_ONLY: + nm_assert( + c_list_contains(&priv->dbus_objects_lst_head_watched_only, &dbobj->dbus_objects_lst)); + break; + case NML_DBUS_OBJ_STATE_ON_DBUS: + nm_assert(c_list_contains(&priv->dbus_objects_lst_head_on_dbus, &dbobj->dbus_objects_lst)); + break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY: + nm_assert(c_list_contains(&priv->dbus_objects_lst_head_with_nmobj_not_ready, + &dbobj->dbus_objects_lst)); + break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY: + nm_assert(c_list_contains(&priv->dbus_objects_lst_head_with_nmobj_ready, + &dbobj->dbus_objects_lst)); + break; + } +#endif + + if (dbobj->obj_state == obj_state) + return; + + NML_NMCLIENT_LOG_T(self, + "[%s]: set D-Bus object state %s", + dbobj->dbus_path->str, + nml_dbus_obj_state_to_string(obj_state)); + + priv = NM_CLIENT_GET_PRIVATE(self); + dbobj->obj_state = obj_state; + switch (obj_state) { + case NML_DBUS_OBJ_STATE_UNLINKED: + c_list_unlink(&dbobj->dbus_objects_lst); + c_list_unlink(&dbobj->obj_changed_lst); + dbobj->obj_changed_type = NML_DBUS_OBJ_CHANGED_TYPE_NONE; + break; + case NML_DBUS_OBJ_STATE_WATCHED_ONLY: + nm_c_list_move_tail(&priv->dbus_objects_lst_head_watched_only, &dbobj->dbus_objects_lst); + break; + case NML_DBUS_OBJ_STATE_ON_DBUS: + nm_c_list_move_tail(&priv->dbus_objects_lst_head_on_dbus, &dbobj->dbus_objects_lst); + break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY: + nm_c_list_move_tail(&priv->dbus_objects_lst_head_with_nmobj_not_ready, + &dbobj->dbus_objects_lst); + break; + case NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY: + nm_c_list_move_tail(&priv->dbus_objects_lst_head_with_nmobj_ready, + &dbobj->dbus_objects_lst); + break; + default: + nm_assert_not_reached(); + } +} + +/*****************************************************************************/ + +static void +nml_dbus_object_obj_changed_link(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjChangedType changed_type) +{ + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + + if (!NM_FLAGS_ALL((NMLDBusObjChangedType) dbobj->obj_changed_type, changed_type)) + NML_NMCLIENT_LOG_T(self, + "[%s]: changed-type 0x%02x linked", + dbobj->dbus_path->str, + (guint) changed_type); + + if (dbobj->obj_changed_type == NML_DBUS_OBJ_CHANGED_TYPE_NONE) { + NMClientPrivate *priv; + + /* We set the changed-type flag. Need to queue the object in the + * changed list. */ + nm_assert(c_list_is_empty(&dbobj->obj_changed_lst)); + priv = NM_CLIENT_GET_PRIVATE(self); + c_list_link_tail(&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + } else { + /* The object has changes flags and must be linked already. Note that + * this may be priv->obj_changed_lst_head, or a temporary list on the + * stack. + * + * This dance with the temporary list is done to ensure we can enqueue + * objects while we process the changes. */ + nm_assert(!c_list_is_empty(&dbobj->obj_changed_lst)); + } + + dbobj->obj_changed_type |= changed_type; + + nm_assert(NM_FLAGS_ALL(dbobj->obj_changed_type, changed_type)); +} + +static NMLDBusObjChangedType +nml_dbus_object_obj_changed_consume(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjChangedType changed_type) +{ + NMClientPrivate * priv; + NMLDBusObjChangedType changed_type_res; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + nm_assert(dbobj->obj_changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + nm_assert(!c_list_is_empty(&dbobj->obj_changed_lst)); + + changed_type_res = dbobj->obj_changed_type & changed_type; + + dbobj->obj_changed_type &= ~changed_type; + + if (dbobj->obj_changed_type == NML_DBUS_OBJ_CHANGED_TYPE_NONE) { + c_list_unlink(&dbobj->obj_changed_lst); + nm_assert(changed_type_res != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + NML_NMCLIENT_LOG_T(self, + "[%s]: changed-type 0x%02x consumed", + dbobj->dbus_path->str, + (guint) changed_type_res); + return changed_type_res; + } + + priv = NM_CLIENT_GET_PRIVATE(self); + + nm_assert(!c_list_contains(&priv->obj_changed_lst_head, &dbobj->obj_changed_lst)); + nm_c_list_move_tail(&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + NML_NMCLIENT_LOG_T(self, + "[%s]: changed-type 0x%02x consumed (still has 0x%02x)", + dbobj->dbus_path->str, + (guint) changed_type_res, + (guint) dbobj->obj_changed_type); + return changed_type_res; +} + +static gboolean +nml_dbus_object_obj_changed_any_linked(NMClient *self, NMLDBusObjChangedType changed_type) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + NMLDBusObject * dbobj; + + nm_assert(changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + + c_list_for_each_entry (dbobj, &priv->obj_changed_lst_head, obj_changed_lst) { + nm_assert(dbobj->obj_changed_type != NML_DBUS_OBJ_CHANGED_TYPE_NONE); + if (NM_FLAGS_ANY(dbobj->obj_changed_type, changed_type)) + return TRUE; + } + return FALSE; +} + +/*****************************************************************************/ + +static void +_dbobjs_notify_watchers_for_dbobj(NMClient *self, NMLDBusObject *dbobj) +{ + NMLDBusObjWatcher *obj_watcher; + NMLDBusObjWatcher *obj_watcher_safe; + + c_list_for_each_entry_safe (obj_watcher, + obj_watcher_safe, + &dbobj->watcher_lst_head, + _priv.watcher_lst) + obj_watcher->_priv.notify_fcn(self, obj_watcher); +} + +static gboolean +_dbobjs_check_dbobj_ready(NMClient *self, NMLDBusObject *dbobj) +{ + nm_assert(NM_IS_CLIENT(self)); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(G_IS_OBJECT(dbobj->nmobj)); + nm_assert(NM_IS_OBJECT(dbobj->nmobj) || NM_IS_CLIENT(dbobj->nmobj)); + nm_assert(NM_IN_SET((NMLDBusObjState) dbobj->obj_state, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)); + + if (G_LIKELY(dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)) + return TRUE; + + if (!NM_OBJECT_GET_CLASS(dbobj->nmobj)->is_ready(NM_OBJECT(dbobj->nmobj))) + return FALSE; + + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, self); + + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + _dbobjs_notify_watchers_for_dbobj(self, dbobj); + + return TRUE; +} + +void +_nm_client_notify_object_changed(NMClient *self, NMLDBusObject *dbobj) +{ + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + _dbobjs_notify_watchers_for_dbobj(self, dbobj); +} + +/*****************************************************************************/ + +static NMLDBusObject * +_dbobjs_dbobj_get_r(NMClient *self, NMRefString *dbus_path_r) +{ + nm_assert(NM_IS_REF_STRING(dbus_path_r)); + + return g_hash_table_lookup(NM_CLIENT_GET_PRIVATE(self)->dbus_objects, &dbus_path_r); +} + +static NMLDBusObject * +_dbobjs_dbobj_get_s(NMClient *self, const char *dbus_path) +{ + nm_auto_ref_string NMRefString *dbus_path_r = NULL; + + nm_assert(dbus_path); + dbus_path_r = nm_ref_string_new(dbus_path); + return _dbobjs_dbobj_get_r(self, dbus_path_r); +} + +static NMLDBusObject * +_dbobjs_dbobj_create(NMClient *self, NMRefString *dbus_path_take) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer(&dbus_path_take); + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + NMLDBusObject * dbobj; + + nm_assert(!_dbobjs_dbobj_get_r(self, dbus_path)); + + dbobj = nml_dbus_object_new(g_steal_pointer(&dbus_path)); + if (!g_hash_table_add(priv->dbus_objects, dbobj)) + nm_assert_not_reached(); + return dbobj; +} + +static NMLDBusObject * +_dbobjs_dbobj_get_or_create(NMClient *self, NMRefString *dbus_path_take) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer(&dbus_path_take); + NMLDBusObject * dbobj; + + dbobj = _dbobjs_dbobj_get_r(self, dbus_path); + if (dbobj) + return dbobj; + return _dbobjs_dbobj_create(self, g_steal_pointer(&dbus_path)); +} + +static NMLDBusObject * +_dbobjs_get_nmobj(NMClient *self, const char *dbus_path, GType gtype) +{ + NMLDBusObject *dbobj; + + nm_assert(gtype == G_TYPE_NONE || g_type_is_a(gtype, NM_TYPE_OBJECT)); + + dbobj = _dbobjs_dbobj_get_s(self, dbus_path); + + if (!dbobj) + return NULL; + if (!dbobj->nmobj) + return NULL; + + if (gtype != G_TYPE_NONE && !g_type_is_a(G_OBJECT_TYPE(dbobj->nmobj), gtype)) + return NULL; + + return dbobj; +} + +static gpointer +_dbobjs_get_nmobj_unpack_visible(NMClient *self, const char *dbus_path, GType gtype) +{ + NMLDBusObject *dbobj; + + dbobj = _dbobjs_get_nmobj(self, dbus_path, gtype); + if (!dbobj) + return NULL; + if (dbobj->obj_state != NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY) + return NULL; + return dbobj->nmobj; +} + +/*****************************************************************************/ + +static gpointer +_dbobjs_obj_watcher_register_o(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjWatchNotifyFcn notify_fcn, + gsize struct_size) +{ + NMLDBusObjWatcher *obj_watcher; + + nm_assert(NM_IS_CLIENT(self)); + _ASSERT_dbobj(dbobj, self); + nm_assert(notify_fcn); + nm_assert(struct_size > sizeof(NMLDBusObjWatcher)); + + obj_watcher = g_malloc(struct_size); + obj_watcher->dbobj = dbobj; + obj_watcher->_priv.notify_fcn = notify_fcn; + + /* we must enqueue the item in the front of the list. That is, because while + * invoking notify_fcn(), we iterate the watchers front-to-end. As we want to + * allow the callee to register new watches and unregister itself, this is + * the right way to do it. */ + c_list_link_front(&dbobj->watcher_lst_head, &obj_watcher->_priv.watcher_lst); + + return obj_watcher; +} + +static gpointer +_dbobjs_obj_watcher_register_r(NMClient * self, + NMRefString * dbus_path_take, + NMLDBusObjWatchNotifyFcn notify_fcn, + gsize struct_size) +{ + nm_auto_ref_string NMRefString *dbus_path = g_steal_pointer(&dbus_path_take); + NMLDBusObject * dbobj; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(notify_fcn); + + dbobj = _dbobjs_dbobj_get_or_create(self, g_steal_pointer(&dbus_path)); + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_UNLINKED) + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WATCHED_ONLY, self); + return _dbobjs_obj_watcher_register_o(self, dbobj, notify_fcn, struct_size); +} + +static void +_dbobjs_obj_watcher_unregister(NMClient *self, gpointer obj_watcher_base) +{ + NMLDBusObjWatcher *obj_watcher = obj_watcher_base; + NMLDBusObject * dbobj; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(obj_watcher); + nm_assert(NML_IS_DBUS_OBJECT(obj_watcher->dbobj)); + nm_assert(g_hash_table_lookup(NM_CLIENT_GET_PRIVATE(self)->dbus_objects, obj_watcher->dbobj) + == obj_watcher->dbobj); + nm_assert( + c_list_contains(&obj_watcher->dbobj->watcher_lst_head, &obj_watcher->_priv.watcher_lst)); + + c_list_unlink(&obj_watcher->_priv.watcher_lst); + + dbobj = obj_watcher->dbobj; + + g_free(obj_watcher); + + if (c_list_is_empty(&dbobj->iface_lst_head) && c_list_is_empty(&dbobj->watcher_lst_head)) { + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + NML_NMCLIENT_LOG_T(self, "[%s]: drop D-Bus watcher", dbobj->dbus_path->str); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_UNLINKED, self); + if (!g_hash_table_steal(priv->dbus_objects, dbobj)) + nm_assert_not_reached(); + nml_dbus_object_unref(dbobj); + } +} + +/*****************************************************************************/ + +typedef struct { + NMLDBusObjWatcher parent; + NMLDBusPropertyO *pr_o; +} PropertyOData; + +gpointer +nml_dbus_property_o_get_obj(NMLDBusPropertyO *pr_o) +{ + nm_assert(!pr_o->nmobj || nml_dbus_property_o_is_ready(pr_o)); + return pr_o->nmobj; +} + +gboolean +nml_dbus_property_o_is_ready(const NMLDBusPropertyO *pr_o) +{ + return pr_o->is_ready || !pr_o->owner_dbobj; +} + +gboolean +nml_dbus_property_o_is_ready_fully(const NMLDBusPropertyO *pr_o) +{ + return !pr_o->owner_dbobj || !pr_o->obj_watcher || pr_o->nmobj; +} + +static void +nml_dbus_property_o_notify_changed(NMLDBusPropertyO *pr_o, NMClient *self) +{ + const NMLDBusPropertVTableO *vtable; + GObject * nmobj = NULL; + gboolean is_ready = TRUE; + gboolean changed_ready; + GType gtype; + + nm_assert(pr_o); + nm_assert(NM_IS_CLIENT(self)); + + if (!pr_o->owner_dbobj) + return; + + if (!pr_o->is_changed) { + if (pr_o->is_ready) + return; + goto done; + } + + pr_o->is_changed = FALSE; + + if (!pr_o->obj_watcher) + goto done; + + if (!pr_o->obj_watcher->dbobj->nmobj) { + if (pr_o->obj_watcher->dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS) { + NML_NMCLIENT_LOG_W( + self, + "[%s]: property %s references %s but object is not created", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str); + } else { + NML_NMCLIENT_LOG_E( + self, + "[%s]: property %s references %s but object is not present on D-Bus", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str); + } + goto done; + } + + vtable = pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].extra.property_vtable_o; + + gtype = vtable->get_o_type_fcn(); + if (!g_type_is_a(G_OBJECT_TYPE(pr_o->obj_watcher->dbobj->nmobj), gtype)) { + NML_NMCLIENT_LOG_E( + self, + "[%s]: property %s references %s with unexpected GObject type %s instead of %s", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name, + pr_o->obj_watcher->dbobj->dbus_path->str, + G_OBJECT_TYPE_NAME(pr_o->obj_watcher->dbobj->nmobj), + g_type_name(gtype)); + goto done; + } + + if (pr_o->obj_watcher->dbobj == pr_o->owner_dbobj) { + NML_NMCLIENT_LOG_W( + self, + "[%s]: property %s references itself", + pr_o->owner_dbobj->dbus_path->str, + pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].dbus_property_name); + nmobj = pr_o->owner_dbobj->nmobj; + goto done; + } + + pr_o->block_is_changed = TRUE; + is_ready = _dbobjs_check_dbobj_ready(self, pr_o->obj_watcher->dbobj); + pr_o->block_is_changed = FALSE; + + if (!is_ready) { + is_ready = vtable->is_always_ready; + goto done; + } + + nmobj = pr_o->obj_watcher->dbobj->nmobj; + +done: + changed_ready = FALSE; + if (!pr_o->is_ready && is_ready) { + pr_o->is_ready = TRUE; + changed_ready = TRUE; + } + if (pr_o->nmobj != nmobj) { + pr_o->nmobj = nmobj; + _nm_client_queue_notify_object( + self, + pr_o->owner_dbobj->nmobj, + pr_o->meta_iface->obj_properties + [pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].obj_properties_idx]); + } + if (changed_ready && pr_o->owner_dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + nml_dbus_object_obj_changed_link(self, pr_o->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +void +nml_dbus_property_o_notify_changed_many(NMLDBusPropertyO *ptr, guint len, NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_o_notify_changed(ptr++, self); +} + +static void +nml_dbus_property_o_notify_watch_cb(NMClient *self, gpointer obj_watcher) +{ + PropertyOData * pr_o_data = obj_watcher; + NMLDBusPropertyO *pr_o = pr_o_data->pr_o; + + nm_assert(pr_o->obj_watcher == obj_watcher); + + if (!pr_o->block_is_changed && !pr_o->is_changed) { + pr_o->is_changed = TRUE; + nml_dbus_object_obj_changed_link(self, pr_o->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } +} + +static NMLDBusNotifyUpdatePropFlags +nml_dbus_property_o_notify(NMClient * self, + NMLDBusPropertyO * pr_o, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + const char *dbus_path = NULL; + gboolean changed = FALSE; + + if (!pr_o->owner_dbobj) { + nm_assert(!pr_o->meta_iface); + nm_assert(pr_o->dbus_property_idx == 0); + nm_assert(!pr_o->is_ready); + pr_o->owner_dbobj = dbobj; + pr_o->meta_iface = meta_iface; + pr_o->dbus_property_idx = dbus_property_idx; + } else { + nm_assert(pr_o->owner_dbobj == dbobj); + nm_assert(pr_o->meta_iface == meta_iface); + nm_assert(pr_o->dbus_property_idx == dbus_property_idx); + } + + if (value) + dbus_path = nm_dbus_path_not_empty(g_variant_get_string(value, NULL)); + + if (pr_o->obj_watcher + && (!dbus_path || !nm_streq(dbus_path, pr_o->obj_watcher->dbobj->dbus_path->str))) { + _dbobjs_obj_watcher_unregister(self, g_steal_pointer(&pr_o->obj_watcher)); + changed = TRUE; + } + if (!pr_o->obj_watcher && dbus_path) { + pr_o->obj_watcher = _dbobjs_obj_watcher_register_r(self, + nm_ref_string_new(dbus_path), + nml_dbus_property_o_notify_watch_cb, + sizeof(PropertyOData)); + ((PropertyOData *) pr_o->obj_watcher)->pr_o = pr_o; + changed = TRUE; + } + + if (changed && !pr_o->is_changed) { + pr_o->is_changed = TRUE; + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +void +nml_dbus_property_o_clear(NMLDBusPropertyO *pr_o, NMClient *self) +{ + if (pr_o->obj_watcher) { + nm_assert(NM_IS_CLIENT(self)); + _dbobjs_obj_watcher_unregister(self, g_steal_pointer(&pr_o->obj_watcher)); + } + if (pr_o->nmobj && pr_o->owner_dbobj && pr_o->owner_dbobj->nmobj) { + _nm_client_queue_notify_object( + self, + pr_o->owner_dbobj->nmobj, + pr_o->meta_iface->obj_properties + [pr_o->meta_iface->dbus_properties[pr_o->dbus_property_idx].obj_properties_idx]); + } + pr_o->owner_dbobj = NULL; + pr_o->meta_iface = NULL; + pr_o->dbus_property_idx = 0; + pr_o->is_ready = FALSE; +} + +void +nml_dbus_property_o_clear_many(NMLDBusPropertyO *pr_o, guint len, NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_o_clear(pr_o++, self); +} + +/*****************************************************************************/ + +typedef struct _NMLDBusPropertyAOData { + NMLDBusObjWatcher obj_watcher; + NMLDBusPropertyAO * parent; + CList data_lst; + GObject * nmobj; + struct _NMLDBusPropertyAOData *changed_next; + bool is_ready : 1; + bool is_notified : 1; + bool is_changed : 1; + bool block_is_changed : 1; +} PropertyAOData; + +static void +_ASSERT_pr_ao(NMLDBusPropertyAO *pr_ao) +{ + nm_assert(pr_ao); + +#if NM_MORE_ASSERTS > 10 + if (pr_ao->owner_dbobj) { + guint n_not_ready = 0; + guint n_is_changed = 0; + guint n_is_changed_2; + PropertyAOData *pr_ao_data; + + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->is_changed) + n_is_changed++; + if (!pr_ao_data->is_ready) + n_not_ready++; + } + nm_assert(n_not_ready == pr_ao->n_not_ready); + + n_is_changed_2 = 0; + pr_ao_data = pr_ao->changed_head; + while (pr_ao_data) { + nm_assert(pr_ao_data->is_changed); + n_is_changed_2++; + pr_ao_data = pr_ao_data->changed_next; + } + nm_assert(n_is_changed == n_is_changed_2); + } +#endif +} + +static gboolean +nml_dbus_property_ao_notify_changed_ao(PropertyAOData *pr_ao_data, + NMClient * self, + gboolean is_added /* or else removed */) +{ + NMLDBusPropertyAO * pr_ao; + const NMLDBusPropertVTableAO *vtable; + + if (!pr_ao_data->nmobj) + return FALSE; + + nm_assert(pr_ao_data->is_ready); + + if (is_added) { + if (pr_ao_data->is_notified) + return FALSE; + pr_ao_data->is_notified = TRUE; + } else { + if (!pr_ao_data->is_notified) + return FALSE; + pr_ao_data->is_notified = FALSE; + } + + pr_ao = pr_ao_data->parent; + + vtable = pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].extra.property_vtable_ao; + + if (vtable->notify_changed_ao) + vtable->notify_changed_ao(pr_ao, self, NM_OBJECT(pr_ao_data->nmobj), is_added); + return TRUE; +} + +const GPtrArray * +nml_dbus_property_ao_get_objs_as_ptrarray(NMLDBusPropertyAO *pr_ao) +{ + if (!pr_ao->arr) { + PropertyAOData *pr_ao_data; + gsize n; + + n = 0; + if (pr_ao->owner_dbobj) { + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->nmobj) + n++; + } + } + + pr_ao->arr = g_ptr_array_new_full(n, g_object_unref); + if (pr_ao->owner_dbobj) { + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->nmobj) + g_ptr_array_add(pr_ao->arr, g_object_ref(pr_ao_data->nmobj)); + } + } + } + return pr_ao->arr; +} + +gboolean +nml_dbus_property_ao_is_ready(const NMLDBusPropertyAO *pr_ao) +{ + return pr_ao->n_not_ready == 0; +} + +static void +nml_dbus_property_ao_notify_changed(NMLDBusPropertyAO *pr_ao, NMClient *self) +{ + gboolean changed_prop = FALSE; + gboolean changed_ready = FALSE; + PropertyAOData *pr_ao_data; + + nm_assert(NM_IS_CLIENT(self)); + _ASSERT_pr_ao(pr_ao); + + if (!pr_ao->owner_dbobj) + return; + + if (!pr_ao->is_changed) { + if (pr_ao->n_not_ready == 0) + return; + goto done; + } + + pr_ao->is_changed = FALSE; + + while (pr_ao->changed_head) { + const NMLDBusPropertVTableAO *vtable; + GObject * nmobj = NULL; + gboolean is_ready = TRUE; + GType gtype; + + pr_ao_data = g_steal_pointer(&pr_ao->changed_head); + nm_assert(pr_ao_data->is_changed); + + pr_ao->changed_head = pr_ao_data->changed_next; + pr_ao_data->is_changed = FALSE; + + if (!pr_ao_data->obj_watcher.dbobj->nmobj) { + if (pr_ao_data->obj_watcher.dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS) { + NML_NMCLIENT_LOG_W( + self, + "[%s]: property %s references %s but object is not created", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str); + } else { + NML_NMCLIENT_LOG_E( + self, + "[%s]: property %s references %s but object is not present on D-Bus", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str); + } + goto done_pr_ao_data; + } + + vtable = + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].extra.property_vtable_ao; + + gtype = vtable->get_o_type_fcn(); + if (!g_type_is_a(G_OBJECT_TYPE(pr_ao_data->obj_watcher.dbobj->nmobj), gtype)) { + NML_NMCLIENT_LOG_E( + self, + "[%s]: property %s references %s with unexpected GObject type %s instead of %s", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name, + pr_ao_data->obj_watcher.dbobj->dbus_path->str, + G_OBJECT_TYPE_NAME(pr_ao_data->obj_watcher.dbobj->nmobj), + g_type_name(gtype)); + goto done_pr_ao_data; + } + + if (pr_ao_data->obj_watcher.dbobj == pr_ao->owner_dbobj) { + NML_NMCLIENT_LOG_W( + self, + "[%s]: property %s references itself", + pr_ao->owner_dbobj->dbus_path->str, + pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].dbus_property_name); + nmobj = pr_ao->owner_dbobj->nmobj; + goto done_pr_ao_data; + } + + pr_ao_data->block_is_changed = TRUE; + is_ready = _dbobjs_check_dbobj_ready(self, pr_ao_data->obj_watcher.dbobj); + pr_ao_data->block_is_changed = FALSE; + + if (!is_ready) { + is_ready = vtable->is_always_ready; + goto done_pr_ao_data; + } + + if (vtable->check_nmobj_visible_fcn + && !vtable->check_nmobj_visible_fcn(pr_ao_data->obj_watcher.dbobj->nmobj)) { + is_ready = TRUE; + goto done_pr_ao_data; + } + + nmobj = pr_ao_data->obj_watcher.dbobj->nmobj; + +done_pr_ao_data: + + if (!pr_ao_data->is_ready && is_ready) { + nm_assert(pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + pr_ao_data->is_ready = TRUE; + changed_ready = TRUE; + } + + if (pr_ao_data->nmobj != nmobj) { + if (nml_dbus_property_ao_notify_changed_ao(pr_ao_data, self, FALSE)) + changed_prop = TRUE; + pr_ao_data->nmobj = nmobj; + } + + if (!pr_ao_data->is_notified) { + if (nml_dbus_property_ao_notify_changed_ao(pr_ao_data, self, TRUE)) + changed_prop = TRUE; + } + } + + _ASSERT_pr_ao(pr_ao); + +done: + if (changed_prop) { + nm_clear_pointer(&pr_ao->arr, g_ptr_array_unref); + _nm_client_queue_notify_object( + self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface->obj_properties + [pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].obj_properties_idx]); + } + if (changed_ready && pr_ao->n_not_ready == 0 + && pr_ao->owner_dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + nml_dbus_object_obj_changed_link(self, pr_ao->owner_dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +void +nml_dbus_property_ao_notify_changed_many(NMLDBusPropertyAO *ptr, guint len, NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_ao_notify_changed(ptr++, self); +} + +static void +nml_dbus_property_ao_notify_watch_cb(NMClient *self, gpointer obj_watcher) +{ + PropertyAOData * pr_ao_data = obj_watcher; + NMLDBusPropertyAO *pr_ao = pr_ao_data->parent; + + nm_assert(g_hash_table_lookup(pr_ao->hash, pr_ao_data) == pr_ao_data); + + if (!pr_ao_data->block_is_changed && !pr_ao_data->is_changed) { + pr_ao_data->is_changed = TRUE; + pr_ao_data->changed_next = pr_ao->changed_head; + pr_ao->changed_head = pr_ao_data; + if (!pr_ao->is_changed) { + pr_ao->is_changed = TRUE; + nml_dbus_object_obj_changed_link(self, + pr_ao->owner_dbobj, + NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + } + + _ASSERT_pr_ao(pr_ao); +} + +static NMLDBusNotifyUpdatePropFlags +nml_dbus_property_ao_notify(NMClient * self, + NMLDBusPropertyAO * pr_ao, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + CList stale_lst_head = C_LIST_INIT(stale_lst_head); + PropertyAOData *pr_ao_data; + gboolean changed_prop = FALSE; + gboolean changed_obj = FALSE; + + if (!pr_ao->owner_dbobj) { + nm_assert(!pr_ao->data_lst_head.next); + nm_assert(!pr_ao->data_lst_head.prev); + nm_assert(!pr_ao->hash); + nm_assert(!pr_ao->meta_iface); + nm_assert(pr_ao->dbus_property_idx == 0); + nm_assert(pr_ao->n_not_ready == 0); + nm_assert(!pr_ao->changed_head); + nm_assert(!pr_ao->is_changed); + + c_list_init(&pr_ao->data_lst_head); + pr_ao->hash = g_hash_table_new(nm_ppdirect_hash, nm_ppdirect_equal); + pr_ao->owner_dbobj = dbobj; + pr_ao->meta_iface = meta_iface; + pr_ao->dbus_property_idx = dbus_property_idx; + } else { + nm_assert(pr_ao->data_lst_head.next); + nm_assert(pr_ao->data_lst_head.prev); + nm_assert(pr_ao->hash); + nm_assert(pr_ao->meta_iface == meta_iface); + nm_assert(pr_ao->dbus_property_idx == dbus_property_idx); + } + + c_list_splice(&stale_lst_head, &pr_ao->data_lst_head); + + if (value) { + GVariantIter iter; + const char * path; + + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "&o", &path)) { + nm_auto_ref_string NMRefString *dbus_path_r = NULL; + gpointer p_dbus_path_1; + + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(PropertyAOData, obj_watcher) == 0); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLDBusObjWatcher, dbobj) == 0); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLDBusObject, dbus_path) == 0); + + if (!nm_dbus_path_not_empty(path)) { + /* should not happen. Anyway, silently skip empty paths. */ + continue; + } + + dbus_path_r = nm_ref_string_new(path); + p_dbus_path_1 = &dbus_path_r; + pr_ao_data = g_hash_table_lookup(pr_ao->hash, &p_dbus_path_1); + + if (pr_ao_data) { + /* With this implementation we cannot track the same path multiple times. + * Of course, for none of the properties where we use this, the server + * should expose the same path more than once, so this limitation is fine + * (maybe even preferable to drop duplicates form NMClient's API). */ + nm_assert(pr_ao_data->obj_watcher.dbobj->dbus_path == dbus_path_r); + if (!changed_prop && pr_ao_data->is_notified) { + /* The order of a notified entry changed. That means, we need to signal + * a change of the property. This detection of a change is not always + * correct, in particular we might detect some changes when there were + * none. That's not a serious problem, and fixing it would be expensive + * to implement. */ + changed_prop = (c_list_first(&stale_lst_head) != &pr_ao_data->data_lst); + } + nm_c_list_move_tail(&pr_ao->data_lst_head, &pr_ao_data->data_lst); + } else { + pr_ao_data = _dbobjs_obj_watcher_register_r(self, + g_steal_pointer(&dbus_path_r), + nml_dbus_property_ao_notify_watch_cb, + sizeof(PropertyAOData)), + pr_ao_data->parent = pr_ao; + pr_ao_data->nmobj = NULL; + pr_ao_data->changed_next = NULL; + pr_ao_data->is_changed = TRUE; + pr_ao_data->block_is_changed = FALSE; + pr_ao_data->is_ready = FALSE; + pr_ao_data->is_notified = FALSE; + c_list_link_tail(&pr_ao->data_lst_head, &pr_ao_data->data_lst); + if (!g_hash_table_add(pr_ao->hash, pr_ao_data)) + nm_assert_not_reached(); + nm_assert(pr_ao->n_not_ready < G_MAXUINT); + pr_ao->n_not_ready++; + } + +#if NM_MORE_ASSERTS > 10 + { + nm_auto_ref_string NMRefString *p = nm_ref_string_new(path); + gpointer pp = &p; + + nm_assert(g_hash_table_lookup(pr_ao->hash, &pp) == pr_ao_data); + } +#endif + } + } + + pr_ao->changed_head = NULL; + c_list_for_each_entry (pr_ao_data, &pr_ao->data_lst_head, data_lst) { + if (pr_ao_data->is_changed) { + pr_ao_data->changed_next = pr_ao->changed_head; + pr_ao->changed_head = pr_ao_data; + changed_obj = TRUE; + } + } + + while ((pr_ao_data = c_list_first_entry(&stale_lst_head, PropertyAOData, data_lst))) { + changed_obj = TRUE; + c_list_unlink(&pr_ao_data->data_lst); + if (!g_hash_table_remove(pr_ao->hash, pr_ao_data)) + nm_assert_not_reached(); + if (!pr_ao_data->is_ready) { + nm_assert(pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + } else { + if (nml_dbus_property_ao_notify_changed_ao(pr_ao_data, self, FALSE)) + changed_prop = TRUE; + } + _dbobjs_obj_watcher_unregister(self, pr_ao_data); + } + + _ASSERT_pr_ao(pr_ao); + + if (changed_obj) { + pr_ao->is_changed = TRUE; + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); + } + + if (changed_prop) { + nm_clear_pointer(&pr_ao->arr, g_ptr_array_unref); + _nm_client_queue_notify_object( + self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface->obj_properties + [pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx].obj_properties_idx]); + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +void +nml_dbus_property_ao_clear(NMLDBusPropertyAO *pr_ao, NMClient *self) +{ + _ASSERT_pr_ao(pr_ao); + + if (!pr_ao->owner_dbobj) { + nm_assert(pr_ao->n_not_ready == 0); + nm_assert((!pr_ao->data_lst_head.next && !pr_ao->data_lst_head.prev) + || (pr_ao->data_lst_head.next == pr_ao->data_lst_head.prev)); + nm_assert(!pr_ao->hash); + nm_assert(!pr_ao->meta_iface); + nm_assert(pr_ao->dbus_property_idx == 0); + nm_assert(!pr_ao->is_changed); + } else { + PropertyAOData *pr_ao_data; + gboolean changed_prop = FALSE; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(pr_ao->data_lst_head.next); + nm_assert(pr_ao->data_lst_head.prev); + nm_assert(pr_ao->hash); + nm_assert(pr_ao->meta_iface); + + while ((pr_ao_data = c_list_first_entry(&pr_ao->data_lst_head, PropertyAOData, data_lst))) { + if (!pr_ao_data->is_ready) { + nm_assert(pr_ao->n_not_ready > 0); + pr_ao->n_not_ready--; + } else { + if (nml_dbus_property_ao_notify_changed_ao(pr_ao_data, self, FALSE)) + changed_prop = TRUE; + } + c_list_unlink(&pr_ao_data->data_lst); + if (!g_hash_table_remove(pr_ao->hash, pr_ao_data)) + nm_assert_not_reached(); + _dbobjs_obj_watcher_unregister(self, pr_ao_data); + } + + nm_assert(c_list_is_empty(&pr_ao->data_lst_head)); + nm_assert(pr_ao->n_not_ready == 0); + nm_assert(g_hash_table_size(pr_ao->hash) == 0); + + if (changed_prop && pr_ao->owner_dbobj->nmobj) { + _nm_client_queue_notify_object( + self, + pr_ao->owner_dbobj->nmobj, + pr_ao->meta_iface + ->obj_properties[pr_ao->meta_iface->dbus_properties[pr_ao->dbus_property_idx] + .obj_properties_idx]); + } + + nm_assert(c_list_is_empty(&pr_ao->data_lst_head)); + nm_assert(pr_ao->n_not_ready == 0); + nm_assert(g_hash_table_size(pr_ao->hash) == 0); + nm_clear_pointer(&pr_ao->hash, g_hash_table_unref); + pr_ao->owner_dbobj = NULL; + pr_ao->meta_iface = NULL; + pr_ao->dbus_property_idx = 0; + pr_ao->data_lst_head.next = NULL; + pr_ao->data_lst_head.prev = NULL; + pr_ao->is_changed = FALSE; + } + + nm_clear_pointer(&pr_ao->arr, g_ptr_array_unref); +} + +void +nml_dbus_property_ao_clear_many(NMLDBusPropertyAO *pr_ao, guint len, NMClient *self) +{ + while (len-- > 0) + nml_dbus_property_ao_clear(pr_ao++, self); +} + +/*****************************************************************************/ + +NMLDBusNotifyUpdatePropFlags +_nml_dbus_notify_update_prop_ignore(NMClient * self, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +NMLDBusNotifyUpdatePropFlags +_nml_dbus_notify_update_prop_o(NMClient * self, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + const char * path = NULL; + NMRefString **p_property; + + if (value) + path = g_variant_get_string(value, NULL); + + p_property = + nml_dbus_object_get_property_location(dbobj, + meta_iface, + &meta_iface->dbus_properties[dbus_property_idx]); + + if (!nm_streq0(nm_ref_string_get_str(*p_property), path)) { + nm_ref_string_unref(*p_property); + *p_property = nm_ref_string_new(path); + } + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +_obj_handle_dbus_prop_changes(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjIfaceData *db_iface_data, + guint dbus_property_idx, + GVariant * value) +{ + const NMLDBusMetaIface * meta_iface = db_iface_data->dbus_iface.meta; + const NMLDBusMetaProperty * meta_property = &meta_iface->dbus_properties[dbus_property_idx]; + gpointer p_property; + const char * dbus_type_s; + const GParamSpec * param_spec; + NMLDBusNotifyUpdatePropFlags notify_update_prop_flags; + + nm_assert(G_IS_OBJECT(dbobj->nmobj)); + + if (value && !g_variant_is_of_type(value, meta_property->dbus_type)) { + NML_NMCLIENT_LOG_E(self, + "[%s] property %s.%s expected of type \"%s\" but is \"%s\". Ignore", + dbobj->dbus_path->str, + meta_iface->dbus_iface_name, + meta_property->dbus_property_name, + (const char *) meta_property->dbus_type, + (const char *) g_variant_get_type(value)); + value = NULL; + } + + if (meta_property->use_notify_update_prop) { + notify_update_prop_flags = + meta_property->notify_update_prop(self, dbobj, meta_iface, dbus_property_idx, value); + if (notify_update_prop_flags == NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE) + return; + + nm_assert(notify_update_prop_flags == NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY); + nm_assert(G_IS_OBJECT(dbobj->nmobj)); + nm_assert(meta_iface->obj_properties); + nm_assert(meta_property->obj_properties_idx > 0); + param_spec = meta_iface->obj_properties[meta_property->obj_properties_idx]; + goto notify; + } + + p_property = nml_dbus_object_get_property_location(dbobj, meta_iface, meta_property); + + dbus_type_s = (const char *) meta_property->dbus_type; + + nm_assert(G_IS_OBJECT(dbobj->nmobj)); + nm_assert(meta_iface->obj_properties); + nm_assert(meta_property->obj_properties_idx > 0); + param_spec = meta_iface->obj_properties[meta_property->obj_properties_idx]; + + notify_update_prop_flags = NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; + + switch (dbus_type_s[0]) { + case 'b': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((bool *) p_property) = g_variant_get_boolean(value); + else if (param_spec->value_type == G_TYPE_BOOLEAN) + *((bool *) p_property) = ((GParamSpecBoolean *) param_spec)->default_value; + else { + nm_assert_not_reached(); + *((bool *) p_property) = FALSE; + } + break; + case 'y': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((guint8 *) p_property) = g_variant_get_byte(value); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((guint8 *) p_property) = 0; + } + break; + case 'q': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((guint16 *) p_property) = g_variant_get_uint16(value); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((guint16 *) p_property) = 0; + } + break; + case 'i': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((gint32 *) p_property) = g_variant_get_int32(value); + else if (param_spec->value_type == G_TYPE_INT) + *((gint32 *) p_property) = ((GParamSpecInt *) param_spec)->default_value; + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((gint32 *) p_property) = 0; + } + break; + case 'u': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((guint32 *) p_property) = g_variant_get_uint32(value); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((guint32 *) p_property) = 0; + } + break; + case 'x': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((gint64 *) p_property) = g_variant_get_int64(value); + else if (param_spec->value_type == G_TYPE_INT64) + *((gint64 *) p_property) = ((GParamSpecInt64 *) param_spec)->default_value; + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((gint64 *) p_property) = 0; + } + break; + case 't': + nm_assert(dbus_type_s[1] == '\0'); + if (value) + *((guint64 *) p_property) = g_variant_get_uint64(value); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((guint64 *) p_property) = 0; + } + break; + case 's': + nm_assert(dbus_type_s[1] == '\0'); + nm_clear_g_free((char **) p_property); + if (value) + *((char **) p_property) = g_variant_dup_string(value, NULL); + else { + nm_assert(nm_utils_g_param_spec_is_default(param_spec)); + *((char **) p_property) = NULL; + } + break; + case 'o': + nm_assert(dbus_type_s[1] == '\0'); + notify_update_prop_flags = nml_dbus_property_o_notify(self, + p_property, + dbobj, + meta_iface, + dbus_property_idx, + value); + break; + case 'a': + switch (dbus_type_s[1]) { + case 'y': + nm_assert(dbus_type_s[2] == '\0'); + { + gconstpointer v; + gsize l; + GBytes * b = NULL; + + if (value) { + v = g_variant_get_fixed_array(value, &l, 1); + + if (l > 0) { + /* empty arrays are coerced to NULL. */ + b = g_bytes_new(v, l); + } + } + + nm_clear_pointer((GBytes **) p_property, g_bytes_unref); + *((GBytes **) p_property) = b; + } + break; + case 's': + nm_assert(dbus_type_s[2] == '\0'); + nm_assert(param_spec->value_type == G_TYPE_STRV); + + g_strfreev(*((char ***) p_property)); + if (value) + *((char ***) p_property) = g_variant_dup_strv(value, NULL); + else + *((char ***) p_property) = NULL; + break; + case 'o': + nm_assert(dbus_type_s[2] == '\0'); + notify_update_prop_flags = nml_dbus_property_ao_notify(self, + p_property, + dbobj, + meta_iface, + dbus_property_idx, + value); + break; + default: + nm_assert_not_reached(); + } + break; + default: + nm_assert_not_reached(); + } + +notify: + if (NM_FLAGS_HAS(notify_update_prop_flags, NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY)) + g_object_notify_by_pspec(dbobj->nmobj, (GParamSpec *) param_spec); +} + +static void +_obj_handle_dbus_iface_changes(NMClient * self, + NMLDBusObject * dbobj, + NMLDBusObjIfaceData *db_iface_data) +{ + NMLDBusObjPropData *db_prop_data; + gboolean is_self = (G_OBJECT(self) == dbobj->nmobj); + gboolean is_removed; + gboolean type_compatible; + guint8 i_prop; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(is_self || NM_IS_OBJECT(dbobj->nmobj)); + + if (G_UNLIKELY(!db_iface_data->nmobj_checked)) { + db_iface_data->nmobj_checked = TRUE; + type_compatible = db_iface_data->dbus_iface.meta->get_type_fcn + && g_type_is_a(G_OBJECT_TYPE(dbobj->nmobj), + db_iface_data->dbus_iface.meta->get_type_fcn()); + db_iface_data->nmobj_compatible = type_compatible; + } else + type_compatible = db_iface_data->nmobj_compatible; + + if (!type_compatible) { + /* on D-Bus, we have this interface associate with the object, but apparently + * it is not compatible. This is either a bug, or NetworkManager exposed an + * unexpected interface on D-Bus object for which we create a certain NMObject + * type. */ + return; + } + + is_removed = c_list_is_empty(&db_iface_data->iface_lst); + + nm_assert(is_removed || !c_list_is_empty(&db_iface_data->changed_prop_lst_head)); + + _nm_client_queue_notify_object(self, dbobj->nmobj, NULL); + + if (is_removed) { + for (i_prop = 0; i_prop < db_iface_data->dbus_iface.meta->n_dbus_properties; i_prop++) { + _obj_handle_dbus_prop_changes(self, dbobj, db_iface_data, i_prop, NULL); + } + } else { + while ((db_prop_data = c_list_first_entry(&db_iface_data->changed_prop_lst_head, + NMLDBusObjPropData, + changed_prop_lst))) { + gs_unref_variant GVariant *prop_data_value = NULL; + + c_list_unlink(&db_prop_data->changed_prop_lst); + + nm_assert(db_prop_data >= db_iface_data->prop_datas); + nm_assert( + db_prop_data + < &db_iface_data->prop_datas[db_iface_data->dbus_iface.meta->n_dbus_properties]); + nm_assert(db_prop_data->prop_data_value); + + /* Currently, NMLDBusObject forgets about the variant. Theoretically, it could cache + * it, but there is no need because we update the property in nmobj (which extracts and + * keeps the property value itself). + * + * Note that we only consume the variant here when we process it. + * That implies that we already created a NMObject for the dbobj + * instance. Unless that happens, we cache the last seen property values. */ + prop_data_value = g_steal_pointer(&db_prop_data->prop_data_value); + + i_prop = (db_prop_data - &db_iface_data->prop_datas[0]); + _obj_handle_dbus_prop_changes(self, dbobj, db_iface_data, i_prop, prop_data_value); + } + } +} + +static void +_obj_handle_dbus_changes(NMClient *self, NMLDBusObject *dbobj) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + NMLDBusObjIfaceData *db_iface_data; + NMLDBusObjIfaceData *db_iface_data_safe; + gs_unref_object GObject *nmobj_unregistering = NULL; + + _ASSERT_dbobj(dbobj, self); + + /* In a first step we only remember all the changes that a D-Bus message brings + * and queue the object to process them. + * + * Here (in step 2) we look at what changed on D-Bus and propagate those changes + * to the NMObject instance. + * + * Note that here we still must not emit any GObject signals. That follows later, + * and again if the object changes, we will just queue that we handle the changes + * later. */ + + c_list_for_each_entry_safe (db_iface_data, + db_iface_data_safe, + &dbobj->iface_lst_head, + iface_lst) { + if (!db_iface_data->iface_removed) + continue; + c_list_unlink(&db_iface_data->iface_lst); + if (db_iface_data->dbus_iface_is_wellknown && dbobj->nmobj) + _obj_handle_dbus_iface_changes(self, dbobj, db_iface_data); + nml_dbus_obj_iface_data_destroy(db_iface_data); + } + + if (G_UNLIKELY(!dbobj->nmobj) && !c_list_is_empty(&dbobj->iface_lst_head)) { + /* Try to create a NMObject for this D-Bus object. Note that we detect the type + * based on the interfaces that it has, and if we make a choice once, we don't + * change. That means, one D-Bus object can only be of one type. */ + + if (NM_IN_SET(dbobj->dbus_path, + _dbus_path_nm, + _dbus_path_settings, + _dbus_path_dns_manager)) { + /* For the main types, we don't detect them based on the interfaces present, + * but on the path names. Of course, both should correspond anyway. */ + NML_NMCLIENT_LOG_T(self, + "[%s]: register NMClient for D-Bus object", + dbobj->dbus_path->str); + dbobj->nmobj = G_OBJECT(self); + if (dbobj->dbus_path == _dbus_path_nm) { + nm_assert(!priv->dbobj_nm); + priv->dbobj_nm = dbobj; + } else if (dbobj->dbus_path == _dbus_path_settings) { + nm_assert(!priv->dbobj_settings); + priv->dbobj_settings = dbobj; + } else { + nm_assert(dbobj->dbus_path == _dbus_path_dns_manager); + nm_assert(!priv->dbobj_dns_manager); + priv->dbobj_dns_manager = dbobj; + } + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, self); + } else { + GType gtype = G_TYPE_NONE; + NMLDBusMetaInteracePrio curr_prio = NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10 - 1; + + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + nm_assert(!db_iface_data->iface_removed); + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (db_iface_data->dbus_iface.meta->interface_prio <= curr_prio) + continue; + curr_prio = db_iface_data->dbus_iface.meta->interface_prio; + gtype = db_iface_data->dbus_iface.meta->get_type_fcn(); + } + if (gtype != G_TYPE_NONE) { + dbobj->nmobj = g_object_new(gtype, NULL); + + NML_NMCLIENT_LOG_T(self, + "[%s]: register new NMObject " NM_HASH_OBFUSCATE_PTR_FMT + " of type %s", + dbobj->dbus_path->str, + NM_HASH_OBFUSCATE_PTR(dbobj->nmobj), + g_type_name(gtype)); + + nm_assert(NM_IS_OBJECT(dbobj->nmobj)); + NM_OBJECT_GET_CLASS(dbobj->nmobj) + ->register_client(NM_OBJECT(dbobj->nmobj), self, dbobj); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, self); + } + } + } + + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) { + nm_assert(!db_iface_data->iface_removed); + if (!db_iface_data->dbus_iface_is_wellknown) + break; + if (c_list_is_empty(&db_iface_data->changed_prop_lst_head)) + continue; + if (dbobj->nmobj) + _obj_handle_dbus_iface_changes(self, dbobj, db_iface_data); + } + + if (c_list_is_empty(&dbobj->iface_lst_head) && dbobj->nmobj) { + if (dbobj->nmobj == G_OBJECT(self)) { + dbobj->nmobj = NULL; + NML_NMCLIENT_LOG_T(self, + "[%s]: unregister NMClient from D-Bus object", + dbobj->dbus_path->str); + if (dbobj->dbus_path == _dbus_path_nm) { + nm_assert(priv->dbobj_nm == dbobj); + priv->dbobj_nm = NULL; + nml_dbus_property_o_clear_many(priv->nm.property_o, + G_N_ELEMENTS(priv->nm.property_o), + self); + nml_dbus_property_ao_clear_many(priv->nm.property_ao, + G_N_ELEMENTS(priv->nm.property_ao), + self); + } else if (dbobj->dbus_path == _dbus_path_settings) { + nm_assert(priv->dbobj_settings == dbobj); + priv->dbobj_settings = NULL; + nml_dbus_property_ao_clear(&priv->settings.connections, self); + } else { + nm_assert(dbobj->dbus_path == _dbus_path_dns_manager); + nm_assert(priv->dbobj_dns_manager == dbobj); + priv->dbobj_dns_manager = NULL; + } + } else { + nmobj_unregistering = g_steal_pointer(&dbobj->nmobj); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_WATCHED_ONLY, self); + NML_NMCLIENT_LOG_T(self, + "[%s]: unregister NMObject " NM_HASH_OBFUSCATE_PTR_FMT " of type %s", + dbobj->dbus_path->str, + NM_HASH_OBFUSCATE_PTR(nmobj_unregistering), + g_type_name(G_OBJECT_TYPE(nmobj_unregistering))); + NM_OBJECT_GET_CLASS(nmobj_unregistering) + ->unregister_client(NM_OBJECT(nmobj_unregistering), self, dbobj); + } + } + + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ); +} + +/*****************************************************************************/ + +static void +_dbus_handle_obj_changed_nmobj(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + NMLDBusObject * dbobj; + CList obj_changed_tmp_lst_head = C_LIST_INIT(obj_changed_tmp_lst_head); + + nm_assert(!nml_dbus_object_obj_changed_any_linked(self, ~NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + + /* First we notify all watchers that these objects changed. Note that we only do that + * here for the list before processing the changes below in a loop. That is, because + * processing changes can again enqueue changed objects, and we only want to want to + * notify watchers for the events that happened earlier (not repeatedly notify them). */ + c_list_splice(&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + while ( + (dbobj = c_list_first_entry(&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + nm_c_list_move_tail(&priv->obj_changed_lst_head, &dbobj->obj_changed_lst); + _dbobjs_notify_watchers_for_dbobj(self, dbobj); + } + +again: + + nm_assert(!nml_dbus_object_obj_changed_any_linked(self, ~NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + + c_list_splice(&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + + while ( + (dbobj = c_list_first_entry(&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + if (!nml_dbus_object_obj_changed_consume(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)) { + nm_assert_not_reached(); + continue; + } + + if (!dbobj->nmobj) + continue; + + if (dbobj->nmobj == G_OBJECT(self)) { + if (dbobj == priv->dbobj_nm) { + nml_dbus_property_o_notify_changed_many(priv->nm.property_o, + G_N_ELEMENTS(priv->nm.property_o), + self); + nml_dbus_property_ao_notify_changed_many(priv->nm.property_ao, + G_N_ELEMENTS(priv->nm.property_ao), + self); + } else if (dbobj == priv->dbobj_settings) + nml_dbus_property_ao_notify_changed(&priv->settings.connections, self); + else + nm_assert(dbobj == priv->dbobj_dns_manager); + } else + NM_OBJECT_GET_CLASS(dbobj->nmobj)->obj_changed_notify(NM_OBJECT(dbobj->nmobj)); + + _dbobjs_check_dbobj_ready(self, dbobj); + } + + if (!c_list_is_empty(&priv->obj_changed_lst_head)) { + nm_assert(nml_dbus_object_obj_changed_any_linked(self, NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ)); + /* we got new changes enqueued. Need to check again. */ + goto again; + } +} + +static void +_dbus_handle_obj_changed_dbus(NMClient *self, const char *log_context) +{ + NMClientPrivate *priv; + NMLDBusObject * dbobj; + CList obj_changed_tmp_lst_head = C_LIST_INIT(obj_changed_tmp_lst_head); + + priv = NM_CLIENT_GET_PRIVATE(self); + + /* We move the changed list onto a temporary list and consume that. + * Note that nml_dbus_object_obj_changed_consume() will move the object + * back to the original list if there are changes of another type. + * + * This is done so that we can enqueue more changes while processing the + * change list. */ + c_list_splice(&obj_changed_tmp_lst_head, &priv->obj_changed_lst_head); + + while ( + (dbobj = c_list_first_entry(&obj_changed_tmp_lst_head, NMLDBusObject, obj_changed_lst))) { + nm_auto_unref_nml_dbusobj NMLDBusObject *dbobj_unref = NULL; + + if (!nml_dbus_object_obj_changed_consume(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS)) + continue; + + nm_assert(dbobj->obj_state >= NML_DBUS_OBJ_STATE_ON_DBUS); + + dbobj_unref = nml_dbus_object_ref(dbobj); + + _obj_handle_dbus_changes(self, dbobj); + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_UNLINKED) + continue; + + if (c_list_is_empty(&dbobj->iface_lst_head) && c_list_is_empty(&dbobj->watcher_lst_head)) { + NML_NMCLIENT_LOG_T(self, "[%s]: drop D-Bus instance", dbobj->dbus_path->str); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_UNLINKED, self); + if (!g_hash_table_steal(priv->dbus_objects, dbobj)) + nm_assert_not_reached(); + nml_dbus_object_unref(dbobj); + } + } + + /* D-Bus changes can only be enqueued in an earlier stage. We don't expect + * anymore changes of type D-Bus at this point. */ + nm_assert(!nml_dbus_object_obj_changed_any_linked(self, NML_DBUS_OBJ_CHANGED_TYPE_DBUS)); +} + +static void +_dbus_handle_changes_commit(NMClient *self, gboolean allow_init_start_check_complete) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + _dbus_handle_obj_changed_nmobj(self); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->main_context); + + _nm_client_notify_event_emit(self); + + _set_nm_running(self); + + if (allow_init_start_check_complete) + _init_start_check_complete(self); +} + +static void +_dbus_handle_changes(NMClient * self, + const char *log_context, + gboolean allow_init_start_check_complete) +{ + _dbus_handle_obj_changed_dbus(self, log_context); + _dbus_handle_changes_commit(self, allow_init_start_check_complete); +} + +static gboolean +_dbus_handle_properties_changed(NMClient * self, + const char * log_context, + const char * object_path, + const char * interface_name, + gboolean allow_add_iface, + GVariant * changed_properties, + NMLDBusObject **inout_dbobj) +{ + NMLDBusObject * dbobj = NULL; + NMLDBusObjIfaceData *db_iface_data = NULL; + nm_auto_ref_string NMRefString *dbus_path = NULL; + + nm_assert(!changed_properties + || g_variant_is_of_type(changed_properties, G_VARIANT_TYPE("a{sv}"))); + + { + gs_free char *ss = NULL; + + NML_NMCLIENT_LOG_T(self, + "[%s]: %s: properties changed for interface %s %s%s%s", + object_path, + log_context, + interface_name, + NM_PRINT_FMT_QUOTED(changed_properties, + "{ ", + (ss = g_variant_print(changed_properties, TRUE)), + " }", + "(no changed properties)")); + } + + if (inout_dbobj) { + dbobj = *inout_dbobj; + nm_assert(!dbobj || nm_streq(object_path, dbobj->dbus_path->str)); + } + if (!dbobj) { + dbus_path = nm_ref_string_new(object_path); + dbobj = _dbobjs_dbobj_get_r(self, dbus_path); + } + + if (dbobj) { + nm_assert(dbobj->obj_state >= NML_DBUS_OBJ_STATE_WATCHED_ONLY); + db_iface_data = nml_dbus_object_iface_data_get(dbobj, interface_name, allow_add_iface); + if (db_iface_data && dbobj->obj_state == NML_DBUS_OBJ_STATE_WATCHED_ONLY) + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_ON_DBUS, self); + } else if (allow_add_iface) { + dbobj = _dbobjs_dbobj_create(self, g_steal_pointer(&dbus_path)); + nml_dbus_object_set_obj_state(dbobj, NML_DBUS_OBJ_STATE_ON_DBUS, self); + db_iface_data = nml_dbus_object_iface_data_get(dbobj, interface_name, TRUE); + nm_assert(db_iface_data); + } + + NM_SET_OUT(inout_dbobj, dbobj); + + if (!db_iface_data) { + if (allow_add_iface) + NML_NMCLIENT_LOG_E(self, + "%s: [%s] too many interfaces on object. Something is very wrong", + log_context, + object_path); + else + NML_NMCLIENT_LOG_E(self, + "%s: [%s] property changed signal for non existing interface %s", + log_context, + object_path, + interface_name); + nm_assert(!dbobj || dbobj->obj_state != NML_DBUS_OBJ_STATE_UNLINKED); + return FALSE; + } + + if (!db_iface_data->dbus_iface_is_wellknown) + NML_NMCLIENT_LOG_W(self, + "%s: [%s] ignore unknown interface %s", + log_context, + object_path, + interface_name); + else if (changed_properties) { + GVariantIter iter_prop; + const char * property_name; + GVariant * property_value_tmp; + + g_variant_iter_init(&iter_prop, changed_properties); + while (g_variant_iter_next(&iter_prop, "{&sv}", &property_name, &property_value_tmp)) { + _nm_unused gs_unref_variant GVariant *property_value = property_value_tmp; + const NMLDBusMetaProperty * meta_property; + NMLDBusObjPropData * db_propdata; + guint property_idx; + + meta_property = nml_dbus_meta_property_get(db_iface_data->dbus_iface.meta, + property_name, + &property_idx); + if (!meta_property) { + NML_NMCLIENT_LOG_W(self, + "%s: [%s]: ignore unknown property %s.%s", + log_context, + object_path, + interface_name, + property_name); + continue; + } + + db_propdata = &db_iface_data->prop_datas[property_idx]; + + NML_NMCLIENT_LOG_T(self, + "[%s]: %s: %s property %s.%s", + object_path, + log_context, + db_propdata->prop_data_value ? "update" : "set", + interface_name, + property_name); + + nm_g_variant_unref(db_propdata->prop_data_value); + db_propdata->prop_data_value = g_steal_pointer(&property_value); + nm_c_list_move_tail(&db_iface_data->changed_prop_lst_head, + &db_propdata->changed_prop_lst); + } + } + + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + return TRUE; +} + +static gboolean +_dbus_handle_interface_added(NMClient * self, + const char *log_context, + const char *object_path, + GVariant * ifaces) +{ + gboolean changed = FALSE; + const char * interface_name; + GVariant * changed_properties; + GVariantIter iter_ifaces; + NMLDBusObject *dbobj = NULL; + + nm_assert(g_variant_is_of_type(ifaces, G_VARIANT_TYPE("a{sa{sv}}"))); + + g_variant_iter_init(&iter_ifaces, ifaces); + while (g_variant_iter_next(&iter_ifaces, "{&s@a{sv}}", &interface_name, &changed_properties)) { + _nm_unused gs_unref_variant GVariant *changed_properties_free = changed_properties; + + if (_dbus_handle_properties_changed(self, + log_context, + object_path, + interface_name, + TRUE, + changed_properties, + &dbobj)) + changed = TRUE; + } + + return changed; +} + +static gboolean +_dbus_handle_interface_removed(NMClient * self, + const char * log_context, + const char * object_path, + NMLDBusObject ** inout_dbobj, + const char *const *removed_interfaces) +{ + gboolean changed = FALSE; + NMLDBusObject *dbobj; + gsize i; + + if (inout_dbobj && *inout_dbobj) { + dbobj = *inout_dbobj; + nm_assert(dbobj == _dbobjs_dbobj_get_s(self, object_path)); + } else { + dbobj = _dbobjs_dbobj_get_s(self, object_path); + if (!dbobj) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s]: receive interface removed event for non existing object", + log_context, + object_path); + return FALSE; + } + NM_SET_OUT(inout_dbobj, dbobj); + } + + for (i = 0; removed_interfaces[i]; i++) { + NMLDBusObjIfaceData *db_iface_data; + const char * interface_name = removed_interfaces[i]; + + db_iface_data = nml_dbus_object_iface_data_get(dbobj, interface_name, FALSE); + if (!db_iface_data) { + NML_NMCLIENT_LOG_E( + self, + "%s: [%s] receive interface remove event for unexpected interface %s", + log_context, + object_path, + interface_name); + continue; + } + + NML_NMCLIENT_LOG_T(self, + "%s: [%s] receive interface remove event for interface %s", + log_context, + object_path, + interface_name); + db_iface_data->iface_removed = TRUE; + changed = TRUE; + } + + if (changed) + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + + return changed; +} + +static void +_dbus_managed_objects_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * arg_object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * log_context; + gboolean changed; + + nm_assert(nm_streq0(interface_name, DBUS_INTERFACE_OBJECT_MANAGER)); + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (nm_streq(signal_name, "InterfacesAdded")) { + gs_unref_variant GVariant *interfaces_and_properties = NULL; + const char * object_path; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(oa{sa{sv}})"))) + return; + + g_variant_get(parameters, "(&o@a{sa{sv}})", &object_path, &interfaces_and_properties); + + log_context = "interfaces-added"; + changed = + _dbus_handle_interface_added(self, log_context, object_path, interfaces_and_properties); + goto out; + } + + if (nm_streq(signal_name, "InterfacesRemoved")) { + gs_free const char **interfaces = NULL; + const char * object_path; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(oas)"))) + return; + + g_variant_get(parameters, "(&o^a&s)", &object_path, &interfaces); + + log_context = "interfaces-removed"; + changed = _dbus_handle_interface_removed(self, log_context, object_path, NULL, interfaces); + goto out; + } + + return; + +out: + if (changed) + _dbus_handle_changes(self, log_context, TRUE); +} + +static void +_dbus_properties_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * interface_name; + gs_unref_variant GVariant *changed_properties = NULL; + gs_free const char ** invalidated_properties = NULL; + const char * log_context = "properties-changed"; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sa{sv}as)"))) + return; + + g_variant_get(parameters, + "(&s@a{sv}^a&s)", + &interface_name, + &changed_properties, + &invalidated_properties); + + if (invalidated_properties && invalidated_properties[0]) { + NML_NMCLIENT_LOG_W(self, + "%s: [%s] ignore invalidated properties on interface %s", + log_context, + object_path, + interface_name); + } + + if (_dbus_handle_properties_changed(self, + log_context, + object_path, + interface_name, + FALSE, + changed_properties, + NULL)) + _dbus_handle_changes(self, log_context, TRUE); +} + +static void +_dbus_get_managed_objects_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMClient * self; + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *managed_objects = NULL; + gs_free_error GError *error = NULL; + gs_unref_object GObject *context_busy_watcher = NULL; + + nm_utils_user_data_unpack(user_data, &self, &context_busy_watcher); + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + nm_assert((!!ret) != (!!error)); + + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + priv = NM_CLIENT_GET_PRIVATE(self); + + if (ret) { + nm_assert(g_variant_is_of_type(ret, G_VARIANT_TYPE("(a{oa{sa{sv}}})"))); + managed_objects = g_variant_get_child_value(ret, 0); + } + + g_clear_object(&priv->get_managed_objects_cancellable); + + if (!managed_objects) { + NML_NMCLIENT_LOG_D(self, "GetManagedObjects() call failed: %s", error->message); + /* hm, now that's odd. Maybe NetworkManager just quit and we are about to get + * a name-owner changed signal soon. Treat this as if we got no managed objects at all. */ + } else + NML_NMCLIENT_LOG_D(self, "GetManagedObjects() completed"); + + if (managed_objects) { + GVariantIter iter; + const char * object_path; + GVariant * ifaces_tmp; + + g_variant_iter_init(&iter, managed_objects); + while (g_variant_iter_next(&iter, "{&o@a{sa{sv}}}", &object_path, &ifaces_tmp)) { + gs_unref_variant GVariant *ifaces = ifaces_tmp; + + _dbus_handle_interface_added(self, "get-managed-objects", object_path, ifaces); + } + } + + /* always call _dbus_handle_changes(), even if nothing changed. We need this to complete + * initialization. */ + _dbus_handle_changes(self, "get-managed-objects", TRUE); +} + +/*****************************************************************************/ + +static void +_nm_client_get_settings_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMRemoteConnection *remote_connection; + NMClient * self; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *settings = NULL; + NMLDBusObject * dbobj; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + remote_connection = user_data; + + self = _nm_object_get_client(remote_connection); + + dbobj = _nm_object_get_dbobj(remote_connection); + + _ASSERT_dbobj(dbobj, self); + + if (!ret) { + NML_NMCLIENT_LOG_T(self, + "[%s] GetSettings() completed with error: %s", + dbobj->dbus_path->str, + error->message); + } else { + NML_NMCLIENT_LOG_T(self, + "[%s] GetSettings() completed with success", + dbobj->dbus_path->str); + g_variant_get(ret, "(@a{sa{sv}})", &settings); + } + + _nm_remote_settings_get_settings_commit(remote_connection, settings); + + _dbus_handle_changes_commit(self, TRUE); +} + +void +_nm_client_get_settings_call(NMClient *self, NMLDBusObject *dbobj) +{ + GCancellable *cancellable; + + cancellable = _nm_remote_settings_get_settings_prepare(NM_REMOTE_CONNECTION(dbobj->nmobj)); + + _nm_client_dbus_call_simple(self, + cancellable, + dbobj->dbus_path->str, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "GetSettings", + g_variant_new("()"), + G_VARIANT_TYPE("(a{sa{sv}})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _nm_client_get_settings_call_cb, + dbobj->nmobj); +} + +static void +_dbus_settings_updated_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * log_context = "settings-updated"; + NMLDBusObject * dbobj; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("()"))) + return; + + dbobj = _dbobjs_dbobj_get_s(self, object_path); + + if (!dbobj || !NM_IS_REMOTE_CONNECTION(dbobj->nmobj)) { + NML_NMCLIENT_LOG_W(self, + "%s: [%s] ignore Updated signal for non-existing setting", + log_context, + object_path); + return; + } + + NML_NMCLIENT_LOG_T(self, "%s: [%s] Updated signal received", log_context, object_path); + + _nm_client_get_settings_call(self, dbobj); +} + +/*****************************************************************************/ + +static void +_dbus_nm_connection_active_state_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * log_context = "active-connection-state-changed"; + NMLDBusObject * dbobj; + guint32 state; + guint32 reason; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(uu)"))) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s] ignore StateChanged signal with unexpected signature", + log_context, + object_path); + return; + } + + dbobj = _dbobjs_dbobj_get_s(self, object_path); + + if (!dbobj || !NM_IS_ACTIVE_CONNECTION(dbobj->nmobj)) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s] ignore StateChanged signal for non-existing active connection", + log_context, + object_path); + return; + } + + g_variant_get(parameters, "(uu)", &state, &reason); + + NML_NMCLIENT_LOG_T(self, "%s: [%s] StateChanged signal received", log_context, object_path); + + _nm_active_connection_state_changed_commit(NM_ACTIVE_CONNECTION(dbobj->nmobj), state, reason); + + _dbus_handle_changes_commit(self, TRUE); +} + +/*****************************************************************************/ + +static void +_dbus_nm_vpn_connection_state_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + const char * log_context = "vpn-connection-state-changed"; + NMLDBusObject * dbobj; + guint32 state; + guint32 reason; + + if (priv->get_managed_objects_cancellable) { + /* we still wait for the initial GetManagedObjects(). Ignore the event. */ + return; + } + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(uu)"))) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s] ignore VpnStateChanged signal with unexpected signature", + log_context, + object_path); + return; + } + + dbobj = _dbobjs_dbobj_get_s(self, object_path); + + if (!dbobj || !NM_IS_VPN_CONNECTION(dbobj->nmobj)) { + NML_NMCLIENT_LOG_E(self, + "%s: [%s] ignore VpnStateChanged signal for non-existing vpn connection", + log_context, + object_path); + return; + } + + g_variant_get(parameters, "(uu)", &state, &reason); + + NML_NMCLIENT_LOG_T(self, "%s: [%s] VpnStateChanged signal received", log_context, object_path); + + _nm_vpn_connection_state_changed_commit(NM_VPN_CONNECTION(dbobj->nmobj), state, reason); + + _dbus_handle_changes_commit(self, TRUE); +} + +/*****************************************************************************/ + +static void +_emit_permissions_changed(NMClient *self, const guint8 *old_permissions, const guint8 *permissions) +{ + int i; + + if (self->obj_base.is_disposing) + return; + + if (old_permissions == permissions) + return; + + for (i = 0; i < (int) G_N_ELEMENTS(nm_auth_permission_sorted); i++) { + NMClientPermission perm = nm_auth_permission_sorted[i]; + NMClientPermissionResult perm_result = NM_CLIENT_PERMISSION_RESULT_UNKNOWN; + NMClientPermissionResult perm_result_old = NM_CLIENT_PERMISSION_RESULT_UNKNOWN; + + if (permissions) + perm_result = permissions[perm - 1]; + if (old_permissions) + perm_result_old = old_permissions[perm - 1]; + + if (perm_result == perm_result_old) + continue; + + g_signal_emit(self, signals[PERMISSION_CHANGED], 0, (guint) perm, (guint) perm_result); + } +} + +static void +_dbus_check_permissions_start_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + NMClient * self; + NMClientPrivate * priv; + gs_unref_variant GVariant *ret = NULL; + nm_auto_free_variant_iter GVariantIter *v_permissions = NULL; + gs_free guint8 *old_permissions = NULL; + gs_free_error GError *error = NULL; + const char * pkey; + const char * pvalue; + int i; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + self = user_data; + priv = NM_CLIENT_GET_PRIVATE(self); + + g_clear_object(&priv->permissions_cancellable); + + old_permissions = g_steal_pointer(&priv->permissions); + + if (!ret) { + /* when the call completes, we always pretend success. Even a failure means + * that we fetched the permissions, however they are all unknown. */ + NML_NMCLIENT_LOG_T(self, "GetPermissions call failed: %s", error->message); + goto out; + } + + NML_NMCLIENT_LOG_T(self, "GetPermissions call finished with success"); + + g_variant_get(ret, "(a{ss})", &v_permissions); + while (g_variant_iter_next(v_permissions, "{&s&s}", &pkey, &pvalue)) { + NMClientPermission perm; + NMClientPermissionResult perm_result; + + perm = nm_auth_permission_from_string(pkey); + if (perm == NM_CLIENT_PERMISSION_NONE) + continue; + + perm_result = nm_client_permission_result_from_string(pvalue); + + if (!priv->permissions) { + if (perm_result == NM_CLIENT_PERMISSION_RESULT_UNKNOWN) + continue; + priv->permissions = g_new(guint8, G_N_ELEMENTS(nm_auth_permission_sorted)); + for (i = 0; i < (int) G_N_ELEMENTS(nm_auth_permission_sorted); i++) + priv->permissions[i] = NM_CLIENT_PERMISSION_RESULT_UNKNOWN; + } + priv->permissions[perm - 1] = perm_result; + } + +out: + priv->permissions_state = NM_TERNARY_TRUE; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->main_context); + _emit_permissions_changed(self, old_permissions, priv->permissions); + _notify(self, PROP_PERMISSIONS_STATE); +} + +static void +_dbus_check_permissions_start(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + gboolean fetch; + + fetch = !NM_FLAGS_HAS((NMClientInstanceFlags) priv->instance_flags, + NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS); + + nm_clear_g_cancellable(&priv->permissions_cancellable); + + if (fetch) { + NML_NMCLIENT_LOG_T(self, "GetPermissions() call started..."); + + priv->permissions_cancellable = g_cancellable_new(); + _nm_client_dbus_call_simple(self, + priv->permissions_cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "GetPermissions", + g_variant_new("()"), + G_VARIANT_TYPE("(a{ss})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _dbus_check_permissions_start_cb, + self); + } +} + +static void +_dbus_nm_check_permissions_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * signal_interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("()"))) { + NML_NMCLIENT_LOG_E(self, + "ignore CheckPermissions signal with unexpected signature %s", + g_variant_get_type_string(parameters)); + return; + } + + _dbus_check_permissions_start(self); + + if (priv->permissions_state == NM_TERNARY_TRUE) + priv->permissions_state = NM_TERNARY_FALSE; + _notify(self, PROP_PERMISSIONS_STATE); +} + +/*****************************************************************************/ + +static void +_property_ao_notify_changed_connections_cb(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(self, + G_OBJECT(self), + nmobj, + is_added, + 5, + is_added ? signals[CONNECTION_ADDED] + : signals[CONNECTION_REMOVED]); +} + +static void +_property_ao_notify_changed_all_devices_cb(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(self, + G_OBJECT(self), + nmobj, + is_added, + 6, + is_added ? signals[ANY_DEVICE_ADDED] + : signals[ANY_DEVICE_REMOVED]); +} + +static void +_property_ao_notify_changed_devices_cb(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(self, + G_OBJECT(self), + nmobj, + is_added, + 7, + is_added ? signals[DEVICE_ADDED] + : signals[DEVICE_REMOVED]); +} + +static void +_property_ao_notify_changed_active_connections_cb(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(self, + G_OBJECT(self), + nmobj, + is_added, + 8, + is_added ? signals[ACTIVE_CONNECTION_ADDED] + : signals[ACTIVE_CONNECTION_REMOVED]); +} + +/*****************************************************************************/ + +typedef struct { + NMLDBusObjWatcherWithPtr *obj_watcher; + const char * op_name; + NMLDBusObject * dbobj; + GTask * task; + GVariant * extra_results; + gpointer result; + GType gtype; + gulong cancellable_id; +} RequestWaitData; + +static void +_request_wait_data_free(RequestWaitData *request_data) +{ + nm_assert(!request_data->obj_watcher); + nm_assert(request_data->cancellable_id == 0); + nm_assert(!request_data->task || G_IS_TASK(request_data->task)); + + nm_g_object_unref(request_data->task); + nm_g_object_unref(request_data->result); + nm_g_variant_unref(request_data->extra_results); + if (request_data->dbobj) + nml_dbus_object_unref(request_data->dbobj); + nm_g_slice_free(request_data); +} + +static void +_request_wait_task_return(RequestWaitData *request_data) +{ + gs_unref_object GTask *task = NULL; + + nm_assert(request_data); + nm_assert(G_IS_TASK(request_data->task)); + nm_assert(request_data->dbobj); + nm_assert(NM_IS_OBJECT(request_data->dbobj->nmobj)); + nm_assert(!request_data->result); + + task = g_steal_pointer(&request_data->task); + + request_data->result = g_object_ref(request_data->dbobj->nmobj); + nm_clear_g_signal_handler(g_task_get_cancellable(task), &request_data->cancellable_id); + nm_clear_pointer(&request_data->dbobj, nml_dbus_object_unref); + g_task_return_pointer(task, request_data, (GDestroyNotify) _request_wait_data_free); +} + +static gboolean +_request_wait_complete(NMClient *self, RequestWaitData *request_data, gboolean force_complete) +{ + NMLDBusObject *dbobj; + + nm_assert(request_data); + nm_assert(!request_data->result); + nm_assert(!request_data->obj_watcher); + nm_assert(request_data->dbobj); + + dbobj = request_data->dbobj; + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY) { + NML_NMCLIENT_LOG_D(self, + "%s() succeeded with %s", + request_data->op_name, + dbobj->dbus_path->str); + nm_assert(G_TYPE_CHECK_INSTANCE_TYPE(dbobj->nmobj, request_data->gtype)); + _request_wait_task_return(request_data); + return TRUE; + } + + if (force_complete || dbobj->obj_state != NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) { + if (force_complete) + NML_NMCLIENT_LOG_D(self, + "%s() succeeded with %s but object is in an unsuitable state", + request_data->op_name, + dbobj->dbus_path->str); + else + NML_NMCLIENT_LOG_W(self, + "%s() succeeded with %s but object is in an unsuitable state", + request_data->op_name, + dbobj->dbus_path->str); + + g_task_return_error( + request_data->task, + g_error_new(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_OBJECT_CREATION_FAILED, + _("request succeeded with %s but object is in an unsuitable state"), + dbobj->dbus_path->str)); + _request_wait_data_free(request_data); + return TRUE; + } + + return FALSE; +} + +static void +_request_wait_complete_cb(NMClient *self, NMClientNotifyEventWithPtr *notify_event) +{ + _request_wait_complete(self, notify_event->user_data, TRUE); +} + +static void +_request_wait_obj_watcher_cb(NMClient *self, gpointer obj_watcher_base) +{ + NMLDBusObjWatcherWithPtr *obj_watcher = obj_watcher_base; + RequestWaitData * request_data = obj_watcher->user_data; + NMLDBusObject * dbobj; + + dbobj = request_data->dbobj; + + if (dbobj->obj_state == NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY) + return; + + nm_assert(NM_IN_SET((NMLDBusObjState) dbobj->obj_state, + NML_DBUS_OBJ_STATE_WATCHED_ONLY, + NML_DBUS_OBJ_STATE_ON_DBUS, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY)); + + _dbobjs_obj_watcher_unregister(self, g_steal_pointer(&request_data->obj_watcher)); + + nm_clear_g_signal_handler(g_task_get_cancellable(request_data->task), + &request_data->cancellable_id); + + _nm_client_notify_event_queue_with_ptr(self, + NM_CLIENT_NOTIFY_EVENT_PRIO_AFTER + 30, + _request_wait_complete_cb, + request_data); +} + +static void +_request_wait_cancelled_cb(GCancellable *cancellable, gpointer user_data) +{ + RequestWaitData *request_data = user_data; + NMClient * self; + GError * error = NULL; + + nm_assert(cancellable == g_task_get_cancellable(request_data->task)); + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + + self = g_task_get_source_object(request_data->task); + + nm_clear_g_signal_handler(cancellable, &request_data->cancellable_id); + + _dbobjs_obj_watcher_unregister(self, g_steal_pointer(&request_data->obj_watcher)); + + g_task_return_error(request_data->task, error); + + _request_wait_data_free(request_data); +} + +static void +_request_wait_start(GTask * task_take, + const char *op_name, + GType gtype, + const char *dbus_path, + GVariant * extra_results_take) +{ + NMClient * self; + gs_unref_object GTask *task = g_steal_pointer(&task_take); + RequestWaitData * request_data; + GCancellable * cancellable; + NMLDBusObject * dbobj; + + nm_assert(G_IS_TASK(task)); + + self = g_task_get_source_object(task); + + dbobj = _dbobjs_get_nmobj(self, dbus_path, gtype); + + if (!dbobj) { + NML_NMCLIENT_LOG_E(self, + "%s() succeeded with %s but object does not exist", + op_name, + dbus_path); + g_task_return_error(task, + g_error_new(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_FAILED, + _("operation succeeded but object %s does not exist"), + dbus_path)); + return; + } + + request_data = g_slice_new(RequestWaitData); + *request_data = (RequestWaitData){ + .task = g_steal_pointer(&task), + .op_name = op_name, + .gtype = gtype, + .dbobj = nml_dbus_object_ref(dbobj), + .obj_watcher = NULL, + .extra_results = g_steal_pointer(&extra_results_take), + .result = NULL, + .cancellable_id = 0, + }; + + if (_request_wait_complete(self, request_data, FALSE)) + return; + + NML_NMCLIENT_LOG_T(self, + "%s() succeeded with %s. Wait for object to become ready", + op_name, + dbobj->dbus_path->str); + + request_data->obj_watcher = _dbobjs_obj_watcher_register_o(self, + dbobj, + _request_wait_obj_watcher_cb, + sizeof(NMLDBusObjWatcherWithPtr)); + request_data->obj_watcher->user_data = request_data; + + cancellable = g_task_get_cancellable(request_data->task); + if (cancellable) { + gulong id; + + id = g_cancellable_connect(cancellable, + G_CALLBACK(_request_wait_cancelled_cb), + request_data, + NULL); + if (id == 0) { + /* the callback was invoked synchronously, which destroyed @request_data. + * We must not touch @info anymore. */ + } else + request_data->cancellable_id = id; + } +} + +static gpointer +_request_wait_finish(NMClient * client, + GAsyncResult *result, + gpointer source_tag, + GVariant ** out_result, + GError ** error) +{ + RequestWaitData *request_data = NULL; + gpointer r; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(nm_g_task_is_valid(result, client, source_tag), NULL); + + request_data = g_task_propagate_pointer(G_TASK(result), error); + if (!request_data) { + NM_SET_OUT(out_result, NULL); + return NULL; + } + + nm_assert(NM_IS_OBJECT(request_data->result)); + + NM_SET_OUT(out_result, g_steal_pointer(&request_data->extra_results)); + r = g_steal_pointer(&request_data->result); + + nm_assert(NM_IS_OBJECT(r)); + + _request_wait_data_free(request_data); + return r; +} + +/*****************************************************************************/ + +/** + * nm_client_get_instance_flags: + * @self: the #NMClient instance. + * + * Returns: the #NMClientInstanceFlags flags. + * + * Since: 1.24 + */ +NMClientInstanceFlags +nm_client_get_instance_flags(NMClient *self) +{ + g_return_val_if_fail(NM_IS_CLIENT(self), NM_CLIENT_INSTANCE_FLAGS_NONE); + + return NM_CLIENT_GET_PRIVATE(self)->instance_flags; +} + +/** + * nm_client_get_dbus_connection: + * @client: a #NMClient + * + * Gets the %GDBusConnection of the instance. This can be either passed when + * constructing the instance (as "dbus-connection" property), or it will be + * automatically initialized during async/sync init. + * + * Returns: (transfer none): the D-Bus connection of the client, or %NULL if none is set. + * + * Since: 1.22 + **/ +GDBusConnection * +nm_client_get_dbus_connection(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->dbus_connection; +} + +/** + * nm_client_get_dbus_name_owner: + * @client: a #NMClient + * + * Returns: (transfer none): the current name owner of the D-Bus service of NetworkManager. + * + * Since: 1.22 + **/ +const char * +nm_client_get_dbus_name_owner(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->name_owner; +} + +/** + * nm_client_get_version: + * @client: a #NMClient + * + * Gets NetworkManager version. + * + * Returns: string with the version (or %NULL if NetworkManager is not running) + **/ +const char * +nm_client_get_version(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->nm.version; +} + +/** + * nm_client_get_state: + * @client: a #NMClient + * + * Gets the current daemon state. + * + * Returns: the current %NMState + **/ +NMState +nm_client_get_state(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NM_STATE_UNKNOWN); + + return NM_CLIENT_GET_PRIVATE(client)->nm.state; +} + +/** + * nm_client_get_startup: + * @client: a #NMClient + * + * Tests whether the daemon is still in the process of activating + * connections at startup. + * + * Returns: whether the daemon is still starting up + **/ +gboolean +nm_client_get_startup(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.startup; +} + +static void +_set_nm_running(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + gboolean nm_running; + + nm_running = priv->name_owner && !priv->get_managed_objects_cancellable; + if (priv->nm_running != nm_running) { + priv->nm_running = nm_running; + _notify(self, PROP_NM_RUNNING); + } +} + +/** + * nm_client_get_nm_running: + * @client: a #NMClient + * + * Determines whether the daemon is running. + * + * Returns: %TRUE if the daemon is running + **/ +gboolean +nm_client_get_nm_running(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm_running; +} + +/** + * nm_client_get_object_by_path: + * @client: the #NMClient instance + * @dbus_path: the D-Bus path of the object to look up + * + * Returns: (transfer none): the #NMObject instance that is + * cached under @dbus_path, or %NULL if no such object exists. + * + * Since: 1.24 + */ +NMObject * +nm_client_get_object_by_path(NMClient *client, const char *dbus_path) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(dbus_path, NULL); + + return _dbobjs_get_nmobj_unpack_visible(client, dbus_path, G_TYPE_NONE); +} + +/** + * nm_client_get_metered: + * @client: a #NMClient + * + * Returns: whether the default route is metered. + * + * Since: 1.22 + */ +NMMetered +nm_client_get_metered(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NM_METERED_UNKNOWN); + + return NM_CLIENT_GET_PRIVATE(client)->nm.metered; +} + +/** + * nm_client_networking_get_enabled: + * @client: a #NMClient + * + * Whether networking is enabled or disabled. + * + * Returns: %TRUE if networking is enabled, %FALSE if networking is disabled + **/ +gboolean +nm_client_networking_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.networking_enabled; +} + +/** + * nm_client_networking_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to set networking enabled, %FALSE to set networking disabled + * @error: (allow-none): return location for a #GError, or %NULL + * + * Enables or disables networking. When networking is disabled, all controlled + * interfaces are disconnected and deactivated. When networking is enabled, + * all controlled interfaces are available for activation. + * + * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22: Use the async command nm_client_dbus_call() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to call "Enable" with "(b)" arguments and no return value. + **/ +gboolean +nm_client_networking_set_enabled(NMClient *client, gboolean enable, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return _nm_client_dbus_call_sync_void(client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "Enable", + g_variant_new("(b)", enable), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_client_wireless_get_enabled: + * @client: a #NMClient + * + * Determines whether the wireless is enabled. + * + * Returns: %TRUE if wireless is enabled + **/ +gboolean +nm_client_wireless_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.wireless_enabled; +} + +/** + * nm_client_wireless_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable wireless + * + * Enables or disables wireless devices. + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to set "WirelessEnabled" property to a "(b)" value. + */ +void +nm_client_wireless_set_enabled(NMClient *client, gboolean enabled) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_set_property_sync_legacy(client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "WirelessEnabled", + "b", + enabled); +} + +/** + * nm_client_wireless_hardware_get_enabled: + * @client: a #NMClient + * + * Determines whether the wireless hardware is enabled. + * + * Returns: %TRUE if the wireless hardware is enabled + **/ +gboolean +nm_client_wireless_hardware_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.wireless_hardware_enabled; +} + +/** + * nm_client_wwan_get_enabled: + * @client: a #NMClient + * + * Determines whether WWAN is enabled. + * + * Returns: %TRUE if WWAN is enabled + **/ +gboolean +nm_client_wwan_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.wwan_enabled; +} + +/** + * nm_client_wwan_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable WWAN + * + * Enables or disables WWAN devices. + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to set "WwanEnabled" property to a "(b)" value. + **/ +void +nm_client_wwan_set_enabled(NMClient *client, gboolean enabled) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_set_property_sync_legacy(client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "WwanEnabled", + "b", + enabled); +} + +/** + * nm_client_wwan_hardware_get_enabled: + * @client: a #NMClient + * + * Determines whether the WWAN hardware is enabled. + * + * Returns: %TRUE if the WWAN hardware is enabled + **/ +gboolean +nm_client_wwan_hardware_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.wwan_hardware_enabled; +} + +/** + * nm_client_wimax_get_enabled: + * @client: a #NMClient + * + * Determines whether WiMAX is enabled. + * + * Returns: %TRUE if WiMAX is enabled + * + * Deprecated: 1.22: This function always returns FALSE because WiMax is no longer supported. + **/ +gboolean +nm_client_wimax_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return FALSE; +} + +/** + * nm_client_wimax_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable WiMAX + * + * Enables or disables WiMAX devices. + * + * Deprecated: 1.22: This function does nothing because WiMax is no longer supported. + **/ +void +nm_client_wimax_set_enabled(NMClient *client, gboolean enabled) +{ + g_return_if_fail(NM_IS_CLIENT(client)); +} + +/** + * nm_client_wimax_hardware_get_enabled: + * @client: a #NMClient + * + * Determines whether the WiMAX hardware is enabled. + * + * Returns: %TRUE if the WiMAX hardware is enabled + * + * Deprecated: 1.22: This function always returns FALSE because WiMax is no longer supported. + **/ +gboolean +nm_client_wimax_hardware_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return FALSE; +} + +/** + * nm_client_connectivity_check_get_available: + * @client: a #NMClient + * + * Determine whether connectivity checking is available. This + * requires that the URI of a connectivity service has been set in the + * configuration file. + * + * Returns: %TRUE if connectivity checking is available. + * + * Since: 1.10 + */ +gboolean +nm_client_connectivity_check_get_available(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.connectivity_check_available; +} + +/** + * nm_client_connectivity_check_get_enabled: + * @client: a #NMClient + * + * Determine whether connectivity checking is enabled. + * + * Returns: %TRUE if connectivity checking is enabled. + * + * Since: 1.10 + */ +gboolean +nm_client_connectivity_check_get_enabled(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + + return NM_CLIENT_GET_PRIVATE(client)->nm.connectivity_check_enabled; +} + +/** + * nm_client_connectivity_check_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable connectivity checking + * + * Enable or disable connectivity checking. Note that if a + * connectivity checking URI has not been configured, this will not + * have any effect. + * + * Since: 1.10 + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to set "ConnectivityCheckEnabled" property to a "(b)" value. + */ +void +nm_client_connectivity_check_set_enabled(NMClient *client, gboolean enabled) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_set_property_sync_legacy(client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "ConnectivityCheckEnabled", + "b", + enabled); +} + +/** + * nm_client_connectivity_check_get_uri: + * @client: a #NMClient + * + * Get the URI that will be queried to determine if there is internet + * connectivity. + * + * Returns: (transfer none): the connectivity URI in use + * + * Since: 1.20 + */ +const char * +nm_client_connectivity_check_get_uri(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->nm.connectivity_check_uri; +} + +/** + * nm_client_get_logging: + * @client: a #NMClient + * @level: (allow-none): return location for logging level string + * @domains: (allow-none): return location for log domains string. The string is + * a list of domains separated by "," + * @error: (allow-none): return location for a #GError, or %NULL + * + * Gets NetworkManager current logging level and domains. + * + * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22: Use the async command nm_client_dbus_call() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to call "GetLogging" with no arguments to get "(ss)" for level + * and domains. + **/ +gboolean +nm_client_get_logging(NMClient *client, char **level, char **domains, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(level == NULL || *level == NULL, FALSE); + g_return_val_if_fail(domains == NULL || *domains == NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + ret = _nm_client_dbus_call_sync(client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "GetLogging", + g_variant_new("()"), + G_VARIANT_TYPE("(ss)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return FALSE; + + g_variant_get(ret, "(ss)", level, domains); + return TRUE; +} + +/** + * nm_client_set_logging: + * @client: a #NMClient + * @level: (allow-none): logging level to set (%NULL or an empty string for no change) + * @domains: (allow-none): logging domains to set. The string should be a list of log + * domains separated by ",". (%NULL or an empty string for no change) + * @error: (allow-none): return location for a #GError, or %NULL + * + * Sets NetworkManager logging level and/or domains. + * + * Returns: %TRUE on success, %FALSE otherwise + * + * Deprecated: 1.22: Use the async command nm_client_dbus_call() on %NM_DBUS_PATH, + * %NM_DBUS_INTERFACE to call "SetLogging" with "(ss)" arguments for level and domains. + **/ +gboolean +nm_client_set_logging(NMClient *client, const char *level, const char *domains, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + return _nm_client_dbus_call_sync_void(client, + NULL, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "SetLogging", + g_variant_new("(ss)", level ?: "", domains ?: ""), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_client_get_permission_result: + * @client: a #NMClient + * @permission: the permission for which to return the result, one of #NMClientPermission + * + * Requests the result of a specific permission, which indicates whether the + * client can or cannot perform the action the permission represents + * + * Returns: the permission's result, one of #NMClientPermissionResult + **/ +NMClientPermissionResult +nm_client_get_permission_result(NMClient *client, NMClientPermission permission) +{ + NMClientPrivate * priv; + NMClientPermissionResult result = NM_CLIENT_PERMISSION_RESULT_UNKNOWN; + + g_return_val_if_fail(NM_IS_CLIENT(client), NM_CLIENT_PERMISSION_RESULT_UNKNOWN); + + if (permission > NM_CLIENT_PERMISSION_NONE && permission <= NM_CLIENT_PERMISSION_LAST) { + priv = NM_CLIENT_GET_PRIVATE(client); + if (priv->permissions) + result = priv->permissions[permission - 1]; + } + + return result; +} + +/** + * nm_client_get_permissions_state: + * @self: the #NMClient instance + * + * Returns: the state of the cached permissions. %NM_TERNARY_DEFAULT + * means that no permissions result was yet received. All permissions + * are unknown. %NM_TERNARY_TRUE means that the permissions got received + * and are cached. %%NM_TERNARY_FALSE means that permissions are cached, + * but they are invalided as "CheckPermissions" signal was received + * in the meantime. + * + * Since: 1.24 + */ +NMTernary +nm_client_get_permissions_state(NMClient *self) +{ + g_return_val_if_fail(NM_IS_CLIENT(self), NM_TERNARY_DEFAULT); + + return NM_CLIENT_GET_PRIVATE(self)->permissions_state; +} + +/** + * nm_client_get_connectivity: + * @client: an #NMClient + * + * Gets the current network connectivity state. Contrast + * nm_client_check_connectivity() and + * nm_client_check_connectivity_async(), which re-check the + * connectivity state first before returning any information. + * + * Returns: the current connectivity state + */ +NMConnectivityState +nm_client_get_connectivity(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NM_CONNECTIVITY_UNKNOWN); + + return NM_CLIENT_GET_PRIVATE(client)->nm.connectivity; +} + +/** + * nm_client_check_connectivity: + * @client: an #NMClient + * @cancellable: a #GCancellable + * @error: return location for a #GError + * + * Updates the network connectivity state and returns the (new) + * current state. Contrast nm_client_get_connectivity(), which returns + * the most recent known state without re-checking. + * + * This is a blocking call; use nm_client_check_connectivity_async() + * if you do not want to block. + * + * Returns: the (new) current connectivity state + * + * Deprecated: 1.22: Use nm_client_check_connectivity_async() or GDBusConnection. + */ +NMConnectivityState +nm_client_check_connectivity(NMClient *client, GCancellable *cancellable, GError **error) +{ + NMClientPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + guint32 connectivity; + + g_return_val_if_fail(NM_IS_CLIENT(client), NM_CONNECTIVITY_UNKNOWN); + + ret = _nm_client_dbus_call_sync(client, + cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckConnectivity", + g_variant_new("()"), + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return NM_CONNECTIVITY_UNKNOWN; + + g_variant_get(ret, "(u)", &connectivity); + + /* upon receiving the synchronous response, we hack the NMClient state + * and update the property outside the ordered D-Bus messages (like + * "PropertiesChanged" signals). + * + * This is really ugly, we shouldn't do this. */ + + priv = NM_CLIENT_GET_PRIVATE(client); + + if (priv->nm.connectivity != connectivity) { + priv->nm.connectivity = connectivity; + _notify(client, PROP_CONNECTIVITY); + } + + return connectivity; +} + +/** + * nm_client_check_connectivity_async: + * @client: an #NMClient + * @cancellable: a #GCancellable + * @callback: callback to call with the result + * @user_data: data for @callback. + * + * Asynchronously updates the network connectivity state and invokes + * @callback when complete. Contrast nm_client_get_connectivity(), + * which (immediately) returns the most recent known state without + * re-checking, and nm_client_check_connectivity(), which blocks. + */ +void +nm_client_check_connectivity_async(NMClient * client, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(client, + client, + nm_client_check_connectivity_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckConnectivity", + g_variant_new("()"), + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_client_check_connectivity_finish: + * @client: an #NMClient + * @result: the #GAsyncResult + * @error: return location for a #GError + * + * Retrieves the result of an nm_client_check_connectivity_async() + * call. + * + * Returns: the (new) current connectivity state + */ +NMConnectivityState +nm_client_check_connectivity_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + guint32 connectivity; + + g_return_val_if_fail(NM_IS_CLIENT(client), NM_CONNECTIVITY_UNKNOWN); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_check_connectivity_async), + NM_CONNECTIVITY_UNKNOWN); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NM_CONNECTIVITY_UNKNOWN; + + g_variant_get(ret, "(u)", &connectivity); + return connectivity; +} + +/** + * nm_client_save_hostname: + * @client: the %NMClient + * @hostname: (allow-none): the new persistent hostname to set, or %NULL to + * clear any existing persistent hostname + * @cancellable: a #GCancellable, or %NULL + * @error: return location for #GError + * + * Requests that the machine's persistent hostname be set to the specified value + * or cleared. + * + * Returns: %TRUE if the request was successful, %FALSE if it failed + * + * Deprecated: 1.22: Use nm_client_save_hostname_async() or GDBusConnection. + **/ +gboolean +nm_client_save_hostname(NMClient * client, + const char * hostname, + GCancellable *cancellable, + GError ** error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + return _nm_client_dbus_call_sync_void(client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "SaveHostname", + g_variant_new("(s)", hostname ?: ""), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_client_save_hostname_async: + * @client: the %NMClient + * @hostname: (allow-none): the new persistent hostname to set, or %NULL to + * clear any existing persistent hostname + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Requests that the machine's persistent hostname be set to the specified value + * or cleared. + **/ +void +nm_client_save_hostname_async(NMClient * client, + const char * hostname, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(client, + client, + nm_client_save_hostname_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "SaveHostname", + g_variant_new("(s)", hostname ?: ""), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_save_hostname_finish: + * @client: the %NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for #GError + * + * Gets the result of an nm_client_save_hostname_async() call. + * + * Returns: %TRUE if the request was successful, %FALSE if it failed + **/ +gboolean +nm_client_save_hostname_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_save_hostname_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ +/* Devices */ +/*****************************************************************************/ + +/** + * nm_client_get_devices: + * @client: a #NMClient + * + * Gets all the known network devices. Use nm_device_get_type() or the + * NM_IS_DEVICE_XXXX functions to determine what kind of + * device member of the returned array is, and then you may use device-specific + * methods such as nm_device_ethernet_get_hw_address(). + * + * Returns: (transfer none) (element-type NMDevice): a #GPtrArray + * containing all the #NMDevices. The returned array is owned by the + * #NMClient object and should not be modified. + **/ +const GPtrArray * +nm_client_get_devices(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_ao[PROPERTY_AO_IDX_DEVICES]); +} + +/** + * nm_client_get_all_devices: + * @client: a #NMClient + * + * Gets both real devices and device placeholders (eg, software devices which + * do not currently exist, but could be created automatically by NetworkManager + * if one of their NMDevice::ActivatableConnections was activated). Use + * nm_device_is_real() to determine whether each device is a real device or + * a placeholder. + * + * Use nm_device_get_type() or the NM_IS_DEVICE_XXXX() functions to determine + * what kind of device each member of the returned array is, and then you may + * use device-specific methods such as nm_device_ethernet_get_hw_address(). + * + * Returns: (transfer none) (element-type NMDevice): a #GPtrArray + * containing all the #NMDevices. The returned array is owned by the + * #NMClient object and should not be modified. + * + * Since: 1.2 + **/ +const GPtrArray * +nm_client_get_all_devices(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_ao[PROPERTY_AO_IDX_ALL_DEVICES]); +} + +/** + * nm_client_get_device_by_path: + * @client: a #NMClient + * @object_path: the object path to search for + * + * Gets a #NMDevice from a #NMClient. + * + * Returns: (transfer none): the #NMDevice for the given @object_path or %NULL if none is found. + **/ +NMDevice * +nm_client_get_device_by_path(NMClient *client, const char *object_path) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(object_path, NULL); + + return _dbobjs_get_nmobj_unpack_visible(client, object_path, NM_TYPE_DEVICE); +} + +/** + * nm_client_get_device_by_iface: + * @client: a #NMClient + * @iface: the interface name to search for + * + * Gets a #NMDevice from a #NMClient. + * + * Returns: (transfer none): the #NMDevice for the given @iface or %NULL if none is found. + **/ +NMDevice * +nm_client_get_device_by_iface(NMClient *client, const char *iface) +{ + const GPtrArray *devices; + guint i; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(iface, NULL); + + devices = nm_client_get_devices(client); + for (i = 0; i < devices->len; i++) { + NMDevice *candidate = g_ptr_array_index(devices, i); + + if (nm_streq0(nm_device_get_iface(candidate), iface)) + return candidate; + } + + return NULL; +} + +/*****************************************************************************/ +/* Active Connections */ +/*****************************************************************************/ + +/** + * nm_client_get_active_connections: + * @client: a #NMClient + * + * Gets the active connections. + * + * Returns: (transfer none) (element-type NMActiveConnection): a #GPtrArray + * containing all the active #NMActiveConnections. + * The returned array is owned by the client and should not be modified. + **/ +const GPtrArray * +nm_client_get_active_connections(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_ao[PROPERTY_AO_IDX_ACTIVE_CONNECTIONS]); +} + +/** + * nm_client_get_primary_connection: + * @client: an #NMClient + * + * Gets the #NMActiveConnection corresponding to the primary active + * network device. + * + * In particular, when there is no VPN active, or the VPN does not + * have the default route, this returns the active connection that has + * the default route. If there is a VPN active with the default route, + * then this function returns the active connection that contains the + * route to the VPN endpoint. + * + * If there is no default route, or the default route is over a + * non-NetworkManager-recognized device, this will return %NULL. + * + * Returns: (transfer none): the appropriate #NMActiveConnection, if + * any + */ +NMActiveConnection * +nm_client_get_primary_connection(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_o_get_obj( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_o[PROPERTY_O_IDX_NM_PRIMAY_CONNECTION]); +} + +/** + * nm_client_get_activating_connection: + * @client: an #NMClient + * + * Gets the #NMActiveConnection corresponding to a + * currently-activating connection that is expected to become the new + * #NMClient:primary-connection upon successful activation. + * + * Returns: (transfer none): the appropriate #NMActiveConnection, if + * any. + */ +NMActiveConnection * +nm_client_get_activating_connection(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_o_get_obj( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_o[PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION]); +} + +/*****************************************************************************/ + +static void +activate_connection_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *ret = NULL; + const char * v_active_connection; + GError * error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled(error)) + g_dbus_error_strip_remote_error(error); + g_task_return_error(task, error); + return; + } + + g_variant_get(ret, "(&o)", &v_active_connection); + + _request_wait_start(g_steal_pointer(&task), + "ActivateConnection", + NM_TYPE_ACTIVE_CONNECTION, + v_active_connection, + NULL); +} + +/** + * nm_client_activate_connection_async: + * @client: a #NMClient + * @connection: (allow-none): an #NMConnection + * @device: (allow-none): the #NMDevice + * @specific_object: (allow-none): the object path of a connection-type-specific + * object this activation should use. This parameter is currently ignored for + * wired and mobile broadband connections, and the value of %NULL should be used + * (ie, no specific object). For Wi-Fi or WiMAX connections, pass the object + * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can + * get using nm_object_get_path(), and which will be used to complete the + * details of the newly added connection. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the activation has started + * @user_data: caller-specific data passed to @callback + * + * Asynchronously starts a connection to a particular network using the + * configuration settings from @connection and the network device @device. + * Certain connection types also take a "specific object" which is the object + * path of a connection- specific object, like an #NMAccessPoint for Wi-Fi + * connections, or an #NMWimaxNsp for WiMAX connections, to which you wish to + * connect. If the specific object is not given, NetworkManager can, in some + * cases, automatically determine which network to connect to given the settings + * in @connection. + * + * If @connection is not given for a device-based activation, NetworkManager + * picks the best available connection for the device and activates it. + * + * Note that the callback is invoked when NetworkManager has started activating + * the new connection, not when it finishes. You can use the returned + * #NMActiveConnection object (in particular, #NMActiveConnection:state) to + * track the activation to its completion. + **/ +void +nm_client_activate_connection_async(NMClient * client, + NMConnection * connection, + NMDevice * device, + const char * specific_object, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + const char *arg_connection = NULL; + const char *arg_device = NULL; + + g_return_if_fail(NM_IS_CLIENT(client)); + + if (connection) { + g_return_if_fail(NM_IS_CONNECTION(connection)); + arg_connection = nm_connection_get_path(connection); + g_return_if_fail(arg_connection); + } + + if (device) { + g_return_if_fail(NM_IS_DEVICE(device)); + arg_device = nm_object_get_path(NM_OBJECT(device)); + g_return_if_fail(arg_device); + } + + NML_NMCLIENT_LOG_T( + client, + "ActivateConnection() for connection \"%s\", device \"%s\", specific_object \"%s", + arg_connection ?: "/", + arg_device ?: "/", + specific_object ?: "/"); + + _nm_client_dbus_call( + client, + client, + nm_client_activate_connection_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "ActivateConnection", + g_variant_new("(ooo)", arg_connection ?: "/", arg_device ?: "/", specific_object ?: "/"), + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + activate_connection_cb); +} + +/** + * nm_client_activate_connection_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_activate_connection_async(). + * + * Returns: (transfer full): the new #NMActiveConnection on success, %NULL on + * failure, in which case @error will be set. + **/ +NMActiveConnection * +nm_client_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + return NM_ACTIVE_CONNECTION( + _request_wait_finish(client, result, nm_client_activate_connection_async, NULL, error)); +} + +/*****************************************************************************/ + +static void +_add_and_activate_connection_done(GObject * object, + GAsyncResult *result, + gboolean use_add_and_activate_v2, + GTask * task_take) +{ + _nm_unused gs_unref_object GTask *task = task_take; + gs_unref_variant GVariant *ret = NULL; + GError * error = NULL; + gs_unref_variant GVariant *v_result = NULL; + const char * v_active_connection; + const char * v_path; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled(error)) + g_dbus_error_strip_remote_error(error); + g_task_return_error(task, error); + return; + } + + if (use_add_and_activate_v2) { + g_variant_get(ret, "(&o&o@a{sv})", &v_path, &v_active_connection, &v_result); + } else { + g_variant_get(ret, "(&o&o)", &v_path, &v_active_connection); + } + + _request_wait_start(g_steal_pointer(&task), + "AddAndActivateConnection", + NM_TYPE_ACTIVE_CONNECTION, + v_active_connection, + g_steal_pointer(&v_result)); +} + +static void +_add_and_activate_connection_v1_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_and_activate_connection_done(object, result, FALSE, user_data); +} + +static void +_add_and_activate_connection_v2_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_and_activate_connection_done(object, result, TRUE, user_data); +} + +static void +_add_and_activate_connection(NMClient * self, + gboolean is_v2, + NMConnection * partial, + NMDevice * device, + const char * specific_object, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVariant * arg_connection = NULL; + gboolean use_add_and_activate_v2 = FALSE; + const char *arg_device = NULL; + gpointer source_tag; + + g_return_if_fail(NM_IS_CLIENT(self)); + g_return_if_fail(!partial || NM_IS_CONNECTION(partial)); + + if (device) { + g_return_if_fail(NM_IS_DEVICE(device)); + arg_device = nm_object_get_path(NM_OBJECT(device)); + g_return_if_fail(arg_device); + } + + if (partial) + arg_connection = nm_connection_to_dbus(partial, NM_CONNECTION_SERIALIZE_ALL); + if (!arg_connection) + arg_connection = nm_g_variant_singleton_aLsaLsvII(); + + if (is_v2) { + if (!options) + options = nm_g_variant_singleton_aLsvI(); + use_add_and_activate_v2 = TRUE; + source_tag = nm_client_add_and_activate_connection2; + } else { + if (options) { + if (g_variant_n_children(options) > 0) + use_add_and_activate_v2 = TRUE; + else + nm_clear_pointer(&options, nm_g_variant_unref_floating); + } + source_tag = nm_client_add_and_activate_connection_async; + } + + NML_NMCLIENT_LOG_D(self, "AddAndActivateConnection() started..."); + + if (use_add_and_activate_v2) { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "AddAndActivateConnection2", + g_variant_new("(@a{sa{sv}}oo@a{sv})", + arg_connection, + arg_device ?: "/", + specific_object ?: "/", + options), + G_VARIANT_TYPE("(ooa{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_and_activate_connection_v2_cb); + } else { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "AddAndActivateConnection", + g_variant_new("(@a{sa{sv}}oo)", + arg_connection, + arg_device ?: "/", + specific_object ?: "/"), + G_VARIANT_TYPE("(oo)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_and_activate_connection_v1_cb); + } +} + +/** + * nm_client_add_and_activate_connection_async: + * @client: a #NMClient + * @partial: (allow-none): an #NMConnection to add; the connection may be + * partially filled (or even %NULL) and will be completed by NetworkManager + * using the given @device and @specific_object before being added + * @device: the #NMDevice + * @specific_object: (allow-none): the object path of a connection-type-specific + * object this activation should use. This parameter is currently ignored for + * wired and mobile broadband connections, and the value of %NULL should be used + * (ie, no specific object). For Wi-Fi or WiMAX connections, pass the object + * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can + * get using nm_object_get_path(), and which will be used to complete the + * details of the newly added connection. + * If the variant is floating, it will be consumed. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the activation has started + * @user_data: caller-specific data passed to @callback + * + * Adds a new connection using the given details (if any) as a template, + * automatically filling in missing settings with the capabilities of the given + * device and specific object. The new connection is then asynchronously + * activated as with nm_client_activate_connection_async(). Cannot be used for + * VPN connections at this time. + * + * Note that the callback is invoked when NetworkManager has started activating + * the new connection, not when it finishes. You can used the returned + * #NMActiveConnection object (in particular, #NMActiveConnection:state) to + * track the activation to its completion. + **/ +void +nm_client_add_and_activate_connection_async(NMClient * client, + NMConnection * partial, + NMDevice * device, + const char * specific_object, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _add_and_activate_connection(client, + FALSE, + partial, + device, + specific_object, + NULL, + cancellable, + callback, + user_data); +} + +/** + * nm_client_add_and_activate_connection_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_add_and_activate_connection_async(). + * + * You can call nm_active_connection_get_connection() on the returned + * #NMActiveConnection to find the path of the created #NMConnection. + * + * Returns: (transfer full): the new #NMActiveConnection on success, %NULL on + * failure, in which case @error will be set. + **/ +NMActiveConnection * +nm_client_add_and_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + return NM_ACTIVE_CONNECTION(_request_wait_finish(client, + result, + nm_client_add_and_activate_connection_async, + NULL, + error)); +} + +/** + * nm_client_add_and_activate_connection2: + * @client: a #NMClient + * @partial: (allow-none): an #NMConnection to add; the connection may be + * partially filled (or even %NULL) and will be completed by NetworkManager + * using the given @device and @specific_object before being added + * @device: the #NMDevice + * @specific_object: (allow-none): the object path of a connection-type-specific + * object this activation should use. This parameter is currently ignored for + * wired and mobile broadband connections, and the value of %NULL should be used + * (i.e., no specific object). For Wi-Fi or WiMAX connections, pass the object + * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can + * get using nm_object_get_path(), and which will be used to complete the + * details of the newly added connection. + * @options: a #GVariant containing a dictionary with options, or %NULL + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the activation has started + * @user_data: caller-specific data passed to @callback + * + * Adds a new connection using the given details (if any) as a template, + * automatically filling in missing settings with the capabilities of the given + * device and specific object. The new connection is then asynchronously + * activated as with nm_client_activate_connection_async(). Cannot be used for + * VPN connections at this time. + * + * Note that the callback is invoked when NetworkManager has started activating + * the new connection, not when it finishes. You can used the returned + * #NMActiveConnection object (in particular, #NMActiveConnection:state) to + * track the activation to its completion. + * + * This is identical to nm_client_add_and_activate_connection_async() but takes + * a further @options parameter. Currently, the following options are supported + * by the daemon: + * * "persist": A string describing how the connection should be stored. + * The default is "disk", but it can be modified to "memory" (until + * the daemon quits) or "volatile" (will be deleted on disconnect). + * * "bind-activation": Bind the connection lifetime to something. The default is "none", + * meaning an explicit disconnect is needed. The value "dbus-client" + * means the connection will automatically be deactivated when the calling + * D-Bus client disappears from the system bus. + * + * Since: 1.16 + **/ +void +nm_client_add_and_activate_connection2(NMClient * client, + NMConnection * partial, + NMDevice * device, + const char * specific_object, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _add_and_activate_connection(client, + TRUE, + partial, + device, + specific_object, + options, + cancellable, + callback, + user_data); +} + +/** + * nm_client_add_and_activate_connection2_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * @out_result: (allow-none) (transfer full): the output result + * of type "a{sv}" returned by D-Bus' AddAndActivate2 call. Currently, no + * output is implemented yet. + * + * Gets the result of a call to nm_client_add_and_activate_connection2(). + * + * You can call nm_active_connection_get_connection() on the returned + * #NMActiveConnection to find the path of the created #NMConnection. + * + * Returns: (transfer full): the new #NMActiveConnection on success, %NULL on + * failure, in which case @error will be set. + **/ +NMActiveConnection * +nm_client_add_and_activate_connection2_finish(NMClient * client, + GAsyncResult *result, + GVariant ** out_result, + GError ** error) +{ + return NM_ACTIVE_CONNECTION(_request_wait_finish(client, + result, + nm_client_add_and_activate_connection2, + out_result, + error)); +} + +/*****************************************************************************/ + +/** + * nm_client_deactivate_connection: + * @client: a #NMClient + * @active: the #NMActiveConnection to deactivate + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Deactivates an active #NMActiveConnection. + * + * Returns: success or failure + * + * Deprecated: 1.22: Use nm_client_deactivate_connection_async() or GDBusConnection. + **/ +gboolean +nm_client_deactivate_connection(NMClient * client, + NMActiveConnection *active, + GCancellable * cancellable, + GError ** error) +{ + const char *active_path; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(active), FALSE); + + active_path = nm_object_get_path(NM_OBJECT(active)); + g_return_val_if_fail(active_path, FALSE); + + return _nm_client_dbus_call_sync_void(client, + cancellable, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "DeactivateConnection", + g_variant_new("(o)", active_path), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_client_deactivate_connection_async: + * @client: a #NMClient + * @active: the #NMActiveConnection to deactivate + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the deactivation has completed + * @user_data: caller-specific data passed to @callback + * + * Asynchronously deactivates an active #NMActiveConnection. + **/ +void +nm_client_deactivate_connection_async(NMClient * client, + NMActiveConnection *active, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + const char *active_path; + + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(NM_IS_ACTIVE_CONNECTION(active)); + + active_path = nm_object_get_path(NM_OBJECT(active)); + g_return_if_fail(active_path); + + _nm_client_dbus_call(client, + client, + nm_client_deactivate_connection_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "DeactivateConnection", + g_variant_new("(o)", active_path), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_deactivate_connection_finish: + * @client: a #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_deactivate_connection_async(). + * + * Returns: success or failure + **/ +gboolean +nm_client_deactivate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_deactivate_connection_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ +/* Connections */ +/*****************************************************************************/ + +/** + * nm_client_get_connections: + * @client: the %NMClient + * + * Returns: (transfer none) (element-type NMRemoteConnection): an array + * containing all connections provided by the remote settings service. The + * returned array is owned by the #NMClient object and should not be modified. + * + * The connections are as received from D-Bus and might not validate according + * to nm_connection_verify(). + **/ +const GPtrArray * +nm_client_get_connections(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->settings.connections); +} + +/** + * nm_client_get_connection_by_id: + * @client: the %NMClient + * @id: the id of the remote connection + * + * Returns the first matching %NMRemoteConnection matching a given @id. + * + * Returns: (transfer none): the remote connection object on success, or %NULL if no + * matching object was found. + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + **/ +NMRemoteConnection * +nm_client_get_connection_by_id(NMClient *client, const char *id) +{ + const GPtrArray *arr; + guint i; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(id, NULL); + + arr = nm_client_get_connections(client); + for (i = 0; i < arr->len; i++) { + NMRemoteConnection *c = NM_REMOTE_CONNECTION(arr->pdata[i]); + + if (nm_streq0(id, nm_connection_get_id(NM_CONNECTION(c)))) + return c; + } + return NULL; +} + +/** + * nm_client_get_connection_by_path: + * @client: the %NMClient + * @path: the D-Bus object path of the remote connection + * + * Returns the %NMRemoteConnection representing the connection at @path. + * + * Returns: (transfer none): the remote connection object on success, or %NULL if the object was + * not known + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + **/ +NMRemoteConnection * +nm_client_get_connection_by_path(NMClient *client, const char *path) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(path != NULL, NULL); + + return _dbobjs_get_nmobj_unpack_visible(client, path, NM_TYPE_REMOTE_CONNECTION); +} + +/** + * nm_client_get_connection_by_uuid: + * @client: the %NMClient + * @uuid: the UUID of the remote connection + * + * Returns the %NMRemoteConnection identified by @uuid. + * + * Returns: (transfer none): the remote connection object on success, or %NULL if the object was + * not known + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + **/ +NMRemoteConnection * +nm_client_get_connection_by_uuid(NMClient *client, const char *uuid) +{ + const GPtrArray *arr; + guint i; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(uuid, NULL); + + arr = nm_client_get_connections(client); + for (i = 0; i < arr->len; i++) { + NMRemoteConnection *c = NM_REMOTE_CONNECTION(arr->pdata[i]); + + if (nm_streq0(uuid, nm_connection_get_uuid(NM_CONNECTION(c)))) + return c; + } + return NULL; +} + +/*****************************************************************************/ + +static void +_add_connection_cb(GObject * source, + GAsyncResult *result, + gboolean with_extra_arg, + gpointer user_data) +{ + gs_unref_variant GVariant *ret = NULL; + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *v_result = NULL; + const char * v_path; + GError * error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled(error)) + g_dbus_error_strip_remote_error(error); + g_task_return_error(task, error); + return; + } + + if (with_extra_arg) { + g_variant_get(ret, "(&o@a{sv})", &v_path, &v_result); + } else { + g_variant_get(ret, "(&o)", &v_path); + } + + _request_wait_start(g_steal_pointer(&task), + "AddConnection", + NM_TYPE_REMOTE_CONNECTION, + v_path, + g_steal_pointer(&v_result)); +} + +static void +_add_connection_cb_without_extra_result(GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_connection_cb(object, result, FALSE, user_data); +} + +static void +_add_connection_cb_with_extra_result(GObject *object, GAsyncResult *result, gpointer user_data) +{ + _add_connection_cb(object, result, TRUE, user_data); +} + +static void +_add_connection_call(NMClient * self, + gpointer source_tag, + gboolean ignore_out_result, + GVariant * settings, + NMSettingsAddConnection2Flags flags, + GVariant * args, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(self)); + g_return_if_fail(!settings || g_variant_is_of_type(settings, G_VARIANT_TYPE("a{sa{sv}}"))); + g_return_if_fail(!args || g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}"))); + + NML_NMCLIENT_LOG_D(self, "AddConnection() started..."); + + if (!settings) + settings = nm_g_variant_singleton_aLsaLsvII(); + + /* Although AddConnection2() being capable to handle also AddConnection() and + * AddConnectionUnsaved() variants, we prefer to use the old D-Bus methods when + * they are sufficient. The reason is that libnm should avoid hard dependencies + * on 1.20 API whenever possible. */ + if (ignore_out_result && flags == NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK) { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnection", + g_variant_new("(@a{sa{sv}})", settings), + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_without_extra_result); + } else if (ignore_out_result && flags == NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY) { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnectionUnsaved", + g_variant_new("(@a{sa{sv}})", settings), + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_without_extra_result); + } else { + _nm_client_dbus_call(self, + self, + source_tag, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "AddConnection2", + g_variant_new("(@a{sa{sv}}u@a{sv})", + settings, + (guint32) flags, + args ?: nm_g_variant_singleton_aLsvI()), + G_VARIANT_TYPE("(oa{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + _add_connection_cb_with_extra_result); + } +} + +/** + * nm_client_add_connection_async: + * @client: the %NMClient + * @connection: the connection to add. Note that this object's settings will be + * added, not the object itself + * @save_to_disk: whether to immediately save the connection to disk + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Requests that the remote settings service add the given settings to a new + * connection. If @save_to_disk is %TRUE, the connection is immediately written + * to disk; otherwise it is initially only stored in memory, but may be saved + * later by calling the connection's nm_remote_connection_commit_changes() + * method. + * + * @connection is untouched by this function and only serves as a template of + * the settings to add. The #NMRemoteConnection object that represents what + * NetworkManager actually added is returned to @callback when the addition + * operation is complete. + * + * Note that the #NMRemoteConnection returned in @callback may not contain + * identical settings to @connection as NetworkManager may perform automatic + * completion and/or normalization of connection properties. + **/ +void +nm_client_add_connection_async(NMClient * client, + NMConnection * connection, + gboolean save_to_disk, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CONNECTION(connection)); + + _add_connection_call(client, + nm_client_add_connection_async, + TRUE, + nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL), + save_to_disk ? NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + : NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY, + NULL, + cancellable, + callback, + user_data); +} + +/** + * nm_client_add_connection_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_add_connection_async(). + * + * Returns: (transfer full): the new #NMRemoteConnection on success, %NULL on + * failure, in which case @error will be set. + **/ +NMRemoteConnection * +nm_client_add_connection_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + return NM_REMOTE_CONNECTION( + _request_wait_finish(client, result, nm_client_add_connection_async, NULL, error)); +} + +/** + * nm_client_add_connection2: + * @client: the %NMClient + * @settings: the "a{sa{sv}}" #GVariant with the content of the setting. + * @flags: the %NMSettingsAddConnection2Flags argument. + * @args: (allow-none): the "a{sv}" #GVariant with extra argument or %NULL + * for no extra arguments. + * @ignore_out_result: this function wraps AddConnection2(), which has an + * additional result "a{sv}" output parameter. By setting this to %TRUE, + * you signal that you are not interested in that output parameter. + * This allows the function to fall back to AddConnection() and AddConnectionUnsaved(), + * which is interesting if you run against an older server version that does + * not yet provide AddConnection2(). By setting this to %FALSE, the function + * under the hood always calls AddConnection2(). + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Call AddConnection2() D-Bus API asynchronously. + * + * Since: 1.20 + **/ +void +nm_client_add_connection2(NMClient * client, + GVariant * settings, + NMSettingsAddConnection2Flags flags, + GVariant * args, + gboolean ignore_out_result, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _add_connection_call(client, + nm_client_add_connection2, + ignore_out_result, + settings, + flags, + args, + cancellable, + callback, + user_data); +} + +/** + * nm_client_add_connection2_finish: + * @client: the #NMClient + * @result: the #GAsyncResult + * @out_result: (allow-none) (transfer full) (out): the output #GVariant + * from AddConnection2(). + * If you care about the output result, then the "ignore_out_result" + * parameter of nm_client_add_connection2() must not be set to %TRUE. + * @error: (allow-none): the error argument. + * + * Returns: (transfer full): on success, a pointer to the added + * #NMRemoteConnection. + * + * Since: 1.20 + */ +NMRemoteConnection * +nm_client_add_connection2_finish(NMClient * client, + GAsyncResult *result, + GVariant ** out_result, + GError ** error) +{ + return NM_REMOTE_CONNECTION( + _request_wait_finish(client, result, nm_client_add_connection2, out_result, error)); +} + +/*****************************************************************************/ + +/** + * nm_client_load_connections: + * @client: the %NMClient + * @filenames: (array zero-terminated=1): %NULL-terminated array of filenames to load + * @failures: (out) (transfer full): on return, a %NULL-terminated array of + * filenames that failed to load + * @cancellable: a #GCancellable, or %NULL + * @error: return location for #GError + * + * Requests that the remote settings service load or reload the given files, + * adding or updating the connections described within. + * + * The changes to the indicated files will not yet be reflected in + * @client's connections array when the function returns. + * + * If all of the indicated files were successfully loaded, the + * function will return %TRUE, and @failures will be set to %NULL. If + * NetworkManager tried to load the files, but some (or all) failed, + * then @failures will be set to a %NULL-terminated array of the + * filenames that failed to load. + * + * Returns: %TRUE on success. + * + * Warning: before libnm 1.22, the boolean return value was inconsistent. + * That is made worse, because when running against certain server versions + * before 1.20, the server would return wrong values for success/failure. + * This means, if you use this function in libnm before 1.22, you are advised + * to ignore the boolean return value and only look at @failures and @error. + * With libnm >= 1.22, the boolean return value corresponds to whether @error was + * set. Note that even in the success case, you might have individual @failures. + * With 1.22, the return value is consistent with nm_client_load_connections_finish(). + * + * Deprecated: 1.22: Use nm_client_load_connections_async() or GDBusConnection. + **/ +gboolean +nm_client_load_connections(NMClient * client, + char ** filenames, + char *** failures, + GCancellable *cancellable, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + ret = _nm_client_dbus_call_sync(client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "LoadConnections", + g_variant_new("(^as)", filenames ?: NM_PTRARRAY_EMPTY(char *)), + G_VARIANT_TYPE("(bas)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) { + *failures = NULL; + return FALSE; + } + + g_variant_get(ret, "(b^as)", NULL, &failures); + + return TRUE; +} + +/** + * nm_client_load_connections_async: + * @client: the %NMClient + * @filenames: (array zero-terminated=1): %NULL-terminated array of filenames to load + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Requests that the remote settings service asynchronously load or reload the + * given files, adding or updating the connections described within. + * + * See nm_client_load_connections() for more details. + **/ +void +nm_client_load_connections_async(NMClient * client, + char ** filenames, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(client, + client, + nm_client_load_connections_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "LoadConnections", + g_variant_new("(^as)", filenames ?: NM_PTRARRAY_EMPTY(char *)), + G_VARIANT_TYPE("(bas)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_client_load_connections_finish: + * @client: the %NMClient + * @failures: (out) (transfer full) (array zero-terminated=1): on return, a + * %NULL-terminated array of filenames that failed to load + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of an nm_client_load_connections_async() call. + + * See nm_client_load_connections() for more details. + * + * Returns: %TRUE on success. + * Note that even in the success case, you might have individual @failures. + **/ +gboolean +nm_client_load_connections_finish(NMClient * client, + char *** failures, + GAsyncResult *result, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_load_connections_async), + FALSE); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) { + *failures = NULL; + return FALSE; + } + + g_variant_get(ret, "(b^as)", NULL, &failures); + + return TRUE; +} + +/** + * nm_client_reload_connections: + * @client: the #NMClient + * @cancellable: a #GCancellable, or %NULL + * @error: return location for #GError + * + * Requests that the remote settings service reload all connection + * files from disk, adding, updating, and removing connections until + * the in-memory state matches the on-disk state. + * + * Return value: %TRUE on success, %FALSE on failure + * + * Deprecated: 1.22: Use nm_client_reload_connections_async() or GDBusConnection. + **/ +gboolean +nm_client_reload_connections(NMClient *client, GCancellable *cancellable, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + ret = _nm_client_dbus_call_sync(client, + cancellable, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "ReloadConnections", + g_variant_new("()"), + G_VARIANT_TYPE("(b)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return FALSE; + + return TRUE; +} + +/** + * nm_client_reload_connections_async: + * @client: the #NMClient + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the reload operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Requests that the remote settings service begin reloading all connection + * files from disk, adding, updating, and removing connections until the + * in-memory state matches the on-disk state. + **/ +void +nm_client_reload_connections_async(NMClient * client, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(client, + client, + nm_client_reload_connections_async, + cancellable, + callback, + user_data, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_INTERFACE_SETTINGS, + "ReloadConnections", + g_variant_new("()"), + G_VARIANT_TYPE("(b)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_client_reload_connections_finish: + * @client: the #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for #GError + * + * Gets the result of an nm_client_reload_connections_async() call. + * + * Return value: %TRUE on success, %FALSE on failure + **/ +gboolean +nm_client_reload_connections_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_reload_connections_async), + FALSE); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return FALSE; + + return TRUE; +} + +/*****************************************************************************/ + +/** + * nm_client_get_dns_mode: + * @client: the #NMClient + * + * Gets the current DNS processing mode. + * + * Return value: the DNS processing mode, or %NULL in case the + * value is not available. + * + * Since: 1.6 + **/ +const char * +nm_client_get_dns_mode(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->dns_manager.mode; +} + +/** + * nm_client_get_dns_rc_manager: + * @client: the #NMClient + * + * Gets the current DNS resolv.conf manager. + * + * Return value: the resolv.conf manager or %NULL in case the + * value is not available. + * + * Since: 1.6 + **/ +const char * +nm_client_get_dns_rc_manager(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->dns_manager.rc_manager; +} + +/** + * nm_client_get_dns_configuration: + * @client: a #NMClient + * + * Gets the current DNS configuration + * + * Returns: (transfer none) (element-type NMDnsEntry): a #GPtrArray + * containing #NMDnsEntry elements or %NULL in case the value is not + * available. The returned array is owned by the #NMClient object + * and should not be modified. + * + * Since: 1.6 + **/ +const GPtrArray * +nm_client_get_dns_configuration(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return NM_CLIENT_GET_PRIVATE(client)->dns_manager.configuration; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_dns_manager_configuration(NMClient * self, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + gs_unref_ptrarray GPtrArray *configuration_old = NULL; + gs_unref_ptrarray GPtrArray *configuration_new = NULL; + + nm_assert(G_OBJECT(self) == dbobj->nmobj); + + if (value) { + GVariant * entry_var_tmp; + GVariantIter iter; + GPtrArray * array; + + configuration_new = g_ptr_array_new_with_free_func((GDestroyNotify) nm_dns_entry_unref); + + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "@a{sv}", &entry_var_tmp)) { + gs_unref_variant GVariant *entry_var = entry_var_tmp; + nm_auto_free_variant_iter GVariantIter *iterp_nameservers = NULL; + nm_auto_free_variant_iter GVariantIter *iterp_domains = NULL; + gs_free char ** nameservers = NULL; + gs_free char ** domains = NULL; + gboolean vpn = FALSE; + NMDnsEntry * entry; + char * interface = NULL; + char * str; + gint32 priority = 0; + + if (!g_variant_lookup(entry_var, "nameservers", "as", &iterp_nameservers) + || !g_variant_lookup(entry_var, "priority", "i", &priority)) { + g_warning("Ignoring invalid DNS configuration"); + continue; + } + + array = g_ptr_array_new(); + while (g_variant_iter_next(iterp_nameservers, "&s", &str)) + g_ptr_array_add(array, str); + g_ptr_array_add(array, NULL); + nameservers = (char **) g_ptr_array_free(array, FALSE); + + if (g_variant_lookup(entry_var, "domains", "as", &iterp_domains)) { + array = g_ptr_array_new(); + while (g_variant_iter_next(iterp_domains, "&s", &str)) + g_ptr_array_add(array, str); + g_ptr_array_add(array, NULL); + domains = (char **) g_ptr_array_free(array, FALSE); + } + + g_variant_lookup(entry_var, "interface", "&s", &interface); + g_variant_lookup(entry_var, "vpn", "b", &vpn); + + entry = nm_dns_entry_new(interface, + (const char *const *) nameservers, + (const char *const *) domains, + priority, + vpn); + if (!entry) { + g_warning("Ignoring invalid DNS entry"); + continue; + } + + g_ptr_array_add(configuration_new, entry); + } + } + + configuration_old = priv->dns_manager.configuration; + priv->dns_manager.configuration = g_steal_pointer(&configuration_new); + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/** + * nm_client_get_capabilities: + * @client: the #NMClient instance + * @length: (out) (allow-none): the number of returned capabilities. + * + * Returns: (transfer none) (array length=length): the + * list of capabilities reported by the server or %NULL + * if the capabilities are unknown. + * The numeric values correspond to #NMCapability enum. + * The array is terminated by a numeric zero sentinel + * at position @length. + * + * Since: 1.24 + */ +const guint32 * +nm_client_get_capabilities(NMClient *client, gsize *length) +{ + NMClientPrivate *priv; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(length, NULL); + + priv = NM_CLIENT_GET_PRIVATE(client); + + NM_SET_OUT(length, priv->nm.capabilities_len); + return priv->nm.capabilities_arr; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_nm_capabilities(NMClient * self, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + nm_assert(G_OBJECT(self) == dbobj->nmobj); + + nm_clear_g_free(&priv->nm.capabilities_arr); + priv->nm.capabilities_len = 0; + + if (value) { + const guint32 *arr; + gsize len; + + arr = g_variant_get_fixed_array(value, &len, sizeof(guint32)); + priv->nm.capabilities_len = len; + priv->nm.capabilities_arr = g_new(guint32, len + 1); + if (len > 0) + memcpy(priv->nm.capabilities_arr, arr, len * sizeof(guint32)); + priv->nm.capabilities_arr[len] = 0; + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +/** + * nm_client_get_checkpoints: + * @client: a #NMClient + * + * Gets all the active checkpoints. + * + * Returns: (transfer none) (element-type NMCheckpoint): a #GPtrArray + * containing all the #NMCheckpoint. The returned array is owned by the + * #NMClient object and should not be modified. + * + * Since: 1.12 + **/ +const GPtrArray * +nm_client_get_checkpoints(NMClient *client) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_CLIENT_GET_PRIVATE(client)->nm.property_ao[PROPERTY_AO_IDX_CHECKPOINTS]); +} + +static void +checkpoint_create_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + gs_unref_object GTask *task = user_data; + gs_unref_variant GVariant *ret = NULL; + const char * v_checkpoint_path; + GError * error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), result, &error); + if (!ret) { + if (!nm_utils_error_is_cancelled(error)) + g_dbus_error_strip_remote_error(error); + g_task_return_error(task, error); + return; + } + + g_variant_get(ret, "(&o)", &v_checkpoint_path); + + _request_wait_start(g_steal_pointer(&task), + "CheckpointCreate", + NM_TYPE_CHECKPOINT, + v_checkpoint_path, + NULL); +} + +/** + * nm_client_checkpoint_create: + * @client: the %NMClient + * @devices: (element-type NMDevice): a list of devices for which a + * checkpoint should be created. + * @rollback_timeout: the rollback timeout in seconds + * @flags: creation flags + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Creates a checkpoint of the current networking configuration + * for given interfaces. An empty @devices argument means all + * devices. If @rollback_timeout is not zero, a rollback is + * automatically performed after the given timeout. + * + * Since: 1.12 + **/ +void +nm_client_checkpoint_create(NMClient * client, + const GPtrArray * devices, + guint32 rollback_timeout, + NMCheckpointCreateFlags flags, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + gs_free const char **paths = NULL; + guint i; + + g_return_if_fail(NM_IS_CLIENT(client)); + + if (devices && devices->len > 0) { + paths = g_new(const char *, devices->len + 1); + for (i = 0; i < devices->len; i++) + paths[i] = nm_object_get_path(NM_OBJECT(devices->pdata[i])); + paths[i] = NULL; + } + + _nm_client_dbus_call( + client, + client, + nm_client_checkpoint_create, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointCreate", + g_variant_new("(^aouu)", paths ?: NM_PTRARRAY_EMPTY(const char *), rollback_timeout, flags), + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + checkpoint_create_cb); +} + +/** + * nm_client_checkpoint_create_finish: + * @client: the #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_checkpoint_create(). + * + * Returns: (transfer full): the new #NMCheckpoint on success, %NULL on + * failure, in which case @error will be set. + * + * Since: 1.12 + **/ +NMCheckpoint * +nm_client_checkpoint_create_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + return NM_CHECKPOINT( + _request_wait_finish(client, result, nm_client_checkpoint_create, NULL, error)); +} + +/** + * nm_client_checkpoint_destroy: + * @client: the %NMClient + * @checkpoint_path: the D-Bus path for the checkpoint + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Destroys an existing checkpoint without performing a rollback. + * + * Since: 1.12 + **/ +void +nm_client_checkpoint_destroy(NMClient * client, + const char * checkpoint_path, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(checkpoint_path && checkpoint_path[0] == '/'); + + _nm_client_dbus_call(client, + client, + nm_client_checkpoint_destroy, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointDestroy", + g_variant_new("(o)", checkpoint_path), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_checkpoint_destroy_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_checkpoint_destroy(). + * + * Returns: %TRUE on success or %FALSE on failure, in which case + * @error will be set. + * + * Since: 1.12 + **/ +gboolean +nm_client_checkpoint_destroy_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_checkpoint_destroy), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_client_checkpoint_rollback: + * @client: the %NMClient + * @checkpoint_path: the D-Bus path to the checkpoint + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Performs the rollback of a checkpoint before the timeout is reached. + * + * Since: 1.12 + **/ +void +nm_client_checkpoint_rollback(NMClient * client, + const char * checkpoint_path, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(checkpoint_path && checkpoint_path[0] == '/'); + + _nm_client_dbus_call(client, + client, + nm_client_checkpoint_rollback, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointRollback", + g_variant_new("(o)", checkpoint_path), + G_VARIANT_TYPE("(a{su})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_client_checkpoint_rollback_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_checkpoint_rollback(). + * + * Returns: (transfer full) (element-type utf8 guint32): an hash table of + * devices and results. Devices are represented by their original + * D-Bus path; each result is a #NMRollbackResult. + * + * Since: 1.12 + **/ +GHashTable * +nm_client_checkpoint_rollback_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *v_result = NULL; + GVariantIter iter; + GHashTable * hash; + const char * path; + guint32 r; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_checkpoint_rollback), NULL); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{su})", &v_result); + + hash = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); + + g_variant_iter_init(&iter, v_result); + while (g_variant_iter_next(&iter, "{&su}", &path, &r)) + g_hash_table_insert(hash, g_strdup(path), GUINT_TO_POINTER(r)); + + return hash; +} + +/** + * nm_client_checkpoint_adjust_rollback_timeout: + * @client: the %NMClient + * @checkpoint_path: a D-Bus path to a checkpoint + * @add_timeout: the timeout in seconds counting from now. + * Set to zero, to disable the timeout. + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Resets the timeout for the checkpoint with path @checkpoint_path + * to @timeout_add. + * + * Since: 1.12 + **/ +void +nm_client_checkpoint_adjust_rollback_timeout(NMClient * client, + const char * checkpoint_path, + guint32 add_timeout, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(checkpoint_path && checkpoint_path[0] == '/'); + + _nm_client_dbus_call(client, + client, + nm_client_checkpoint_adjust_rollback_timeout, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "CheckpointAdjustRollbackTimeout", + g_variant_new("(ou)", checkpoint_path, add_timeout), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_checkpoint_adjust_rollback_timeout_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_checkpoint_adjust_rollback_timeout(). + * + * Returns: %TRUE on success or %FALSE on failure. + * + * Since: 1.12 + **/ +gboolean +nm_client_checkpoint_adjust_rollback_timeout_finish(NMClient * client, + GAsyncResult *result, + GError ** error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail( + nm_g_task_is_valid(result, client, nm_client_checkpoint_adjust_rollback_timeout), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_client_reload: + * @client: the %NMClient + * @flags: flags indicating what to reload. + * @cancellable: a #GCancellable, or %NULL + * @callback: (scope async): callback to be called when the add operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Reload NetworkManager's configuration and perform certain updates, like + * flushing caches or rewriting external state to disk. This is similar to + * sending SIGHUP to NetworkManager but it allows for more fine-grained control + * over what to reload (see @flags). It also allows non-root access via + * PolicyKit and contrary to signals it is synchronous. + * + * Since: 1.22 + **/ +void +nm_client_reload(NMClient * client, + NMManagerReloadFlags flags, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_dbus_call(client, + client, + nm_client_reload, + cancellable, + callback, + user_data, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "Reload", + g_variant_new("(u)", (guint32) flags), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_client_reload_finish: + * @client: an #NMClient + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_reload(). + * + * Returns: %TRUE on success or %FALSE on failure. + * + * Since: 1.22 + **/ +gboolean +nm_client_reload_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_reload), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_client_dbus_call: + * @client: the #NMClient + * @object_path: path of remote object + * @interface_name: D-Bus interface to invoke method on + * @method_name: the name of the method to invoke + * @parameters: (nullable): a #GVariant tuple with parameters for the method + * or %NULL if not passing parameters + * @reply_type: (nullable): the expected type of the reply (which will be a + * tuple), or %NULL + * @timeout_msec: the timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout + * @cancellable: (nullable): a #GCancellable or %NULL + * @callback: (nullable): a #GAsyncReadyCallback to call when the request + * is satisfied or %NULL if you don't care about the result of the + * method invocation + * @user_data: the data to pass to @callback + * + * Call g_dbus_connection_call() on the current name owner with the specified + * arguments. Most importantly, this invokes g_dbus_connection_call() with the + * client's #GMainContext, so that the response is always in order with other + * events D-Bus events. Of course, the call uses #GTask and will invoke the + * callback on the current g_main_context_get_thread_default(). + * + * This API is merely a convenient wrapper for g_dbus_connection_call(). You can + * also use g_dbus_connection_call() directly, with the same effect. + * + * Since: 1.24 + **/ +void +nm_client_dbus_call(NMClient * client, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + int timeout_msec, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + + _nm_client_dbus_call(client, + client, + nm_client_dbus_call, + cancellable, + callback, + user_data, + object_path, + interface_name, + method_name, + parameters, + reply_type, + G_DBUS_CALL_FLAGS_NONE, + timeout_msec == -1 ? NM_DBUS_DEFAULT_TIMEOUT_MSEC : timeout_msec, + nm_dbus_connection_call_finish_variant_cb); +} + +/** + * nm_client_dbus_call_finish: + * @client: the #NMClient instance + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_dbus_call(). + * + * Returns: (transfer full): the result #GVariant or %NULL on error. + * + * Since: 1.24 + **/ +GVariant * +nm_client_dbus_call_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_dbus_call), FALSE); + + return g_task_propagate_pointer(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_client_dbus_set_property: + * @client: the #NMClient + * @object_path: path of remote object + * @interface_name: D-Bus interface for the property to set. + * @property_name: the name of the property to set + * @value: a #GVariant with the value to set. + * @timeout_msec: the timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout + * @cancellable: (nullable): a #GCancellable or %NULL + * @callback: (nullable): a #GAsyncReadyCallback to call when the request + * is satisfied or %NULL if you don't care about the result of the + * method invocation + * @user_data: the data to pass to @callback + * + * Like nm_client_dbus_call() but calls "Set" on the standard "org.freedesktop.DBus.Properties" + * D-Bus interface. + * + * Since: 1.24 + **/ +void +nm_client_dbus_set_property(NMClient * client, + const char * object_path, + const char * interface_name, + const char * property_name, + GVariant * value, + int timeout_msec, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(interface_name); + g_return_if_fail(property_name); + g_return_if_fail(value); + + _nm_client_dbus_call(client, + client, + nm_client_dbus_set_property, + cancellable, + callback, + user_data, + object_path, + DBUS_INTERFACE_PROPERTIES, + "Set", + g_variant_new("(ssv)", interface_name, property_name, value), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + timeout_msec == -1 ? NM_DBUS_DEFAULT_TIMEOUT_MSEC : timeout_msec, + nm_dbus_connection_call_finish_void_cb); +} + +/** + * nm_client_dbus_set_property_finish: + * @client: the #NMClient instance + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_client_dbus_set_property(). + * + * Returns: %TRUE on success or %FALSE on failure. + * + * Since: 1.24 + **/ +gboolean +nm_client_dbus_set_property_finish(NMClient *client, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(client), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, client, nm_client_dbus_set_property), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +static void +_init_fetch_all(NMClient *self) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + NML_NMCLIENT_LOG_D(self, "fetch all"); + + nm_assert(!priv->get_managed_objects_cancellable); + + priv->get_managed_objects_cancellable = g_cancellable_new(); + + priv->dbsid_nm_object_manager = + nm_dbus_connection_signal_subscribe_object_manager(priv->dbus_connection, + priv->name_owner, + "/org/freedesktop", + NULL, + _dbus_managed_objects_changed_cb, + self, + NULL); + + priv->dbsid_dbus_properties_properties_changed = + nm_dbus_connection_signal_subscribe_properties_changed(priv->dbus_connection, + priv->name_owner, + NULL, + NULL, + _dbus_properties_changed_cb, + self, + NULL); + + priv->dbsid_nm_settings_connection_updated = + g_dbus_connection_signal_subscribe(priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Updated", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_settings_updated_cb, + self, + NULL); + + priv->dbsid_nm_connection_active_state_changed = + g_dbus_connection_signal_subscribe(priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + "StateChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_connection_active_state_changed_cb, + self, + NULL); + + priv->dbsid_nm_vpn_connection_state_changed = + g_dbus_connection_signal_subscribe(priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE_VPN_CONNECTION, + "VpnStateChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_vpn_connection_state_changed_cb, + self, + NULL); + + priv->dbsid_nm_check_permissions = + g_dbus_connection_signal_subscribe(priv->dbus_connection, + priv->name_owner, + NM_DBUS_INTERFACE, + "CheckPermissions", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_nm_check_permissions_cb, + self, + NULL); + + g_dbus_connection_call(priv->dbus_connection, + priv->name_owner, + "/org/freedesktop", + DBUS_INTERFACE_OBJECT_MANAGER, + "GetManagedObjects", + NULL, + G_VARIANT_TYPE("(a{oa{sa{sv}}})"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + priv->get_managed_objects_cancellable, + _dbus_get_managed_objects_cb, + nm_utils_user_data_pack(self, g_object_ref(priv->context_busy_watcher))); + + _dbus_check_permissions_start(self); +} + +static void +_init_release_all(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + CList ** dbus_objects_lst_heads; + NMLDBusObject * dbobj; + int i; + gboolean permissions_state_changed = FALSE; + + NML_NMCLIENT_LOG_D(self, "release all"); + + nm_clear_g_cancellable(&priv->permissions_cancellable); + nm_clear_g_cancellable(&priv->get_managed_objects_cancellable); + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->dbsid_nm_object_manager); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, + &priv->dbsid_dbus_properties_properties_changed); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, + &priv->dbsid_nm_settings_connection_updated); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, + &priv->dbsid_nm_connection_active_state_changed); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, + &priv->dbsid_nm_vpn_connection_state_changed); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->dbsid_nm_check_permissions); + + if (priv->permissions_state != NM_TERNARY_DEFAULT) { + priv->permissions_state = NM_TERNARY_DEFAULT; + permissions_state_changed = TRUE; + } + + if (priv->permissions) { + gs_free guint8 *old_permissions = g_steal_pointer(&priv->permissions); + + _emit_permissions_changed(self, old_permissions, NULL); + } + + if (permissions_state_changed) + _notify(self, PROP_PERMISSIONS_STATE); + + nm_assert(c_list_is_empty(&priv->obj_changed_lst_head)); + + dbus_objects_lst_heads = ((CList *[]){ + &priv->dbus_objects_lst_head_on_dbus, + &priv->dbus_objects_lst_head_with_nmobj_not_ready, + &priv->dbus_objects_lst_head_with_nmobj_ready, + NULL, + }); + for (i = 0; dbus_objects_lst_heads[i]; i++) { + c_list_for_each_entry (dbobj, dbus_objects_lst_heads[i], dbus_objects_lst) { + NMLDBusObjIfaceData *db_iface_data; + + nm_assert(c_list_is_empty(&dbobj->obj_changed_lst)); + c_list_for_each_entry (db_iface_data, &dbobj->iface_lst_head, iface_lst) + db_iface_data->iface_removed = TRUE; + nml_dbus_object_obj_changed_link(self, dbobj, NML_DBUS_OBJ_CHANGED_TYPE_DBUS); + } + } + + _dbus_handle_changes(self, "release-all", FALSE); + + /* We require that when we remove all D-Bus interfaces, that all object will go + * away. Note that a NMLDBusObject can be alive due to a NMLDBusObjWatcher, but + * even those should be all cleaned up. */ + nm_assert(c_list_is_empty(&priv->obj_changed_lst_head)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_watched_only)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_on_dbus)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_not_ready)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_ready)); + nm_assert(nm_g_hash_table_size(priv->dbus_objects) == 0); +} + +/*****************************************************************************/ + +static void +name_owner_changed(NMClient *self, const char *name_owner) +{ + NMClientPrivate * priv = NM_CLIENT_GET_PRIVATE(self); + gboolean changed; + gs_free char * old_name_owner_free = NULL; + const char * old_name_owner; + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + name_owner = nm_str_not_empty(name_owner); + + changed = !nm_streq0(priv->name_owner, name_owner); + + if (!name_owner && priv->main_context != priv->dbus_context) { + gs_unref_object GObject *old_context_busy_watcher = NULL; + + NML_NMCLIENT_LOG_D(self, "resync main context as we have no name owner"); + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + + /* Our instance was initialized synchronously. Usually we must henceforth + * stick to a internal main context. But now we have no name-owner... + * at this point, we anyway are going to do a full resync. Swap the main + * contexts again. */ + + old_context_busy_watcher = g_steal_pointer(&priv->context_busy_watcher); + priv->context_busy_watcher = g_object_ref( + g_object_get_qdata(old_context_busy_watcher, nm_context_busy_watcher_quark())); + + g_main_context_ref(priv->main_context); + g_main_context_unref(priv->dbus_context); + priv->dbus_context = priv->main_context; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + /* we need to sync again... */ + + _assert_main_context_is_current_thread_default(self, dbus_context); + + priv->name_owner_changed_id = + nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, + NM_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + name_owner_get_call(self); + } else + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + if (changed) { + NML_NMCLIENT_LOG_D(self, + "name owner changed: %s%s%s -> %s%s%s", + NM_PRINT_FMT_QUOTE_STRING(priv->name_owner), + NM_PRINT_FMT_QUOTE_STRING(name_owner)); + old_name_owner_free = priv->name_owner; + priv->name_owner = g_strdup(name_owner); + old_name_owner = old_name_owner_free; + } else + old_name_owner = priv->name_owner; + + if (changed) + _notify(self, PROP_DBUS_NAME_OWNER); + + if (changed && old_name_owner) + _init_release_all(self); + + if (changed && priv->name_owner) + _init_fetch_all(self); + + _set_nm_running(self); + + if (priv->init_data) { + nm_auto_pop_gmaincontext GMainContext *main_context = NULL; + + if (priv->main_context != priv->dbus_context) + main_context = nm_g_main_context_push_thread_default_if_necessary(priv->main_context); + _init_start_check_complete(self); + } +} + +static void +name_owner_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv; + const char * new_owner; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) + return; + + priv = NM_CLIENT_GET_PRIVATE(self); + if (priv->name_owner_get_cancellable) + return; + + g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); + + name_owner_changed(self, new_owner); +} + +static void +name_owner_get_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMClient * self; + NMClientPrivate *priv; + gs_unref_object GObject *context_busy_watcher = NULL; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + const char * name_owner = NULL; + + nm_utils_user_data_unpack(user_data, &self, &context_busy_watcher); + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + priv = NM_CLIENT_GET_PRIVATE(self); + + g_clear_object(&priv->name_owner_get_cancellable); + + if (ret) + g_variant_get(ret, "(&s)", &name_owner); + + name_owner_changed(self, name_owner); +} + +static void +name_owner_get_call(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + nm_assert(!priv->name_owner_get_cancellable); + priv->name_owner_get_cancellable = g_cancellable_new(); + + g_dbus_connection_call(priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetNameOwner", + g_variant_new("(s)", NM_DBUS_SERVICE), + G_VARIANT_TYPE("(s)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + priv->name_owner_get_cancellable, + name_owner_get_cb, + nm_utils_user_data_pack(self, g_object_ref(priv->context_busy_watcher))); +} + +/*****************************************************************************/ + +static inline gboolean +_nml_cleanup_context_busy_watcher_on_idle_cb(gpointer user_data) +{ + nm_auto_unref_gmaincontext GMainContext *context = NULL; + gs_unref_object GObject *context_busy_watcher = NULL; + + nm_utils_user_data_unpack(user_data, &context, &context_busy_watcher); + + nm_assert(context); + nm_assert(G_IS_OBJECT(context_busy_watcher)); + return G_SOURCE_REMOVE; +} + +void +nml_cleanup_context_busy_watcher_on_idle(GObject *context_busy_watcher_take, GMainContext *context) +{ + gs_unref_object GObject *context_busy_watcher = g_steal_pointer(&context_busy_watcher_take); + GSource * cleanup_source; + + nm_assert(G_IS_OBJECT(context_busy_watcher)); + nm_assert(context); + + /* Technically, we cancelled all pending actions (and these actions + * (GTask) keep the context_busy_watcher object alive). Also, we passed + * no destroy notify to g_dbus_connection_signal_subscribe(). + * That means, there should be no other unaccounted GSource'es left. + * + * Another reason is g_dbus_connection_signal_unsubscribe() which does + * not guarantee that everything was unsubscribed right away. Instead, + * there might still be idle actions queued (which will be thrown away + * once processed). Still, that means, the caller must continue to iterate + * the main context afterwards. Maybe we should pass a GDestroyNotify to + * g_dbus_connection_signal_subscribe() which handles an additional reference + * to the context_busy_watcher object. That would be the proper way to handle + * this. We don't do that, so in practice at this point there might still + * be some idle actions (with G_PRIORITY_DEFAULT) pending. As we schedule here + * another idle action with lower priority, we handle those cases without + * using a GDestroyNotify. + * + * However, we really need to be sure that the context_busy_watcher's + * lifetime matches the time that the context is busy. That is especially + * important with synchronous initialization, where the context-busy-watcher + * keeps the inner GMainContext integrated in the caller's. + * We must not g_source_destroy() that integration too early. + * + * So to be really sure all this is given, always schedule one last + * cleanup idle action with low priority. This should be the last + * thing related to this instance that keeps the context busy. + * + * Note that we could also *not* take a reference on @context + * and unref @context_busy_watcher via the GDestroyNotify. That would + * allow for the context to be wrapped up early, and when the last user + * gives up the reference to the context, the destroy notify could complete + * without even invoke the idle handler. However, that destroy notify may + * not be called in the right thread. So, we want to be sure that we unref + * the context-busy-watcher in the right context. Hence, we always take an + * additional reference and always cleanup in the idle handler. This means: + * the user *MUST* always keep iterating the context after NMClient got destroyed. + * But that is not a severe limitation, because the user anyway must be prepared + * to do that. That is because in many cases it is necessary anyway (and the user + * wouldn't know a priory when not). This way, it is just always necessary. + * + * It might be nice to use G_DEFAULT_PRIORITY here to minimize how long the + * instance stays alive. Probably that would be fine, even without passing + * a GDestroyNotify to g_dbus_connection_signal_subscribe(). But to be extra + * careful, use a low priority. + **/ + + cleanup_source = + nm_g_idle_source_new(G_PRIORITY_LOW + 10, + _nml_cleanup_context_busy_watcher_on_idle_cb, + nm_utils_user_data_pack(g_main_context_ref(context), + g_steal_pointer(&context_busy_watcher)), + NULL); + g_source_attach(cleanup_source, context); + g_source_unref(cleanup_source); +} + +/*****************************************************************************/ + +static void +_init_start_complete(NMClient *self, GError *error_take) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + NML_NMCLIENT_LOG_D( + self, + "%s init complete with %s%s%s", + priv->init_data->is_sync ? "sync" : "async", + NM_PRINT_FMT_QUOTED(error_take, "error: ", error_take->message, "", "success")); + + nml_init_data_return(g_steal_pointer(&priv->init_data), error_take); +} + +static void +_init_start_check_complete(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + _assert_main_context_is_current_thread_default(self, main_context); + + if (!priv->init_data) + return; + + if (priv->get_managed_objects_cancellable) { + /* still initializing. Wait. */ + return; + } + +#if NM_MORE_ASSERTS > 10 + { + NMLDBusObject *dbobj; + + c_list_for_each_entry (dbobj, + &priv->dbus_objects_lst_head_with_nmobj_not_ready, + dbus_objects_lst) { + NML_NMCLIENT_LOG_T(self, "init-start waiting for %s", dbobj->dbus_path->str); + break; + } + } +#endif + + if (!c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_not_ready)) + return; + + _init_start_complete(self, NULL); +} + +static void +_init_start_cancelled_cb(GCancellable *cancellable, gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + GError * error; + + nm_assert(NM_IS_CLIENT(self)); + nm_assert(priv->init_data); + nm_assert(priv->init_data->cancellable == cancellable); + + if (priv->init_data->cancelled_id == 0) { + /* this only can happen if the cancellable was already cancelled initially. + * In this case we are called synchronously. Do nothing, and let the caller + * handle it. */ + return; + } + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + _init_start_complete(self, error); +} + +static gboolean +_init_start_cancel_on_idle_cb(gpointer user_data) +{ + NMClient *self = user_data; + GError * error; + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + _init_start_complete(self, error); + return G_SOURCE_CONTINUE; +} + +static void +_init_start_with_bus(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + if (priv->init_data->cancellable) { + gulong id; + + id = g_cancellable_connect(priv->init_data->cancellable, + G_CALLBACK(_init_start_cancelled_cb), + self, + NULL); + if (id == 0) { + priv->init_data->cancel_on_idle_source = + nm_g_idle_source_new(G_PRIORITY_DEFAULT, _init_start_cancel_on_idle_cb, self, NULL); + g_source_attach(priv->init_data->cancel_on_idle_source, priv->main_context); + return; + } + + priv->init_data->cancelled_id = id; + } + + _assert_main_context_is_current_thread_default(self, dbus_context); + + priv->name_owner_changed_id = + nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, + NM_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + name_owner_get_call(self); +} + +static void +_init_start_bus_get_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMClient * self = user_data; + NMClientPrivate *priv; + GDBusConnection *dbus_connection; + GError * error = NULL; + + nm_assert(NM_IS_CLIENT(self)); + + dbus_connection = g_bus_get_finish(result, &error); + + if (!dbus_connection) { + _init_start_complete(self, error); + return; + } + + priv = NM_CLIENT_GET_PRIVATE(self); + priv->dbus_connection = dbus_connection; + + _init_start_with_bus(self); + + _notify(self, PROP_DBUS_CONNECTION); +} + +static void +_init_start(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + NML_NMCLIENT_LOG_D(self, + "starting %s initialization...", + priv->init_data->is_sync ? "sync" : "async"); + + if (!priv->dbus_connection) { + g_bus_get(_nm_dbus_bus_type(), priv->init_data->cancellable, _init_start_bus_get_cb, self); + return; + } + + _init_start_with_bus(self); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMClient * self = NM_CLIENT(object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_INSTANCE_FLAGS: + g_value_set_uint(value, priv->instance_flags); + break; + case PROP_DBUS_CONNECTION: + g_value_set_object(value, priv->dbus_connection); + break; + case PROP_DBUS_NAME_OWNER: + g_value_set_string(value, nm_client_get_dbus_name_owner(self)); + break; + case PROP_NM_RUNNING: + g_value_set_boolean(value, nm_client_get_nm_running(self)); + break; + + /* Manager properties. */ + case PROP_VERSION: + g_value_set_string(value, nm_client_get_version(self)); + break; + case PROP_STATE: + g_value_set_enum(value, nm_client_get_state(self)); + break; + case PROP_STARTUP: + g_value_set_boolean(value, nm_client_get_startup(self)); + break; + case PROP_NETWORKING_ENABLED: + g_value_set_boolean(value, nm_client_networking_get_enabled(self)); + break; + case PROP_WIRELESS_ENABLED: + g_value_set_boolean(value, nm_client_wireless_get_enabled(self)); + break; + case PROP_WIRELESS_HARDWARE_ENABLED: + g_value_set_boolean(value, nm_client_wireless_hardware_get_enabled(self)); + break; + case PROP_WWAN_ENABLED: + g_value_set_boolean(value, nm_client_wwan_get_enabled(self)); + break; + case PROP_WWAN_HARDWARE_ENABLED: + g_value_set_boolean(value, nm_client_wwan_hardware_get_enabled(self)); + break; + case PROP_WIMAX_ENABLED: + g_value_set_boolean(value, FALSE); + break; + case PROP_WIMAX_HARDWARE_ENABLED: + g_value_set_boolean(value, FALSE); + break; + case PROP_ACTIVE_CONNECTIONS: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_client_get_active_connections(self))); + break; + case PROP_CONNECTIVITY: + g_value_set_enum(value, nm_client_get_connectivity(self)); + break; + case PROP_CONNECTIVITY_CHECK_AVAILABLE: + g_value_set_boolean(value, nm_client_connectivity_check_get_available(self)); + break; + case PROP_CONNECTIVITY_CHECK_ENABLED: + g_value_set_boolean(value, nm_client_connectivity_check_get_enabled(self)); + break; + case PROP_CONNECTIVITY_CHECK_URI: + g_value_set_string(value, nm_client_connectivity_check_get_uri(self)); + break; + case PROP_PRIMARY_CONNECTION: + g_value_set_object(value, nm_client_get_primary_connection(self)); + break; + case PROP_ACTIVATING_CONNECTION: + g_value_set_object(value, nm_client_get_activating_connection(self)); + break; + case PROP_DEVICES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_devices(self))); + break; + case PROP_METERED: + g_value_set_uint(value, nm_client_get_metered(self)); + break; + case PROP_ALL_DEVICES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_all_devices(self))); + break; + case PROP_CHECKPOINTS: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_checkpoints(self))); + break; + case PROP_CAPABILITIES: + { + const guint32 *arr; + GArray * out; + gsize len; + + arr = nm_client_get_capabilities(self, &len); + if (arr) { + out = g_array_new(TRUE, FALSE, sizeof(guint32)); + g_array_append_vals(out, arr, len); + } else + out = NULL; + g_value_take_boxed(value, out); + } break; + case PROP_PERMISSIONS_STATE: + g_value_set_enum(value, priv->permissions_state); + break; + + /* Settings properties. */ + case PROP_CONNECTIONS: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_connections(self))); + break; + case PROP_HOSTNAME: + g_value_set_string(value, priv->settings.hostname); + break; + case PROP_CAN_MODIFY: + g_value_set_boolean(value, priv->settings.can_modify); + break; + + /* DNS properties */ + case PROP_DNS_MODE: + g_value_set_string(value, nm_client_get_dns_mode(self)); + break; + case PROP_DNS_RC_MANAGER: + g_value_set_string(value, nm_client_get_dns_rc_manager(self)); + break; + case PROP_DNS_CONFIGURATION: + g_value_take_boxed(value, + _nm_utils_copy_array(nm_client_get_dns_configuration(self), + (NMUtilsCopyFunc) nm_dns_entry_dup, + (GDestroyNotify) nm_dns_entry_unref)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMClient * self = NM_CLIENT(object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + gboolean b; + guint v_uint; + + switch (prop_id) { + case PROP_INSTANCE_FLAGS: + /* construct */ + + v_uint = g_value_get_uint(value); + g_return_if_fail(!NM_FLAGS_ANY(v_uint, ~((guint) NM_CLIENT_INSTANCE_FLAGS_ALL))); + v_uint &= ((guint) NM_CLIENT_INSTANCE_FLAGS_ALL); + + if (!priv->instance_flags_constructed) { + priv->instance_flags_constructed = TRUE; + priv->instance_flags = v_uint; + nm_assert((guint) priv->instance_flags == v_uint); + } else { + NMClientInstanceFlags flags = v_uint; + + /* After object construction, we only allow to toggle certain flags and + * ignore all other flags. */ + + if ((priv->instance_flags ^ flags) + & NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS) { + if (NM_FLAGS_HAS(flags, NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS)) + priv->instance_flags |= NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS; + else + priv->instance_flags &= ~NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS; + if (priv->dbsid_nm_check_permissions != 0) + _dbus_check_permissions_start(self); + } + } + break; + + case PROP_DBUS_CONNECTION: + /* construct-only */ + priv->dbus_connection = g_value_dup_object(value); + break; + + case PROP_NETWORKING_ENABLED: + b = g_value_get_boolean(value); + if (priv->nm.networking_enabled != b) { + nm_client_networking_set_enabled(self, b, NULL); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WIRELESS_ENABLED: + b = g_value_get_boolean(value); + if (priv->nm.wireless_enabled != b) { + nm_client_wireless_set_enabled(self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WWAN_ENABLED: + b = g_value_get_boolean(value); + if (priv->nm.wwan_enabled != b) { + nm_client_wwan_set_enabled(self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_CONNECTIVITY_CHECK_ENABLED: + b = g_value_get_boolean(value); + if (priv->nm.connectivity_check_enabled != b) { + nm_client_connectivity_check_set_enabled(self, b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WIMAX_ENABLED: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static gboolean +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + gs_unref_object NMClient *self = NULL; + NMClientPrivate * priv; + GMainContext * dbus_context; + GError * local_error = NULL; + GMainLoop * main_loop; + GObject * parent_context_busy_watcher; + + g_return_val_if_fail(NM_IS_CLIENT(initable), FALSE); + + self = g_object_ref(NM_CLIENT(initable)); /* keep instance alive. */ + + priv = NM_CLIENT_GET_PRIVATE(self); + + g_return_val_if_fail(!priv->dbus_context, FALSE); + + /* when using init_sync(), we use a separate internal GMainContext for + * all D-Bus operations and use our regular async-init code. That means, + * also in sync-init, we don't actually block waiting for our D-Bus requests, + * instead, we only block (g_main_loop_run()) for the overall result. + * + * Doing this has a performance overhead. Also, we cannot ever fall back + * to the regular main-context (not unless we lose the main-owner and + * need to re-initialize). The reason is that we receive events on our + * dbus_context, and this cannot be brought in sync -- short of full + * reinitalizing. Therefor, using sync init not only is slower during + * construction of the object, but NMClient will stick to the dual GMainContext + * mode. + * + * Aside from this downside, the solution is good: + * + * - we don't duplicate the implementation of async-init. + * - we don't iterate the main-context of the caller while waiting for + * initialization to happen + * - we still invoke all changes under the main_context of the caller. + * - all D-Bus events strictly go through dbus_context and are in order. + */ + + dbus_context = g_main_context_new(); + priv->dbus_context = g_main_context_ref(dbus_context); + + /* We have an inner context. Note that if we loose the name owner, we have a chance + * to resync and drop the inner context. That means, requests made against the inner + * context have a different lifetime. Hence, we create a separate tracking + * object. This "wraps" the outer context-busy-watcher and references it, so + * that the work together. Grep for nm_context_busy_watcher_quark() to + * see how this works. */ + parent_context_busy_watcher = g_steal_pointer(&priv->context_busy_watcher); + priv->context_busy_watcher = g_object_new(G_TYPE_OBJECT, NULL); + g_object_set_qdata_full(priv->context_busy_watcher, + nm_context_busy_watcher_quark(), + parent_context_busy_watcher, + g_object_unref); + + g_main_context_push_thread_default(dbus_context); + + main_loop = g_main_loop_new(dbus_context, FALSE); + + priv->init_data = nml_init_data_new_sync(cancellable, main_loop, &local_error); + + _init_start(self); + + g_main_loop_run(main_loop); + + g_main_loop_unref(main_loop); + + g_main_context_pop_thread_default(dbus_context); + + if (priv->main_context != priv->dbus_context) { + nm_context_busy_watcher_integrate_source(priv->main_context, + priv->dbus_context, + priv->context_busy_watcher); + } + + g_main_context_unref(dbus_context); + + if (local_error) { + g_propagate_error(error, local_error); + return FALSE; + } + return TRUE; +} + +/*****************************************************************************/ + +static void +init_async(GAsyncInitable * initable, + int io_priority, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientPrivate * priv; + NMClient * self; + nm_auto_pop_gmaincontext GMainContext *context = NULL; + GTask * task; + + g_return_if_fail(NM_IS_CLIENT(initable)); + + self = NM_CLIENT(initable); + priv = NM_CLIENT_GET_PRIVATE(self); + + g_return_if_fail(!priv->dbus_context); + + priv->dbus_context = g_main_context_ref(priv->main_context); + + context = nm_g_main_context_push_thread_default_if_necessary(priv->main_context); + + task = nm_g_task_new(self, cancellable, init_async, callback, user_data); + g_task_set_priority(task, io_priority); + + priv->init_data = nml_init_data_new_async(cancellable, g_steal_pointer(&task)); + + _init_start(self); +} + +static gboolean +init_finish(GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_CLIENT(initable), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, initable, init_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +static void +nm_client_init(NMClient *self) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + priv->permissions_state = NM_TERNARY_DEFAULT; + + priv->context_busy_watcher = g_object_new(G_TYPE_OBJECT, NULL); + + c_list_init(&self->obj_base.queue_notify_lst); + c_list_init(&priv->queue_notify_lst_head); + c_list_init(&priv->notify_event_lst_head); + + priv->dbus_objects = g_hash_table_new(nm_pdirect_hash, nm_pdirect_equal); + c_list_init(&priv->dbus_objects_lst_head_watched_only); + c_list_init(&priv->dbus_objects_lst_head_on_dbus); + c_list_init(&priv->dbus_objects_lst_head_with_nmobj_not_ready); + c_list_init(&priv->dbus_objects_lst_head_with_nmobj_ready); + c_list_init(&priv->obj_changed_lst_head); +} + +/** + * nm_client_new: + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Creates a new #NMClient synchronously. + * + * Note that this will block until a NMClient instance is fully initialized. + * This does nothing beside calling g_initable_new(). You are free to call + * g_initable_new() or g_object_new()/g_initable_init() directly for more + * control, to set GObject properties or get access to the NMClient instance + * while it is still initializing. + * + * Using the synchronous initialization creates an #NMClient instance + * that uses an internal #GMainContext. This context is invisible to the + * user. This introduces an additional overhead that is payed not + * only during object initialization, but for the entire lifetime of + * this object. + * Also, due to this internal #GMainContext, the events are no longer + * in sync with other messages from #GDBusConnection (but all events + * of the NMClient will themselves still be ordered). + * For a serious program, you should therefore avoid these problems by + * using g_async_initable_init_async() or nm_client_new_async() instead. + * The sync initialization is still useful for simple scripts or interactive + * testing for example via pygobject. + * + * Creating an #NMClient instance can only fail for two reasons. First, if you didn't + * provide a %NM_CLIENT_DBUS_CONNECTION and the call to g_bus_get() + * fails. You can avoid that by using g_initable_new() directly and + * set a D-Bus connection. + * Second, if you cancelled the creation. If you do that, then note + * that after the failure there might still be idle actions pending + * which keep nm_client_get_main_context() alive. That means, + * in that case you must continue iterating the context to avoid + * leaks. See nm_client_get_context_busy_watcher(). + * + * Creating an #NMClient instance when NetworkManager is not running + * does not cause a failure. + * + * Returns: a new #NMClient or NULL on an error + **/ +NMClient * +nm_client_new(GCancellable *cancellable, GError **error) +{ + return g_initable_new(NM_TYPE_CLIENT, cancellable, error, NULL); +} + +/** + * nm_client_new_async: + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the client is created + * @user_data: data for @callback + * + * Creates a new #NMClient asynchronously. + * @callback will be called when it is done. Use + * nm_client_new_finish() to get the result. + * + * This does nothing beside calling g_async_initable_new_async(). You are free to + * call g_async_initable_new_async() or g_object_new()/g_async_initable_init_async() + * directly for more control, to set GObject properties or get access to the NMClient + * instance while it is still initializing. + * + * Creating an #NMClient instance can only fail for two reasons. First, if you didn't + * provide a %NM_CLIENT_DBUS_CONNECTION and the call to g_bus_get() + * fails. You can avoid that by using g_async_initable_new_async() directly and + * set a D-Bus connection. + * Second, if you cancelled the creation. If you do that, then note + * that after the failure there might still be idle actions pending + * which keep nm_client_get_main_context() alive. That means, + * in that case you must continue iterating the context to avoid + * leaks. See nm_client_get_context_busy_watcher(). + * + * Creating an #NMClient instance when NetworkManager is not running + * does not cause a failure. + **/ +void +nm_client_new_async(GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) +{ + g_async_initable_new_async(NM_TYPE_CLIENT, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + NULL); +} + +/** + * nm_client_new_finish: + * @result: a #GAsyncResult + * @error: location for a #GError, or %NULL + * + * Gets the result of an nm_client_new_async() call. + * + * Returns: a new #NMClient, or %NULL on error + **/ +NMClient * +nm_client_new_finish(GAsyncResult *result, GError **error) +{ + gs_unref_object GObject *source_object = NULL; + GObject * object; + + source_object = g_async_result_get_source_object(result); + g_return_val_if_fail(source_object, NULL); + + object = g_async_initable_new_finish(G_ASYNC_INITABLE(source_object), result, error); + g_return_val_if_fail(!object || NM_IS_CLIENT(object), FALSE); + + return NM_CLIENT(object); +} + +static void +constructed(GObject *object) +{ + NMClient * self = NM_CLIENT(object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + priv->main_context = g_main_context_ref_thread_default(); + + G_OBJECT_CLASS(nm_client_parent_class)->constructed(object); + + NML_NMCLIENT_LOG_D(self, "new NMClient instance"); +} + +static void +dispose(GObject *object) +{ + NMClient * self = NM_CLIENT(object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + + nm_assert(!priv->init_data); + + self->obj_base.is_disposing = TRUE; + + nm_clear_g_cancellable(&priv->name_owner_get_cancellable); + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + + nm_clear_g_free(&priv->name_owner); + + _init_release_all(self); + + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_watched_only)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_on_dbus)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_not_ready)); + nm_assert(c_list_is_empty(&priv->dbus_objects_lst_head_with_nmobj_ready)); + + nm_assert(c_list_is_empty(&priv->queue_notify_lst_head)); + nm_assert(c_list_is_empty(&priv->notify_event_lst_head)); + nm_assert(c_list_is_empty(&self->obj_base.queue_notify_lst)); + nm_assert(nm_g_hash_table_size(priv->dbus_objects) == 0); + + nml_dbus_property_o_clear_many(priv->nm.property_o, G_N_ELEMENTS(priv->nm.property_o), NULL); + nml_dbus_property_ao_clear_many(priv->nm.property_ao, G_N_ELEMENTS(priv->nm.property_ao), NULL); + + nm_clear_g_free(&priv->nm.connectivity_check_uri); + nm_clear_g_free(&priv->nm.version); + + nml_dbus_property_ao_clear(&priv->settings.connections, NULL); + nm_clear_g_free(&priv->settings.hostname); + + nm_clear_pointer(&priv->dns_manager.configuration, g_ptr_array_unref); + nm_clear_g_free(&priv->dns_manager.mode); + nm_clear_g_free(&priv->dns_manager.rc_manager); + + nm_clear_pointer(&priv->dbus_objects, g_hash_table_destroy); + + G_OBJECT_CLASS(nm_client_parent_class)->dispose(object); + + nm_clear_pointer(&priv->udev, udev_unref); + + if (priv->context_busy_watcher && priv->dbus_context) { + nml_cleanup_context_busy_watcher_on_idle(g_steal_pointer(&priv->context_busy_watcher), + priv->dbus_context); + } + + nm_clear_pointer(&priv->dbus_context, g_main_context_unref); + nm_clear_pointer(&priv->main_context, g_main_context_unref); + + nm_clear_g_free(&priv->permissions); + + g_clear_object(&priv->dbus_connection); + + g_clear_object(&priv->context_busy_watcher); + + nm_clear_g_free(&priv->name_owner); + + priv->nm.capabilities_len = 0; + nm_clear_g_free(&priv->nm.capabilities_arr); + + NML_NMCLIENT_LOG_D(self, "disposed"); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_agentmanager = + NML_DBUS_META_IFACE_INIT(NM_DBUS_INTERFACE_AGENT_MANAGER, + NULL, + NML_DBUS_META_INTERFACE_PRIO_NONE, ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_O_PROP( + "ActivatingConnection", + PROP_ACTIVATING_CONNECTION, + NMClient, + _priv.nm.property_o[PROPERTY_O_IDX_NM_ACTIVATING_CONNECTION], + nm_active_connection_get_type), + NML_DBUS_META_PROPERTY_INIT_AO_PROP( + "ActiveConnections", + PROP_ACTIVE_CONNECTIONS, + NMClient, + _priv.nm.property_ao[PROPERTY_AO_IDX_ACTIVE_CONNECTIONS], + nm_active_connection_get_type, + .notify_changed_ao = _property_ao_notify_changed_active_connections_cb), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("AllDevices", + PROP_ALL_DEVICES, + NMClient, + _priv.nm.property_ao[PROPERTY_AO_IDX_ALL_DEVICES], + nm_device_get_type, + .notify_changed_ao = + _property_ao_notify_changed_all_devices_cb), + NML_DBUS_META_PROPERTY_INIT_FCN("Capabilities", + PROP_CAPABILITIES, + "au", + _notify_update_prop_nm_capabilities, ), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Checkpoints", + PROP_CHECKPOINTS, + NMClient, + _priv.nm.property_ao[PROPERTY_AO_IDX_CHECKPOINTS], + nm_checkpoint_get_type), + NML_DBUS_META_PROPERTY_INIT_U("Connectivity", + PROP_CONNECTIVITY, + NMClient, + _priv.nm.connectivity), + NML_DBUS_META_PROPERTY_INIT_B("ConnectivityCheckAvailable", + PROP_CONNECTIVITY_CHECK_AVAILABLE, + NMClient, + _priv.nm.connectivity_check_available), + NML_DBUS_META_PROPERTY_INIT_B("ConnectivityCheckEnabled", + PROP_CONNECTIVITY_CHECK_ENABLED, + NMClient, + _priv.nm.connectivity_check_enabled), + NML_DBUS_META_PROPERTY_INIT_S("ConnectivityCheckUri", + PROP_CONNECTIVITY_CHECK_URI, + NMClient, + _priv.nm.connectivity_check_uri), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Devices", + PROP_DEVICES, + NMClient, + _priv.nm.property_ao[PROPERTY_AO_IDX_DEVICES], + nm_device_get_type, + .notify_changed_ao = + _property_ao_notify_changed_devices_cb), + NML_DBUS_META_PROPERTY_INIT_IGNORE("GlobalDnsConfiguration", "a{sv}"), + NML_DBUS_META_PROPERTY_INIT_U("Metered", PROP_METERED, NMClient, _priv.nm.metered), + NML_DBUS_META_PROPERTY_INIT_B("NetworkingEnabled", + PROP_NETWORKING_ENABLED, + NMClient, + _priv.nm.networking_enabled), + NML_DBUS_META_PROPERTY_INIT_O_PROP("PrimaryConnection", + PROP_PRIMARY_CONNECTION, + NMClient, + _priv.nm.property_o[PROPERTY_O_IDX_NM_PRIMAY_CONNECTION], + nm_active_connection_get_type), + NML_DBUS_META_PROPERTY_INIT_IGNORE("PrimaryConnectionType", "s"), + NML_DBUS_META_PROPERTY_INIT_B("Startup", PROP_STARTUP, NMClient, _priv.nm.startup), + NML_DBUS_META_PROPERTY_INIT_U("State", PROP_STATE, NMClient, _priv.nm.state), + NML_DBUS_META_PROPERTY_INIT_S("Version", PROP_VERSION, NMClient, _priv.nm.version), + NML_DBUS_META_PROPERTY_INIT_IGNORE("WimaxEnabled", "b"), + NML_DBUS_META_PROPERTY_INIT_IGNORE("WimaxHardwareEnabled", "b"), + NML_DBUS_META_PROPERTY_INIT_B("WirelessEnabled", + PROP_WIRELESS_ENABLED, + NMClient, + _priv.nm.wireless_enabled), + NML_DBUS_META_PROPERTY_INIT_B("WirelessHardwareEnabled", + PROP_WIRELESS_HARDWARE_ENABLED, + NMClient, + _priv.nm.wireless_hardware_enabled), + NML_DBUS_META_PROPERTY_INIT_B("WwanEnabled", + PROP_WWAN_ENABLED, + NMClient, + _priv.nm.wwan_enabled), + NML_DBUS_META_PROPERTY_INIT_B("WwanHardwareEnabled", + PROP_WWAN_HARDWARE_ENABLED, + NMClient, + _priv.nm.wwan_hardware_enabled), ), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_SETTINGS, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("CanModify", + PROP_CAN_MODIFY, + NMClient, + _priv.settings.can_modify), + NML_DBUS_META_PROPERTY_INIT_AO_PROP( + "Connections", + PROP_CONNECTIONS, + NMClient, + _priv.settings.connections, + nm_remote_connection_get_type, + .notify_changed_ao = _property_ao_notify_changed_connections_cb, + .check_nmobj_visible_fcn = (gboolean(*)(GObject *)) nm_remote_connection_get_visible), + NML_DBUS_META_PROPERTY_INIT_S("Hostname", + PROP_HOSTNAME, + NMClient, + _priv.settings.hostname), ), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dnsmanager = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DNS_MANAGER, + nm_client_get_type, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("Configuration", + PROP_DNS_CONFIGURATION, + "aa{sv}", + _notify_update_prop_dns_manager_configuration), + NML_DBUS_META_PROPERTY_INIT_S("Mode", PROP_DNS_MODE, NMClient, _priv.dns_manager.mode), + NML_DBUS_META_PROPERTY_INIT_S("RcManager", + PROP_DNS_RC_MANAGER, + NMClient, + _priv.dns_manager.rc_manager), ), ); + +static void +nm_client_class_init(NMClientClass *client_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(client_class); + + _dbus_path_nm = nm_ref_string_new(NM_DBUS_PATH); + _dbus_path_settings = nm_ref_string_new(NM_DBUS_PATH_SETTINGS); + _dbus_path_dns_manager = nm_ref_string_new(NM_DBUS_PATH_DNS_MANAGER); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->constructed = constructed; + object_class->dispose = dispose; + + /** + * NMClient:dbus-connection: + * + * The #GDBusConnection to use. + * + * If this is not set during object construction, the D-Bus connection will + * automatically be chosen during async/sync initalization via g_bus_get(). + * + * Since: 1.22 + */ + obj_properties[PROP_DBUS_CONNECTION] = g_param_spec_object( + NM_CLIENT_DBUS_CONNECTION, + "", + "", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:instance-flags: + * + * #NMClientInstanceFlags for the instance. These affect behavior of #NMClient. + * This is a construct property and you may only set most flags only during + * construction. + * + * The flag %NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS can be toggled any time, + * even after constructing the instance. Note that you may want to watch NMClient:permissions-state + * property to know whether permissions are ready. Note that permissions are only fetched + * when NMClient has a D-Bus name owner. + * + * Since: 1.24 + */ + obj_properties[PROP_INSTANCE_FLAGS] = g_param_spec_uint( + NM_CLIENT_INSTANCE_FLAGS, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:dbus-name-owner: + * + * The name owner of the NetworkManager D-Bus service. + * + * Since: 1.22 + **/ + obj_properties[PROP_DBUS_NAME_OWNER] = + g_param_spec_string(NM_CLIENT_DBUS_NAME_OWNER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:version: + * + * The NetworkManager version. + **/ + obj_properties[PROP_VERSION] = g_param_spec_string(NM_CLIENT_VERSION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:state: + * + * The current daemon state. + **/ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_CLIENT_STATE, + "", + "", + NM_TYPE_STATE, + NM_STATE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:startup: + * + * Whether the daemon is still starting up. + **/ + obj_properties[PROP_STARTUP] = g_param_spec_boolean(NM_CLIENT_STARTUP, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:nm-running: + * + * Whether the daemon is running. + **/ + obj_properties[PROP_NM_RUNNING] = + g_param_spec_boolean(NM_CLIENT_NM_RUNNING, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:networking-enabled: + * + * Whether networking is enabled. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + */ + obj_properties[PROP_NETWORKING_ENABLED] = + g_param_spec_boolean(NM_CLIENT_NETWORKING_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wireless-enabled: + * + * Whether wireless is enabled. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + **/ + obj_properties[PROP_WIRELESS_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WIRELESS_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wireless-hardware-enabled: + * + * Whether the wireless hardware is enabled. + **/ + obj_properties[PROP_WIRELESS_HARDWARE_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WIRELESS_HARDWARE_ENABLED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wwan-enabled: + * + * Whether WWAN functionality is enabled. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + */ + obj_properties[PROP_WWAN_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WWAN_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wwan-hardware-enabled: + * + * Whether the WWAN hardware is enabled. + **/ + obj_properties[PROP_WWAN_HARDWARE_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WWAN_HARDWARE_ENABLED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wimax-enabled: + * + * Whether WiMAX functionality is enabled. + * + * Deprecated: 1.22: WiMAX is no longer supported and this always returns FALSE. The setter has no effect. + */ + obj_properties[PROP_WIMAX_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WIMAX_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:wimax-hardware-enabled: + * + * Whether the WiMAX hardware is enabled. + * + * Deprecated: 1.22: WiMAX is no longer supported and this always returns FALSE. + **/ + obj_properties[PROP_WIMAX_HARDWARE_ENABLED] = + g_param_spec_boolean(NM_CLIENT_WIMAX_HARDWARE_ENABLED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:active-connections: (type GPtrArray(NMActiveConnection)) + * + * The active connections. + **/ + obj_properties[PROP_ACTIVE_CONNECTIONS] = + g_param_spec_boxed(NM_CLIENT_ACTIVE_CONNECTIONS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:connectivity: + * + * The network connectivity state. + */ + obj_properties[PROP_CONNECTIVITY] = + g_param_spec_enum(NM_CLIENT_CONNECTIVITY, + "", + "", + NM_TYPE_CONNECTIVITY_STATE, + NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient::connectivity-check-available + * + * Whether a connectivity checking service has been configured. + * + * Since: 1.10 + */ + obj_properties[PROP_CONNECTIVITY_CHECK_AVAILABLE] = + g_param_spec_boolean(NM_CLIENT_CONNECTIVITY_CHECK_AVAILABLE, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient::connectivity-check-enabled + * + * Whether a connectivity checking service has been enabled. + * + * Since: 1.10 + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + */ + obj_properties[PROP_CONNECTIVITY_CHECK_ENABLED] = + g_param_spec_boolean(NM_CLIENT_CONNECTIVITY_CHECK_ENABLED, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:connectivity-check-uri: + * + * The used URI for connectivity checking. + * + * Since: 1.22 + **/ + obj_properties[PROP_CONNECTIVITY_CHECK_URI] = + g_param_spec_string(NM_CLIENT_CONNECTIVITY_CHECK_URI, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:primary-connection: + * + * The #NMActiveConnection of the device with the default route; + * see nm_client_get_primary_connection() for more details. + **/ + obj_properties[PROP_PRIMARY_CONNECTION] = + g_param_spec_object(NM_CLIENT_PRIMARY_CONNECTION, + "", + "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:activating-connection: + * + * The #NMActiveConnection of the activating connection that is + * likely to become the new #NMClient:primary-connection. + **/ + obj_properties[PROP_ACTIVATING_CONNECTION] = + g_param_spec_object(NM_CLIENT_ACTIVATING_CONNECTION, + "", + "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:devices: (type GPtrArray(NMDevice)) + * + * List of real network devices. Does not include placeholder devices. + **/ + obj_properties[PROP_DEVICES] = g_param_spec_boxed(NM_CLIENT_DEVICES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:all-devices: (type GPtrArray(NMDevice)) + * + * List of both real devices and device placeholders. + * Since: 1.2 + **/ + obj_properties[PROP_ALL_DEVICES] = + g_param_spec_boxed(NM_CLIENT_ALL_DEVICES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:connections: (type GPtrArray(NMRemoteConnection)) + * + * The list of configured connections that are available to the user. (Note + * that this differs from the underlying D-Bus property, which may also + * contain the object paths of connections that the user does not have + * permission to read the details of.) + */ + obj_properties[PROP_CONNECTIONS] = + g_param_spec_boxed(NM_CLIENT_CONNECTIONS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:hostname: + * + * The machine hostname stored in persistent configuration. This can be + * modified by calling nm_client_save_hostname(). + */ + obj_properties[PROP_HOSTNAME] = g_param_spec_string(NM_CLIENT_HOSTNAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:can-modify: + * + * If %TRUE, adding and modifying connections is supported. + */ + obj_properties[PROP_CAN_MODIFY] = + g_param_spec_boolean(NM_CLIENT_CAN_MODIFY, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.2 + **/ + obj_properties[PROP_METERED] = g_param_spec_uint(NM_CLIENT_METERED, + "", + "", + 0, + G_MAXUINT32, + NM_METERED_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:dns-mode: + * + * The current DNS processing mode. + * + * Since: 1.6 + **/ + obj_properties[PROP_DNS_MODE] = g_param_spec_string(NM_CLIENT_DNS_MODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:dns-rc-manager: + * + * The current resolv.conf management mode. + * + * Since: 1.6 + **/ + obj_properties[PROP_DNS_RC_MANAGER] = + g_param_spec_string(NM_CLIENT_DNS_RC_MANAGER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:dns-configuration: (type GPtrArray(NMDnsEntry)) + * + * The current DNS configuration, represented as an array + * of #NMDnsEntry objects. + * + * Since: 1.6 + **/ + obj_properties[PROP_DNS_CONFIGURATION] = + g_param_spec_boxed(NM_CLIENT_DNS_CONFIGURATION, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:checkpoints: (type GPtrArray(NMCheckpoint)) + * + * The list of active checkpoints. + * + * Since: 1.12 + */ + obj_properties[PROP_CHECKPOINTS] = + g_param_spec_boxed(NM_CLIENT_CHECKPOINTS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:capabilities: (type GArray(guint32)) + * + * The list of capabilities numbers as guint32 or %NULL if + * there are no capabilities. The numeric value correspond + * to %NMCapability enum. + * + * Since: 1.24 + */ + obj_properties[PROP_CAPABILITIES] = + g_param_spec_boxed(NM_CLIENT_CAPABILITIES, + "", + "", + G_TYPE_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMClient:permissions-state: + * + * The state of the cached permissions. The value %NM_TERNARY_DEFAULT + * means that no permissions are yet received (or not yet requested). + * %NM_TERNARY_TRUE means that permissions are received, cached and up + * to date. %NM_TERNARY_FALSE means that permissions were received and are + * cached, but in the meantime a "CheckPermissions" signal was received + * that invalidated the cached permissions. + * Note that NMClient will always emit a notify::permissions-state signal + * when a "CheckPermissions" signal got received or after new permissions + * got received (that is regardless whether the value of the permission state + * actually changed). With this you can watch the permissions-state property + * to know whether the permissions are ready. Note that while NMClient has + * no D-Bus name owner, no permissions are fetched (and this property won't + * change). + * + * Since: 1.24 + */ + obj_properties[PROP_PERMISSIONS_STATE] = + g_param_spec_enum(NM_CLIENT_PERMISSIONS_STATE, + "", + "", + NM_TYPE_TERNARY, + NM_TERNARY_DEFAULT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm, + &_nml_dbus_meta_iface_nm_settings, + &_nml_dbus_meta_iface_nm_dnsmanager); + + /** + * NMClient::device-added: + * @client: the client that received the signal + * @device: (type NMDevice): the new device + * + * Notifies that a #NMDevice is added. This signal is not emitted for + * placeholder devices. + **/ + signals[DEVICE_ADDED] = g_signal_new(NM_CLIENT_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMClient::device-removed: + * @client: the client that received the signal + * @device: (type NMDevice): the removed device + * + * Notifies that a #NMDevice is removed. This signal is not emitted for + * placeholder devices. + **/ + signals[DEVICE_REMOVED] = g_signal_new(NM_CLIENT_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMClient::any-device-added: + * @client: the client that received the signal + * @device: (type NMDevice): the new device + * + * Notifies that a #NMDevice is added. This signal is emitted for both + * regular devices and placeholder devices. + **/ + signals[ANY_DEVICE_ADDED] = g_signal_new(NM_CLIENT_ANY_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMClient::any-device-removed: + * @client: the client that received the signal + * @device: (type NMDevice): the removed device + * + * Notifies that a #NMDevice is removed. This signal is emitted for both + * regular devices and placeholder devices. + **/ + signals[ANY_DEVICE_REMOVED] = g_signal_new(NM_CLIENT_ANY_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMClient::permission-changed: + * @client: the client that received the signal + * @permission: a permission from #NMClientPermission + * @result: the permission's result, one of #NMClientPermissionResult + * + * Notifies that a permission has changed + **/ + signals[PERMISSION_CHANGED] = g_signal_new(NM_CLIENT_PERMISSION_CHANGED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_UINT, + G_TYPE_UINT); + /** + * NMClient::connection-added: + * @client: the settings object that received the signal + * @connection: the new connection + * + * Notifies that a #NMConnection has been added. + **/ + signals[CONNECTION_ADDED] = g_signal_new(NM_CLIENT_CONNECTION_ADDED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_REMOTE_CONNECTION); + + /** + * NMClient::connection-removed: + * @client: the settings object that received the signal + * @connection: the removed connection + * + * Notifies that a #NMConnection has been removed. + **/ + signals[CONNECTION_REMOVED] = g_signal_new(NM_CLIENT_CONNECTION_REMOVED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_REMOTE_CONNECTION); + + /** + * NMClient::active-connection-added: + * @client: the settings object that received the signal + * @active_connection: the new active connection + * + * Notifies that a #NMActiveConnection has been added. + **/ + signals[ACTIVE_CONNECTION_ADDED] = g_signal_new(NM_CLIENT_ACTIVE_CONNECTION_ADDED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_ACTIVE_CONNECTION); + + /** + * NMClient::active-connection-removed: + * @client: the settings object that received the signal + * @active_connection: the removed active connection + * + * Notifies that a #NMActiveConnection has been removed. + **/ + signals[ACTIVE_CONNECTION_REMOVED] = g_signal_new(NM_CLIENT_ACTIVE_CONNECTION_REMOVED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_ACTIVE_CONNECTION); +} + +static void +nm_client_initable_iface_init(GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_client_async_initable_iface_init(GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} + +/***************************************************************************** + * Backported symbols. Usually, new API is only added in new major versions + * of NetworkManager (that is, on "master" branch). Sometimes however, we might + * have to backport some API to an older stable branch. In that case, we backport + * the symbols with a different version corresponding to the minor API. + * + * To allow upgrading from such a extended minor-release, "master" contains these + * backported symbols too. + * + * For example, 1.2.0 added nm_setting_connection_autoconnect_slaves_get_type. + * This was backported for 1.0.4 as nm_setting_connection_autoconnect_slaves_get_type@libnm_1_0_4 + * To allow an application that was linked against 1.0.4 to seamlessly upgrade to + * a newer major version, the same symbols is also exposed on "master". Note, that + * a user can only seamlessly upgrade to a newer major version, that is released + * *after* 1.0.4 is out. In this example, 1.2.0 was released after 1.4.0, and thus + * a 1.0.4 user can upgrade to 1.2.0 ABI. + *****************************************************************************/ + +NM_BACKPORT_SYMBOL(libnm_1_0_4, + NMSettingConnectionAutoconnectSlaves, + nm_setting_connection_get_autoconnect_slaves, + (NMSettingConnection * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_0_4, + GType, + nm_setting_connection_autoconnect_slaves_get_type, + (void), + ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + NMMetered, + nm_setting_connection_get_metered, + (NMSettingConnection * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, GType, nm_metered_get_type, (void), ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + NMSettingWiredWakeOnLan, + nm_setting_wired_get_wake_on_lan, + (NMSettingWired * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + const char *, + nm_setting_wired_get_wake_on_lan_password, + (NMSettingWired * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, GType, nm_setting_wired_wake_on_lan_get_type, (void), ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, const guint *, nm_utils_wifi_2ghz_freqs, (void), ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, const guint *, nm_utils_wifi_5ghz_freqs, (void), ()); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + char *, + nm_utils_enum_to_str, + (GType type, int value), + (type, value)); + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + gboolean, + nm_utils_enum_from_str, + (GType type, const char *str, int *out_value, char **err_token), + (type, str, out_value, err_token)); + +NM_BACKPORT_SYMBOL(libnm_1_2_4, + int, + nm_setting_ip_config_get_dns_priority, + (NMSettingIPConfig * setting), + (setting)); + +NM_BACKPORT_SYMBOL(libnm_1_10_14, + NMSettingConnectionMdns, + nm_setting_connection_get_mdns, + (NMSettingConnection * setting), + (setting)); +NM_BACKPORT_SYMBOL(libnm_1_10_14, GType, nm_setting_connection_mdns_get_type, (void), ()); diff --git a/src/libnm-client-impl/nm-dbus-helpers.c b/src/libnm-client-impl/nm-dbus-helpers.c new file mode 100644 index 00000000..3b5f9ff8 --- /dev/null +++ b/src/libnm-client-impl/nm-dbus-helpers.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dbus-helpers.h" + +#include "nm-dbus-interface.h" + +GBusType +_nm_dbus_bus_type(void) +{ + static volatile int bus_type = G_BUS_TYPE_NONE; + int v; + + v = g_atomic_int_get(&bus_type); + if (G_UNLIKELY(v == G_BUS_TYPE_NONE)) { + v = G_BUS_TYPE_SYSTEM; + if (g_getenv("LIBNM_USE_SESSION_BUS")) + v = G_BUS_TYPE_SESSION; + if (!g_atomic_int_compare_and_exchange(&bus_type, G_BUS_TYPE_NONE, v)) + v = g_atomic_int_get(&bus_type); + nm_assert(v != G_BUS_TYPE_NONE); + } + return v; +} + +/* Binds the properties on a generated server-side GDBus object to the + * corresponding properties on the public object. + */ +void +_nm_dbus_bind_properties(gpointer object, gpointer skeleton) +{ + GParamSpec **properties; + guint n_properties; + int i; + + properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(skeleton), &n_properties); + for (i = 0; i < n_properties; i++) { + if (g_str_has_prefix(properties[i]->name, "g-")) + continue; + + g_object_bind_property(object, + properties[i]->name, + skeleton, + properties[i]->name, + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + } + g_free(properties); +} + +static char * +signal_name_from_method_name(const char *method_name) +{ + GString * signal_name; + const char *p; + + signal_name = g_string_new("handle"); + for (p = method_name; *p; p++) { + if (g_ascii_isupper(*p)) + g_string_append_c(signal_name, '-'); + g_string_append_c(signal_name, g_ascii_tolower(*p)); + } + + return g_string_free(signal_name, FALSE); +} + +static void +_nm_dbus_method_meta_marshal(GClosure * closure, + GValue * return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + closure->marshal(closure, + return_value, + n_param_values, + param_values, + invocation_hint, + ((GCClosure *) closure)->callback); + + g_value_set_boolean(return_value, TRUE); +} + +/* Takes (method_name, handler_func) pairs and connects the handlers to the + * signals on skeleton, with object as the user_data, but swapped so it comes + * first in the argument list, and handling the return value automatically. + */ +void +_nm_dbus_bind_methods(gpointer object, gpointer skeleton, ...) +{ + va_list ap; + const char *method_name; + char * signal_name; + GCallback handler; + GClosure * closure; + + va_start(ap, skeleton); + while ((method_name = va_arg(ap, const char *)) && (handler = va_arg(ap, GCallback))) { + signal_name = signal_name_from_method_name(method_name); + closure = g_cclosure_new_swap(handler, object, NULL); + g_closure_set_meta_marshal(closure, NULL, _nm_dbus_method_meta_marshal); + g_signal_connect_closure(skeleton, signal_name, closure, FALSE); + g_free(signal_name); + } + va_end(ap); +} diff --git a/src/libnm-client-impl/nm-dbus-helpers.h b/src/libnm-client-impl/nm-dbus-helpers.h new file mode 100644 index 00000000..e519f733 --- /dev/null +++ b/src/libnm-client-impl/nm-dbus-helpers.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef __NM_DBUS_HELPERS_PRIVATE_H__ +#define __NM_DBUS_HELPERS_PRIVATE_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "libnm-std-aux/nm-dbus-compat.h" + +#define NM_DBUS_DEFAULT_TIMEOUT_MSEC 25000 + +GBusType _nm_dbus_bus_type(void); + +void _nm_dbus_bind_properties(gpointer object, gpointer skeleton); + +void _nm_dbus_bind_methods(gpointer object, gpointer skeleton, ...) G_GNUC_NULL_TERMINATED; + +#endif /* __NM_DBUS_HELPERS_PRIVATE_H__ */ diff --git a/src/libnm-client-impl/nm-default-libnm.h b/src/libnm-client-impl/nm-default-libnm.h new file mode 100644 index 00000000..5b3a8e51 --- /dev/null +++ b/src/libnm-client-impl/nm-default-libnm.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_DEFAULT_LIBNM_H__ +#define __NM_DEFAULT_LIBNM_H__ + +/*****************************************************************************/ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#undef NETWORKMANAGER_COMPILATION +#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_LIBNM + +/*****************************************************************************/ + +#include "nm-version.h" +#include "nm-libnm-utils.h" + +/*****************************************************************************/ + +#endif /* __NM_DEFAULT_LIBNM_H__ */ diff --git a/src/libnm-client-impl/nm-device-6lowpan.c b/src/libnm-client-impl/nm-device-6lowpan.c new file mode 100644 index 00000000..e03b4a00 --- /dev/null +++ b/src/libnm-client-impl/nm-device-6lowpan.c @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-6lowpan.h" + +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, ); + +typedef struct { + NMLDBusPropertyO parent; +} NMDevice6LowpanPrivate; + +struct _NMDevice6Lowpan { + NMDevice parent; + NMDevice6LowpanPrivate _priv; +}; + +struct _NMDevice6LowpanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDevice6Lowpan, nm_device_6lowpan, NM_TYPE_DEVICE) + +#define NM_DEVICE_6LOWPAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDevice6Lowpan, NM_IS_DEVICE_6LOWPAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_6lowpan_get_parent: + * @device: a #NMDevice6Lowpan + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.14 + **/ +NMDevice * +nm_device_6lowpan_get_parent(NMDevice6Lowpan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_6LOWPAN(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_6LOWPAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_6lowpan_get_hw_address: (skip) + * @device: a #NMDevice6Lowpan + * + * Gets the hardware (MAC) address of the #NMDevice6Lowpan + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.14 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_6lowpan_get_hw_address(NMDevice6Lowpan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_6LOWPAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/*****************************************************************************/ + +static void +nm_device_6lowpan_init(NMDevice6Lowpan *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDevice6Lowpan *device = NM_DEVICE_6LOWPAN(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_6lowpan_get_parent(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_lowpan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_6LOWPAN, + nm_device_6lowpan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDevice6Lowpan, + _priv.parent, + nm_device_get_type), ), ); + +static void +nm_device_6lowpan_class_init(NMDevice6LowpanClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDevice6Lowpan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDevice6LowpanPrivate, parent); + + /** + * NMDevice6Lowpan:parent: + * + * The devices's parent device. + * + * Since: 1.14 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_6LOWPAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_lowpan); +} diff --git a/src/libnm-client-impl/nm-device-adsl.c b/src/libnm-client-impl/nm-device-adsl.c new file mode 100644 index 00000000..65f87545 --- /dev/null +++ b/src/libnm-client-impl/nm-device-adsl.c @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Author: Pantelis Koukousoulas + * Copyright (C) 2009 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-adsl.h" + +#include "nm-object-private.h" +#include "nm-setting-adsl.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, ); + +typedef struct { + bool carrier; +} NMDeviceAdslPrivate; + +struct _NMDeviceAdsl { + NMDevice parent; + NMDeviceAdslPrivate _priv; +}; + +struct _NMDeviceAdslClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceAdsl, nm_device_adsl, NM_TYPE_DEVICE) + +#define NM_DEVICE_ADSL_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceAdsl, NM_IS_DEVICE_ADSL, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_adsl_get_carrier: + * @device: a #NMDeviceAdsl + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_adsl_get_carrier(NMDeviceAdsl *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ADSL(device), FALSE); + + return NM_DEVICE_ADSL_GET_PRIVATE(device)->carrier; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_adsl_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_ADSL_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an ADSL connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_ADSL; +} + +/*****************************************************************************/ + +static void +nm_device_adsl_init(NMDeviceAdsl *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceAdsl *device = NM_DEVICE_ADSL(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_adsl_get_carrier(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_adsl = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_ADSL, + nm_device_adsl_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceAdsl, _priv.carrier), ), ); + +static void +nm_device_adsl_class_init(NMDeviceAdslClass *adsl_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(adsl_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(adsl_class); + + object_class->get_property = get_property; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceAdsl:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_ADSL_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_adsl); +} diff --git a/src/libnm-client-impl/nm-device-bond.c b/src/libnm-client-impl/nm-device-bond.c new file mode 100644 index 00000000..48dfd9b2 --- /dev/null +++ b/src/libnm-client-impl/nm-device-bond.c @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-setting-bond.h" + +#include "nm-setting-connection.h" +#include "nm-utils.h" +#include "nm-device-bond.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, PROP_SLAVES, ); + +typedef struct { + NMLDBusPropertyAO slaves; + char * hw_address; + bool carrier; +} NMDeviceBondPrivate; + +struct _NMDeviceBond { + NMDevice parent; + NMDeviceBondPrivate _priv; +}; + +struct _NMDeviceBondClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE) + +#define NM_DEVICE_BOND_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceBond, NM_IS_DEVICE_BOND, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_bond_get_hw_address: (skip) + * @device: a #NMDeviceBond + * + * Gets the hardware (MAC) address of the #NMDeviceBond + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_bond_get_hw_address(NMDeviceBond *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BOND(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_bond_get_carrier: + * @device: a #NMDeviceBond + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_bond_get_carrier(NMDeviceBond *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BOND(device), FALSE); + + return NM_DEVICE_BOND_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_bond_get_slaves: + * @device: a #NMDeviceBond + * + * Gets the devices currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + **/ +const GPtrArray * +nm_device_bond_get_slaves(NMDeviceBond *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BOND(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray(&NM_DEVICE_BOND_GET_PRIVATE(device)->slaves); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_bond_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_BOND_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a bond connection.")); + return FALSE; + } + + /* FIXME: check slaves? */ + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_BOND; +} + +/*****************************************************************************/ + +static void +nm_device_bond_init(NMDeviceBond *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE(object); + + g_free(priv->hw_address); + + G_OBJECT_CLASS(nm_device_bond_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceBond *device = NM_DEVICE_BOND(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_bond_get_carrier(device)); + break; + case PROP_SLAVES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_device_bond_get_slaves(device))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bond = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_BOND, + nm_device_bond_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceBond, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceBond, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_bond_class_init(NMDeviceBondClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceBond); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceBondPrivate, slaves); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceBond:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_BOND_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceBond:slaves: (type GPtrArray(NMDevice)) + * + * The devices enslaved to the bond device. + **/ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_BOND_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_bond); +} diff --git a/src/libnm-client-impl/nm-device-bridge.c b/src/libnm-client-impl/nm-device-bridge.c new file mode 100644 index 00000000..9bc41ac8 --- /dev/null +++ b/src/libnm-client-impl/nm-device-bridge.c @@ -0,0 +1,204 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-setting-bridge.h" + +#include "nm-setting-connection.h" +#include "nm-utils.h" +#include "nm-device-bridge.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, PROP_SLAVES, ); + +typedef struct { + NMLDBusPropertyAO slaves; + bool carrier; +} NMDeviceBridgePrivate; + +struct _NMDeviceBridge { + NMDevice parent; + NMDeviceBridgePrivate _priv; +}; + +struct _NMDeviceBridgeClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceBridge, nm_device_bridge, NM_TYPE_DEVICE) + +#define NM_DEVICE_BRIDGE_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceBridge, NM_IS_DEVICE_BRIDGE, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_bridge_get_hw_address: (skip) + * @device: a #NMDeviceBridge + * + * Gets the hardware (MAC) address of the #NMDeviceBridge + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_bridge_get_hw_address(NMDeviceBridge *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BRIDGE(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_bridge_get_carrier: + * @device: a #NMDeviceBridge + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_bridge_get_carrier(NMDeviceBridge *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BRIDGE(device), FALSE); + + return NM_DEVICE_BRIDGE_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_bridge_get_slaves: + * @device: a #NMDeviceBridge + * + * Gets the devices currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + **/ +const GPtrArray * +nm_device_bridge_get_slaves(NMDeviceBridge *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BRIDGE(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray(&NM_DEVICE_BRIDGE_GET_PRIVATE(device)->slaves); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_bridge_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_BRIDGE_SETTING_NAME)) { + if (_nm_connection_get_setting_bluetooth_for_nap(connection) + && nm_connection_is_type(connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + /* a bluetooth NAP setting is a compatible connection for a bridge. */ + } else { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a bridge connection.")); + return FALSE; + } + } + + /* FIXME: check ports? */ + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_BRIDGE; +} + +/*****************************************************************************/ + +static void +nm_device_bridge_init(NMDeviceBridge *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceBridge *device = NM_DEVICE_BRIDGE(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_bridge_get_carrier(device)); + break; + case PROP_SLAVES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_device_bridge_get_slaves(device))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bridge = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_BRIDGE, + nm_device_bridge_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceBridge, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceBridge, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_bridge_class_init(NMDeviceBridgeClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceBridge); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceBridgePrivate, slaves); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceBridge:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_BRIDGE_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceBridge:slaves: (type GPtrArray(NMDevice)) + * + * The devices enslaved to the bridge device. + **/ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_BRIDGE_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_bridge); +} diff --git a/src/libnm-client-impl/nm-device-bt.c b/src/libnm-client-impl/nm-device-bt.c new file mode 100644 index 00000000..074ada66 --- /dev/null +++ b/src/libnm-client-impl/nm-device-bt.c @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-bt.h" + +#include + +#include "nm-setting-connection.h" +#include "nm-setting-bluetooth.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "nm-enum-types.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_NAME, PROP_BT_CAPABILITIES, ); + +typedef struct { + char * name; + guint32 bt_capabilities; +} NMDeviceBtPrivate; + +struct _NMDeviceBt { + NMDevice parent; + NMDeviceBtPrivate _priv; +}; + +struct _NMDeviceBtClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE) + +#define NM_DEVICE_BT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceBt, NM_IS_DEVICE_BT, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_bt_get_hw_address: (skip) + * @device: a #NMDeviceBt + * + * Gets the hardware (MAC) address of the #NMDeviceBt + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_bt_get_hw_address(NMDeviceBt *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BT(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_bt_get_name: + * @device: a #NMDeviceBt + * + * Gets the name of the #NMDeviceBt. + * + * Returns: the name of the device + **/ +const char * +nm_device_bt_get_name(NMDeviceBt *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BT(device), NULL); + + return NM_DEVICE_BT_GET_PRIVATE(device)->name; +} + +/** + * nm_device_bt_get_capabilities: + * @device: a #NMDeviceBt + * + * Returns the Bluetooth device's usable capabilities. + * + * Returns: a combination of #NMBluetoothCapabilities + **/ +NMBluetoothCapabilities +nm_device_bt_get_capabilities(NMDeviceBt *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_BT(device), NM_BT_CAPABILITY_NONE); + + return NM_DEVICE_BT_GET_PRIVATE(device)->bt_capabilities; +} + +static NMBluetoothCapabilities +get_connection_bt_type(NMConnection *connection) +{ + NMSettingBluetooth *s_bt; + const char * bt_type; + + s_bt = nm_connection_get_setting_bluetooth(connection); + if (!s_bt) + return NM_BT_CAPABILITY_NONE; + + bt_type = nm_setting_bluetooth_get_connection_type(s_bt); + g_assert(bt_type); + + if (!strcmp(bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)) + return NM_BT_CAPABILITY_DUN; + else if (!strcmp(bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) + return NM_BT_CAPABILITY_NAP; + + return NM_BT_CAPABILITY_NONE; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingBluetooth * s_bt; + const char * hw_addr, *setting_addr; + NMBluetoothCapabilities dev_caps; + NMBluetoothCapabilities bt_type; + + if (!NM_DEVICE_CLASS(nm_device_bt_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_BLUETOOTH_SETTING_NAME) + || !(s_bt = nm_connection_get_setting_bluetooth(connection))) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a Bluetooth connection.")); + return FALSE; + } + + if (nm_streq0(nm_setting_bluetooth_get_connection_type(s_bt), NM_SETTING_BLUETOOTH_TYPE_NAP)) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection is of Bluetooth NAP type.")); + return FALSE; + } + + /* Check BT address */ + hw_addr = nm_device_get_hw_address(device); + if (hw_addr) { + if (!nm_utils_hwaddr_valid(hw_addr, ETH_ALEN)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("Invalid device Bluetooth address.")); + return FALSE; + } + setting_addr = nm_setting_bluetooth_get_bdaddr(s_bt); + if (setting_addr && !nm_utils_hwaddr_matches(setting_addr, -1, hw_addr, -1)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The Bluetooth addresses of the device and the connection didn't match.")); + return FALSE; + } + } + + dev_caps = nm_device_bt_get_capabilities(NM_DEVICE_BT(device)); + bt_type = get_connection_bt_type(connection); + if (!(bt_type & dev_caps)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The device is lacking Bluetooth capabilities required by the connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_BLUETOOTH; +} + +/*****************************************************************************/ + +static void +nm_device_bt_init(NMDeviceBt *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE(object); + + g_free(priv->name); + + G_OBJECT_CLASS(nm_device_bt_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceBt *device = NM_DEVICE_BT(object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string(value, nm_device_bt_get_name(device)); + break; + case PROP_BT_CAPABILITIES: + g_value_set_flags(value, nm_device_bt_get_capabilities(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bluetooth = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_BLUETOOTH, + nm_device_bt_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("BtCapabilities", + PROP_BT_CAPABILITIES, + NMDeviceBt, + _priv.bt_capabilities), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("Name", PROP_NAME, NMDeviceBt, _priv.name), ), ); + +static void +nm_device_bt_class_init(NMDeviceBtClass *bt_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(bt_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(bt_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceBt:name: + * + * The name of the bluetooth device. + **/ + obj_properties[PROP_NAME] = g_param_spec_string(NM_DEVICE_BT_NAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceBt:bt-capabilities: + * + * The device's bluetooth capabilities, a combination of #NMBluetoothCapabilities. + **/ + obj_properties[PROP_BT_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_BT_CAPABILITIES, + "", + "", + NM_TYPE_BLUETOOTH_CAPABILITIES, + NM_BT_CAPABILITY_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_bluetooth); +} diff --git a/src/libnm-client-impl/nm-device-dummy.c b/src/libnm-client-impl/nm-device-dummy.c new file mode 100644 index 00000000..d56f6cda --- /dev/null +++ b/src/libnm-client-impl/nm-device-dummy.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-dummy.h" + +#include "nm-object-private.h" +#include "nm-setting-dummy.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +struct _NMDeviceDummy { + NMDevice parent; +}; + +struct _NMDeviceDummyClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceDummy, nm_device_dummy, NM_TYPE_DEVICE) + +#define NM_DEVICE_DUMMY_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceDummy, NM_IS_DEVICE_DUMMY, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_dummy_get_hw_address: (skip) + * @device: a #NMDeviceDummy + * + * Gets the hardware (MAC) address of the #NMDeviceDummy + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.10 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_dummy_get_hw_address(NMDeviceDummy *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_DUMMY(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_dummy_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_DUMMY_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a dummy connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_DUMMY; +} + +/*****************************************************************************/ + +static void +nm_device_dummy_init(NMDeviceDummy *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_dummy = NML_DBUS_META_IFACE_INIT( + NM_DBUS_INTERFACE_DEVICE_DUMMY, + nm_device_dummy_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), ), ); + +static void +nm_device_dummy_class_init(NMDeviceDummyClass *dummy_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS(dummy_class); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/src/libnm-client-impl/nm-device-ethernet.c b/src/libnm-client-impl/nm-device-ethernet.c new file mode 100644 index 00000000..b32decde --- /dev/null +++ b/src/libnm-client-impl/nm-device-ethernet.c @@ -0,0 +1,404 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ethernet.h" + +#include + +#include "nm-libnm-utils.h" +#include "nm-setting-connection.h" +#include "nm-setting-wired.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-veth.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "nm-device-veth.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PERM_HW_ADDRESS, + PROP_SPEED, + PROP_CARRIER, + PROP_S390_SUBCHANNELS, ); + +typedef struct _NMDeviceEthernetPrivate { + char ** s390_subchannels; + char * perm_hw_address; + guint32 speed; + bool carrier; +} NMDeviceEthernetPrivate; + +G_DEFINE_TYPE(NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE) + +#define NM_DEVICE_ETHERNET_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMDeviceEthernet, NM_IS_DEVICE_ETHERNET, NMObject) + +/*****************************************************************************/ + +/** + * nm_device_ethernet_get_hw_address: (skip) + * @device: a #NMDeviceEthernet + * + * Gets the active hardware (MAC) address of the #NMDeviceEthernet + * + * Returns: the active hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_ethernet_get_hw_address(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_ethernet_get_permanent_hw_address: + * @device: a #NMDeviceEthernet + * + * Gets the permanent hardware (MAC) address of the #NMDeviceEthernet + * + * Returns: the permanent hardware address. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_ethernet_get_permanent_hw_address(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), NULL); + + return _nml_coerce_property_str_not_empty( + NM_DEVICE_ETHERNET_GET_PRIVATE(device)->perm_hw_address); +} + +/** + * nm_device_ethernet_get_speed: + * @device: a #NMDeviceEthernet + * + * Gets the speed of the #NMDeviceEthernet in Mbit/s. + * + * Returns: the speed of the device in Mbit/s + **/ +guint32 +nm_device_ethernet_get_speed(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), 0); + + return NM_DEVICE_ETHERNET_GET_PRIVATE(device)->speed; +} + +/** + * nm_device_ethernet_get_carrier: + * @device: a #NMDeviceEthernet + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_ethernet_get_carrier(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), FALSE); + + return NM_DEVICE_ETHERNET_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_ethernet_get_s390_subchannels: + * @device: a #NMDeviceEthernet + * + * Return the list of s390 subchannels if the device supports them. + * + * Returns: (transfer none) (element-type utf8): array of strings, each specifying + * one subchannel the s390 device uses to communicate to the host. + * + * Since: 1.2 + **/ +const char *const * +nm_device_ethernet_get_s390_subchannels(NMDeviceEthernet *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_ETHERNET(device), NULL); + + return (const char *const *) NM_DEVICE_ETHERNET_GET_PRIVATE(device)->s390_subchannels; +} + +static gboolean +match_subchans(NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_mac) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(self); + const char *const * subchans; + gsize num1, num2; + gsize i, j; + + *try_mac = TRUE; + + subchans = nm_setting_wired_get_s390_subchannels(s_wired); + num1 = NM_PTRARRAY_LEN(subchans); + num2 = NM_PTRARRAY_LEN(priv->s390_subchannels); + /* connection has no subchannels */ + if (num1 == 0) + return TRUE; + /* connection requires subchannels but the device has none */ + if (num2 == 0) + return FALSE; + /* number of subchannels differ */ + if (num1 != num2) + return FALSE; + + /* Make sure each subchannel in the connection is a subchannel of this device */ + for (i = 0; subchans[i]; i++) { + const char *candidate = subchans[i]; + gboolean found = FALSE; + + for (j = 0; priv->s390_subchannels[j]; j++) { + if (!g_strcmp0(priv->s390_subchannels[j], candidate)) + found = TRUE; + } + if (!found) + return FALSE; /* a subchannel was not found */ + } + + *try_mac = FALSE; + return TRUE; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingWired *s_wired; + + if (!NM_DEVICE_CLASS(nm_device_ethernet_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (nm_connection_is_type(connection, NM_SETTING_PPPOE_SETTING_NAME) + || nm_connection_is_type(connection, NM_SETTING_WIRED_SETTING_NAME) + || (nm_connection_is_type(connection, NM_SETTING_VETH_SETTING_NAME) + && NM_IS_DEVICE_VETH(device))) { + /* NOP */ + } else { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an Ethernet or PPPoE connection.")); + return FALSE; + } + + s_wired = nm_connection_get_setting_wired(connection); + /* Wired setting optional for PPPoE */ + if (s_wired) { + const char * perm_addr, *s_mac; + gboolean try_mac = TRUE; + const char *const *mac_blacklist; + int i; + + /* Check s390 subchannels */ + if (!match_subchans(NM_DEVICE_ETHERNET(device), s_wired, &try_mac)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection and device differ in S390 subchannels.")); + return FALSE; + } + + /* Check MAC address */ + perm_addr = nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device)); + s_mac = nm_setting_wired_get_mac_address(s_wired); + if (perm_addr) { + /* Virtual devices will have empty permanent addr but they should not be excluded + * from the MAC address check specified in the connection */ + if (*perm_addr == 0) + perm_addr = nm_device_get_hw_address(NM_DEVICE(device)); + + if (!nm_utils_hwaddr_valid(perm_addr, ETH_ALEN)) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("Invalid device MAC address %s."), + perm_addr); + return FALSE; + } + if (try_mac && s_mac && !nm_utils_hwaddr_matches(s_mac, -1, perm_addr, -1)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The MACs of the device and the connection do not match.")); + return FALSE; + } + + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wired_get_mac_address_blacklist(s_wired); + for (i = 0; mac_blacklist[i]; i++) { + if (!nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN)) { + g_warn_if_reached(); + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("Invalid MAC in the blacklist: %s."), + mac_blacklist[i]); + return FALSE; + } + + if (nm_utils_hwaddr_matches(mac_blacklist[i], -1, perm_addr, -1)) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("Device MAC (%s) is blacklisted by the connection."), + perm_addr); + return FALSE; + } + } + } + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_WIRED; +} + +/*****************************************************************************/ + +static void +nm_device_ethernet_init(NMDeviceEthernet *device) +{ + NMDeviceEthernetPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(device, NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate); + + device->_priv = priv; +} + +static void +finalize(GObject *object) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(object); + + g_free(priv->perm_hw_address); + g_strfreev(priv->s390_subchannels); + + G_OBJECT_CLASS(nm_device_ethernet_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceEthernet * device = NM_DEVICE_ETHERNET(object); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(device); + + switch (prop_id) { + case PROP_PERM_HW_ADDRESS: + g_value_set_string(value, nm_device_ethernet_get_permanent_hw_address(device)); + break; + case PROP_SPEED: + g_value_set_uint(value, nm_device_ethernet_get_speed(device)); + break; + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_ethernet_get_carrier(device)); + break; + case PROP_S390_SUBCHANNELS: + g_value_set_boxed(value, priv->s390_subchannels); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wired = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_WIRED, + nm_device_ethernet_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceEthernetPrivate, carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("PermHwAddress", + PROP_PERM_HW_ADDRESS, + NMDeviceEthernetPrivate, + perm_hw_address), + NML_DBUS_META_PROPERTY_INIT_AS("S390Subchannels", + PROP_S390_SUBCHANNELS, + NMDeviceEthernetPrivate, + s390_subchannels), + NML_DBUS_META_PROPERTY_INIT_U("Speed", PROP_SPEED, NMDeviceEthernetPrivate, speed), ), + .base_struct_offset = G_STRUCT_OFFSET(NMDeviceEthernet, _priv), ); + +static void +nm_device_ethernet_class_init(NMDeviceEthernetClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + g_type_class_add_private(klass, sizeof(NMDeviceEthernetPrivate)); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceEthernet:perm-hw-address: + * + * The permanent hardware (MAC) address of the device. + **/ + obj_properties[PROP_PERM_HW_ADDRESS] = + g_param_spec_string(NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceEthernet:speed: + * + * The speed of the device. + **/ + obj_properties[PROP_SPEED] = g_param_spec_uint(NM_DEVICE_ETHERNET_SPEED, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceEthernet:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_ETHERNET_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceEthernet:s390-subchannels: + * + * Identifies subchannels of this network device used for + * communication with z/VM or s390 host. + * + * Since: 1.2 + **/ + obj_properties[PROP_S390_SUBCHANNELS] = + g_param_spec_boxed(NM_DEVICE_ETHERNET_S390_SUBCHANNELS, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_wired); +} diff --git a/src/libnm-client-impl/nm-device-generic.c b/src/libnm-client-impl/nm-device-generic.c new file mode 100644 index 00000000..9dcc8c91 --- /dev/null +++ b/src/libnm-client-impl/nm-device-generic.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-generic.h" + +#include "nm-object-private.h" +#include "nm-setting-generic.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE_DESCRIPTION, ); + +typedef struct { + char *type_description; +} NMDeviceGenericPrivate; + +struct _NMDeviceGeneric { + NMDevice parent; + NMDeviceGenericPrivate _priv; +}; + +struct _NMDeviceGenericClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceGeneric, nm_device_generic, NM_TYPE_DEVICE) + +#define NM_DEVICE_GENERIC_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceGeneric, NM_IS_DEVICE_GENERIC, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_generic_get_hw_address: (skip) + * @device: a #NMDeviceGeneric + * + * Gets the hardware address of the #NMDeviceGeneric + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_generic_get_hw_address(NMDeviceGeneric *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_GENERIC(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/*****************************************************************************/ + +static const char * +get_type_description(NMDevice *device) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(device); + + return _nml_coerce_property_str_not_empty(priv->type_description); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_generic_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_GENERIC_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a generic connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_GENERIC; +} + +/*****************************************************************************/ + +static void +nm_device_generic_init(NMDeviceGeneric *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(object); + + g_free(priv->type_description); + + G_OBJECT_CLASS(nm_device_generic_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceGeneric *self = NM_DEVICE_GENERIC(object); + + switch (prop_id) { + case PROP_TYPE_DESCRIPTION: + g_value_set_string(value, get_type_description((NMDevice *) self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_generic = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_GENERIC, + nm_device_generic_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("TypeDescription", + PROP_TYPE_DESCRIPTION, + NMDeviceGeneric, + _priv.type_description), ), ); + +static void +nm_device_generic_class_init(NMDeviceGenericClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceGeneric:type-description: + * + * A description of the specific type of device this is, or %NULL + * if not known. + **/ + obj_properties[PROP_TYPE_DESCRIPTION] = + g_param_spec_string(NM_DEVICE_GENERIC_TYPE_DESCRIPTION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_generic); +} diff --git a/src/libnm-client-impl/nm-device-infiniband.c b/src/libnm-client-impl/nm-device-infiniband.c new file mode 100644 index 00000000..7e4cc054 --- /dev/null +++ b/src/libnm-client-impl/nm-device-infiniband.c @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-infiniband.h" + +#include + +#include "nm-setting-connection.h" +#include "nm-setting-infiniband.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, ); + +typedef struct { + bool carrier; +} NMDeviceInfinibandPrivate; + +struct _NMDeviceInfiniband { + NMDevice parent; + NMDeviceInfinibandPrivate _priv; +}; + +struct _NMDeviceInfinibandClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE) + +#define NM_DEVICE_INFINIBAND_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceInfiniband, NM_IS_DEVICE_INFINIBAND, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_infiniband_get_hw_address: (skip) + * @device: a #NMDeviceInfiniband + * + * Gets the hardware (MAC) address of the #NMDeviceInfiniband + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_infiniband_get_hw_address(NMDeviceInfiniband *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_INFINIBAND(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_infiniband_get_carrier: + * @device: a #NMDeviceInfiniband + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_infiniband_get_carrier(NMDeviceInfiniband *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_INFINIBAND(device), FALSE); + + return NM_DEVICE_INFINIBAND_GET_PRIVATE(device)->carrier; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingInfiniband *s_infiniband; + const char * hwaddr, *setting_hwaddr; + + if (!NM_DEVICE_CLASS(nm_device_infiniband_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an InfiniBand connection.")); + return FALSE; + } + + hwaddr = nm_device_get_hw_address(NM_DEVICE(device)); + if (hwaddr) { + if (!nm_utils_hwaddr_valid(hwaddr, INFINIBAND_ALEN)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("Invalid device MAC address.")); + return FALSE; + } + + s_infiniband = nm_connection_get_setting_infiniband(connection); + setting_hwaddr = nm_setting_infiniband_get_mac_address(s_infiniband); + if (setting_hwaddr && !nm_utils_hwaddr_matches(setting_hwaddr, -1, hwaddr, -1)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The MACs of the device and the connection didn't match.")); + return FALSE; + } + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_INFINIBAND; +} + +/*****************************************************************************/ + +static void +nm_device_infiniband_init(NMDeviceInfiniband *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceInfiniband *device = NM_DEVICE_INFINIBAND(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_infiniband_get_carrier(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_infiniband = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_INFINIBAND, + nm_device_infiniband_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceInfiniband, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), ), ); + +static void +nm_device_infiniband_class_init(NMDeviceInfinibandClass *ib_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(ib_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(ib_class); + + object_class->get_property = get_property; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceInfiniband:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_INFINIBAND_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_infiniband); +} diff --git a/src/libnm-client-impl/nm-device-ip-tunnel.c b/src/libnm-client-impl/nm-device-ip-tunnel.c new file mode 100644 index 00000000..14c72282 --- /dev/null +++ b/src/libnm-client-impl/nm-device-ip-tunnel.c @@ -0,0 +1,578 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ip-tunnel.h" + +#include "nm-setting-connection.h" +#include "nm-setting-ip-tunnel.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_MODE, + PROP_PARENT, + PROP_LOCAL, + PROP_REMOTE, + PROP_TTL, + PROP_TOS, + PROP_PATH_MTU_DISCOVERY, + PROP_INPUT_KEY, + PROP_OUTPUT_KEY, + PROP_ENCAPSULATION_LIMIT, + PROP_FLOW_LABEL, + PROP_FLAGS, ); + +typedef struct { + NMLDBusPropertyO parent; + char * local; + char * remote; + char * input_key; + char * output_key; + guint32 mode; + guint32 flow_label; + guint32 flags; + guint8 ttl; + guint8 tos; + guint8 encapsulation_limit; + bool path_mtu_discovery; +} NMDeviceIPTunnelPrivate; + +struct _NMDeviceIPTunnel { + NMDevice parent; + NMDeviceIPTunnelPrivate _priv; +}; + +struct _NMDeviceIPTunnelClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceIPTunnel, nm_device_ip_tunnel, NM_TYPE_DEVICE) + +#define NM_DEVICE_IP_TUNNEL_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceIPTunnel, NM_IS_DEVICE_IP_TUNNEL, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_ip_tunnel_get_mode: + * @device: a #NMDeviceIPTunnel + * + * Returns: the tunneling mode + * + * Since: 1.2 + **/ +NMIPTunnelMode +nm_device_ip_tunnel_get_mode(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->mode; +} + +/** + * nm_device_ip_tunnel_get_parent: + * @device: a #NMDeviceIPTunnel + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.2 + **/ +NMDevice * +nm_device_ip_tunnel_get_parent(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_ip_tunnel_get_local: + * @device: a #NMDeviceIPTunnel + * + * Returns: the local endpoint of the tunnel + * + * Since: 1.2 + **/ +const char * +nm_device_ip_tunnel_get_local(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->local); +} + +/** + * nm_device_ip_tunnel_get_remote: + * @device: a #NMDeviceIPTunnel + * + * Returns: the remote endpoint of the tunnel + * + * Since: 1.2 + **/ +const char * +nm_device_ip_tunnel_get_remote(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->remote); +} + +/** + * nm_device_ip_tunnel_get_ttl: + * @device: a #NMDeviceIPTunnel + * + * Returns: the TTL assigned to tunneled packets + * + * Since: 1.2 + **/ +guint8 +nm_device_ip_tunnel_get_ttl(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->ttl; +} + +/** + * nm_device_ip_tunnel_get_tos: + * @device: a #NMDeviceIPTunnel + * + * Returns: type of service (IPv4) or traffic class (IPv6) assigned + * to tunneled packets. + * + * Since: 1.2 + **/ +guint8 +nm_device_ip_tunnel_get_tos(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->tos; +} + +/** + * nm_device_ip_tunnel_get_path_mtu_discovery: + * @device: a #NMDeviceIPTunnel + * + * Returns: whether path MTU discovery is enabled + * + * Since: 1.2 + **/ +gboolean +nm_device_ip_tunnel_get_path_mtu_discovery(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), TRUE); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->path_mtu_discovery; +} + +/** + * nm_device_ip_tunnel_get_input_key: + * @device: a #NMDeviceIPTunnel + * + * Returns: the key used for incoming packets + * + * Since: 1.2 + **/ +const char * +nm_device_ip_tunnel_get_input_key(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->input_key); +} + +/** + * nm_device_ip_tunnel_get_output_key: + * @device: a #NMDeviceIPTunnel + * + * Returns: the key used for outgoing packets + * + * Since: 1.2 + **/ +const char * +nm_device_ip_tunnel_get_output_key(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->output_key); +} + +/** + * nm_device_ip_tunnel_get_encapsulation_limit: + * @device: a #NMDeviceIPTunnel + * + * Returns: the maximum permitted encapsulation level + * + * Since: 1.2 + **/ +guint8 +nm_device_ip_tunnel_get_encapsulation_limit(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->encapsulation_limit; +} + +/** + * nm_device_ip_tunnel_get_flow_label: + * @device: a #NMDeviceIPTunnel + * + * Returns: the flow label assigned to tunnel packets + * + * Since: 1.2 + **/ +guint +nm_device_ip_tunnel_get_flow_label(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), 0); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->flow_label; +} + +/** + * nm_device_ip_tunnel_get_flags: + * @device: a #NMDeviceIPTunnel + * + * Returns: the tunnel flags + * + * Since: 1.12 + **/ +NMIPTunnelFlags +nm_device_ip_tunnel_get_flags(NMDeviceIPTunnel *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_IP_TUNNEL(device), NM_IP_TUNNEL_FLAG_NONE); + + return NM_DEVICE_IP_TUNNEL_GET_PRIVATE(device)->flags; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_ip_tunnel_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_IP_TUNNEL_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an IP tunnel connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_IP_TUNNEL; +} + +/*****************************************************************************/ + +static void +nm_device_ip_tunnel_init(NMDeviceIPTunnel *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE(object); + + g_free(priv->local); + g_free(priv->remote); + g_free(priv->input_key); + g_free(priv->output_key); + + G_OBJECT_CLASS(nm_device_ip_tunnel_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceIPTunnel *device = NM_DEVICE_IP_TUNNEL(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_ip_tunnel_get_parent(device)); + break; + case PROP_MODE: + g_value_set_uint(value, nm_device_ip_tunnel_get_mode(device)); + break; + case PROP_LOCAL: + g_value_set_string(value, nm_device_ip_tunnel_get_local(device)); + break; + case PROP_REMOTE: + g_value_set_string(value, nm_device_ip_tunnel_get_remote(device)); + break; + case PROP_TTL: + g_value_set_uint(value, nm_device_ip_tunnel_get_ttl(device)); + break; + case PROP_TOS: + g_value_set_uint(value, nm_device_ip_tunnel_get_tos(device)); + break; + case PROP_PATH_MTU_DISCOVERY: + g_value_set_boolean(value, nm_device_ip_tunnel_get_path_mtu_discovery(device)); + break; + case PROP_INPUT_KEY: + g_value_set_string(value, nm_device_ip_tunnel_get_input_key(device)); + break; + case PROP_OUTPUT_KEY: + g_value_set_string(value, nm_device_ip_tunnel_get_output_key(device)); + break; + case PROP_ENCAPSULATION_LIMIT: + g_value_set_uint(value, nm_device_ip_tunnel_get_encapsulation_limit(device)); + break; + case PROP_FLOW_LABEL: + g_value_set_uint(value, nm_device_ip_tunnel_get_flow_label(device)); + break; + case PROP_FLAGS: + g_value_set_uint(value, nm_device_ip_tunnel_get_flags(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_iptunnel = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL, + nm_device_ip_tunnel_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_Y("EncapsulationLimit", + PROP_ENCAPSULATION_LIMIT, + NMDeviceIPTunnel, + _priv.encapsulation_limit), + NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMDeviceIPTunnel, _priv.flags), + NML_DBUS_META_PROPERTY_INIT_U("FlowLabel", + PROP_FLOW_LABEL, + NMDeviceIPTunnel, + _priv.flow_label), + NML_DBUS_META_PROPERTY_INIT_S("InputKey", + PROP_INPUT_KEY, + NMDeviceIPTunnel, + _priv.input_key), + NML_DBUS_META_PROPERTY_INIT_S("Local", PROP_LOCAL, NMDeviceIPTunnel, _priv.local), + NML_DBUS_META_PROPERTY_INIT_U("Mode", PROP_MODE, NMDeviceIPTunnel, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_S("OutputKey", + PROP_OUTPUT_KEY, + NMDeviceIPTunnel, + _priv.output_key), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceIPTunnel, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("PathMtuDiscovery", + PROP_PATH_MTU_DISCOVERY, + NMDeviceIPTunnel, + _priv.path_mtu_discovery), + NML_DBUS_META_PROPERTY_INIT_S("Remote", PROP_REMOTE, NMDeviceIPTunnel, _priv.remote), + NML_DBUS_META_PROPERTY_INIT_Y("Tos", PROP_TOS, NMDeviceIPTunnel, _priv.tos), + NML_DBUS_META_PROPERTY_INIT_Y("Ttl", PROP_TTL, NMDeviceIPTunnel, _priv.ttl), ), ); + +static void +nm_device_ip_tunnel_class_init(NMDeviceIPTunnelClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceIPTunnel); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceIPTunnelPrivate, parent); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceIPTunnel:mode: + * + * The tunneling mode of the device. + * + * Since: 1.2 + **/ + obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_IP_TUNNEL_MODE, + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:parent: + * + * The devices's parent device. + * + * Since: 1.2 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_IP_TUNNEL_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:local: + * + * The local endpoint of the tunnel. + * + * Since: 1.2 + **/ + obj_properties[PROP_LOCAL] = g_param_spec_string(NM_DEVICE_IP_TUNNEL_LOCAL, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:remote: + * + * The remote endpoint of the tunnel. + * + * Since: 1.2 + **/ + obj_properties[PROP_REMOTE] = g_param_spec_string(NM_DEVICE_IP_TUNNEL_REMOTE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:ttl: + * + * The TTL assigned to tunneled packets. 0 is a special value + * meaning that packets inherit the TTL value + * + * Since: 1.2 + **/ + obj_properties[PROP_TTL] = g_param_spec_uchar(NM_DEVICE_IP_TUNNEL_TTL, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:tos: + * + * The type of service (IPv4) or traffic class (IPv6) assigned to + * tunneled packets. + * + * Since: 1.2 + **/ + obj_properties[PROP_TOS] = g_param_spec_uchar(NM_DEVICE_IP_TUNNEL_TOS, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:path-mtu-discovery: + * + * Whether path MTU discovery is enabled on this tunnel. + * + * Since: 1.2 + **/ + obj_properties[PROP_PATH_MTU_DISCOVERY] = + g_param_spec_boolean(NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:input-key: + * + * The key used for tunneled input packets, if applicable. + * + * Since: 1.2 + **/ + obj_properties[PROP_INPUT_KEY] = g_param_spec_string(NM_DEVICE_IP_TUNNEL_INPUT_KEY, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:output-key: + * + * The key used for tunneled output packets, if applicable. + * + * Since: 1.2 + **/ + obj_properties[PROP_OUTPUT_KEY] = + g_param_spec_string(NM_DEVICE_IP_TUNNEL_OUTPUT_KEY, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:encapsulation-limit: + * + * How many additional levels of encapsulation are permitted to + * be prepended to packets. This property applies only to IPv6 + * tunnels. + * + * Since: 1.2 + **/ + obj_properties[PROP_ENCAPSULATION_LIMIT] = + g_param_spec_uchar(NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:flow-label: + * + * The flow label to assign to tunnel packets. This property + * applies only to IPv6 tunnels. + * + * Since: 1.2 + **/ + obj_properties[PROP_FLOW_LABEL] = g_param_spec_uint(NM_DEVICE_IP_TUNNEL_FLOW_LABEL, + "", + "", + 0, + (1 << 20) - 1, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceIPTunnel:flags: + * + * Tunnel flags. + * + * Since: 1.12 + **/ + obj_properties[PROP_FLAGS] = g_param_spec_uint(NM_DEVICE_IP_TUNNEL_FLAGS, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_iptunnel); +} diff --git a/src/libnm-client-impl/nm-device-macsec.c b/src/libnm-client-impl/nm-device-macsec.c new file mode 100644 index 00000000..faa5be7e --- /dev/null +++ b/src/libnm-client-impl/nm-device-macsec.c @@ -0,0 +1,631 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-macsec.h" + +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, + PROP_SCI, + PROP_CIPHER_SUITE, + PROP_ICV_LENGTH, + PROP_WINDOW, + PROP_ENCODING_SA, + PROP_ENCRYPT, + PROP_PROTECT, + PROP_INCLUDE_SCI, + PROP_ES, + PROP_SCB, + PROP_REPLAY_PROTECT, + PROP_VALIDATION, ); + +typedef struct { + NMLDBusPropertyO parent; + char * validation; + guint64 sci; + guint64 cipher_suite; + guint32 window; + guint8 icv_length; + guint8 encoding_sa; + bool encrypt; + bool protect; + bool include_sci; + bool es; + bool scb; + bool replay_protect; +} NMDeviceMacsecPrivate; + +struct _NMDeviceMacsec { + NMDevice parent; + NMDeviceMacsecPrivate _priv; +}; + +struct _NMDeviceMacsecClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceMacsec, nm_device_macsec, NM_TYPE_DEVICE) + +#define NM_DEVICE_MACSEC_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceMacsec, NM_IS_DEVICE_MACSEC, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_macsec_get_parent: + * @device: a #NMDeviceMacsec + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.6 + **/ +NMDevice * +nm_device_macsec_get_parent(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_MACSEC_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_macsec_get_hw_address: (skip) + * @device: a #NMDeviceMacsec + * + * Gets the hardware (MAC) address of the #NMDeviceMacsec + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.6 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_macsec_get_hw_address(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_macsec_get_sci: + * @device: a #NMDeviceMacsec + * + * Gets the Secure Channel Identifier in use + * + * Returns: the SCI + * + * Since: 1.6 + **/ +guint64 +nm_device_macsec_get_sci(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->sci; +} + +/** + * nm_device_macsec_get_icv_length: + * @device: a #NMDeviceMacsec + * + * Gets the length of ICV (Integrity Check Value) + * + * Returns: the length of ICV + * + * Since: 1.6 + **/ +guint8 +nm_device_macsec_get_icv_length(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->icv_length; +} + +/** + * nm_device_macsec_get_cipher_suite: + * @device: a #NMDeviceMacsec + * + * Gets the set of cryptographic algorithms in use + * + * Returns: the set of cryptographic algorithms in use + * + * Since: 1.6 + **/ +guint64 +nm_device_macsec_get_cipher_suite(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->cipher_suite; +} + +/** + * nm_device_macsec_get_window: + * @device: a #NMDeviceMacsec + * + * Gets the size of the replay window + * + * Returns: size of the replay window + * + * Since: 1.6 + **/ +guint +nm_device_macsec_get_window(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->window; +} + +/** + * nm_device_macsec_get_encoding_sa: + * @device: a #NMDeviceMacsec + * + * Gets the value of the Association Number (0..3) for the Security + * Association in use. + * + * Returns: the current Security Association + * + * Since: 1.6 + **/ +guint8 +nm_device_macsec_get_encoding_sa(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), 0); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->encoding_sa; +} + +/** + * nm_device_macsec_get_validation: + * @device: a #NMDeviceMacsec + * + * Gets the validation mode for incoming packets (strict, check, + * disabled) + * + * Returns: the validation mode + * + * Since: 1.6 + **/ +const char * +nm_device_macsec_get_validation(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), NULL); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->validation; +} + +/** + * nm_device_macsec_get_encrypt: + * @device: a #NMDeviceMacsec + * + * Gets whether encryption of transmitted frames is enabled + * + * Returns: whether encryption is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_encrypt(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->encrypt; +} + +/** + * nm_device_macsec_get_protect: + * @device: a #NMDeviceMacsec + * + * Gets whether protection of transmitted frames is enabled + * + * Returns: whether protection is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_protect(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->protect; +} + +/** + * nm_device_macsec_get_include_sci: + * @device: a #NMDeviceMacsec + * + * Gets whether the SCI is always included in SecTAG for transmitted + * frames + * + * Returns: whether the SCI is always included + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_include_sci(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->include_sci; +} + +/** + * nm_device_macsec_get_es: + * @device: a #NMDeviceMacsec + * + * Gets whether the ES (End station) bit is enabled in SecTAG for + * transmitted frames + * + * Returns: whether the ES (End station) bit is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_es(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->es; +} + +/** + * nm_device_macsec_get_scb: + * @device: a #NMDeviceMacsec + * + * Gets whether the SCB (Single Copy Broadcast) bit is enabled in + * SecTAG for transmitted frames + * + * Returns: whether the SCB (Single Copy Broadcast) bit is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_scb(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->scb; +} + +/** + * nm_device_macsec_get_replay_protect: + * @device: a #NMDeviceMacsec + * + * Gets whether replay protection is enabled + * + * Returns: whether replay protection is enabled + * + * Since: 1.6 + **/ +gboolean +nm_device_macsec_get_replay_protect(NMDeviceMacsec *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACSEC(device), FALSE); + + return NM_DEVICE_MACSEC_GET_PRIVATE(device)->replay_protect; +} + +/***********************************************************/ + +static void +nm_device_macsec_init(NMDeviceMacsec *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE(object); + + g_free(priv->validation); + + G_OBJECT_CLASS(nm_device_macsec_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceMacsec *device = NM_DEVICE_MACSEC(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_macsec_get_parent(device)); + break; + case PROP_SCI: + g_value_set_uint64(value, nm_device_macsec_get_sci(device)); + break; + case PROP_ICV_LENGTH: + g_value_set_uchar(value, nm_device_macsec_get_icv_length(device)); + break; + case PROP_CIPHER_SUITE: + g_value_set_uint64(value, nm_device_macsec_get_cipher_suite(device)); + break; + case PROP_WINDOW: + g_value_set_uint(value, nm_device_macsec_get_window(device)); + break; + case PROP_ENCODING_SA: + g_value_set_uchar(value, nm_device_macsec_get_encoding_sa(device)); + break; + case PROP_VALIDATION: + g_value_set_string(value, nm_device_macsec_get_validation(device)); + break; + case PROP_ENCRYPT: + g_value_set_boolean(value, nm_device_macsec_get_encrypt(device)); + break; + case PROP_PROTECT: + g_value_set_boolean(value, nm_device_macsec_get_protect(device)); + break; + case PROP_INCLUDE_SCI: + g_value_set_boolean(value, nm_device_macsec_get_include_sci(device)); + break; + case PROP_ES: + g_value_set_boolean(value, nm_device_macsec_get_es(device)); + break; + case PROP_SCB: + g_value_set_boolean(value, nm_device_macsec_get_scb(device)); + break; + case PROP_REPLAY_PROTECT: + g_value_set_boolean(value, nm_device_macsec_get_replay_protect(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macsec = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_MACSEC, + nm_device_macsec_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_T("CipherSuite", + PROP_CIPHER_SUITE, + NMDeviceMacsec, + _priv.cipher_suite), + NML_DBUS_META_PROPERTY_INIT_Y("EncodingSa", + PROP_ENCODING_SA, + NMDeviceMacsec, + _priv.encoding_sa), + NML_DBUS_META_PROPERTY_INIT_B("Encrypt", PROP_ENCRYPT, NMDeviceMacsec, _priv.encrypt), + NML_DBUS_META_PROPERTY_INIT_B("Es", PROP_ES, NMDeviceMacsec, _priv.es), + NML_DBUS_META_PROPERTY_INIT_Y("IcvLength", + PROP_ICV_LENGTH, + NMDeviceMacsec, + _priv.icv_length), + NML_DBUS_META_PROPERTY_INIT_B("IncludeSci", + PROP_INCLUDE_SCI, + NMDeviceMacsec, + _priv.include_sci), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceMacsec, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Protect", PROP_PROTECT, NMDeviceMacsec, _priv.protect), + NML_DBUS_META_PROPERTY_INIT_B("ReplayProtect", + PROP_REPLAY_PROTECT, + NMDeviceMacsec, + _priv.replay_protect), + NML_DBUS_META_PROPERTY_INIT_B("Scb", PROP_SCB, NMDeviceMacsec, _priv.scb), + NML_DBUS_META_PROPERTY_INIT_T("Sci", PROP_SCI, NMDeviceMacsec, _priv.sci), + NML_DBUS_META_PROPERTY_INIT_S("Validation", + PROP_VALIDATION, + NMDeviceMacsec, + _priv.validation), + NML_DBUS_META_PROPERTY_INIT_U("Window", PROP_WINDOW, NMDeviceMacsec, _priv.window), ), ); + +static void +nm_device_macsec_class_init(NMDeviceMacsecClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceMacsec); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceMacsecPrivate, parent); + + /** + * NMDeviceMacsec:parent: + * + * The devices's parent device. + * + * Since: 1.6 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_MACSEC_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:sci: + * + * The Secure Channel Identifier in use. + * + * Since: 1.6 + **/ + obj_properties[PROP_SCI] = g_param_spec_uint64(NM_DEVICE_MACSEC_SCI, + "", + "", + 0, + G_MAXUINT64, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:icv-length: + * + * The length of ICV (Integrity Check Value). + * + * Since: 1.6 + **/ + obj_properties[PROP_ICV_LENGTH] = g_param_spec_uchar(NM_DEVICE_MACSEC_ICV_LENGTH, + "", + "", + 0, + G_MAXUINT8, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:cipher-suite: + * + * The set of cryptographic algorithms in use. + * + * Since: 1.6 + **/ + obj_properties[PROP_CIPHER_SUITE] = + g_param_spec_uint64(NM_DEVICE_MACSEC_CIPHER_SUITE, + "", + "", + 0, + G_MAXUINT64, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:window: + * + * The size of the replay window. + * + * Since: 1.6 + **/ + obj_properties[PROP_WINDOW] = g_param_spec_uint(NM_DEVICE_MACSEC_WINDOW, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:encoding-sa: + * + * The value of the Association Number (0..3) for the Security + * Association in use. + * + * Since: 1.6 + **/ + obj_properties[PROP_ENCODING_SA] = + g_param_spec_uchar(NM_DEVICE_MACSEC_ENCODING_SA, + "", + "", + 0, + G_MAXUINT8, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:validation: + * + * The validation mode for incoming packets (strict, check, + * disabled). + * + * Since: 1.6 + **/ + obj_properties[PROP_VALIDATION] = + g_param_spec_string(NM_DEVICE_MACSEC_VALIDATION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:encrypt: + * + * Whether encryption of transmitted frames is enabled. + * + * Since: 1.6 + **/ + obj_properties[PROP_ENCRYPT] = g_param_spec_boolean(NM_DEVICE_MACSEC_ENCRYPT, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:protect: + * + * Whether protection of transmitted frames is enabled. + * + * Since: 1.6 + **/ + obj_properties[PROP_PROTECT] = g_param_spec_boolean(NM_DEVICE_MACSEC_PROTECT, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:include-sci: + * + * Whether the SCI is always included in SecTAG for transmitted + * frames. + * + * Since: 1.6 + **/ + obj_properties[PROP_INCLUDE_SCI] = + g_param_spec_boolean(NM_DEVICE_MACSEC_INCLUDE_SCI, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:es: + * + * Whether the ES (End station) bit is enabled in SecTAG for + * transmitted frames. + * + * Since: 1.6 + **/ + obj_properties[PROP_ES] = g_param_spec_boolean(NM_DEVICE_MACSEC_ES, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:scb: + * + * Whether the SCB (Single Copy Broadcast) bit is enabled in + * SecTAG for transmitted frames. + * + * Since: 1.6 + **/ + obj_properties[PROP_SCB] = g_param_spec_boolean(NM_DEVICE_MACSEC_SCB, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacsec:replay-protect: + * + * Whether replay protection is enabled. + * + * Since: 1.6 + **/ + obj_properties[PROP_REPLAY_PROTECT] = + g_param_spec_boolean(NM_DEVICE_MACSEC_REPLAY_PROTECT, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_macsec); +} diff --git a/src/libnm-client-impl/nm-device-macvlan.c b/src/libnm-client-impl/nm-device-macvlan.c new file mode 100644 index 00000000..ddb36bab --- /dev/null +++ b/src/libnm-client-impl/nm-device-macvlan.c @@ -0,0 +1,299 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-setting-macvlan.h" + +#include "nm-setting-connection.h" +#include "nm-setting-wired.h" +#include "nm-utils.h" +#include "nm-device-macvlan.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, PROP_MODE, PROP_NO_PROMISC, PROP_TAP, ); + +typedef struct { + NMLDBusPropertyO parent; + char * mode; + bool no_promisc; + bool tap; +} NMDeviceMacvlanPrivate; + +struct _NMDeviceMacvlan { + NMDevice parent; + NMDeviceMacvlanPrivate _priv; +}; + +struct _NMDeviceMacvlanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE) + +#define NM_DEVICE_MACVLAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceMacvlan, NM_IS_DEVICE_MACVLAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_macvlan_get_parent: + * @device: a #NMDeviceMacvlan + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.2 + **/ +NMDevice * +nm_device_macvlan_get_parent(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), FALSE); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_MACVLAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_macvlan_get_mode: + * @device: a #NMDeviceMacvlan + * + * Gets the MACVLAN mode of the device. + * + * Returns: the MACVLAN mode. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.2 + **/ +const char * +nm_device_macvlan_get_mode(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_MACVLAN_GET_PRIVATE(device)->mode); +} + +/** + * nm_device_macvlan_get_no_promisc + * @device: a #NMDeviceMacvlan + * + * Gets the no-promiscuous flag of the device. + * + * Returns: the no-promiscuous flag of the device. + * + * Since: 1.2 + **/ +gboolean +nm_device_macvlan_get_no_promisc(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), FALSE); + + return NM_DEVICE_MACVLAN_GET_PRIVATE(device)->no_promisc; +} + +/** + * nm_device_macvlan_get_tap: + * @device: a #NMDeviceMacvlan + * + * Gets the device type (MACVLAN or MACVTAP). + * + * Returns: %TRUE if the device is a MACVTAP, %FALSE if it is a MACVLAN. + * + * Since: 1.2 + **/ +gboolean +nm_device_macvlan_get_tap(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), FALSE); + + return NM_DEVICE_MACVLAN_GET_PRIVATE(device)->tap; +} + +/** + * nm_device_macvlan_get_hw_address: (skip) + * @device: a #NMDeviceMacvlan + * + * Gets the hardware (MAC) address of the #NMDeviceMacvlan + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.2 + * + * This property is not implemented yet, and the function always return NULL. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_macvlan_get_hw_address(NMDeviceMacvlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_MACVLAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE(device); + NMSettingMacvlan * s_macvlan; + + if (!NM_DEVICE_CLASS(nm_device_macvlan_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_MACVLAN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a MAC-VLAN connection.")); + return FALSE; + } + + s_macvlan = nm_connection_get_setting_macvlan(connection); + if (s_macvlan) { + if (nm_setting_macvlan_get_tap(s_macvlan) != priv->tap) + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_MACVLAN; +} + +/*****************************************************************************/ + +static void +nm_device_macvlan_init(NMDeviceMacvlan *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE(object); + + g_free(priv->mode); + + G_OBJECT_CLASS(nm_device_macvlan_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceMacvlan *device = NM_DEVICE_MACVLAN(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, nm_device_macvlan_get_parent(device)); + break; + case PROP_MODE: + g_value_set_string(value, nm_device_macvlan_get_mode(device)); + break; + case PROP_NO_PROMISC: + g_value_set_boolean(value, nm_device_macvlan_get_no_promisc(device)); + break; + case PROP_TAP: + g_value_set_boolean(value, nm_device_macvlan_get_tap(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macvlan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_MACVLAN, + nm_device_macvlan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Mode", PROP_MODE, NMDeviceMacvlan, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_B("NoPromisc", + PROP_NO_PROMISC, + NMDeviceMacvlan, + _priv.no_promisc), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceMacvlan, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Tap", PROP_TAP, NMDeviceMacvlan, _priv.tap), ), ); + +static void +nm_device_macvlan_class_init(NMDeviceMacvlanClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceMacvlan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceMacvlanPrivate, parent); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceMacvlan:parent: + * + * The devices's parent device. + * + * Since: 1.2 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_MACVLAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacvlan:mode: + * + * The MACVLAN mode. + * + * Since: 1.2 + **/ + obj_properties[PROP_MODE] = g_param_spec_string(NM_DEVICE_MACVLAN_MODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacvlan:no-promisc: + * + * Whether the device has the no-promiscuos flag. + * + * Since: 1.2 + **/ + obj_properties[PROP_NO_PROMISC] = + g_param_spec_boolean(NM_DEVICE_MACVLAN_NO_PROMISC, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceMacvlan:tap: + * + * Whether the device is a MACVTAP. + * + * Since: 1.2 + **/ + obj_properties[PROP_TAP] = g_param_spec_boolean(NM_DEVICE_MACVLAN_TAP, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_macvlan); +} diff --git a/src/libnm-client-impl/nm-device-modem.c b/src/libnm-client-impl/nm-device-modem.c new file mode 100644 index 00000000..57e09bc6 --- /dev/null +++ b/src/libnm-client-impl/nm-device-modem.c @@ -0,0 +1,360 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 - 2012 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-modem.h" + +#include "nm-setting-connection.h" +#include "nm-setting-gsm.h" +#include "nm-setting-cdma.h" +#include "nm-object-private.h" +#include "nm-enum-types.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_MODEM_CAPABILITIES, + PROP_CURRENT_CAPABILITIES, + PROP_DEVICE_ID, + PROP_OPERATOR_CODE, + PROP_APN, ); + +typedef struct { + char * device_id; + char * operator_code; + char * apn; + guint32 modem_capabilities; + guint32 current_capabilities; +} NMDeviceModemPrivate; + +struct _NMDeviceModem { + NMDevice parent; + NMDeviceModemPrivate _priv; +}; + +struct _NMDeviceModemClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE) + +#define NM_DEVICE_MODEM_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceModem, NM_IS_DEVICE_MODEM, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_modem_get_modem_capabilities: + * @self: a #NMDeviceModem + * + * Returns a bitfield of the generic access technology families the modem + * supports. Not all capabilities are available concurrently however; some + * may require a firmware reload or reinitialization. + * + * Returns: the generic access technology families the modem supports + **/ +NMDeviceModemCapabilities +nm_device_modem_get_modem_capabilities(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NM_DEVICE_MODEM_CAPABILITY_NONE); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->modem_capabilities; +} + +/** + * nm_device_modem_get_current_capabilities: + * @self: a #NMDeviceModem + * + * Returns a bitfield of the generic access technology families the modem + * supports without a firmware reload or reinitialization. This value + * represents the network types the modem can immediately connect to. + * + * Returns: the generic access technology families the modem supports without + * a firmware reload or other reinitialization + **/ +NMDeviceModemCapabilities +nm_device_modem_get_current_capabilities(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NM_DEVICE_MODEM_CAPABILITY_NONE); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->current_capabilities; +} + +/** + * nm_device_modem_get_device_id: + * @self: a #NMDeviceModem + * + * An identifier used by the modem backend (ModemManager) that aims to + * uniquely identify the a device. Can be used to match a connection to a + * particular device. + * + * Returns: a device-id string + * + * Since: 1.20 + **/ +const char * +nm_device_modem_get_device_id(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NULL); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->device_id; +} + +/** + * nm_device_modem_get_operator_code: + * @self: a #NMDeviceModem + * + * The MCC and MNC (concatenated) of the network the modem is connected to. + * + * Returns: the operator code or %NULL if disconnected or not a 3GPP modem. + * + * Since: 1.20 + **/ +const char * +nm_device_modem_get_operator_code(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NULL); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->operator_code; +} + +/** + * nm_device_modem_get_apn: + * @self: a #NMDeviceModem + * + * The access point name the modem is connected to. + * + * Returns: the APN name or %NULL if disconnected + * + * Since: 1.20 + **/ +const char * +nm_device_modem_get_apn(NMDeviceModem *self) +{ + g_return_val_if_fail(NM_IS_DEVICE_MODEM(self), NULL); + + return NM_DEVICE_MODEM_GET_PRIVATE(self)->apn; +} + +static const char * +get_type_description(NMDevice *device) +{ + NMDeviceModemCapabilities caps; + + caps = nm_device_modem_get_current_capabilities(NM_DEVICE_MODEM(device)); + if (caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) + return "gsm"; + else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) + return "cdma"; + else + return NULL; +} + +#define MODEM_CAPS_3GPP(caps) \ + (caps & (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS | NM_DEVICE_MODEM_CAPABILITY_LTE)) + +#define MODEM_CAPS_3GPP2(caps) (caps & (NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)) + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingGsm * s_gsm; + NMSettingCdma * s_cdma; + NMDeviceModemCapabilities current_caps; + + if (!NM_DEVICE_CLASS(nm_device_modem_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_GSM_SETTING_NAME) + && !nm_connection_is_type(connection, NM_SETTING_CDMA_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a modem connection.")); + return FALSE; + } + + s_gsm = nm_connection_get_setting_gsm(connection); + s_cdma = nm_connection_get_setting_cdma(connection); + if (!s_cdma && !s_gsm) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection was not a valid modem connection.")); + return FALSE; + } + + current_caps = nm_device_modem_get_current_capabilities(NM_DEVICE_MODEM(device)); + if (!(s_gsm && MODEM_CAPS_3GPP(current_caps)) && !(s_cdma && MODEM_CAPS_3GPP2(current_caps))) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The device is lacking capabilities required by the connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + NMDeviceModemCapabilities caps; + + caps = nm_device_modem_get_current_capabilities(NM_DEVICE_MODEM(device)); + if (caps & (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS | NM_DEVICE_MODEM_CAPABILITY_LTE)) + return NM_TYPE_SETTING_GSM; + else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) + return NM_TYPE_SETTING_CDMA; + else + return G_TYPE_INVALID; +} + +/*****************************************************************************/ + +static void +nm_device_modem_init(NMDeviceModem *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(object); + + g_free(priv->device_id); + g_free(priv->operator_code); + g_free(priv->apn); + + G_OBJECT_CLASS(nm_device_modem_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceModem *self = NM_DEVICE_MODEM(object); + + switch (prop_id) { + case PROP_MODEM_CAPABILITIES: + g_value_set_flags(value, nm_device_modem_get_modem_capabilities(self)); + break; + case PROP_CURRENT_CAPABILITIES: + g_value_set_flags(value, nm_device_modem_get_current_capabilities(self)); + break; + case PROP_DEVICE_ID: + g_value_set_string(value, nm_device_modem_get_device_id(self)); + break; + case PROP_OPERATOR_CODE: + g_value_set_string(value, nm_device_modem_get_operator_code(self)); + break; + case PROP_APN: + g_value_set_string(value, nm_device_modem_get_apn(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_modem = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_MODEM, + nm_device_modem_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Apn", PROP_APN, NMDeviceModem, _priv.apn), + NML_DBUS_META_PROPERTY_INIT_U("CurrentCapabilities", + PROP_CURRENT_CAPABILITIES, + NMDeviceModem, + _priv.current_capabilities), + NML_DBUS_META_PROPERTY_INIT_S("DeviceId", PROP_DEVICE_ID, NMDeviceModem, _priv.device_id), + NML_DBUS_META_PROPERTY_INIT_U("ModemCapabilities", + PROP_MODEM_CAPABILITIES, + NMDeviceModem, + _priv.modem_capabilities), + NML_DBUS_META_PROPERTY_INIT_S("OperatorCode", + PROP_OPERATOR_CODE, + NMDeviceModem, + _priv.operator_code), ), ); + +static void +nm_device_modem_class_init(NMDeviceModemClass *modem_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(modem_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(modem_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceModem:modem-capabilities: + * + * The generic family of access technologies the modem supports. Not all + * capabilities are available at the same time however; some modems require + * a firmware reload or other reinitialization to switch between eg + * CDMA/EVDO and GSM/UMTS. + **/ + obj_properties[PROP_MODEM_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_MODEM_MODEM_CAPABILITIES, + "", + "", + NM_TYPE_DEVICE_MODEM_CAPABILITIES, + NM_DEVICE_MODEM_CAPABILITY_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceModem:current-capabilities: + * + * The generic family of access technologies the modem currently supports + * without a firmware reload or reinitialization. + **/ + obj_properties[PROP_CURRENT_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_MODEM_CURRENT_CAPABILITIES, + "", + "", + NM_TYPE_DEVICE_MODEM_CAPABILITIES, + NM_DEVICE_MODEM_CAPABILITY_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceModem:device-id: + * + * Since: 1.20 + **/ + obj_properties[PROP_DEVICE_ID] = g_param_spec_string(NM_DEVICE_MODEM_DEVICE_ID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceModem:operator-code: + * + * Since: 1.20 + **/ + obj_properties[PROP_OPERATOR_CODE] = + g_param_spec_string(NM_DEVICE_MODEM_OPERATOR_CODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceModem:apn: + * + * Since: 1.20 + **/ + obj_properties[PROP_APN] = g_param_spec_string(NM_DEVICE_MODEM_APN, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_modem); +} diff --git a/src/libnm-client-impl/nm-device-olpc-mesh.c b/src/libnm-client-impl/nm-device-olpc-mesh.c new file mode 100644 index 00000000..779990aa --- /dev/null +++ b/src/libnm-client-impl/nm-device-olpc-mesh.c @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-olpc-mesh.h" + +#include "nm-setting-connection.h" +#include "nm-setting-olpc-mesh.h" +#include "nm-object-private.h" +#include "nm-device-wifi.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_COMPANION, PROP_ACTIVE_CHANNEL, ); + +typedef struct { + NMLDBusPropertyO companion; + guint32 active_channel; +} NMDeviceOlpcMeshPrivate; + +struct _NMDeviceOlpcMesh { + NMDevice parent; + NMDeviceOlpcMeshPrivate _priv; +}; + +struct _NMDeviceOlpcMeshClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) + +#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceOlpcMesh, NM_IS_DEVICE_OLPC_MESH, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_olpc_mesh_get_hw_address: (skip) + * @device: a #NMDeviceOlpcMesh + * + * Gets the hardware (MAC) address of the #NMDeviceOlpcMesh + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_olpc_mesh_get_hw_address(NMDeviceOlpcMesh *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OLPC_MESH(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_olpc_mesh_get_companion: + * @device: a #NMDeviceOlpcMesh + * + * Gets the companion device of the #NMDeviceOlpcMesh. + * + * Returns: (transfer none): the companion of the device of %NULL + **/ +NMDeviceWifi * +nm_device_olpc_mesh_get_companion(NMDeviceOlpcMesh *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OLPC_MESH(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_OLPC_MESH_GET_PRIVATE(device)->companion); +} + +/** + * nm_device_olpc_mesh_get_active_channel: + * @device: a #NMDeviceOlpcMesh + * + * Returns the active channel of the #NMDeviceOlpcMesh device. + * + * Returns: active channel of the device + **/ +guint32 +nm_device_olpc_mesh_get_active_channel(NMDeviceOlpcMesh *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OLPC_MESH(device), 0); + + return NM_DEVICE_OLPC_MESH_GET_PRIVATE(device)->active_channel; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_olpc_mesh_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_OLPC_MESH_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not an OLPC Mesh connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_OLPC_MESH; +} + +/*****************************************************************************/ + +static void +nm_device_olpc_mesh_init(NMDeviceOlpcMesh *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH(object); + + switch (prop_id) { + case PROP_COMPANION: + g_value_set_object(value, nm_device_olpc_mesh_get_companion(device)); + break; + case PROP_ACTIVE_CHANNEL: + g_value_set_uint(value, nm_device_olpc_mesh_get_active_channel(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_olpcmesh = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_OLPC_MESH, + nm_device_olpc_mesh_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("ActiveChannel", + PROP_ACTIVE_CHANNEL, + NMDeviceOlpcMesh, + _priv.active_channel), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Companion", + PROP_COMPANION, + NMDeviceOlpcMesh, + _priv.companion, + nm_device_wifi_get_type), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), ), ); + +static void +nm_device_olpc_mesh_class_init(NMDeviceOlpcMeshClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceOlpcMesh); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceOlpcMeshPrivate, companion); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceOlpcMesh:companion: + * + * The companion device. + **/ + obj_properties[PROP_COMPANION] = g_param_spec_object(NM_DEVICE_OLPC_MESH_COMPANION, + "", + "", + NM_TYPE_DEVICE_WIFI, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceOlpcMesh:active-channel: + * + * The device's active channel. + **/ + obj_properties[PROP_ACTIVE_CHANNEL] = + g_param_spec_uint(NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_olpcmesh); +} diff --git a/src/libnm-client-impl/nm-device-ovs-bridge.c b/src/libnm-client-impl/nm-device-ovs-bridge.c new file mode 100644 index 00000000..898a41fa --- /dev/null +++ b/src/libnm-client-impl/nm-device-ovs-bridge.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017, 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ovs-bridge.h" + +#include "nm-object-private.h" +#include "nm-setting-ovs-bridge.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_SLAVES, ); + +typedef struct { + NMLDBusPropertyAO slaves; +} NMDeviceOvsBridgePrivate; + +struct _NMDeviceOvsBridge { + NMDevice parent; + NMDeviceOvsBridgePrivate _priv; +}; + +struct _NMDeviceOvsBridgeClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceOvsBridge, nm_device_ovs_bridge, NM_TYPE_DEVICE) + +#define NM_DEVICE_OVS_BRIDGE_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceOvsBridge, NM_IS_DEVICE_OVS_BRIDGE, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_ovs_bridge_get_slaves: + * @device: a #NMDeviceOvsBridge + * + * Gets the ports currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + * + * Since: 1.14 + **/ +const GPtrArray * +nm_device_ovs_bridge_get_slaves(NMDeviceOvsBridge *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OVS_BRIDGE(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_OVS_BRIDGE_GET_PRIVATE(device)->slaves); +} + +static const char * +get_type_description(NMDevice *device) +{ + return "ovs-bridge"; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_ovs_bridge_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_bridge connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_BRIDGE; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceOvsBridge *device = NM_DEVICE_OVS_BRIDGE(object); + + switch (prop_id) { + case PROP_SLAVES: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_device_ovs_bridge_get_slaves(device))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_ovs_bridge_init(NMDeviceOvsBridge *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsbridge = + NML_DBUS_META_IFACE_INIT_PROP(NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE, + nm_device_ovs_bridge_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceOvsBridge, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceOvsBridge); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceOvsBridgePrivate, slaves); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceOvsBridge:slaves: (type GPtrArray(NMDevice)) + * + * Gets the ports currently enslaved to the device. + * + * Since: 1.22 + */ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_OVS_BRIDGE_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_ovsbridge); +} diff --git a/src/libnm-client-impl/nm-device-ovs-interface.c b/src/libnm-client-impl/nm-device-ovs-interface.c new file mode 100644 index 00000000..8632057a --- /dev/null +++ b/src/libnm-client-impl/nm-device-ovs-interface.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ovs-interface.h" + +#include "nm-object-private.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +struct _NMDeviceOvsInterface { + NMDevice parent; +}; + +struct _NMDeviceOvsInterfaceClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceOvsInterface, nm_device_ovs_interface, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description(NMDevice *device) +{ + return "ovs-interface"; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_ovs_interface_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_interface connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_INTERFACE; +} + +/*****************************************************************************/ + +static void +nm_device_ovs_interface_init(NMDeviceOvsInterface *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsinterface = + NML_DBUS_META_IFACE_INIT(NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE, + nm_device_ovs_interface_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, ); + +static void +nm_device_ovs_interface_class_init(NMDeviceOvsInterfaceClass *ovs_interface_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS(ovs_interface_class); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/src/libnm-client-impl/nm-device-ovs-port.c b/src/libnm-client-impl/nm-device-ovs-port.c new file mode 100644 index 00000000..db30e1c7 --- /dev/null +++ b/src/libnm-client-impl/nm-device-ovs-port.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017, 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ovs-port.h" + +#include "nm-object-private.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_SLAVES, ); + +typedef struct { + NMLDBusPropertyAO slaves; +} NMDeviceOvsPortPrivate; + +struct _NMDeviceOvsPort { + NMDevice parent; + NMDeviceOvsPortPrivate _priv; +}; + +struct _NMDeviceOvsPortClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceOvsPort, nm_device_ovs_port, NM_TYPE_DEVICE) + +#define NM_DEVICE_OVS_PORT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceOvsPort, NM_IS_DEVICE_OVS_PORT, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_ovs_port_get_slaves: + * @device: a #NMDeviceOvsPort + * + * Gets the interfaces currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + * + * Since: 1.14 + **/ +const GPtrArray * +nm_device_ovs_port_get_slaves(NMDeviceOvsPort *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_OVS_PORT(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_OVS_PORT_GET_PRIVATE(device)->slaves); +} + +static const char * +get_type_description(NMDevice *device) +{ + return "ovs-port"; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_ovs_port_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_OVS_PORT_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_port connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_PORT; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceOvsPort *device = NM_DEVICE_OVS_PORT(object); + + switch (prop_id) { + case PROP_SLAVES: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_device_ovs_port_get_slaves(device))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_ovs_port_init(NMDeviceOvsPort *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsport = + NML_DBUS_META_IFACE_INIT_PROP(NM_DBUS_INTERFACE_DEVICE_OVS_PORT, + nm_device_ovs_port_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceOvsPort, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceOvsPort); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceOvsPortPrivate, slaves); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceOvsPort:slaves: (type GPtrArray(NMDevice)) + * + * Gets the interfaces currently enslaved to the device. + * + * Since: 1.22 + */ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_OVS_PORT_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_ovsport); +} diff --git a/src/libnm-client-impl/nm-device-ppp.c b/src/libnm-client-impl/nm-device-ppp.c new file mode 100644 index 00000000..1dd275cf --- /dev/null +++ b/src/libnm-client-impl/nm-device-ppp.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-ppp.h" + +#include "nm-device.h" + +/*****************************************************************************/ + +struct _NMDevicePpp { + NMDevice parent; +}; + +struct _NMDevicePppClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDevicePpp, nm_device_ppp, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static void +nm_device_ppp_init(NMDevicePpp *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ppp = + NML_DBUS_META_IFACE_INIT(NM_DBUS_INTERFACE_DEVICE_PPP, + nm_device_ppp_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, ); + +static void +nm_device_ppp_class_init(NMDevicePppClass *klass) +{} diff --git a/src/libnm-client-impl/nm-device-private.h b/src/libnm-client-impl/nm-device-private.h new file mode 100644 index 00000000..e69de29b diff --git a/src/libnm-client-impl/nm-device-team.c b/src/libnm-client-impl/nm-device-team.c new file mode 100644 index 00000000..ca29c9f7 --- /dev/null +++ b/src/libnm-client-impl/nm-device-team.c @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2013 Jiri Pirko + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-team.h" + +#include "nm-setting-connection.h" +#include "nm-setting-team.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, PROP_SLAVES, PROP_CONFIG, ); + +typedef struct { + NMLDBusPropertyAO slaves; + char * config; + bool carrier; +} NMDeviceTeamPrivate; + +struct _NMDeviceTeam { + NMDevice parent; + NMDeviceTeamPrivate _priv; +}; + +struct _NMDeviceTeamClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE) + +#define NM_DEVICE_TEAM_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceTeam, NM_IS_DEVICE_TEAM, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_team_get_hw_address: (skip) + * @device: a #NMDeviceTeam + * + * Gets the hardware (MAC) address of the #NMDeviceTeam + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_team_get_hw_address(NMDeviceTeam *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TEAM(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_team_get_carrier: + * @device: a #NMDeviceTeam + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_team_get_carrier(NMDeviceTeam *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TEAM(device), FALSE); + + return NM_DEVICE_TEAM_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_team_get_slaves: + * @device: a #NMDeviceTeam + * + * Gets the devices currently enslaved to @device. + * + * Returns: (element-type NMDevice): the #GPtrArray containing + * #NMDevices that are slaves of @device. This is the internal + * copy used by the device, and must not be modified. + **/ +const GPtrArray * +nm_device_team_get_slaves(NMDeviceTeam *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TEAM(device), FALSE); + + return nml_dbus_property_ao_get_objs_as_ptrarray(&NM_DEVICE_TEAM_GET_PRIVATE(device)->slaves); +} + +/** + * nm_device_team_get_config: + * @device: a #NMDeviceTeam + * + * Gets the current JSON configuration of the #NMDeviceTeam + * + * Returns: the current configuration. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.4 + **/ +const char * +nm_device_team_get_config(NMDeviceTeam *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TEAM(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_TEAM_GET_PRIVATE(device)->config); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_team_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_TEAM_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection was not a team connection.")); + return FALSE; + } + + /* FIXME: check slaves? */ + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_TEAM; +} + +/*****************************************************************************/ + +static void +nm_device_team_init(NMDeviceTeam *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(object); + + g_free(priv->config); + + G_OBJECT_CLASS(nm_device_team_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceTeam *device = NM_DEVICE_TEAM(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_team_get_carrier(device)); + break; + case PROP_SLAVES: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_device_team_get_slaves(device))); + break; + case PROP_CONFIG: + g_value_set_string(value, nm_device_team_get_config(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_team = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_TEAM, + nm_device_team_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceTeam, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_S("Config", PROP_CONFIG, NMDeviceTeam, _priv.config), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Slaves", + PROP_SLAVES, + NMDeviceTeam, + _priv.slaves, + nm_device_get_type), ), ); + +static void +nm_device_team_class_init(NMDeviceTeamClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceTeam); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceTeamPrivate, slaves); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceTeam:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_TEAM_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTeam:slaves: (type GPtrArray(NMDevice)) + * + * The devices enslaved to the team device. + **/ + obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_TEAM_SLAVES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTeam:config: + * + * The current JSON configuration of the device. + * + * Since: 1.4 + **/ + obj_properties[PROP_CONFIG] = g_param_spec_string(NM_DEVICE_TEAM_CONFIG, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_team); +} diff --git a/src/libnm-client-impl/nm-device-tun.c b/src/libnm-client-impl/nm-device-tun.c new file mode 100644 index 00000000..8b8ca0fe --- /dev/null +++ b/src/libnm-client-impl/nm-device-tun.c @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-tun.h" + +#include + +#include "nm-setting-connection.h" +#include "nm-setting-tun.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_MODE, + PROP_OWNER, + PROP_GROUP, + PROP_NO_PI, + PROP_VNET_HDR, + PROP_MULTI_QUEUE, ); + +typedef struct { + char * mode; + gint64 owner; + gint64 group; + bool no_pi; + bool vnet_hdr; + bool multi_queue; +} NMDeviceTunPrivate; + +struct _NMDeviceTun { + NMDevice parent; + NMDeviceTunPrivate _priv; +}; + +struct _NMDeviceTunClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE) + +#define NM_DEVICE_TUN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceTun, NM_IS_DEVICE_TUN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_tun_get_hw_address: (skip) + * @device: a #NMDeviceTun + * + * Gets the hardware (MAC) address of the #NMDeviceTun + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.2 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_tun_get_hw_address(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_tun_get_mode: + * @device: a #NMDeviceTun + * + * Returns the TUN/TAP mode for the device. + * + * Returns: 'tun' or 'tap' + * + * Since: 1.2 + **/ +const char * +nm_device_tun_get_mode(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_TUN_GET_PRIVATE(device)->mode); +} + +/** + * nm_device_tun_get_owner: + * @device: a #NMDeviceTun + * + * Gets the tunnel owner. + * + * Returns: the uid of the tunnel owner, or -1 if it has no owner. + * + * Since: 1.2 + **/ +gint64 +nm_device_tun_get_owner(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), -1); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->owner; +} + +/** + * nm_device_tun_get_group: + * @device: a #NMDeviceTun + * + * Gets the tunnel group. + * + * Returns: the gid of the tunnel group, or -1 if it has no owner. + * + * Since: 1.2 + **/ +gint64 +nm_device_tun_get_group(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), -1); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->group; +} + +/** + * nm_device_tun_get_pi: + * @device: a #NMDeviceTun + * + * Returns whether the #NMDeviceTun has the IFF_NO_PI flag. + * + * Returns: %TRUE if the device has the flag, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_device_tun_get_no_pi(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), FALSE); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->no_pi; +} + +/** + * nm_device_tun_get_vnet_hdr: + * @device: a #NMDeviceTun + * + * Returns whether the #NMDeviceTun has the IFF_VNET_HDR flag. + * + * Returns: %TRUE if the device has the flag, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_device_tun_get_vnet_hdr(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), FALSE); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->vnet_hdr; +} + +/** + * nm_device_tun_get_multi_queue: + * @device: a #NMDeviceTun + * + * Returns whether the #NMDeviceTun has the IFF_MULTI_QUEUE flag. + * + * Returns: %TRUE if the device doesn't have the flag, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_device_tun_get_multi_queue(NMDeviceTun *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_TUN(device), FALSE); + + return NM_DEVICE_TUN_GET_PRIVATE(device)->multi_queue; +} + +static int +tun_mode_from_string(const char *string) +{ + if (!g_strcmp0(string, "tap")) + return NM_SETTING_TUN_MODE_TAP; + else + return NM_SETTING_TUN_MODE_TUN; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE(device); + NMSettingTunMode mode; + NMSettingTun * s_tun; + + if (!NM_DEVICE_CLASS(nm_device_tun_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_TUN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a tun connection.")); + return FALSE; + } + + s_tun = nm_connection_get_setting_tun(connection); + + mode = tun_mode_from_string(priv->mode); + if (s_tun && mode != nm_setting_tun_get_mode(s_tun)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The mode of the device and the connection didn't match")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_TUN; +} + +/*****************************************************************************/ + +static void +nm_device_tun_init(NMDeviceTun *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE(object); + + g_free(priv->mode); + + G_OBJECT_CLASS(nm_device_tun_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceTun *device = NM_DEVICE_TUN(object); + + switch (prop_id) { + case PROP_MODE: + g_value_set_string(value, nm_device_tun_get_mode(device)); + break; + case PROP_OWNER: + g_value_set_int64(value, nm_device_tun_get_owner(device)); + break; + case PROP_GROUP: + g_value_set_int64(value, nm_device_tun_get_group(device)); + break; + case PROP_NO_PI: + g_value_set_boolean(value, nm_device_tun_get_no_pi(device)); + break; + case PROP_VNET_HDR: + g_value_set_boolean(value, nm_device_tun_get_vnet_hdr(device)); + break; + case PROP_MULTI_QUEUE: + g_value_set_boolean(value, nm_device_tun_get_multi_queue(device)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_tun = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_TUN, + nm_device_tun_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_X("Group", PROP_GROUP, NMDeviceTun, _priv.group), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("Mode", PROP_MODE, NMDeviceTun, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_B("MultiQueue", + PROP_MULTI_QUEUE, + NMDeviceTun, + _priv.multi_queue), + NML_DBUS_META_PROPERTY_INIT_B("NoPi", PROP_NO_PI, NMDeviceTun, _priv.no_pi), + NML_DBUS_META_PROPERTY_INIT_X("Owner", PROP_OWNER, NMDeviceTun, _priv.owner), + NML_DBUS_META_PROPERTY_INIT_B("VnetHdr", PROP_VNET_HDR, NMDeviceTun, _priv.vnet_hdr), ), ); + +static void +nm_device_tun_class_init(NMDeviceTunClass *gre_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(gre_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS(gre_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceTun:mode: + * + * The tunnel mode, either "tun" or "tap". + * + * Since: 1.2 + **/ + obj_properties[PROP_MODE] = g_param_spec_string(NM_DEVICE_TUN_MODE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:owner: + * + * The uid of the tunnel owner, or -1 if it has no owner. + * + * Since: 1.2 + **/ + obj_properties[PROP_OWNER] = g_param_spec_int64(NM_DEVICE_TUN_OWNER, + "", + "", + -1, + G_MAXUINT32, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:group: + * + * The gid of the tunnel group, or -1 if it has no owner. + * + * Since: 1.2 + **/ + obj_properties[PROP_GROUP] = g_param_spec_int64(NM_DEVICE_TUN_GROUP, + "", + "", + -1, + G_MAXUINT32, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:no-pi: + * + * The tunnel's "TUN_NO_PI" flag; true if no protocol info is + * prepended to the tunnel packets. + * + * Since: 1.2 + **/ + obj_properties[PROP_NO_PI] = g_param_spec_boolean(NM_DEVICE_TUN_NO_PI, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:vnet-hdr: + * + * The tunnel's "TUN_VNET_HDR" flag; true if the tunnel packets + * include a virtio network header. + * + * Since: 1.2 + **/ + obj_properties[PROP_VNET_HDR] = g_param_spec_boolean(NM_DEVICE_TUN_VNET_HDR, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceTun:multi-queue: + * + * The tunnel's "TUN_TAP_MQ" flag; true if callers can connect to + * the tap device multiple times, for multiple send/receive + * queues. + * + * Since: 1.2 + **/ + obj_properties[PROP_MULTI_QUEUE] = + g_param_spec_boolean(NM_DEVICE_TUN_MULTI_QUEUE, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_tun); +} diff --git a/src/libnm-client-impl/nm-device-veth.c b/src/libnm-client-impl/nm-device-veth.c new file mode 100644 index 00000000..f35f09b1 --- /dev/null +++ b/src/libnm-client-impl/nm-device-veth.c @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-veth.h" + +#include "nm-setting-connection.h" +#include "nm-setting-veth.h" +#include "nm-setting-wired.h" +#include "nm-utils.h" +#include "nm-device-ethernet.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PEER, ); + +typedef struct { + NMLDBusPropertyO peer; +} NMDeviceVethPrivate; + +struct _NMDeviceVeth { + NMDeviceEthernet parent; + NMDeviceVethPrivate _priv; +}; + +struct _NMDeviceVethClass { + NMDeviceEthernetClass parent; +}; + +G_DEFINE_TYPE(NMDeviceVeth, nm_device_veth, NM_TYPE_DEVICE_ETHERNET) + +#define NM_DEVICE_VETH_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceVeth, NM_IS_DEVICE_VETH, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_veth_get_peer: + * @device: a #NMDeviceVeth + * + * Returns: (transfer none): the device's peer device + * + * Since: 1.30 + **/ +NMDevice * +nm_device_veth_get_peer(NMDeviceVeth *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VETH(device), FALSE); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_VETH_GET_PRIVATE(device)->peer); +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_VETH; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceVeth *device = NM_DEVICE_VETH(object); + + switch (prop_id) { + case PROP_PEER: + g_value_set_object(value, nm_device_veth_get_peer(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nm_device_veth_init(NMDeviceVeth *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_veth = + NML_DBUS_META_IFACE_INIT_PROP(NM_DBUS_INTERFACE_DEVICE_VETH, + nm_device_veth_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_O_PROP("Peer", + PROP_PEER, + NMDeviceVeth, + _priv.peer, + nm_device_get_type), ), ); + +static void +nm_device_veth_class_init(NMDeviceVethClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceVeth); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceVethPrivate, peer); + + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceVeth:peer: + * + * The device's peer device. + * + * Since: 1.30 + **/ + obj_properties[PROP_PEER] = g_param_spec_object(NM_DEVICE_VETH_PEER, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_veth); +} diff --git a/src/libnm-client-impl/nm-device-vlan.c b/src/libnm-client-impl/nm-device-vlan.c new file mode 100644 index 00000000..4c2a435b --- /dev/null +++ b/src/libnm-client-impl/nm-device-vlan.c @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-vlan.h" + +#include "nm-setting-connection.h" +#include "nm-setting-vlan.h" +#include "nm-setting-wired.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, PROP_PARENT, PROP_VLAN_ID, ); + +typedef struct { + NMLDBusPropertyO parent; + guint32 vlan_id; + bool carrier; +} NMDeviceVlanPrivate; + +struct _NMDeviceVlan { + NMDevice parent; + NMDeviceVlanPrivate _priv; +}; + +struct _NMDeviceVlanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE) + +#define NM_DEVICE_VLAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceVlan, NM_IS_DEVICE_VLAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_vlan_get_hw_address: (skip) + * @device: a #NMDeviceVlan + * + * Gets the hardware (MAC) address of the #NMDeviceVlan + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_vlan_get_hw_address(NMDeviceVlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VLAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_vlan_get_carrier: + * @device: a #NMDeviceVlan + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + **/ +gboolean +nm_device_vlan_get_carrier(NMDeviceVlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VLAN(device), FALSE); + + return NM_DEVICE_VLAN_GET_PRIVATE(device)->carrier; +} + +/** + * nm_device_vlan_get_parent: + * @device: a #NMDeviceVlan + * + * Returns: (transfer none): the device's parent device + **/ +NMDevice * +nm_device_vlan_get_parent(NMDeviceVlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VLAN(device), FALSE); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_VLAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_vlan_get_vlan_id: + * @device: a #NMDeviceVlan + * + * Returns: the device's VLAN ID + **/ +guint +nm_device_vlan_get_vlan_id(NMDeviceVlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VLAN(device), FALSE); + + return NM_DEVICE_VLAN_GET_PRIVATE(device)->vlan_id; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingVlan * s_vlan; + NMSettingWired *s_wired; + const char * setting_hwaddr; + const char * hw_address; + + if (!NM_DEVICE_CLASS(nm_device_vlan_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_VLAN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a VLAN connection.")); + return FALSE; + } + + s_vlan = nm_connection_get_setting_vlan(connection); + if (nm_setting_vlan_get_id(s_vlan) != nm_device_vlan_get_vlan_id(NM_DEVICE_VLAN(device))) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The VLAN identifiers of the device and the connection didn't match.")); + return FALSE; + } + + s_wired = nm_connection_get_setting_wired(connection); + if (s_wired) + setting_hwaddr = nm_setting_wired_get_mac_address(s_wired); + else + setting_hwaddr = NULL; + if (setting_hwaddr) { + hw_address = nm_device_get_hw_address(NM_DEVICE(device)); + + if (!hw_address || !nm_utils_hwaddr_matches(setting_hwaddr, -1, hw_address, -1)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The hardware address of the device and the connection didn't match.")); + } + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_VLAN; +} + +/*****************************************************************************/ + +static void +nm_device_vlan_init(NMDeviceVlan *device) +{} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceVlan *device = NM_DEVICE_VLAN(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_vlan_get_carrier(device)); + break; + case PROP_PARENT: + g_value_set_object(value, nm_device_vlan_get_parent(device)); + break; + case PROP_VLAN_ID: + g_value_set_uint(value, nm_device_vlan_get_vlan_id(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vlan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_VLAN, + nm_device_vlan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_B("Carrier", PROP_CARRIER, NMDeviceVlan, _priv.carrier), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceVlan, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_U("VlanId", PROP_VLAN_ID, NMDeviceVlan, _priv.vlan_id), ), ); + +static void +nm_device_vlan_class_init(NMDeviceVlanClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceVlan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceVlanPrivate, parent); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceVlan:carrier: + * + * Whether the device has carrier. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_VLAN_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVlan:parent: + * + * The devices's parent device. + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_VLAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVlan:vlan-id: + * + * The device's VLAN ID. + **/ + obj_properties[PROP_VLAN_ID] = g_param_spec_uint(NM_DEVICE_VLAN_VLAN_ID, + "", + "", + 0, + 4095, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_vlan); +} diff --git a/src/libnm-client-impl/nm-device-vrf.c b/src/libnm-client-impl/nm-device-vrf.c new file mode 100644 index 00000000..bbf37810 --- /dev/null +++ b/src/libnm-client-impl/nm-device-vrf.c @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-vrf.h" + +#include "nm-setting-connection.h" +#include "nm-setting-vrf.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TABLE, ); + +typedef struct { + guint32 table; +} NMDeviceVrfPrivate; + +struct _NMDeviceVrf { + NMDevice parent; + NMDeviceVrfPrivate _priv; +}; + +struct _NMDeviceVrfClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceVrf, nm_device_vrf, NM_TYPE_DEVICE) + +#define NM_DEVICE_VRF_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceVrf, NM_IS_DEVICE_VRF, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_vrf_get_table: + * @device: a #NMDeviceVrf + * + * Returns: the device's VRF routing table. + * + * Since: 1.24 + **/ +guint32 +nm_device_vrf_get_table(NMDeviceVrf *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VRF(device), 0); + + return NM_DEVICE_VRF_GET_PRIVATE(device)->table; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingVrf *s_vrf; + + if (!NM_DEVICE_CLASS(nm_device_vrf_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_VRF_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a VRF connection.")); + return FALSE; + } + + s_vrf = (NMSettingVrf *) nm_connection_get_setting(connection, NM_TYPE_SETTING_VRF); + if (nm_setting_vrf_get_table(s_vrf) != nm_device_vrf_get_table(NM_DEVICE_VRF(device))) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The VRF table of the device and the connection didn't match.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_VRF; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceVrf *device = NM_DEVICE_VRF(object); + + switch (prop_id) { + case PROP_TABLE: + g_value_set_uint(value, nm_device_vrf_get_table(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nm_device_vrf_init(NMDeviceVrf *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vrf = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_VRF, + nm_device_vrf_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("Table", PROP_TABLE, NMDeviceVrf, _priv.table), ), ); + +static void +nm_device_vrf_class_init(NMDeviceVrfClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceVrf); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceVrf:table: + * + * The device's VRF table. + * + * Since: 1.24 + **/ + obj_properties[PROP_TABLE] = g_param_spec_uint(NM_DEVICE_VRF_TABLE, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_vrf); +} diff --git a/src/libnm-client-impl/nm-device-vxlan.c b/src/libnm-client-impl/nm-device-vxlan.c new file mode 100644 index 00000000..6fac3b71 --- /dev/null +++ b/src/libnm-client-impl/nm-device-vxlan.c @@ -0,0 +1,787 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-vxlan.h" + +#include "nm-setting-connection.h" +#include "nm-setting-vxlan.h" +#include "nm-utils.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CARRIER, + PROP_PARENT, + PROP_ID, + PROP_GROUP, + PROP_LOCAL, + PROP_TOS, + PROP_TTL, + PROP_LIMIT, + PROP_LEARNING, + PROP_AGEING, + PROP_DST_PORT, + PROP_SRC_PORT_MIN, + PROP_SRC_PORT_MAX, + PROP_PROXY, + PROP_RSC, + PROP_L2MISS, + PROP_L3MISS, ); + +typedef struct { + NMLDBusPropertyO parent; + char * group; + char * local; + guint32 id; + guint32 limit; + guint32 ageing; + guint16 src_port_min; + guint16 src_port_max; + guint16 dst_port; + guint8 tos; + guint8 ttl; + bool learning; + bool proxy; + bool rsc; + bool l2miss; + bool l3miss; +} NMDeviceVxlanPrivate; + +struct _NMDeviceVxlan { + NMDevice parent; + NMDeviceVxlanPrivate _priv; +}; + +struct _NMDeviceVxlanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceVxlan, nm_device_vxlan, NM_TYPE_DEVICE) + +#define NM_DEVICE_VXLAN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceVxlan, NM_IS_DEVICE_VXLAN, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_vxlan_get_hw_address: (skip) + * @device: a #NMDeviceVxlan + * + * Gets the hardware (MAC) address of the #NMDeviceVxlan + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.2 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_vxlan_get_hw_address(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_vxlan_get_carrier: + * @device: a #NMDeviceVxlan + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier. + * + * Since: 1.2 + * + * This property is not implemented yet, and the function always returns + * FALSE. + **/ +gboolean +nm_device_vxlan_get_carrier(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return FALSE; +} + +/** + * nm_device_vxlan_get_parent: + * @device: a #NMDeviceVxlan + * + * Returns: (transfer none): the device's parent device + * + * Since: 1.2 + **/ +NMDevice * +nm_device_vxlan_get_parent(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_VXLAN_GET_PRIVATE(device)->parent); +} + +/** + * nm_device_vxlan_get_id: + * @device: a #NMDeviceVxlan + * + * Returns: the device's VXLAN ID. + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_id(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->id; +} + +/** + * nm_device_vxlan_get_group: + * @device: a #NMDeviceVxlan + * + * Returns: The unicast destination IP address or the multicast + * IP address joined + * + * Since: 1.2 + **/ +const char * +nm_device_vxlan_get_group(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_VXLAN_GET_PRIVATE(device)->group); +} + +/** + * nm_device_vxlan_get_local: + * @device: a #NMDeviceVxlan + * + * Returns: the source IP address to use in outgoing packets + * + * Since: 1.2 + **/ +const char * +nm_device_vxlan_get_local(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_VXLAN_GET_PRIVATE(device)->local); +} + +/** + * nm_device_vxlan_get_src_port_min: + * @device: a #NMDeviceVxlan + * + * Returns: the minimum UDP source port + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_src_port_min(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->src_port_min; +} + +/** + * nm_device_vxlan_get_src_port_max: + * @device: a #NMDeviceVxlan + * + * Returns: the maximum UDP source port + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_src_port_max(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->src_port_max; +} + +/** + * nm_device_vxlan_get_dst_port: + * @device: a #NMDeviceVxlan + * + * Returns: the UDP destination port + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_dst_port(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->dst_port; +} + +/** + * nm_device_vxlan_get_learning: + * @device: a #NMDeviceVxlan + * + * Returns: whether address learning is enabled + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_learning(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->learning; +} + +/** + * nm_device_vxlan_get_ageing: + * @device: a #NMDeviceVxlan + * + * Returns: the lifetime in seconds of FDB entries learnt by the kernel + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_ageing(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->ageing; +} + +/** + * nm_device_vxlan_get_tos: + * @device: a #NMDeviceVxlan + * + * Returns: the TOS value to use in outgoing packets + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_tos(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->tos; +} + +/** + * nm_device_vxlan_get_ttl: + * @device: a #NMDeviceVxlan + * + * Returns: the time-to-live value to use in outgoing packets + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_ttl(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->ttl; +} + +/** + * nm_device_vxlan_get_limit: + * @device: a #NMDeviceVxlan + * + * Returns: the maximum number of entries that can be added to the + * forwarding table + * + * Since: 1.2 + **/ +guint +nm_device_vxlan_get_limit(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), 0); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->limit; +} + +/** + * nm_device_vxlan_get_proxy: + * @device: a #NMDeviceVxlan + * + * Returns: whether ARP proxy is turned on + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_proxy(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->proxy; +} + +/** + * nm_device_vxlan_get_rsc: + * @device: a #NMDeviceVxlan + * + * Returns: whether route short circuit is turned on + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_rsc(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->rsc; +} + +/** + * nm_device_vxlan_get_l2miss: + * @device: a #NMDeviceVxlan + * + * Returns: whether netlink LL ADDR miss notifications are generated + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_l2miss(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->l2miss; +} + +/** + * nm_device_vxlan_get_l3miss: + * @device: a #NMDeviceVxlan + * + * Returns: whether netlink IP ADDR miss notifications are generated + * + * Since: 1.2 + **/ +gboolean +nm_device_vxlan_get_l3miss(NMDeviceVxlan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_VXLAN(device), FALSE); + + return NM_DEVICE_VXLAN_GET_PRIVATE(device)->l3miss; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingVxlan *s_vxlan; + + if (!NM_DEVICE_CLASS(nm_device_vxlan_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_VXLAN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a VXLAN connection.")); + return FALSE; + } + + s_vxlan = nm_connection_get_setting_vxlan(connection); + if (nm_setting_vxlan_get_id(s_vxlan) != nm_device_vxlan_get_id(NM_DEVICE_VXLAN(device))) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The VXLAN identifiers of the device and the connection didn't match.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_VXLAN; +} + +/*****************************************************************************/ + +static void +nm_device_vxlan_init(NMDeviceVxlan *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE(object); + + g_free(priv->group); + g_free(priv->local); + + G_OBJECT_CLASS(nm_device_vxlan_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceVxlan *device = NM_DEVICE_VXLAN(object); + + switch (prop_id) { + case PROP_CARRIER: + g_value_set_boolean(value, nm_device_vxlan_get_carrier(device)); + break; + case PROP_PARENT: + g_value_set_object(value, nm_device_vxlan_get_parent(device)); + break; + case PROP_ID: + g_value_set_uint(value, nm_device_vxlan_get_id(device)); + break; + case PROP_GROUP: + g_value_set_string(value, nm_device_vxlan_get_group(device)); + break; + case PROP_LOCAL: + g_value_set_string(value, nm_device_vxlan_get_local(device)); + break; + case PROP_TOS: + g_value_set_uint(value, nm_device_vxlan_get_tos(device)); + break; + case PROP_TTL: + g_value_set_uint(value, nm_device_vxlan_get_ttl(device)); + break; + case PROP_LIMIT: + g_value_set_uint(value, nm_device_vxlan_get_limit(device)); + break; + case PROP_LEARNING: + g_value_set_boolean(value, nm_device_vxlan_get_learning(device)); + break; + case PROP_AGEING: + g_value_set_uint(value, nm_device_vxlan_get_ageing(device)); + break; + case PROP_DST_PORT: + g_value_set_uint(value, nm_device_vxlan_get_dst_port(device)); + break; + case PROP_SRC_PORT_MIN: + g_value_set_uint(value, nm_device_vxlan_get_src_port_min(device)); + break; + case PROP_SRC_PORT_MAX: + g_value_set_uint(value, nm_device_vxlan_get_src_port_max(device)); + break; + case PROP_PROXY: + g_value_set_boolean(value, nm_device_vxlan_get_proxy(device)); + break; + case PROP_RSC: + g_value_set_boolean(value, nm_device_vxlan_get_rsc(device)); + break; + case PROP_L2MISS: + g_value_set_boolean(value, nm_device_vxlan_get_l2miss(device)); + break; + case PROP_L3MISS: + g_value_set_boolean(value, nm_device_vxlan_get_l3miss(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vxlan = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_VXLAN, + nm_device_vxlan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("Ageing", PROP_AGEING, NMDeviceVxlan, _priv.ageing), + NML_DBUS_META_PROPERTY_INIT_Q("DstPort", PROP_DST_PORT, NMDeviceVxlan, _priv.dst_port), + NML_DBUS_META_PROPERTY_INIT_S("Group", PROP_GROUP, NMDeviceVxlan, _priv.group), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_U("Id", PROP_ID, NMDeviceVxlan, _priv.id), + NML_DBUS_META_PROPERTY_INIT_B("L2miss", PROP_L2MISS, NMDeviceVxlan, _priv.l2miss), + NML_DBUS_META_PROPERTY_INIT_B("L3miss", PROP_L3MISS, NMDeviceVxlan, _priv.l3miss), + NML_DBUS_META_PROPERTY_INIT_B("Learning", PROP_LEARNING, NMDeviceVxlan, _priv.learning), + NML_DBUS_META_PROPERTY_INIT_U("Limit", PROP_LIMIT, NMDeviceVxlan, _priv.limit), + NML_DBUS_META_PROPERTY_INIT_S("Local", PROP_LOCAL, NMDeviceVxlan, _priv.local), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Parent", + PROP_PARENT, + NMDeviceVxlan, + _priv.parent, + nm_device_get_type), + NML_DBUS_META_PROPERTY_INIT_B("Proxy", PROP_PROXY, NMDeviceVxlan, _priv.proxy), + NML_DBUS_META_PROPERTY_INIT_B("Rsc", PROP_RSC, NMDeviceVxlan, _priv.rsc), + NML_DBUS_META_PROPERTY_INIT_Q("SrcPortMax", + PROP_SRC_PORT_MAX, + NMDeviceVxlan, + _priv.src_port_max), + NML_DBUS_META_PROPERTY_INIT_Q("SrcPortMin", + PROP_SRC_PORT_MIN, + NMDeviceVxlan, + _priv.src_port_min), + NML_DBUS_META_PROPERTY_INIT_Y("Tos", PROP_TOS, NMDeviceVxlan, _priv.tos), + NML_DBUS_META_PROPERTY_INIT_Y("Ttl", PROP_TTL, NMDeviceVxlan, _priv.ttl), ), ); + +static void +nm_device_vxlan_class_init(NMDeviceVxlanClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceVxlan); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, NMDeviceVxlanPrivate, parent); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceVxlan:carrier: + * + * Whether the device has carrier. + * + * Since: 1.2 + * + * This property is not implemented yet, and the property is always FALSE. + **/ + obj_properties[PROP_CARRIER] = g_param_spec_boolean(NM_DEVICE_VXLAN_CARRIER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:parent: + * + * The devices's parent device. + * + * Since: 1.2 + **/ + obj_properties[PROP_PARENT] = g_param_spec_object(NM_DEVICE_VXLAN_PARENT, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:id: + * + * The device's VXLAN ID. + * + * Since: 1.2 + **/ + obj_properties[PROP_ID] = g_param_spec_uint(NM_DEVICE_VXLAN_ID, + "", + "", + 0, + (1 << 24) - 1, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:group: + * + * The unicast destination IP address used in outgoing packets when the + * destination link layer address is not known in the VXLAN device + * forwarding database or the multicast IP address joined. + * + * Since: 1.2 + */ + obj_properties[PROP_GROUP] = g_param_spec_string(NM_DEVICE_VXLAN_GROUP, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:local: + * + * The source IP address to use in outgoing packets. + * + * Since: 1.2 + */ + obj_properties[PROP_LOCAL] = g_param_spec_string(NM_DEVICE_VXLAN_LOCAL, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:tos: + * + * The TOS value to use in outgoing packets. + * + * Since: 1.2 + */ + obj_properties[PROP_TOS] = g_param_spec_uchar(NM_DEVICE_VXLAN_TOS, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:ttl: + * + * The time-to-live value to use in outgoing packets. + * + * Since: 1.2 + */ + obj_properties[PROP_TTL] = g_param_spec_uchar(NM_DEVICE_VXLAN_TTL, + "", + "", + 0, + 255, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:learning: + * + * Whether unknown source link layer addresses and IP addresses are entered + * into the VXLAN device forwarding database. + * + * Since: 1.2 + */ + obj_properties[PROP_LEARNING] = g_param_spec_boolean(NM_DEVICE_VXLAN_LEARNING, + "", + "", + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:ageing: + * + * The lifetime in seconds of FDB entries learnt by the kernel. + * + * Since: 1.2 + */ + obj_properties[PROP_AGEING] = g_param_spec_uint(NM_DEVICE_VXLAN_AGEING, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:limit: + * + * The maximum number of entries that can be added to the forwarding table. + * + * Since: 1.2 + */ + obj_properties[PROP_LIMIT] = g_param_spec_uint(NM_DEVICE_VXLAN_LIMIT, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:dst-port: + * + * The UDP destination port used to communicate with the remote VXLAN tunnel + * endpoint. + * + * Since: 1.2 + */ + obj_properties[PROP_DST_PORT] = g_param_spec_uint(NM_DEVICE_VXLAN_DST_PORT, + "", + "", + 0, + 65535, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:src-port-min: + * + * The minimum UDP source port used to communicate with the remote VXLAN + * tunnel endpoint. + * + * Since: 1.2 + */ + obj_properties[PROP_SRC_PORT_MIN] = + g_param_spec_uint(NM_DEVICE_VXLAN_SRC_PORT_MIN, + "", + "", + 0, + 65535, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:src-port-max: + * + * The maximum UDP source port used to communicate with the remote VXLAN + * tunnel endpoint. + * + * Since: 1.2 + */ + obj_properties[PROP_SRC_PORT_MAX] = + g_param_spec_uint(NM_DEVICE_VXLAN_SRC_PORT_MAX, + "", + "", + 0, + 65535, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:proxy: + * + * Whether ARP proxy is turned on. + * + * Since: 1.2 + */ + obj_properties[PROP_PROXY] = g_param_spec_boolean(NM_DEVICE_VXLAN_PROXY, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:rsc: + * + * Whether route short circuit is turned on. + * + * Since: 1.2 + */ + obj_properties[PROP_RSC] = g_param_spec_boolean(NM_DEVICE_VXLAN_RSC, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:l2miss: + * + * Whether netlink LL ADDR miss notifications are generated. + * + * Since: 1.2 + */ + obj_properties[PROP_L2MISS] = g_param_spec_boolean(NM_DEVICE_VXLAN_L2MISS, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceVxlan:l3miss: + * + * Whether netlink IP ADDR miss notifications are generated. + * + * Since: 1.2 + */ + obj_properties[PROP_L3MISS] = g_param_spec_boolean(NM_DEVICE_VXLAN_L3MISS, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_vxlan); +} diff --git a/src/libnm-client-impl/nm-device-wifi-p2p.c b/src/libnm-client-impl/nm-device-wifi-p2p.c new file mode 100644 index 00000000..2daaf774 --- /dev/null +++ b/src/libnm-client-impl/nm-device-wifi-p2p.c @@ -0,0 +1,414 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 - 2019 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wifi-p2p.h" + +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-setting-connection.h" +#include "nm-setting-wifi-p2p.h" +#include "nm-utils.h" +#include "nm-wifi-p2p-peer.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-helpers.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PEERS, ); + +enum { + PEER_ADDED, + PEER_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef struct { + NMLDBusPropertyAO peers; +} NMDeviceWifiP2PPrivate; + +struct _NMDeviceWifiP2P { + NMDevice parent; + NMDeviceWifiP2PPrivate _priv; +}; + +struct _NMDeviceWifiP2PClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWifiP2P, nm_device_wifi_p2p, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIFI_P2P_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceWifiP2P, NM_IS_DEVICE_WIFI_P2P, NMDevice, NMObject) + +/*****************************************************************************/ + +/** + * nm_device_wifi_p2p_get_hw_address: (skip) + * @device: a #NMDeviceWifiP2P + * + * Gets the actual hardware (MAC) address of the #NMDeviceWifiP2P + * + * Returns: the actual hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Since: 1.16 + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_wifi_p2p_get_hw_address(NMDeviceWifiP2P *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_wifi_p2p_get_peers: + * @device: a #NMDeviceWifiP2P + * + * Gets all the found peers of the #NMDeviceWifiP2P. + * + * Returns: (element-type NMWifiP2PPeer): a #GPtrArray containing all the + * found #NMWifiP2PPeers. + * The returned array is owned by the client and should not be modified. + * + * Since: 1.16 + **/ +const GPtrArray * +nm_device_wifi_p2p_get_peers(NMDeviceWifiP2P *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_WIFI_P2P_GET_PRIVATE(device)->peers); +} + +/** + * nm_device_wifi_p2p_get_peer_by_path: + * @device: a #NMDeviceWifiP2P + * @path: the object path of the peer + * + * Gets a #NMWifiP2PPeer by path. + * + * Returns: (transfer none): the peer or %NULL if none is found. + * + * Since: 1.16 + **/ +NMWifiP2PPeer * +nm_device_wifi_p2p_get_peer_by_path(NMDeviceWifiP2P *device, const char *path) +{ + const GPtrArray *peers; + int i; + NMWifiP2PPeer * peer = NULL; + + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), NULL); + g_return_val_if_fail(path != NULL, NULL); + + peers = nm_device_wifi_p2p_get_peers(device); + if (!peers) + return NULL; + + for (i = 0; i < peers->len; i++) { + NMWifiP2PPeer *candidate = g_ptr_array_index(peers, i); + if (!strcmp(nm_object_get_path(NM_OBJECT(candidate)), path)) { + peer = candidate; + break; + } + } + + return peer; +} + +/** + * nm_device_wifi_p2p_start_find: + * @device: a #NMDeviceWifiP2P + * @options: (allow-none): optional options passed to StartFind. + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback, or %NULL + * @user_data: user_data for @callback + * + * Request NM to search for Wi-Fi P2P peers on @device. Note that the call + * returns immediately after requesting the find, and it may take some time + * after that for peers to be found. + * + * The find operation will run for 30s by default. You can stop it earlier + * using nm_device_p2p_wifi_stop_find(). + * + * Since: 1.16 + **/ +void +nm_device_wifi_p2p_start_find(NMDeviceWifiP2P * device, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE_WIFI_P2P(device)); + g_return_if_fail(!options || g_variant_is_of_type(options, G_VARIANT_TYPE_VARDICT)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + if (!options) + options = nm_g_variant_singleton_aLsvI(); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_wifi_p2p_start_find, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + "StartFind", + g_variant_new("(@a{sv})", options), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_cb); +} + +/** + * nm_device_wifi_p2p_start_find_finish: + * @device: a #NMDeviceWifiP2P + * @result: the #GAsyncResult + * @error: #GError return address + * + * Finish an operation started by nm_device_wifi_p2p_start_find(). + * + * Returns: %TRUE if the call was successful + * + * Since: 1.16 + **/ +gboolean +nm_device_wifi_p2p_start_find_finish(NMDeviceWifiP2P *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_wifi_p2p_start_find), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_device_wifi_p2p_stop_find: + * @device: a #NMDeviceWifiP2P + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback, or %NULL + * @user_data: user_data for @callback + * + * Request NM to stop any ongoing find operation for Wi-Fi P2P peers on @device. + * + * Since: 1.16 + **/ +void +nm_device_wifi_p2p_stop_find(NMDeviceWifiP2P * device, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE_WIFI_P2P(device)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_wifi_p2p_stop_find, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + "StopFind", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_cb); +} + +/** + * nm_device_wifi_p2p_stop_find_finish: + * @device: a #NMDeviceWifiP2P + * @result: the #GAsyncResult + * @error: #GError return address + * + * Finish an operation started by nm_device_wifi_p2p_stop_find(). + * + * Returns: %TRUE if the call was successful + * + * Since: 1.16 + **/ +gboolean +nm_device_wifi_p2p_stop_find_finish(NMDeviceWifiP2P *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI_P2P(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_wifi_p2p_stop_find), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_wifi_p2p_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_WIFI_P2P_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a Wi-Fi P2P connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_WIRELESS; +} + +static const char * +get_type_description(NMDevice *device) +{ + return "wifi-p2p"; +} + +/*****************************************************************************/ + +static void +_property_ao_notify_changed_peers_cb(NMLDBusPropertyAO *pr_ao, + NMClient * client, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(client, + G_OBJECT(pr_ao->owner_dbobj->nmobj), + nmobj, + is_added, + 10, + is_added ? signals[PEER_ADDED] + : signals[PEER_REMOVED]); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P(object); + + switch (prop_id) { + case PROP_PEERS: + g_value_take_boxed(value, _nm_utils_copy_object_array(nm_device_wifi_p2p_get_peers(self))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_wifi_p2p_init(NMDeviceWifiP2P *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wifip2p = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + nm_device_wifi_p2p_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("Peers", + PROP_PEERS, + NMDeviceWifiP2P, + _priv.peers, + nm_wifi_p2p_peer_get_type, + .notify_changed_ao = + _property_ao_notify_changed_peers_cb), ), ); + +static void +nm_device_wifi_p2p_class_init(NMDeviceWifiP2PClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceWifiP2P); + + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceWifiP2PPrivate, peers); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_type_description = get_type_description; + + /** + * NMDeviceWifiP2P:peers: (type GPtrArray(NMWifiP2PPeer)) + * + * List of all Wi-Fi P2P peers the device can see. + * + * Since: 1.16 + **/ + obj_properties[PROP_PEERS] = g_param_spec_boxed(NM_DEVICE_WIFI_P2P_PEERS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_wifip2p); + + /** + * NMDeviceWifiP2P::peer-added: + * @device: the Wi-Fi P2P device that received the signal + * @peer: the new access point + * + * Notifies that a #NMWifiP2PPeer is added to the Wi-Fi P2P device. + * + * Since: 1.16 + **/ + signals[PEER_ADDED] = g_signal_new("peer-added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMDeviceWifiP2P::peer-removed: + * @device: the Wi-Fi P2P device that received the signal + * @peer: the removed access point + * + * Notifies that a #NMWifiP2PPeer is removed from the Wi-Fi P2P device. + * + * Since: 1.16 + **/ + signals[PEER_REMOVED] = g_signal_new("peer-removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} diff --git a/src/libnm-client-impl/nm-device-wifi.c b/src/libnm-client-impl/nm-device-wifi.c new file mode 100644 index 00000000..fee7e8a2 --- /dev/null +++ b/src/libnm-client-impl/nm-device-wifi.c @@ -0,0 +1,789 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wifi.h" + +#include + +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-utils.h" +#include "nm-access-point.h" +#include "nm-object-private.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-helpers.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PERM_HW_ADDRESS, + PROP_MODE, + PROP_BITRATE, + PROP_ACCESS_POINTS, + PROP_ACTIVE_ACCESS_POINT, + PROP_WIRELESS_CAPABILITIES, + PROP_LAST_SCAN, ); + +typedef struct { + NMLDBusPropertyAO access_points; + NMLDBusPropertyO active_access_point; + char * perm_hw_address; + gint64 last_scan; + guint32 mode; + guint32 bitrate; + guint32 wireless_capabilities; +} NMDeviceWifiPrivate; + +enum { + ACCESS_POINT_ADDED, + ACCESS_POINT_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +struct _NMDeviceWifi { + NMDevice parent; + NMDeviceWifiPrivate _priv; +}; + +struct _NMDeviceWifiClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIFI_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceWifi, NM_IS_DEVICE_WIFI, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_wifi_get_hw_address: (skip) + * @device: a #NMDeviceWifi + * + * Gets the actual hardware (MAC) address of the #NMDeviceWifi + * + * Returns: the actual hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_wifi_get_hw_address(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +/** + * nm_device_wifi_get_permanent_hw_address: + * @device: a #NMDeviceWifi + * + * Gets the permanent hardware (MAC) address of the #NMDeviceWifi + * + * Returns: the permanent hardware address. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_wifi_get_permanent_hw_address(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_WIFI_GET_PRIVATE(device)->perm_hw_address); +} + +/** + * nm_device_wifi_get_mode: + * @device: a #NMDeviceWifi + * + * Gets the #NMDeviceWifi mode. + * + * Returns: the mode + **/ +NM80211Mode +nm_device_wifi_get_mode(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), 0); + + return NM_DEVICE_WIFI_GET_PRIVATE(device)->mode; +} + +/** + * nm_device_wifi_get_bitrate: + * @device: a #NMDeviceWifi + * + * Gets the bit rate of the #NMDeviceWifi in kbit/s. + * + * Returns: the bit rate (kbit/s) + **/ +guint32 +nm_device_wifi_get_bitrate(NMDeviceWifi *device) +{ + NMDeviceState state; + + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), 0); + + state = nm_device_get_state(NM_DEVICE(device)); + switch (state) { + case NM_DEVICE_STATE_IP_CONFIG: + case NM_DEVICE_STATE_IP_CHECK: + case NM_DEVICE_STATE_SECONDARIES: + case NM_DEVICE_STATE_ACTIVATED: + case NM_DEVICE_STATE_DEACTIVATING: + break; + default: + return 0; + } + + return NM_DEVICE_WIFI_GET_PRIVATE(device)->bitrate; +} + +/** + * nm_device_wifi_get_capabilities: + * @device: a #NMDeviceWifi + * + * Gets the Wi-Fi capabilities of the #NMDeviceWifi. + * + * Returns: the capabilities + **/ +NMDeviceWifiCapabilities +nm_device_wifi_get_capabilities(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), 0); + + return NM_DEVICE_WIFI_GET_PRIVATE(device)->wireless_capabilities; +} + +/** + * nm_device_wifi_get_active_access_point: + * @device: a #NMDeviceWifi + * + * Gets the active #NMAccessPoint. + * + * Returns: (transfer none): the access point or %NULL if none is active + **/ +NMAccessPoint * +nm_device_wifi_get_active_access_point(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + + return nml_dbus_property_o_get_obj(&NM_DEVICE_WIFI_GET_PRIVATE(device)->active_access_point); +} + +/** + * nm_device_wifi_get_access_points: + * @device: a #NMDeviceWifi + * + * Gets all the scanned access points of the #NMDeviceWifi. + * + * Returns: (element-type NMAccessPoint): a #GPtrArray containing all the + * scanned #NMAccessPoints. + * The returned array is owned by the client and should not be modified. + **/ +const GPtrArray * +nm_device_wifi_get_access_points(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_WIFI_GET_PRIVATE(device)->access_points); +} + +/** + * nm_device_wifi_get_access_point_by_path: + * @device: a #NMDeviceWifi + * @path: the object path of the access point + * + * Gets a #NMAccessPoint by path. + * + * Returns: (transfer none): the access point or %NULL if none is found. + **/ +NMAccessPoint * +nm_device_wifi_get_access_point_by_path(NMDeviceWifi *device, const char *path) +{ + const GPtrArray *aps; + int i; + NMAccessPoint * ap = NULL; + + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), NULL); + g_return_val_if_fail(path != NULL, NULL); + + aps = nm_device_wifi_get_access_points(device); + if (!aps) + return NULL; + + for (i = 0; i < aps->len; i++) { + NMAccessPoint *candidate = g_ptr_array_index(aps, i); + if (!strcmp(nm_object_get_path(NM_OBJECT(candidate)), path)) { + ap = candidate; + break; + } + } + + return ap; +} + +/** + * nm_device_wifi_get_last_scan: + * @device: a #NMDeviceWifi + * + * Returns the timestamp (in CLOCK_BOOTTIME milliseconds) for the last finished + * network scan. A value of -1 means the device never scanned for access points. + * + * Use nm_utils_get_timestamp_msec() to obtain current time value suitable for + * comparing to this value. + * + * Returns: the last scan time in seconds + * + * Since: 1.12 + **/ +gint64 +nm_device_wifi_get_last_scan(NMDeviceWifi *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), -1); + + return NM_DEVICE_WIFI_GET_PRIVATE(device)->last_scan; +} + +/** + * nm_device_wifi_request_scan: + * @device: a #NMDeviceWifi + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Request NM to scan for access points on @device. Note that the function + * returns immediately after requesting the scan, and it may take some time + * after that for the scan to complete. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be + * set. + * + * Deprecated: 1.22: Use nm_device_wifi_request_scan_async() or GDBusConnection. + **/ +gboolean +nm_device_wifi_request_scan(NMDeviceWifi *device, GCancellable *cancellable, GError **error) +{ + return nm_device_wifi_request_scan_options(device, NULL, cancellable, error); +} + +/** + * nm_device_wifi_request_scan_options: + * @device: a #NMDeviceWifi + * @options: dictionary with options for RequestScan(), or %NULL + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Request NM to scan for access points on @device. Note that the function + * returns immediately after requesting the scan, and it may take some time + * after that for the scan to complete. + * This is the same as @nm_device_wifi_request_scan except it accepts @options + * for the scanning. The argument is the dictionary passed to RequestScan() + * D-Bus call. Valid options inside the dictionary are: + * 'ssids' => array of SSIDs (saay) + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be + * set. + * + * Since: 1.2 + * + * Deprecated: 1.22: Use nm_device_wifi_request_scan_options_async() or GDBusConnection. + **/ +gboolean +nm_device_wifi_request_scan_options(NMDeviceWifi *device, + GVariant * options, + GCancellable *cancellable, + GError ** error) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), FALSE); + g_return_val_if_fail(!options || g_variant_is_of_type(options, G_VARIANT_TYPE_VARDICT), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + if (!options) + options = nm_g_variant_singleton_aLsvI(); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + "RequestScan", + g_variant_new("(@a{sv})", options), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +NM_BACKPORT_SYMBOL( + libnm_1_0_6, + gboolean, + nm_device_wifi_request_scan_options, + (NMDeviceWifi * device, GVariant *options, GCancellable *cancellable, GError **error), + (device, options, cancellable, error)); + +/** + * nm_device_wifi_request_scan_async: + * @device: a #NMDeviceWifi + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the scan has been requested + * @user_data: caller-specific data passed to @callback + * + * Request NM to scan for access points on @device. Note that @callback will be + * called immediately after requesting the scan, and it may take some time after + * that for the scan to complete. + **/ +void +nm_device_wifi_request_scan_async(NMDeviceWifi * device, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + nm_device_wifi_request_scan_options_async(device, NULL, cancellable, callback, user_data); +} + +/** + * nm_device_wifi_request_scan_options_async: + * @device: a #NMDeviceWifi + * @options: dictionary with options for RequestScan(), or %NULL + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the scan has been requested + * @user_data: caller-specific data passed to @callback + * + * Request NM to scan for access points on @device. Note that @callback will be + * called immediately after requesting the scan, and it may take some time after + * that for the scan to complete. + * This is the same as @nm_device_wifi_request_scan_async except it accepts @options + * for the scanning. The argument is the dictionary passed to RequestScan() + * D-Bus call. Valid options inside the dictionary are: + * 'ssids' => array of SSIDs (saay) + * + * To complete the request call nm_device_wifi_request_scan_finish(). + * + * Since: 1.2 + **/ +void +nm_device_wifi_request_scan_options_async(NMDeviceWifi * device, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE_WIFI(device)); + g_return_if_fail(!options || g_variant_is_of_type(options, G_VARIANT_TYPE_VARDICT)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + if (!options) + options = nm_g_variant_singleton_aLsvI(); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_wifi_request_scan_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + "RequestScan", + g_variant_new("(@a{sv})", options), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +NM_BACKPORT_SYMBOL(libnm_1_0_6, + void, + nm_device_wifi_request_scan_options_async, + (NMDeviceWifi * device, + GVariant * options, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data), + (device, options, cancellable, callback, user_data)); + +/** + * nm_device_wifi_request_scan_finish: + * @device: a #NMDeviceWifi + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_wifi_request_scan_async() and + * nm_device_wifi_request_scan_options_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be + * set. + **/ +gboolean +nm_device_wifi_request_scan_finish(NMDeviceWifi *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIFI(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_wifi_request_scan_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +#define WPA_CAPS \ + (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_WPA \ + | NM_WIFI_DEVICE_CAP_RSN) + +#define RSN_CAPS (NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN) + +static gboolean +has_proto(NMSettingWirelessSecurity *s_wsec, const char *proto) +{ + int i; + + for (i = 0; i < nm_setting_wireless_security_get_num_protos(s_wsec); i++) { + if (g_strcmp0(proto, nm_setting_wireless_security_get_proto(s_wsec, i)) == 0) + return TRUE; + } + return FALSE; +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity *s_wsec; + const char * hwaddr, *setting_hwaddr; + NMDeviceWifiCapabilities wifi_caps; + const char * key_mgmt; + + if (!NM_DEVICE_CLASS(nm_device_wifi_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_WIRELESS_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a Wi-Fi connection.")); + return FALSE; + } + + /* Check MAC address */ + hwaddr = nm_device_wifi_get_permanent_hw_address(NM_DEVICE_WIFI(device)); + if (hwaddr) { + if (!nm_utils_hwaddr_valid(hwaddr, ETH_ALEN)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("Invalid device MAC address.")); + return FALSE; + } + s_wifi = nm_connection_get_setting_wireless(connection); + setting_hwaddr = nm_setting_wireless_get_mac_address(s_wifi); + if (setting_hwaddr && !nm_utils_hwaddr_matches(setting_hwaddr, -1, hwaddr, -1)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The MACs of the device and the connection didn't match.")); + return FALSE; + } + } + + /* Check device capabilities; we assume all devices can do WEP at least */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + if (s_wsec) { + /* Connection has security, verify it against the device's capabilities */ + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + if (nm_streq(key_mgmt, "wpa-psk") || nm_streq(key_mgmt, "wpa-eap") + || nm_streq(key_mgmt, "wpa-eap-suite-b-192")) { + wifi_caps = nm_device_wifi_get_capabilities(NM_DEVICE_WIFI(device)); + + /* Is device only WEP capable? */ + if (!(wifi_caps & WPA_CAPS)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The device is lacking WPA capabilities required by the connection.")); + return FALSE; + } + + /* Make sure WPA2/RSN-only connections don't get chosen for WPA-only cards */ + if (has_proto(s_wsec, "rsn") && !has_proto(s_wsec, "wpa") && !(wifi_caps & RSN_CAPS)) { + g_set_error_literal( + error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The device is lacking WPA2/RSN capabilities required by the connection.")); + return FALSE; + } + } + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_WIRELESS; +} + +/*****************************************************************************/ + +static void +_property_ao_notify_changed_access_points_cb(NMLDBusPropertyAO *pr_ao, + NMClient * client, + NMObject * nmobj, + gboolean is_added /* or else removed */) +{ + _nm_client_notify_event_queue_emit_obj_signal(client, + G_OBJECT(pr_ao->owner_dbobj->nmobj), + nmobj, + is_added, + 10, + is_added ? signals[ACCESS_POINT_ADDED] + : signals[ACCESS_POINT_REMOVED]); +} + +/*****************************************************************************/ + +static void +nm_device_wifi_init(NMDeviceWifi *device) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(device); + + priv->last_scan = -1; +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI(object); + + switch (prop_id) { + case PROP_PERM_HW_ADDRESS: + g_value_set_string(value, nm_device_wifi_get_permanent_hw_address(self)); + break; + case PROP_MODE: + g_value_set_enum(value, nm_device_wifi_get_mode(self)); + break; + case PROP_BITRATE: + g_value_set_uint(value, nm_device_wifi_get_bitrate(self)); + break; + case PROP_ACTIVE_ACCESS_POINT: + g_value_set_object(value, nm_device_wifi_get_active_access_point(self)); + break; + case PROP_WIRELESS_CAPABILITIES: + g_value_set_flags(value, nm_device_wifi_get_capabilities(self)); + break; + case PROP_ACCESS_POINTS: + g_value_take_boxed(value, + _nm_utils_copy_object_array(nm_device_wifi_get_access_points(self))); + break; + case PROP_LAST_SCAN: + g_value_set_int64(value, nm_device_wifi_get_last_scan(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +finalize(GObject *object) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(object); + + g_free(priv->perm_hw_address); + + G_OBJECT_CLASS(nm_device_wifi_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wireless = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + nm_device_wifi_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_AO_PROP("AccessPoints", + PROP_ACCESS_POINTS, + NMDeviceWifi, + _priv.access_points, + nm_access_point_get_type, + .notify_changed_ao = + _property_ao_notify_changed_access_points_cb), + NML_DBUS_META_PROPERTY_INIT_O_PROP("ActiveAccessPoint", + PROP_ACTIVE_ACCESS_POINT, + NMDeviceWifi, + _priv.active_access_point, + nm_access_point_get_type), + NML_DBUS_META_PROPERTY_INIT_U("Bitrate", PROP_BITRATE, NMDeviceWifi, _priv.bitrate), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_X("LastScan", PROP_LAST_SCAN, NMDeviceWifi, _priv.last_scan), + NML_DBUS_META_PROPERTY_INIT_U("Mode", PROP_MODE, NMDeviceWifi, _priv.mode), + NML_DBUS_META_PROPERTY_INIT_S("PermHwAddress", + PROP_PERM_HW_ADDRESS, + NMDeviceWifi, + _priv.perm_hw_address), + NML_DBUS_META_PROPERTY_INIT_U("WirelessCapabilities", + PROP_WIRELESS_CAPABILITIES, + NMDeviceWifi, + _priv.wireless_capabilities), ), ); + +static void +nm_device_wifi_class_init(NMDeviceWifiClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceWifi); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, + NMDeviceWifiPrivate, + active_access_point); + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, NMDeviceWifiPrivate, access_points); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceWifi:perm-hw-address: + * + * The hardware (MAC) address of the device. + **/ + obj_properties[PROP_PERM_HW_ADDRESS] = + g_param_spec_string(NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:mode: + * + * The mode of the device. + **/ + obj_properties[PROP_MODE] = g_param_spec_enum(NM_DEVICE_WIFI_MODE, + "", + "", + NM_TYPE_802_11_MODE, + NM_802_11_MODE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:bitrate: + * + * The bit rate of the device in kbit/s. + **/ + obj_properties[PROP_BITRATE] = g_param_spec_uint(NM_DEVICE_WIFI_BITRATE, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:active-access-point: + * + * The active #NMAccessPoint of the device. + **/ + obj_properties[PROP_ACTIVE_ACCESS_POINT] = + g_param_spec_object(NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, + "", + "", + NM_TYPE_ACCESS_POINT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:wireless-capabilities: + * + * The wireless capabilities of the device. + **/ + obj_properties[PROP_WIRELESS_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_WIFI_CAPABILITIES, + "", + "", + NM_TYPE_DEVICE_WIFI_CAPABILITIES, + NM_WIFI_DEVICE_CAP_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:access-points: (type GPtrArray(NMAccessPoint)) + * + * List of all Wi-Fi access points the device can see. + **/ + obj_properties[PROP_ACCESS_POINTS] = + g_param_spec_boxed(NM_DEVICE_WIFI_ACCESS_POINTS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWifi:last-scan: + * + * The timestamp (in CLOCK_BOOTTIME seconds) for the last finished + * network scan. A value of -1 means the device never scanned for + * access points. + * + * Since: 1.12 + **/ + obj_properties[PROP_LAST_SCAN] = g_param_spec_int64(NM_DEVICE_WIFI_LAST_SCAN, + "", + "", + -1, + G_MAXINT64, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_wireless); + + /** + * NMDeviceWifi::access-point-added: + * @device: the Wi-Fi device that received the signal + * @ap: the new access point + * + * Notifies that a #NMAccessPoint is added to the Wi-Fi device. + **/ + signals[ACCESS_POINT_ADDED] = g_signal_new("access-point-added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMDeviceWifi::access-point-removed: + * @device: the Wi-Fi device that received the signal + * @ap: the removed access point + * + * Notifies that a #NMAccessPoint is removed from the Wi-Fi device. + **/ + signals[ACCESS_POINT_REMOVED] = g_signal_new("access-point-removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} diff --git a/src/libnm-client-impl/nm-device-wimax.c b/src/libnm-client-impl/nm-device-wimax.c new file mode 100644 index 00000000..b9db20bb --- /dev/null +++ b/src/libnm-client-impl/nm-device-wimax.c @@ -0,0 +1,389 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 - 2012 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wimax.h" + +#include "nm-wimax-nsp.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_HW_ADDRESS, + PROP_ACTIVE_NSP, + PROP_CENTER_FREQ, + PROP_RSSI, + PROP_CINR, + PROP_TX_POWER, + PROP_BSID, + PROP_NSPS, ); + +enum { + NSP_ADDED, + NSP_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +struct _NMDeviceWimax { + NMDevice parent; +}; + +struct _NMDeviceWimaxClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWimax, nm_device_wimax, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIMAX_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceWimax, NM_IS_DEVICE_WIMAX, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_wimax_get_hw_address: + * @wimax: a #NMDeviceWimax + * + * Gets the hardware (MAC) address of the #NMDeviceWimax + * + * Returns: the hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +const char * +nm_device_wimax_get_hw_address(NMDeviceWimax *wimax) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_device_wimax_get_active_nsp: + * @wimax: a #NMDeviceWimax + * + * Gets the active #NMWimaxNsp. + * + * Returns: (transfer full): the access point or %NULL if none is active + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +NMWimaxNsp * +nm_device_wimax_get_active_nsp(NMDeviceWimax *wimax) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_device_wimax_get_nsps: + * @wimax: a #NMDeviceWimax + * + * Gets all the scanned NSPs of the #NMDeviceWimax. + * + * Returns: (element-type NMWimaxNsp): a #GPtrArray containing + * all the scanned #NMWimaxNsps. + * The returned array is owned by the client and should not be modified. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +const GPtrArray * +nm_device_wimax_get_nsps(NMDeviceWimax *wimax) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_device_wimax_get_nsp_by_path: + * @wimax: a #NMDeviceWimax + * @path: the object path of the NSP + * + * Gets a #NMWimaxNsp by path. + * + * Returns: (transfer none): the access point or %NULL if none is found. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +NMWimaxNsp * +nm_device_wimax_get_nsp_by_path(NMDeviceWimax *wimax, const char *path) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_device_wimax_get_center_frequency: + * @self: a #NMDeviceWimax + * + * Gets the center frequency (in KHz) of the radio channel the device is using + * to communicate with the network when connected. Has no meaning when the + * device is not connected. + * + * Returns: the center frequency in KHz, or 0 + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +guint +nm_device_wimax_get_center_frequency(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/** + * nm_device_wimax_get_rssi: + * @self: a #NMDeviceWimax + * + * Gets the RSSI of the current radio link in dBm. This value indicates how + * strong the raw received RF signal from the base station is, but does not + * indicate the overall quality of the radio link. Has no meaning when the + * device is not connected. + * + * Returns: the RSSI in dBm, or 0 + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +int +nm_device_wimax_get_rssi(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/** + * nm_device_wimax_get_cinr: + * @self: a #NMDeviceWimax + * + * Gets the CINR (Carrier to Interference + Noise Ratio) of the current radio + * link in dB. CINR is a more accurate measure of radio link quality. Has no + * meaning when the device is not connected. + * + * Returns: the CINR in dB, or 0 + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +int +nm_device_wimax_get_cinr(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/** + * nm_device_wimax_get_tx_power: + * @self: a #NMDeviceWimax + * + * Average power of the last burst transmitted by the device, in units of + * 0.5 dBm. i.e. a TxPower of -11 represents an actual device TX power of + * -5.5 dBm. Has no meaning when the device is not connected. + * + * Returns: the TX power in dBm, or 0 + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +int +nm_device_wimax_get_tx_power(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/** + * nm_device_wimax_get_bsid: + * @self: a #NMDeviceWimax + * + * Gets the ID of the serving Base Station when the device is connected. + * + * Returns: the ID of the serving Base Station, or %NULL + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ +const char * +nm_device_wimax_get_bsid(NMDeviceWimax *self) +{ + g_return_val_if_reached(0); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + g_return_if_reached(); +} + +static void +nm_device_wimax_init(NMDeviceWimax *device) +{ + g_return_if_reached(); +} + +static void +nm_device_wimax_class_init(NMDeviceWimaxClass *wimax_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(wimax_class); + + object_class->get_property = get_property; + + /** + * NMDeviceWimax:hw-address: + * + * The hardware (MAC) address of the device. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string(NM_DEVICE_WIMAX_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:active-nsp: + * + * The active #NMWimaxNsp of the device. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_ACTIVE_NSP] = + g_param_spec_object(NM_DEVICE_WIMAX_ACTIVE_NSP, + "", + "", + NM_TYPE_WIMAX_NSP, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:center-frequency: + * + * The center frequency (in KHz) of the radio channel the device is using to + * communicate with the network when connected. Has no meaning when the + * device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_CENTER_FREQ] = g_param_spec_uint(NM_DEVICE_WIMAX_CENTER_FREQUENCY, + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:rssi: + * + * RSSI of the current radio link in dBm. This value indicates how strong + * the raw received RF signal from the base station is, but does not + * indicate the overall quality of the radio link. Has no meaning when the + * device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_RSSI] = g_param_spec_int(NM_DEVICE_WIMAX_RSSI, + "", + "", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:cinr: + * + * CINR (Carrier to Interference + Noise Ratio) of the current radio link + * in dB. CINR is a more accurate measure of radio link quality. Has no + * meaning when the device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_CINR] = g_param_spec_int(NM_DEVICE_WIMAX_CINR, + "", + "", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:tx-power: + * + * Average power of the last burst transmitted by the device, in units of + * 0.5 dBm. i.e. a TxPower of -11 represents an actual device TX power of + * -5.5 dBm. Has no meaning when the device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_TX_POWER] = g_param_spec_int(NM_DEVICE_WIMAX_TX_POWER, + "", + "", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:bsid: + * + * The ID of the serving base station as received from the network. Has + * no meaning when the device is not connected. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + obj_properties[PROP_BSID] = g_param_spec_string(NM_DEVICE_WIMAX_BSID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWimax:nsps: (type GPtrArray(NMWimaxNsp)) + * + * List of all WiMAX Network Service Providers the device can see. + **/ + obj_properties[PROP_NSPS] = g_param_spec_boxed(NM_DEVICE_WIMAX_NSPS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + /** + * NMDeviceWimax::nsp-added: + * @self: the wimax device that received the signal + * @nsp: the new NSP + * + * Notifies that a #NMWimaxNsp is added to the wimax device. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + signals[NSP_ADDED] = g_signal_new("nsp-added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + /** + * NMDeviceWimax::nsp-removed: + * @self: the wimax device that received the signal + * @nsp: the removed NSP + * + * Notifies that a #NMWimaxNsp is removed from the wimax device. + * + * Deprecated: 1.2: WiMAX is no longer supported. + **/ + signals[NSP_REMOVED] = g_signal_new("nsp-removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} diff --git a/src/libnm-client-impl/nm-device-wireguard.c b/src/libnm-client-impl/nm-device-wireguard.c new file mode 100644 index 00000000..cbbd904b --- /dev/null +++ b/src/libnm-client-impl/nm-device-wireguard.c @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Javier Arteaga + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wireguard.h" + +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PUBLIC_KEY, PROP_LISTEN_PORT, PROP_FWMARK, ); + +typedef struct { + GBytes *public_key; + guint32 fwmark; + guint16 listen_port; +} NMDeviceWireGuardPrivate; + +struct _NMDeviceWireGuard { + NMDevice parent; + NMDeviceWireGuardPrivate _priv; +}; + +struct _NMDeviceWireGuardClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWireGuard, nm_device_wireguard, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIREGUARD_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMDeviceWireGuard, NM_IS_DEVICE_WIREGUARD, NMObject, NMDevice) + +/*****************************************************************************/ + +/** + * nm_device_wireguard_get_public_key: + * @device: a #NMDeviceWireGuard + * + * Gets the public key for this interface + * + * Returns: (transfer none): the #GBytes containing the 32-byte public key + * + * Since: 1.14 + **/ +GBytes * +nm_device_wireguard_get_public_key(NMDeviceWireGuard *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIREGUARD(device), NULL); + + return NM_DEVICE_WIREGUARD_GET_PRIVATE(device)->public_key; +} + +/** + * nm_device_wireguard_get_listen_port: + * @device: a #NMDeviceWireGuard + * + * Gets the local UDP port this interface listens on + * + * Returns: UDP listen port + * + * Since: 1.14 + **/ +guint16 +nm_device_wireguard_get_listen_port(NMDeviceWireGuard *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIREGUARD(device), 0); + + return NM_DEVICE_WIREGUARD_GET_PRIVATE(device)->listen_port; +} + +/** + * nm_device_wireguard_get_fwmark: + * @device: a #NMDeviceWireGuard + * + * Gets the fwmark (firewall mark) for this interface. + * It can be used to set routing policy for outgoing encrypted packets. + * See: ip-rule(8) + * + * Returns: 0 if fwmark not in use, 32-bit fwmark value otherwise + * + * Since: 1.14 + **/ +guint32 +nm_device_wireguard_get_fwmark(NMDeviceWireGuard *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WIREGUARD(device), 0); + + return NM_DEVICE_WIREGUARD_GET_PRIVATE(device)->fwmark; +} + +/***********************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDeviceWireGuard *device = NM_DEVICE_WIREGUARD(object); + + switch (prop_id) { + case PROP_PUBLIC_KEY: + g_value_set_boxed(value, nm_device_wireguard_get_public_key(device)); + break; + case PROP_LISTEN_PORT: + g_value_set_uint(value, nm_device_wireguard_get_listen_port(device)); + break; + case PROP_FWMARK: + g_value_set_uint(value, nm_device_wireguard_get_fwmark(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nm_device_wireguard_init(NMDeviceWireGuard *device) +{} + +static void +finalize(GObject *object) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(object); + + g_bytes_unref(priv->public_key); + + G_OBJECT_CLASS(nm_device_wireguard_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wireguard = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE_WIREGUARD, + nm_device_wireguard_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("FwMark", PROP_FWMARK, NMDeviceWireGuard, _priv.fwmark), + NML_DBUS_META_PROPERTY_INIT_Q("ListenPort", + PROP_LISTEN_PORT, + NMDeviceWireGuard, + _priv.listen_port), + NML_DBUS_META_PROPERTY_INIT_AY("PublicKey", + PROP_PUBLIC_KEY, + NMDeviceWireGuard, + _priv.public_key), ), ); + +static void +nm_device_wireguard_class_init(NMDeviceWireGuardClass *wireguard_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(wireguard_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMDeviceWireGuard:public-key: + * + * 32-byte public key, derived from the current private key. + * + * Since: 1.14 + **/ + obj_properties[PROP_PUBLIC_KEY] = g_param_spec_boxed(NM_DEVICE_WIREGUARD_PUBLIC_KEY, + "", + "", + G_TYPE_BYTES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWireGuard:listen-port: + * + * Local UDP listen port. + * Set to 0 to allow a random port to be chosen (default). + * + * Since: 1.14 + **/ + obj_properties[PROP_LISTEN_PORT] = g_param_spec_uint(NM_DEVICE_WIREGUARD_LISTEN_PORT, + "", + "", + 0, + G_MAXUINT16, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDeviceWireGuard:fwmark: + * + * Optional firewall mark - see ip-rule(8). + * Used when setting routing policy for outgoing encrypted packets. + * Set to 0 to disable the mark (default). + * + * Since: 1.14 + **/ + obj_properties[PROP_FWMARK] = g_param_spec_uint(NM_DEVICE_WIREGUARD_FWMARK, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_device_wireguard); +} diff --git a/src/libnm-client-impl/nm-device-wpan.c b/src/libnm-client-impl/nm-device-wpan.c new file mode 100644 index 00000000..30c1ea16 --- /dev/null +++ b/src/libnm-client-impl/nm-device-wpan.c @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Lubomir Rintel + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-wpan.h" + +#include "nm-object-private.h" +#include "nm-setting-wpan.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +struct _NMDeviceWpan { + NMDevice parent; +}; + +struct _NMDeviceWpanClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceWpan, nm_device_wpan, NM_TYPE_DEVICE) +/*****************************************************************************/ + +/** + * nm_device_wpan_get_hw_address: (skip) + * @device: a #NMDeviceWpan + * + * Gets the active hardware (MAC) address of the #NMDeviceWpan + * + * Returns: the active hardware address. This is the internal string used by the + * device, and must not be modified. + * + * Deprecated: 1.24: Use nm_device_get_hw_address() instead. + **/ +const char * +nm_device_wpan_get_hw_address(NMDeviceWpan *device) +{ + g_return_val_if_fail(NM_IS_DEVICE_WPAN(device), NULL); + + return nm_device_get_hw_address(NM_DEVICE(device)); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS(nm_device_wpan_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_WPAN_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a wpan connection.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_WPAN; +} + +/*****************************************************************************/ + +static void +nm_device_wpan_init(NMDeviceWpan *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wpan = NML_DBUS_META_IFACE_INIT( + NM_DBUS_INTERFACE_DEVICE_WPAN, + nm_device_wpan_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), ), ); + +static void +nm_device_wpan_class_init(NMDeviceWpanClass *wpan_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS(wpan_class); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/src/libnm-client-impl/nm-device.c b/src/libnm-client-impl/nm-device.c new file mode 100644 index 00000000..4d097935 --- /dev/null +++ b/src/libnm-client-impl/nm-device.c @@ -0,0 +1,3080 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device.h" + +#include + +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-dbus-interface.h" +#include "nm-active-connection.h" +#include "nm-device-bt.h" +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-object-private.h" +#include "nm-remote-connection.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-utils.h" +#include "nm-dbus-helpers.h" +#include "nm-device-tun.h" +#include "nm-setting-connection.h" +#include "libnm-udev-aux/nm-udev-utils.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, + PROP_INTERFACE, + PROP_UDI, + PROP_PATH, + PROP_DRIVER, + PROP_DRIVER_VERSION, + PROP_FIRMWARE_VERSION, + PROP_CAPABILITIES, + PROP_REAL, + PROP_MANAGED, + PROP_AUTOCONNECT, + PROP_FIRMWARE_MISSING, + PROP_NM_PLUGIN_MISSING, + PROP_IP4_CONFIG, + PROP_DHCP4_CONFIG, + PROP_IP6_CONFIG, + PROP_STATE, + PROP_STATE_REASON, + PROP_PRODUCT, + PROP_VENDOR, + PROP_DHCP6_CONFIG, + PROP_IP_INTERFACE, + PROP_DEVICE_TYPE, + PROP_ACTIVE_CONNECTION, + PROP_AVAILABLE_CONNECTIONS, + PROP_PHYSICAL_PORT_ID, + PROP_MTU, + PROP_METERED, + PROP_LLDP_NEIGHBORS, + PROP_IP4_CONNECTIVITY, + PROP_IP6_CONNECTIVITY, + PROP_INTERFACE_FLAGS, + PROP_HW_ADDRESS, ); + +enum { + STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +enum { + PROPERTY_O_IDX_ACTIVE_CONNECTION, + PROPERTY_O_IDX_IP4_CONFIG, + PROPERTY_O_IDX_IP6_CONFIG, + PROPERTY_O_IDX_DHCP4_CONFIG, + PROPERTY_O_IDX_DHCP6_CONFIG, + _PROPERTY_O_IDX_NUM, +}; + +typedef struct _NMDevicePrivate { + NMLDBusPropertyO property_o[_PROPERTY_O_IDX_NUM]; + NMLDBusPropertyAO available_connections; + GPtrArray * lldp_neighbors; + char * driver; + char * driver_version; + char * hw_address; + char * interface; + char * ip_interface; + char * firmware_version; + char * physical_port_id; + char * udi; + char * path; + guint32 capabilities; + guint32 device_type; + guint32 ip4_connectivity; + guint32 ip6_connectivity; + guint32 metered; + guint32 mtu; + guint32 state; + guint32 state_reason; + guint32 interface_flags; + bool firmware_missing; + bool nm_plugin_missing; + bool autoconnect; + bool managed; + bool real; + + bool hw_address_is_new : 1; + + guint32 old_state; + + struct udev *udev; + char * type_description; + char * product; + char * vendor; + char * short_vendor; + char * description; + char * bus_name; + +} NMDevicePrivate; + +G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_OBJECT); + +#define NM_DEVICE_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMDevice, NM_IS_DEVICE, NMObject) + +/*****************************************************************************/ + +static gboolean connection_compatible(NMDevice *device, NMConnection *connection, GError **error); +static NMLldpNeighbor *_nm_lldp_neighbor_dup(NMLldpNeighbor *neighbor); + +/*****************************************************************************/ + +struct _NMLldpNeighbor { + int refcount; + GHashTable *attrs; +}; + +G_DEFINE_BOXED_TYPE(NMLldpNeighbor, nm_lldp_neighbor, _nm_lldp_neighbor_dup, nm_lldp_neighbor_unref) + +/*****************************************************************************/ + +static void +nm_device_init(NMDevice *self) +{ + NMDevicePrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_DEVICE, NMDevicePrivate); + + self->_priv = priv; + + priv->old_state = NM_DEVICE_STATE_UNKNOWN; +} + +/*****************************************************************************/ + +static void +_notify_event_state_changed(NMClient *client, NMClientNotifyEventWithPtr *notify_event) +{ + gs_unref_object NMDevice *self = notify_event->user_data; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + + NML_NMCLIENT_LOG_T(client, + "[%s] emit Device's StateChanged signal %u -> %u, reason: %u", + _nm_object_get_path(self), + (guint) priv->old_state, + (guint) priv->state, + (guint) priv->state_reason); + + g_signal_emit(self, + signals[STATE_CHANGED], + 0, + (guint) priv->state, + (guint) priv->old_state, + (guint) priv->state_reason); +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_state_reason(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMDevice * self = NM_DEVICE(dbobj->nmobj); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + guint32 new_state = NM_DEVICE_STATE_UNKNOWN; + guint32 reason = NM_DEVICE_STATE_REASON_NONE; + + /* We ignore the "State" property and the "StateChanged" signal of the device. + * This information is redundant to the "StateReason" property, and we rely + * on that one alone. In the best case, the information is identical. If it + * would not be, then we stick to the information from "StateReason" property. */ + + if (value) + g_variant_get(value, "(uu)", &new_state, &reason); + + if (priv->state == new_state && priv->state_reason == reason) { + /* no changes. */ + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } + + if (priv->state != new_state) { + priv->old_state = priv->state; + priv->state = new_state; + _nm_client_queue_notify_object(client, self, obj_properties[PROP_STATE]); + } + + if (priv->state_reason != reason) { + priv->state_reason = reason; + _nm_client_queue_notify_object(client, self, obj_properties[PROP_STATE_REASON]); + } + + _nm_client_notify_event_queue_with_ptr(client, + NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP + 1, + _notify_event_state_changed, + g_object_ref(self)); + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_lldp_neighbors(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMDevice * self = NM_DEVICE(dbobj->nmobj); + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + gs_unref_ptrarray GPtrArray *old = NULL; + gs_unref_ptrarray GPtrArray *new = NULL; + GVariantIter * attrs_iter; + GVariantIter iter; + + new = g_ptr_array_new_with_free_func((GDestroyNotify) nm_lldp_neighbor_unref); + + if (value) { + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "a{sv}", &attrs_iter)) { + GVariant * attr_variant; + const char * attr_name; + NMLldpNeighbor *neigh; + + /* Note that there is no public API to mutate a NMLldpNeighbor instance. + * This is the only place where we actually mutate it. */ + neigh = nm_lldp_neighbor_new(); + while (g_variant_iter_next(attrs_iter, "{&sv}", &attr_name, &attr_variant)) { + if (attr_name[0] == '\0') { + g_variant_unref(attr_variant); + continue; + } + g_hash_table_insert(neigh->attrs, g_strdup(attr_name), attr_variant); + } + + g_ptr_array_add(new, neigh); + + g_variant_iter_free(attrs_iter); + } + } + + old = g_steal_pointer(&priv->lldp_neighbors); + priv->lldp_neighbors = g_steal_pointer(&new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static NMDeviceType +coerce_type(NMDeviceType type) +{ + switch (type) { + case NM_DEVICE_TYPE_ETHERNET: + case NM_DEVICE_TYPE_WIFI: + case NM_DEVICE_TYPE_BT: + case NM_DEVICE_TYPE_OLPC_MESH: + case NM_DEVICE_TYPE_OVS_INTERFACE: + case NM_DEVICE_TYPE_OVS_PORT: + case NM_DEVICE_TYPE_OVS_BRIDGE: + case NM_DEVICE_TYPE_WIMAX: + case NM_DEVICE_TYPE_MODEM: + case NM_DEVICE_TYPE_INFINIBAND: + case NM_DEVICE_TYPE_BOND: + case NM_DEVICE_TYPE_TEAM: + case NM_DEVICE_TYPE_BRIDGE: + case NM_DEVICE_TYPE_VLAN: + case NM_DEVICE_TYPE_ADSL: + case NM_DEVICE_TYPE_MACSEC: + case NM_DEVICE_TYPE_MACVLAN: + case NM_DEVICE_TYPE_VXLAN: + case NM_DEVICE_TYPE_IP_TUNNEL: + case NM_DEVICE_TYPE_TUN: + case NM_DEVICE_TYPE_VETH: + case NM_DEVICE_TYPE_GENERIC: + case NM_DEVICE_TYPE_UNUSED1: + case NM_DEVICE_TYPE_UNUSED2: + case NM_DEVICE_TYPE_UNKNOWN: + case NM_DEVICE_TYPE_DUMMY: + case NM_DEVICE_TYPE_PPP: + case NM_DEVICE_TYPE_WPAN: + case NM_DEVICE_TYPE_6LOWPAN: + case NM_DEVICE_TYPE_WIREGUARD: + case NM_DEVICE_TYPE_WIFI_P2P: + case NM_DEVICE_TYPE_VRF: + return type; + } + return NM_DEVICE_TYPE_UNKNOWN; +} + +/*****************************************************************************/ + +static void +register_client(NMObject *nmobj, NMClient *client, NMLDBusObject *dbobj) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(nmobj); + + priv->udev = _nm_client_get_udev(client); + if (priv->udev) + udev_ref(priv->udev); + + NM_OBJECT_CLASS(nm_device_parent_class)->register_client(nmobj, client, dbobj); +} + +/*****************************************************************************/ + +static void +finalize(GObject *object) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(object); + + nm_clear_pointer(&priv->lldp_neighbors, g_ptr_array_unref); + + g_free(priv->interface); + g_free(priv->ip_interface); + g_free(priv->udi); + g_free(priv->path); + g_free(priv->driver); + g_free(priv->driver_version); + g_free(priv->firmware_version); + g_free(priv->product); + g_free(priv->vendor); + g_free(priv->short_vendor); + g_free(priv->description); + g_free(priv->bus_name); + g_free(priv->type_description); + g_free(priv->physical_port_id); + g_free(priv->hw_address); + + nm_clear_pointer(&priv->udev, udev_unref); + + G_OBJECT_CLASS(nm_device_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDevice *device = NM_DEVICE(object); + + switch (prop_id) { + case PROP_DEVICE_TYPE: + g_value_set_enum(value, nm_device_get_device_type(device)); + break; + case PROP_UDI: + g_value_set_string(value, nm_device_get_udi(device)); + break; + case PROP_PATH: + g_value_set_string(value, nm_device_get_path(device)); + break; + case PROP_INTERFACE: + g_value_set_string(value, nm_device_get_iface(device)); + break; + case PROP_IP_INTERFACE: + g_value_set_string(value, nm_device_get_ip_iface(device)); + break; + case PROP_DRIVER: + g_value_set_string(value, nm_device_get_driver(device)); + break; + case PROP_DRIVER_VERSION: + g_value_set_string(value, nm_device_get_driver_version(device)); + break; + case PROP_FIRMWARE_VERSION: + g_value_set_string(value, nm_device_get_firmware_version(device)); + break; + case PROP_CAPABILITIES: + g_value_set_flags(value, nm_device_get_capabilities(device)); + break; + case PROP_REAL: + g_value_set_boolean(value, nm_device_is_real(device)); + break; + case PROP_MANAGED: + g_value_set_boolean(value, nm_device_get_managed(device)); + break; + case PROP_AUTOCONNECT: + g_value_set_boolean(value, nm_device_get_autoconnect(device)); + break; + case PROP_FIRMWARE_MISSING: + g_value_set_boolean(value, nm_device_get_firmware_missing(device)); + break; + case PROP_NM_PLUGIN_MISSING: + g_value_set_boolean(value, nm_device_get_nm_plugin_missing(device)); + break; + case PROP_IP4_CONFIG: + g_value_set_object(value, nm_device_get_ip4_config(device)); + break; + case PROP_DHCP4_CONFIG: + g_value_set_object(value, nm_device_get_dhcp4_config(device)); + break; + case PROP_IP6_CONFIG: + g_value_set_object(value, nm_device_get_ip6_config(device)); + break; + case PROP_DHCP6_CONFIG: + g_value_set_object(value, nm_device_get_dhcp6_config(device)); + break; + case PROP_STATE: + g_value_set_enum(value, nm_device_get_state(device)); + break; + case PROP_STATE_REASON: + g_value_set_uint(value, nm_device_get_state_reason(device)); + break; + case PROP_ACTIVE_CONNECTION: + g_value_set_object(value, nm_device_get_active_connection(device)); + break; + case PROP_AVAILABLE_CONNECTIONS: + g_value_take_boxed( + value, + _nm_utils_copy_object_array(nm_device_get_available_connections(device))); + break; + case PROP_PRODUCT: + g_value_set_string(value, nm_device_get_product(device)); + break; + case PROP_VENDOR: + g_value_set_string(value, nm_device_get_vendor(device)); + break; + case PROP_PHYSICAL_PORT_ID: + g_value_set_string(value, nm_device_get_physical_port_id(device)); + break; + case PROP_MTU: + g_value_set_uint(value, nm_device_get_mtu(device)); + break; + case PROP_METERED: + g_value_set_uint(value, nm_device_get_metered(device)); + break; + case PROP_LLDP_NEIGHBORS: + g_value_set_boxed(value, nm_device_get_lldp_neighbors(device)); + break; + case PROP_IP4_CONNECTIVITY: + g_value_set_enum(value, nm_device_get_connectivity(device, AF_INET)); + break; + case PROP_IP6_CONNECTIVITY: + g_value_set_enum(value, nm_device_get_connectivity(device, AF_INET6)); + break; + case PROP_INTERFACE_FLAGS: + g_value_set_uint(value, nm_device_get_interface_flags(device)); + break; + case PROP_HW_ADDRESS: + g_value_set_string(value, nm_device_get_hw_address(device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMDevice * self = NM_DEVICE(object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean b; + + switch (prop_id) { + case PROP_AUTOCONNECT: + b = g_value_get_boolean(value); + if (priv->autoconnect != b) + nm_device_set_autoconnect(NM_DEVICE(object), b); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/* TODO: statistics interface not yet implemented. */ +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_statistics = NML_DBUS_META_IFACE_INIT( + NM_DBUS_INTERFACE_DEVICE_STATISTICS, + NULL, + NML_DBUS_META_INTERFACE_PRIO_NONE, + NML_DBUS_META_IFACE_DBUS_PROPERTIES(NML_DBUS_META_PROPERTY_INIT_TODO("RefreshRateMs", "u"), + NML_DBUS_META_PROPERTY_INIT_TODO("RxBytes", "t"), + NML_DBUS_META_PROPERTY_INIT_TODO("TxBytes", "t"), ), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DEVICE, + nm_device_get_type, + NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_O_PROP("ActiveConnection", + PROP_ACTIVE_CONNECTION, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_ACTIVE_CONNECTION], + nm_active_connection_get_type, + .is_always_ready = TRUE), + NML_DBUS_META_PROPERTY_INIT_B("Autoconnect", + PROP_AUTOCONNECT, + NMDevicePrivate, + autoconnect), + NML_DBUS_META_PROPERTY_INIT_AO_PROP("AvailableConnections", + PROP_AVAILABLE_CONNECTIONS, + NMDevicePrivate, + available_connections, + nm_remote_connection_get_type, + .is_always_ready = TRUE), + NML_DBUS_META_PROPERTY_INIT_U("Capabilities", + PROP_CAPABILITIES, + NMDevicePrivate, + capabilities), + NML_DBUS_META_PROPERTY_INIT_U("DeviceType", PROP_DEVICE_TYPE, NMDevicePrivate, device_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Dhcp4Config", + PROP_DHCP4_CONFIG, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_DHCP4_CONFIG], + nm_dhcp4_config_get_type), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Dhcp6Config", + PROP_DHCP6_CONFIG, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_DHCP6_CONFIG], + nm_dhcp6_config_get_type), + NML_DBUS_META_PROPERTY_INIT_S("Driver", PROP_DRIVER, NMDevicePrivate, driver), + NML_DBUS_META_PROPERTY_INIT_S("DriverVersion", + PROP_DRIVER_VERSION, + NMDevicePrivate, + driver_version), + NML_DBUS_META_PROPERTY_INIT_B("FirmwareMissing", + PROP_FIRMWARE_MISSING, + NMDevicePrivate, + firmware_missing), + NML_DBUS_META_PROPERTY_INIT_S("FirmwareVersion", + PROP_FIRMWARE_VERSION, + NMDevicePrivate, + firmware_version), + NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress", + 0, + "s", + _nm_device_notify_update_prop_hw_address), + NML_DBUS_META_PROPERTY_INIT_S("Interface", PROP_INTERFACE, NMDevicePrivate, interface), + NML_DBUS_META_PROPERTY_INIT_U("InterfaceFlags", + PROP_INTERFACE_FLAGS, + NMDevicePrivate, + interface_flags), + NML_DBUS_META_PROPERTY_INIT_IGNORE("Ip4Address", "u"), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Ip4Config", + PROP_IP4_CONFIG, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_IP4_CONFIG], + nm_ip4_config_get_type), + NML_DBUS_META_PROPERTY_INIT_U("Ip4Connectivity", + PROP_IP4_CONNECTIVITY, + NMDevicePrivate, + ip4_connectivity), + NML_DBUS_META_PROPERTY_INIT_O_PROP("Ip6Config", + PROP_IP6_CONFIG, + NMDevicePrivate, + property_o[PROPERTY_O_IDX_IP6_CONFIG], + nm_ip6_config_get_type), + NML_DBUS_META_PROPERTY_INIT_U("Ip6Connectivity", + PROP_IP6_CONNECTIVITY, + NMDevicePrivate, + ip6_connectivity), + NML_DBUS_META_PROPERTY_INIT_S("IpInterface", + PROP_IP_INTERFACE, + NMDevicePrivate, + ip_interface), + NML_DBUS_META_PROPERTY_INIT_FCN("LldpNeighbors", + PROP_LLDP_NEIGHBORS, + "aa{sv}", + _notify_update_prop_lldp_neighbors), + NML_DBUS_META_PROPERTY_INIT_B("Managed", PROP_MANAGED, NMDevicePrivate, managed), + NML_DBUS_META_PROPERTY_INIT_U("Metered", PROP_METERED, NMDevicePrivate, metered), + NML_DBUS_META_PROPERTY_INIT_U("Mtu", PROP_MTU, NMDevicePrivate, mtu), + NML_DBUS_META_PROPERTY_INIT_B("NmPluginMissing", + PROP_NM_PLUGIN_MISSING, + NMDevicePrivate, + nm_plugin_missing), + NML_DBUS_META_PROPERTY_INIT_S("Path", PROP_PATH, NMDevicePrivate, path), + NML_DBUS_META_PROPERTY_INIT_S("PhysicalPortId", + PROP_PHYSICAL_PORT_ID, + NMDevicePrivate, + physical_port_id), + NML_DBUS_META_PROPERTY_INIT_B("Real", PROP_REAL, NMDevicePrivate, real), + NML_DBUS_META_PROPERTY_INIT_IGNORE("State", "u"), + NML_DBUS_META_PROPERTY_INIT_FCN("StateReason", + PROP_STATE_REASON, + "(uu)", + _notify_update_prop_state_reason), + NML_DBUS_META_PROPERTY_INIT_S("Udi", PROP_UDI, NMDevicePrivate, udi), ), + .base_struct_offset = G_STRUCT_OFFSET(NMDevice, _priv), ); + +static void +nm_device_class_init(NMDeviceClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(NMDevicePrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + nm_object_class->register_client = register_client; + + _NM_OBJECT_CLASS_INIT_PRIV_PTR_INDIRECT(nm_object_class, NMDevice); + + _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_N(nm_object_class, NMDevicePrivate, property_o); + _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, + NMDevicePrivate, + available_connections); + + klass->connection_compatible = connection_compatible; + + /** + * NMDevice:interface: + * + * The interface of the device. + **/ + obj_properties[PROP_INTERFACE] = g_param_spec_string(NM_DEVICE_INTERFACE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip-interface: + * + * The IP interface of the device which should be used for all IP-related + * operations like addressing and routing. + **/ + obj_properties[PROP_IP_INTERFACE] = + g_param_spec_string(NM_DEVICE_IP_INTERFACE, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:device-type: + * + * The numeric type of the device. + **/ + obj_properties[PROP_DEVICE_TYPE] = g_param_spec_enum(NM_DEVICE_DEVICE_TYPE, + "", + "", + NM_TYPE_DEVICE_TYPE, + NM_DEVICE_TYPE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * NMDevice:udi: + * + * An operating-system specific device hardware identifier; this is not + * unique to a specific hardware device across reboots or hotplugs. It + * is an opaque string which for some device types (Bluetooth, Modem) + * contains an identifier provided by the underlying hardware service daemon + * such as Bluez or ModemManager, and clients can use this property to + * request more information about the device from those services. + **/ + obj_properties[PROP_UDI] = + g_param_spec_string(NM_DEVICE_UDI, "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:path: + * + * The device path as exposed by the udev property ID_PATH. + * + * The string is backslash escaped (C escaping) for invalid + * characters. The escaping can be reverted with g_strcompress(), + * however the result may not be valid UTF-8. + * + * Since: 1.26 + **/ + obj_properties[PROP_PATH] = g_param_spec_string(NM_DEVICE_PATH, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:driver: + * + * The driver of the device. + **/ + obj_properties[PROP_DRIVER] = g_param_spec_string(NM_DEVICE_DRIVER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:driver-version: + * + * The version of the device driver. + **/ + obj_properties[PROP_DRIVER_VERSION] = + g_param_spec_string(NM_DEVICE_DRIVER_VERSION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:firmware-version: + * + * The firmware version of the device. + **/ + obj_properties[PROP_FIRMWARE_VERSION] = + g_param_spec_string(NM_DEVICE_FIRMWARE_VERSION, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:capabilities: + * + * The capabilities of the device. + **/ + obj_properties[PROP_CAPABILITIES] = + g_param_spec_flags(NM_DEVICE_CAPABILITIES, + "", + "", + NM_TYPE_DEVICE_CAPABILITIES, + NM_DEVICE_CAP_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:real: + * + * Whether the device is real or is a placeholder device that could + * be created automatically by NetworkManager if one of its + * #NMDevice:available-connections was activated. + * + * Since: 1.2 + **/ + obj_properties[PROP_REAL] = g_param_spec_boolean(NM_DEVICE_REAL, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:managed: + * + * Whether the device is managed by NetworkManager. + **/ + obj_properties[PROP_MANAGED] = g_param_spec_boolean(NM_DEVICE_MANAGED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:autoconnect: + * + * Whether the device can auto-activate a connection. + * + * The property setter is a synchronous D-Bus call. This is deprecated since 1.22. + **/ + obj_properties[PROP_AUTOCONNECT] = + g_param_spec_boolean(NM_DEVICE_AUTOCONNECT, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:firmware-missing: + * + * When %TRUE indicates the device is likely missing firmware required + * for its operation. + **/ + obj_properties[PROP_FIRMWARE_MISSING] = + g_param_spec_boolean(NM_DEVICE_FIRMWARE_MISSING, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:nm-plugin-missing: + * + * When %TRUE indicates that the NetworkManager plugin for the device + * is not installed. + * + * Since: 1.2 + **/ + obj_properties[PROP_NM_PLUGIN_MISSING] = + g_param_spec_boolean(NM_DEVICE_NM_PLUGIN_MISSING, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip4-config: + * + * The #NMIP4Config of the device. + **/ + obj_properties[PROP_IP4_CONFIG] = + g_param_spec_object(NM_DEVICE_IP4_CONFIG, + "", + "", + NM_TYPE_IP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:dhcp4-config: + * + * The IPv4 #NMDhcpConfig of the device. + **/ + obj_properties[PROP_DHCP4_CONFIG] = + g_param_spec_object(NM_DEVICE_DHCP4_CONFIG, + "", + "", + NM_TYPE_DHCP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip6-config: + * + * The IPv6 #NMIPConfig of the device. + **/ + obj_properties[PROP_IP6_CONFIG] = + g_param_spec_object(NM_DEVICE_IP6_CONFIG, + "", + "", + NM_TYPE_IP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:dhcp6-config: + * + * The IPv6 #NMDhcpConfig of the device. + **/ + obj_properties[PROP_DHCP6_CONFIG] = + g_param_spec_object(NM_DEVICE_DHCP6_CONFIG, + "", + "", + NM_TYPE_DHCP_CONFIG, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip4-connectivity: + * + * The IPv4 connectivity state of the device. + * + * Since: 1.16 + **/ + obj_properties[PROP_IP4_CONNECTIVITY] = + g_param_spec_enum(NM_DEVICE_IP4_CONNECTIVITY, + "", + "", + NM_TYPE_CONNECTIVITY_STATE, + NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:ip6-connectivity: + * + * The IPv6 connectivity state of the device. + * + * Since: 1.16 + **/ + obj_properties[PROP_IP6_CONNECTIVITY] = + g_param_spec_enum(NM_DEVICE_IP6_CONNECTIVITY, + "", + "", + NM_TYPE_CONNECTIVITY_STATE, + NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:state: + * + * The state of the device. + **/ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_DEVICE_STATE, + "", + "", + NM_TYPE_DEVICE_STATE, + NM_DEVICE_STATE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:state-reason: + * + * The reason for the device state. + **/ + obj_properties[PROP_STATE_REASON] = + g_param_spec_uint(NM_DEVICE_STATE_REASON, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:active-connection: + * + * The #NMActiveConnection object that "owns" this device during activation. + **/ + obj_properties[PROP_ACTIVE_CONNECTION] = + g_param_spec_object(NM_DEVICE_ACTIVE_CONNECTION, + "", + "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:available-connections: (type GPtrArray(NMRemoteConnection)) + * + * The available connections of the device + **/ + obj_properties[PROP_AVAILABLE_CONNECTIONS] = + g_param_spec_boxed(NM_DEVICE_AVAILABLE_CONNECTIONS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:vendor: + * + * The vendor string of the device. + **/ + obj_properties[PROP_VENDOR] = g_param_spec_string(NM_DEVICE_VENDOR, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:product: + * + * The product string of the device. + **/ + obj_properties[PROP_PRODUCT] = g_param_spec_string(NM_DEVICE_PRODUCT, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:physical-port-id: + * + * The physical port ID of the device. (See + * nm_device_get_physical_port_id().) + **/ + obj_properties[PROP_PHYSICAL_PORT_ID] = + g_param_spec_string(NM_DEVICE_PHYSICAL_PORT_ID, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:mtu: + * + * The MTU of the device. + **/ + obj_properties[PROP_MTU] = g_param_spec_uint(NM_DEVICE_MTU, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:metered: + * + * Whether the device is metered. + * + * Since: 1.2 + **/ + obj_properties[PROP_METERED] = g_param_spec_uint(NM_DEVICE_METERED, + "", + "", + 0, + G_MAXUINT32, + NM_METERED_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:lldp-neighbors: + * + * The LLDP neighbors. + **/ + obj_properties[PROP_LLDP_NEIGHBORS] = + g_param_spec_boxed(NM_DEVICE_LLDP_NEIGHBORS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:interface-flags: + * + * The interface flags. + * + * Since: 1.22 + **/ + obj_properties[PROP_INTERFACE_FLAGS] = + g_param_spec_uint(NM_DEVICE_INTERFACE_FLAGS, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDevice:hw-address: + * + * The hardware address of the device. + * + * Since: 1.24 + **/ + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string(NM_DEVICE_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device); + + /** + * NMDevice::state-changed: + * @device: the device object that received the signal + * @new_state: the new state of the device + * @old_state: the previous state of the device + * @reason: the reason describing the state change + * + * Notifies the state change of a #NMDevice. + **/ + signals[STATE_CHANGED] = g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 3, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_UINT); +} + +/** + * nm_device_get_iface: + * @device: a #NMDevice + * + * Gets the interface name of the #NMDevice. + * + * Returns: the interface of the device. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_get_iface(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->interface); +} + +/** + * nm_device_get_ip_iface: + * @device: a #NMDevice + * + * Gets the IP interface name of the #NMDevice over which IP traffic flows + * when the device is in the ACTIVATED state. + * + * Returns: the IP traffic interface of the device. This is the internal string + * used by the device, and must not be modified. + **/ +const char * +nm_device_get_ip_iface(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->ip_interface); +} + +/** + * nm_device_get_device_type: + * @device: a #NMDevice + * + * Returns the numeric type of the #NMDevice, ie Ethernet, Wi-Fi, etc. + * + * Returns: the device type + **/ +NMDeviceType +nm_device_get_device_type(NMDevice *self) +{ + g_return_val_if_fail(NM_IS_DEVICE(self), NM_DEVICE_TYPE_UNKNOWN); + + return coerce_type(NM_DEVICE_GET_PRIVATE(self)->device_type); +} + +/** + * nm_device_get_udi: + * @device: a #NMDevice + * + * Gets the Unique Device Identifier of the #NMDevice. + * + * Returns: the Unique Device Identifier of the device. This identifier may be + * used to gather more information about the device from various operating + * system services like udev or sysfs. + **/ +const char * +nm_device_get_udi(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->udi); +} + +/** + * nm_device_get_path: + * @device: a #NMDevice + * + * Gets the path of the #NMDevice as exposed by the udev property ID_PATH. + * + * Returns: the path of the device. + * + * The string is backslash escaped (C escaping) for invalid characters. The escaping + * can be reverted with g_strcompress(), however the result may not be valid UTF-8. + * + * Since: 1.26 + **/ +const char * +nm_device_get_path(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->path); +} + +/** + * nm_device_get_driver: + * @device: a #NMDevice + * + * Gets the driver of the #NMDevice. + * + * Returns: the driver of the device. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_get_driver(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->driver); +} + +/** + * nm_device_get_driver_version: + * @device: a #NMDevice + * + * Gets the driver version of the #NMDevice. + * + * Returns: the version of the device driver. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_get_driver_version(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->driver_version); +} + +/** + * nm_device_get_firmware_version: + * @device: a #NMDevice + * + * Gets the firmware version of the #NMDevice. + * + * Returns: the firmware version of the device. This is the internal string used by the + * device, and must not be modified. + **/ +const char * +nm_device_get_firmware_version(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->firmware_version); +} + +/** + * nm_device_get_type_description: + * @device: a #NMDevice + * + * Gets a (non-localized) description of the type of device that + * @device is. + * + * Returns: the type description of the device. This is the internal + * string used by the device, and must not be modified. + **/ +const char * +nm_device_get_type_description(NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(device); + const char * desc, *typename; + + /* BEWARE: this function should return the same value + * as nm_device_get_type_description() in nm-core. */ + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + if (priv->type_description) + return _nml_coerce_property_str_not_empty(priv->type_description); + + if (NM_DEVICE_GET_CLASS(device)->get_type_description) { + desc = NM_DEVICE_GET_CLASS(device)->get_type_description(device); + if (desc) + return desc; + } + + typename = G_OBJECT_TYPE_NAME(device); + if (g_str_has_prefix(typename, "NMDevice")) { + typename += 8; + if (nm_streq(typename, "Veth")) + typename = "Ethernet"; + } + priv->type_description = g_ascii_strdown(typename, -1); + + return _nml_coerce_property_str_not_empty(priv->type_description); +} + +NMLDBusNotifyUpdatePropFlags +_nm_device_notify_update_prop_hw_address(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMDevice * self = NM_DEVICE(dbobj->nmobj); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean is_new = (meta_iface == &_nml_dbus_meta_iface_nm_device); + gboolean changed = FALSE; + + if (!is_new && priv->hw_address_is_new) { + /* once the instance is marked to honor the new property, the + * changed signal for the old variant gets ignored. */ + goto out; + } + + if (!value) { + if (nm_clear_g_free(&priv->hw_address)) + changed = TRUE; + goto out; + } + + priv->hw_address_is_new = is_new; + + nm_utils_strdup_reset(&priv->hw_address, + _nml_coerce_property_str_not_empty(g_variant_get_string(value, NULL))); + + /* always emit a changed signal here, even if "priv->hw_address" might be unchanged. + * We want to emit the signal because we received a PropertiesChanged signal on D-Bus, + * even if nothing actually changed. */ + changed = TRUE; + +out: + if (changed) { + _nm_client_queue_notify_object(client, self, obj_properties[PROP_HW_ADDRESS]); + } + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; +} + +/** + * nm_device_get_hw_address: + * @device: a #NMDevice + * + * Gets the current a hardware address (MAC) for the @device. + * + * Returns: the current MAC of the device, or %NULL. + * This is the internal string used by the device, and must not be modified. + **/ +const char * +nm_device_get_hw_address(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + + nm_assert(!nm_streq0(priv->hw_address, "")); + + return priv->hw_address; +} + +/** + * nm_device_get_capabilities: + * @device: a #NMDevice + * + * Gets the device' capabilities. + * + * Returns: the capabilities + **/ +NMDeviceCapabilities +nm_device_get_capabilities(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), 0); + + return NM_DEVICE_GET_PRIVATE(device)->capabilities; +} + +/** + * nm_device_get_managed: + * @device: a #NMDevice + * + * Whether the #NMDevice is managed by NetworkManager. + * + * Returns: %TRUE if the device is managed by NetworkManager + **/ +gboolean +nm_device_get_managed(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), 0); + + return NM_DEVICE_GET_PRIVATE(device)->managed; +} + +/** + * nm_device_set_managed: + * @device: a #NMDevice + * @managed: %TRUE to make the device managed by NetworkManager. + * + * Enables or disables management of #NMDevice by NetworkManager. + * + * Since: 1.2 + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on + * nm_object_get_path(), interface %NM_DBUS_INTERFACE_DEVICE to set the + * "Managed" property to a "(b)" boolean value. + * This function is deprecated because it calls a synchronous D-Bus method + * and modifies the content of the NMClient cache client side. Also, it does + * not emit a property changed signal. + **/ +void +nm_device_set_managed(NMDevice *device, gboolean managed) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + + managed = !!managed; + + NM_DEVICE_GET_PRIVATE(device)->managed = managed; + + _nm_client_set_property_sync_legacy(_nm_object_get_client(device), + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Managed", + "b", + managed); +} + +/** + * nm_device_get_autoconnect: + * @device: a #NMDevice + * + * Whether the #NMDevice can be autoconnected. + * + * Returns: %TRUE if the device is allowed to be autoconnected + **/ +gboolean +nm_device_get_autoconnect(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + return NM_DEVICE_GET_PRIVATE(device)->autoconnect; +} + +/** + * nm_device_set_autoconnect: + * @device: a #NMDevice + * @autoconnect: %TRUE to enable autoconnecting + * + * Enables or disables automatic activation of the #NMDevice. + * + * Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on + * nm_object_get_path(), %NM_DBUS_INTERFACE_DEVICE to set "Autoconnect" property to a "(b)" value. + * This function is deprecated because it calls a synchronous D-Bus method + * and modifies the content of the NMClient cache client side. + **/ +void +nm_device_set_autoconnect(NMDevice *device, gboolean autoconnect) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + + NM_DEVICE_GET_PRIVATE(device)->autoconnect = autoconnect; + + _nm_client_set_property_sync_legacy(_nm_object_get_client(device), + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Autoconnect", + "b", + autoconnect); +} + +/** + * nm_device_get_firmware_missing: + * @device: a #NMDevice + * + * Indicates that firmware required for the device's operation is likely + * to be missing. + * + * Returns: %TRUE if firmware required for the device's operation is likely + * to be missing. + **/ +gboolean +nm_device_get_firmware_missing(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), 0); + + return NM_DEVICE_GET_PRIVATE(device)->firmware_missing; +} + +/** + * nm_device_get_nm_plugin_missing: + * @device: a #NMDevice + * + * Indicates that the NetworkManager plugin for the device is not installed. + * + * Returns: %TRUE if the device plugin not installed. + * + * Since: 1.2 + **/ +gboolean +nm_device_get_nm_plugin_missing(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + return NM_DEVICE_GET_PRIVATE(device)->nm_plugin_missing; +} + +/** + * nm_device_get_ip4_config: + * @device: a #NMDevice + * + * Gets the current IPv4 #NMIPConfig associated with the #NMDevice. + * + * You can alternatively use nm_active_connection_get_ip4_config(), which also + * works with VPN connections. + * + * Returns: (transfer none): the IPv4 #NMIPConfig, or %NULL if the device is not + * activated. + **/ +NMIPConfig * +nm_device_get_ip4_config(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_IP4_CONFIG]); +} + +/** + * nm_device_get_dhcp4_config: + * @device: a #NMDevice + * + * Gets the current IPv4 #NMDhcpConfig associated with the #NMDevice. + * + * You can alternatively use nm_active_connection_get_dhcp4_config(), which also + * works with VPN connections. + * + * Returns: (transfer none): the IPv4 #NMDhcpConfig, or %NULL if the device is + * not activated or not using DHCP. + **/ +NMDhcpConfig * +nm_device_get_dhcp4_config(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_DHCP4_CONFIG]); +} + +/** + * nm_device_get_ip6_config: + * @device: a #NMDevice + * + * Gets the current IPv6 #NMIPConfig associated with the #NMDevice. + * + * You can alternatively use nm_active_connection_get_ip6_config(), which also + * works with VPN connections. + * + * Returns: (transfer none): the IPv6 #NMIPConfig or %NULL if the device is not activated. + **/ +NMIPConfig * +nm_device_get_ip6_config(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_IP6_CONFIG]); +} + +/** + * nm_device_get_dhcp6_config: + * @device: a #NMDevice + * + * Gets the current IPv6 #NMDhcpConfig associated with the #NMDevice. + * + * You can alternatively use nm_active_connection_get_dhcp6_config(), which also + * works with VPN connections. + * + * Returns: (transfer none): the IPv6 #NMDhcpConfig, or %NULL if the device is + * not activated or not using DHCPv6. + **/ +NMDhcpConfig * +nm_device_get_dhcp6_config(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_DHCP6_CONFIG]); +} + +/** + * nm_device_get_connectivity: + * @device: a #NMDevice + * @addr_family: network address family + * + * The connectivity state of the device for given address family. + * Supported address families are %AF_INET for IPv4, %AF_INET6 + * for IPv6 or %AF_UNSPEC for any. + * + * Returns: the current connectivity state + * + * Since: 1.16 + **/ +NMConnectivityState +nm_device_get_connectivity(NMDevice *device, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(device); + + switch (addr_family) { + case AF_INET: + return priv->ip4_connectivity; + case AF_INET6: + return priv->ip6_connectivity; + case AF_UNSPEC: + return NM_MAX(priv->ip4_connectivity, priv->ip6_connectivity); + default: + g_return_val_if_reached(NM_CONNECTIVITY_UNKNOWN); + } +} + +/** + * nm_device_get_interface_flags: + * @device: a #NMDevice + * + * Gets the interface flags of the device. + * + * Returns: the flags + * + * Since: 1.22 + **/ +NMDeviceInterfaceFlags +nm_device_get_interface_flags(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NM_DEVICE_INTERFACE_FLAG_NONE); + + return NM_DEVICE_GET_PRIVATE(device)->interface_flags; +} + +/** + * nm_device_get_state: + * @device: a #NMDevice + * + * Gets the current #NMDevice state. + * + * Returns: the current device state + **/ +NMDeviceState +nm_device_get_state(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NM_DEVICE_STATE_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE(device)->state; +} + +/** + * nm_device_get_state_reason: + * @device: a #NMDevice + * + * Gets the reason for entering the current #NMDevice state. + * + * Returns: the reason for entering the current device state + **/ +NMDeviceStateReason +nm_device_get_state_reason(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NM_DEVICE_STATE_REASON_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE(device)->state_reason; +} + +/** + * nm_device_get_active_connection: + * @device: a #NMDevice + * + * Gets the #NMActiveConnection object which owns this device during activation. + * + * Returns: (transfer none): the #NMActiveConnection or %NULL if the device is + * not part of an active connection + **/ +NMActiveConnection * +nm_device_get_active_connection(NMDevice *device) +{ + NMActiveConnection *ac; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + ac = nml_dbus_property_o_get_obj( + &NM_DEVICE_GET_PRIVATE(device)->property_o[PROPERTY_O_IDX_ACTIVE_CONNECTION]); + + nm_assert(!ac || NM_IS_ACTIVE_CONNECTION(ac)); + return ac; +} + +/** + * nm_device_get_available_connections: + * @device: a #NMDevice + * + * Gets the #NMRemoteConnections currently known to the daemon that could + * be activated on @device. + * + * Returns: (element-type NMRemoteConnection): the #GPtrArray + * containing #NMRemoteConnections. This is the internal copy used by + * the connection, and must not be modified. + **/ +const GPtrArray * +nm_device_get_available_connections(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return nml_dbus_property_ao_get_objs_as_ptrarray( + &NM_DEVICE_GET_PRIVATE(device)->available_connections); +} + +static const char * +get_type_name(NMDevice *device) +{ + switch (nm_device_get_device_type(device)) { + case NM_DEVICE_TYPE_ETHERNET: + return _("Ethernet"); + case NM_DEVICE_TYPE_WIFI: + return _("Wi-Fi"); + case NM_DEVICE_TYPE_BT: + return _("Bluetooth"); + case NM_DEVICE_TYPE_OLPC_MESH: + return _("OLPC Mesh"); + case NM_DEVICE_TYPE_OVS_INTERFACE: + return _("Open vSwitch Interface"); + case NM_DEVICE_TYPE_OVS_PORT: + return _("Open vSwitch Port"); + case NM_DEVICE_TYPE_OVS_BRIDGE: + return _("Open vSwitch Bridge"); + case NM_DEVICE_TYPE_WIMAX: + return _("WiMAX"); + case NM_DEVICE_TYPE_MODEM: + return _("Mobile Broadband"); + case NM_DEVICE_TYPE_INFINIBAND: + return _("InfiniBand"); + case NM_DEVICE_TYPE_BOND: + return _("Bond"); + case NM_DEVICE_TYPE_TEAM: + return _("Team"); + case NM_DEVICE_TYPE_BRIDGE: + return _("Bridge"); + case NM_DEVICE_TYPE_VLAN: + return _("VLAN"); + case NM_DEVICE_TYPE_ADSL: + return _("ADSL"); + case NM_DEVICE_TYPE_MACVLAN: + return _("MACVLAN"); + case NM_DEVICE_TYPE_VXLAN: + return _("VXLAN"); + case NM_DEVICE_TYPE_IP_TUNNEL: + return _("IPTunnel"); + case NM_DEVICE_TYPE_TUN: + return _("Tun"); + case NM_DEVICE_TYPE_VETH: + return _("Veth"); + case NM_DEVICE_TYPE_MACSEC: + return _("MACsec"); + case NM_DEVICE_TYPE_DUMMY: + return _("Dummy"); + case NM_DEVICE_TYPE_PPP: + return _("PPP"); + case NM_DEVICE_TYPE_WPAN: + return _("IEEE 802.15.4"); + case NM_DEVICE_TYPE_6LOWPAN: + return _("6LoWPAN"); + case NM_DEVICE_TYPE_WIREGUARD: + return _("WireGuard"); + case NM_DEVICE_TYPE_WIFI_P2P: + return _("Wi-Fi P2P"); + case NM_DEVICE_TYPE_VRF: + return _("VRF"); + case NM_DEVICE_TYPE_GENERIC: + case NM_DEVICE_TYPE_UNUSED1: + case NM_DEVICE_TYPE_UNUSED2: + case NM_DEVICE_TYPE_UNKNOWN: + break; + } + return _("Unknown"); +} + +static char * +get_device_type_name_with_iface(NMDevice *device) +{ + const char *type_name = get_type_name(device); + + switch (nm_device_get_device_type(device)) { + case NM_DEVICE_TYPE_BOND: + case NM_DEVICE_TYPE_TEAM: + case NM_DEVICE_TYPE_BRIDGE: + case NM_DEVICE_TYPE_VLAN: + return g_strdup_printf("%s (%s)", type_name, nm_device_get_iface(device)); + default: + return g_strdup(type_name); + } +} + +static char * +get_device_generic_type_name_with_iface(NMDevice *device) +{ + switch (nm_device_get_device_type(device)) { + case NM_DEVICE_TYPE_ETHERNET: + case NM_DEVICE_TYPE_INFINIBAND: + return g_strdup(_("Wired")); + default: + return get_device_type_name_with_iface(device); + } +} + +static const char * +get_bus_name(NMDevice *device) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(device); + struct udev_device *udevice; + const char * ifname; + const char * bus; + + if (priv->bus_name) + goto out; + + if (!priv->udev) + return NULL; + + ifname = nm_device_get_iface(device); + if (!ifname) + return NULL; + + udevice = udev_device_new_from_subsystem_sysname(priv->udev, "net", ifname); + if (!udevice) { + udevice = udev_device_new_from_subsystem_sysname(priv->udev, "tty", ifname); + if (!udevice) + return NULL; + } + bus = udev_device_get_property_value(udevice, "ID_BUS"); + if (!g_strcmp0(bus, "pci")) + priv->bus_name = g_strdup(_("PCI")); + else if (!g_strcmp0(bus, "usb")) + priv->bus_name = g_strdup(_("USB")); + else { + /* Use "" instead of NULL so we can tell later that we've + * already tried. + */ + priv->bus_name = g_strdup(""); + } + udev_device_unref(udevice); + +out: + if (*priv->bus_name) + return priv->bus_name; + else + return NULL; +} + +static char * +_get_udev_property(NMDevice * device, + const char *enc_prop, /* ID_XXX_ENC */ + const char *db_prop) /* ID_XXX_FROM_DATABASE */ +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(device); + struct udev_device *udev_device; + struct udev_device *tmpdev; + const char * ifname; + guint32 count = 0; + char * enc_value = NULL; + char * db_value = NULL; + + if (!priv->udev) + return NULL; + + ifname = nm_device_get_iface(device); + if (!ifname) + return NULL; + + udev_device = udev_device_new_from_subsystem_sysname(priv->udev, "net", ifname); + if (!udev_device) { + udev_device = udev_device_new_from_subsystem_sysname(priv->udev, "tty", ifname); + if (!udev_device) + return NULL; + } + /* Walk up the chain of the device and its parents a few steps to grab + * vendor and device ID information off it. + */ + tmpdev = udev_device; + while ((count++ < 3) && tmpdev && !enc_value) { + if (!enc_value) + enc_value = + nm_udev_utils_property_decode_cp(udev_device_get_property_value(tmpdev, enc_prop)); + if (!db_value) + db_value = g_strdup(udev_device_get_property_value(tmpdev, db_prop)); + + tmpdev = udev_device_get_parent(tmpdev); + } + udev_device_unref(udev_device); + + /* Prefer the hwdata database value over what comes directly + * from the device. */ + if (db_value) { + g_free(enc_value); + return db_value; + } + + return enc_value; +} + +static char * +_get_udev_property_utf8safe(NMDevice * device, + const char *enc_prop, /* ID_XXX_ENC */ + const char *db_prop) /* ID_XXX_FROM_DATABASE */ +{ + return nm_utils_str_utf8safe_escape_take(_get_udev_property(device, enc_prop, db_prop), + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL); +} + +/** + * nm_device_get_product: + * @device: a #NMDevice + * + * Gets the product string of the #NMDevice. + * + * Returns: the product name of the device. This is the internal string used by the + * device, and must not be modified. + * + * The string is backslash escaped (C escaping) for invalid characters. The escaping + * can be reverted with g_strcompress(), however the result may not be valid UTF-8. + **/ +const char * +nm_device_get_product(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + if (!priv->product) { + priv->product = + _get_udev_property_utf8safe(device, "ID_MODEL_ENC", "ID_MODEL_FROM_DATABASE"); + + /* Sometimes ID_PRODUCT_FROM_DATABASE is used? */ + if (!priv->product) + priv->product = + _get_udev_property_utf8safe(device, "ID_MODEL_ENC", "ID_PRODUCT_FROM_DATABASE"); + + if (!priv->product) + priv->product = g_strdup(""); + } + + return priv->product; +} + +/** + * nm_device_get_vendor: + * @device: a #NMDevice + * + * Gets the vendor string of the #NMDevice. + * + * Returns: the vendor name of the device. This is the internal string used by the + * device, and must not be modified. + * + * The string is backslash escaped (C escaping) for invalid characters. The escaping + * can be reverted with g_strcompress(), however the result may not be valid UTF-8. + **/ +const char * +nm_device_get_vendor(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + + if (!priv->vendor) + priv->vendor = + _get_udev_property_utf8safe(device, "ID_VENDOR_ENC", "ID_VENDOR_FROM_DATABASE"); + + if (!priv->vendor) + priv->vendor = g_strdup(""); + + return priv->vendor; +} + +static void +ensure_description(NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(device); + GParamSpec * name_prop; + gs_free char * short_product = NULL; + + priv->short_vendor = nm_str_realloc(nm_utils_fixup_vendor_string(nm_device_get_vendor(device))); + + /* Grab device's preferred name, if any */ + name_prop = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(device)), "name"); + if (name_prop) { + g_object_get(device, "name", &priv->description, NULL); + if (priv->description && priv->description[0]) + return; + nm_clear_g_free(&priv->description); + } + + if (!priv->short_vendor) { + priv->description = g_strdup(nm_device_get_iface(device) ?: ""); + return; + } + + short_product = nm_utils_fixup_product_string(nm_device_get_product(device)); + if (short_product == NULL) + short_product = g_strdup(get_type_name(device)); + + /* Another quick hack; if all of the fixed up vendor string + * is found in product, ignore the vendor. + */ + { + gs_free char *pdown = g_ascii_strdown(short_product, -1); + gs_free char *vdown = g_ascii_strdown(priv->short_vendor, -1); + + if (!strstr(pdown, vdown)) + priv->description = g_strconcat(priv->short_vendor, " ", short_product, NULL); + else + priv->description = g_steal_pointer(&short_product); + } +} + +static const char * +get_short_vendor(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + + if (!priv->description) + ensure_description(device); + + return priv->short_vendor; +} + +/** + * nm_device_get_description: + * @device: an #NMDevice + * + * Gets a description of @device, based on its vendor and product names. + * + * Returns: a description of @device. If either the vendor or the + * product name is unknown, this returns the interface name. + */ +const char * +nm_device_get_description(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + + if (!priv->description) + ensure_description(device); + + return priv->description; +} + +static gboolean +find_duplicates(char **names, gboolean *duplicates, int num_devices) +{ + int i, j; + gboolean found_any = FALSE; + + memset(duplicates, 0, num_devices * sizeof(gboolean)); + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) + continue; + for (j = i + 1; j < num_devices; j++) { + if (duplicates[j]) + continue; + if (!strcmp(names[i], names[j])) + duplicates[i] = duplicates[j] = found_any = TRUE; + } + } + + return found_any; +} + +/** + * nm_device_disambiguate_names: + * @devices: (array length=num_devices): an array of #NMDevice + * @num_devices: length of @devices + * + * Generates a list of short-ish unique presentation names for the + * devices in @devices. + * + * Returns: (transfer full) (array zero-terminated=1): the device names + */ +char ** +nm_device_disambiguate_names(NMDevice **devices, int num_devices) +{ + char ** names; + gboolean *duplicates; + int i; + + names = g_new(char *, num_devices + 1); + duplicates = g_new(gboolean, num_devices); + + /* Generic device name */ + for (i = 0; i < num_devices; i++) + names[i] = get_device_generic_type_name_with_iface(devices[i]); + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* Try specific names (eg, "Ethernet" and "InfiniBand" rather + * than "Wired") + */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + g_free(names[i]); + names[i] = get_device_type_name_with_iface(devices[i]); + } + } + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* Try prefixing bus name (eg, "PCI Ethernet" vs "USB Ethernet") */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *bus = get_bus_name(devices[i]); + char * name; + + if (!bus) + continue; + + g_free(names[i]); + name = get_device_type_name_with_iface(devices[i]); + /* TRANSLATORS: the first %s is a bus name (eg, "USB") or + * product name, the second is a device type (eg, + * "Ethernet"). You can change this to something like + * "%2$s (%1$s)" if there's no grammatical way to combine + * the strings otherwise. + */ + names[i] = g_strdup_printf(C_("long device name", "%s %s"), bus, name); + g_free(name); + } + } + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* Try prefixing vendor name */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *vendor = get_short_vendor(devices[i]); + char * name; + + if (!vendor) + continue; + + g_free(names[i]); + name = get_device_type_name_with_iface(devices[i]); + names[i] = + g_strdup_printf(C_("long device name", "%s %s"), vendor, get_type_name(devices[i])); + g_free(name); + } + } + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* If dealing with Bluetooth devices, try to distinguish them by + * device name. + */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i] && NM_IS_DEVICE_BT(devices[i])) { + const char *devname = nm_device_bt_get_name(NM_DEVICE_BT(devices[i])); + char * name; + + if (!devname) + continue; + + g_free(names[i]); + name = get_device_type_name_with_iface(devices[i]); + names[i] = g_strdup_printf("%s (%s)", name, devname); + g_free(name); + } + } + if (!find_duplicates(names, duplicates, num_devices)) + goto done; + + /* We have multiple identical network cards, so we have to differentiate + * them by interface name. + */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *interface = nm_device_get_iface(devices[i]); + + if (!interface) + continue; + + g_free(names[i]); + names[i] = g_strdup_printf("%s (%s)", get_type_name(devices[i]), interface); + } + } + +done: + g_free(duplicates); + names[num_devices] = NULL; + return names; +} + +/** + * nm_device_get_physical_port_id: + * @device: a #NMDevice + * + * Gets the physical port ID of the #NMDevice. If non-%NULL, this is + * an opaque string that can be used to recognize when + * seemingly-unrelated #NMDevices are actually just different virtual + * ports on a single physical port. (Eg, NPAR / SR-IOV.) + * + * Returns: the physical port ID of the device, or %NULL if the port + * ID is unknown. This is the internal string used by the device and + * must not be modified. + **/ +const char * +nm_device_get_physical_port_id(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + return _nml_coerce_property_str_not_empty(NM_DEVICE_GET_PRIVATE(device)->physical_port_id); +} + +/** + * nm_device_get_mtu: + * @device: a #NMDevice + * + * Gets the MTU of the #NMDevice. + * + * Returns: the MTU of the device in bytes. + **/ +guint32 +nm_device_get_mtu(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), 0); + + return NM_DEVICE_GET_PRIVATE(device)->mtu; +} + +/** + * nm_device_get_metered: + * @device: a #NMDevice + * + * Gets the metered setting of a #NMDevice. + * + * Returns: the metered setting. + * + * Since: 1.2 + **/ +NMMetered +nm_device_get_metered(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), NM_METERED_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE(device)->metered; +} + +NM_BACKPORT_SYMBOL(libnm_1_0_6, NMMetered, nm_device_get_metered, (NMDevice * device), (device)); + +/** + * nm_device_get_lldp_neighbors: + * @device: a #NMDevice + * + * Gets the list of neighbors discovered through LLDP. + * + * Returns: (element-type NMLldpNeighbor) (transfer none): the #GPtrArray + * containing #NMLldpNeighbors. This is the internal copy used by the + * device and must not be modified. The library never modifies the returned + * array and thus it is safe for callers to reference and keep using it. + * + * Since: 1.2 + **/ +GPtrArray * +nm_device_get_lldp_neighbors(NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + + priv = NM_DEVICE_GET_PRIVATE(device); + if (!priv->lldp_neighbors) + priv->lldp_neighbors = + g_ptr_array_new_with_free_func((GDestroyNotify) nm_lldp_neighbor_unref); + return priv->lldp_neighbors; +} + +/** + * nm_device_is_real: + * @device: a #NMDevice + * + * Returns: %TRUE if the device exists, or %FALSE if it is a placeholder device + * that could be automatically created by NetworkManager if one of its + * #NMDevice:available-connections was activated. + * + * Since: 1.2 + **/ +gboolean +nm_device_is_real(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + return NM_DEVICE_GET_PRIVATE(device)->real; +} + +/** + * nm_device_is_software: + * @device: a #NMDevice + * + * Whether the device is a software device. + * + * Returns: %TRUE if @device is a software device, %FALSE if it is a hardware device. + **/ +gboolean +nm_device_is_software(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + return !!(NM_DEVICE_GET_PRIVATE(device)->capabilities & NM_DEVICE_CAP_IS_SOFTWARE); +} + +/** + * nm_device_reapply: + * @device: a #NMDevice + * @connection: (allow-none): the #NMConnection to replace the applied + * settings with or %NULL to reuse existing + * @version_id: zero or the expected version id of the applied connection. + * If specified and the version id mismatches, the call fails without + * modification. This allows to catch concurrent accesses. + * @flags: always set this to zero + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Attempts to update device with changes to the currently active connection + * made since it was last applied. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Since: 1.2 + * + * Deprecated: 1.22: Use nm_device_reapply_async() or GDBusConnection. + **/ +gboolean +nm_device_reapply(NMDevice * device, + NMConnection *connection, + guint64 version_id, + guint32 flags, + GCancellable *cancellable, + GError ** error) +{ + GVariant *arg_connection = NULL; + + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(!connection || NM_IS_CONNECTION(connection), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + if (connection) + arg_connection = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + if (!arg_connection) + arg_connection = nm_g_variant_singleton_aLsaLsvII(); + + return _nm_client_dbus_call_sync_void( + _nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Reapply", + g_variant_new("(@a{sa{sv}}tu)", arg_connection, version_id, flags), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_device_reapply_async: + * @device: a #NMDevice + * @connection: (allow-none): the #NMConnection to replace the applied + * settings with or %NULL to reuse existing + * @version_id: zero or the expected version id of the applied + * connection. If specified and the version id mismatches, the call + * fails without modification. This allows to catch concurrent + * accesses. + * @flags: always set this to zero + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the reapply operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins an attempt to update device with changes to the + * currently active connection made since it was last applied. + * + * Since: 1.2 + **/ +void +nm_device_reapply_async(NMDevice * device, + NMConnection * connection, + guint64 version_id, + guint32 flags, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVariant *arg_connection = NULL; + + g_return_if_fail(NM_IS_DEVICE(device)); + g_return_if_fail(!connection || NM_IS_CONNECTION(connection)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + if (connection) + arg_connection = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + if (!arg_connection) + arg_connection = nm_g_variant_singleton_aLsaLsvII(); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_reapply_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Reapply", + g_variant_new("(@a{sa{sv}}tu)", arg_connection, version_id, flags), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_device_reapply_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_reapply_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + * + * Since: 1.2 + **/ +gboolean +nm_device_reapply_finish(NMDevice *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_reapply_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_device_get_applied_connection: + * @device: a #NMDevice + * @flags: the flags argument. Currently, this value must always be zero. + * @version_id: (out) (allow-none): returns the current version id of + * the applied connection + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Fetch the currently applied connection on the device. + * + * Returns: (transfer full): a %NMConnection with the currently applied settings + * or %NULL on error. + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + * + * Since: 1.2 + * + * Deprecated: 1.22: Use nm_device_get_applied_connection_async() or GDBusConnection. + **/ +NMConnection * +nm_device_get_applied_connection(NMDevice * device, + guint32 flags, + guint64 * version_id, + GCancellable *cancellable, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *v_connection = NULL; + guint64 v_version_id; + NMConnection * connection; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(!error || !*error, NULL); + + ret = _nm_client_dbus_call_sync(_nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "GetAppliedConnection", + g_variant_new("(u)", flags), + G_VARIANT_TYPE("(a{sa{sv}}t)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sa{sv}}t)", &v_connection, &v_version_id); + + connection = _nm_simple_connection_new_from_dbus(v_connection, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT, + error); + if (!connection) + return NULL; + + NM_SET_OUT(version_id, v_version_id); + return connection; +} + +/** + * nm_device_get_applied_connection_async: + * @device: a #NMDevice + * @flags: the flags argument. Currently, this value must always be zero. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the reapply operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins and gets the currently applied connection. + * + * Since: 1.2 + **/ +void +nm_device_get_applied_connection_async(NMDevice * device, + guint32 flags, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_get_applied_connection_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "GetAppliedConnection", + g_variant_new("(u)", flags), + G_VARIANT_TYPE("(a{sa{sv}}t)"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_device_get_applied_connection_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @version_id: (out) (allow-none): the current version id of the applied + * connection. + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_get_applied_connection_async(). + * + * Returns: (transfer full): a currently applied %NMConnection or %NULL in case + * of error. + * + * The connection is as received from D-Bus and might not validate according + * to nm_connection_verify(). + * + * Since: 1.2 + **/ +NMConnection * +nm_device_get_applied_connection_finish(NMDevice * device, + GAsyncResult *result, + guint64 * version_id, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *v_connection = NULL; + guint64 v_version_id; + NMConnection * connection; + + g_return_val_if_fail(NM_IS_DEVICE(device), NULL); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_get_applied_connection_async), + NULL); + g_return_val_if_fail(!error || !*error, NULL); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sa{sv}}t)", &v_connection, &v_version_id); + + connection = _nm_simple_connection_new_from_dbus(v_connection, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT, + error); + if (!connection) + return NULL; + + NM_SET_OUT(version_id, v_version_id); + return connection; +} + +/*****************************************************************************/ + +/** + * nm_device_disconnect: + * @device: a #NMDevice + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Disconnects the device if currently connected, and prevents the device from + * automatically connecting to networks until the next manual network connection + * request. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Deprecated: 1.22: Use nm_device_disconnect_async() or GDBusConnection. + **/ +gboolean +nm_device_disconnect(NMDevice *device, GCancellable *cancellable, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Disconnect", + g_variant_new("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_device_disconnect_async: + * @device: a #NMDevice + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the disconnect operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins disconnecting the device if currently connected, and + * prevents the device from automatically connecting to networks until the next + * manual network connection request. + **/ +void +nm_device_disconnect_async(NMDevice * device, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_disconnect_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Disconnect", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_device_disconnect_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_disconnect_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + **/ +gboolean +nm_device_disconnect_finish(NMDevice *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_disconnect_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_device_delete: + * @device: a #NMDevice + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Deletes the software device. Hardware devices can't be deleted. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + * + * Deprecated: 1.22: Use nm_device_delete_async() or GDBusConnection. + **/ +gboolean +nm_device_delete(NMDevice *device, GCancellable *cancellable, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(device), + cancellable, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Delete", + g_variant_new("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_device_delete_async: + * @device: a #NMDevice + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when delete operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins deleting the software device. Hardware devices can't + * be deleted. + **/ +void +nm_device_delete_async(NMDevice * device, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_DEVICE(device)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(device), + device, + nm_device_delete_async, + cancellable, + callback, + user_data, + _nm_object_get_path(device), + NM_DBUS_INTERFACE_DEVICE, + "Delete", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_device_delete_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_delete_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + **/ +gboolean +nm_device_delete_finish(NMDevice *device, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_delete_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_device_connection_valid: + * @device: an #NMDevice to validate @connection against + * @connection: an #NMConnection to validate against @device + * + * Validates a given connection for a given #NMDevice object and returns + * whether the connection may be activated with the device. For example if + * @device is a Wi-Fi device that supports only WEP encryption, the connection + * will only be valid if it is a Wi-Fi connection which describes a WEP or open + * network, and will not be valid if it describes a WPA network, or if it is + * an Ethernet, Bluetooth, WWAN, etc connection that is incompatible with the + * device. + * + * Returns: %TRUE if the connection may be activated with this device, %FALSE + * if is incompatible with the device's capabilities and characteristics. + **/ +gboolean +nm_device_connection_valid(NMDevice *device, NMConnection *connection) +{ + return nm_device_connection_compatible(device, connection, NULL); +} + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *config_iface, *device_iface; + GError * local = NULL; + + if (!nm_connection_verify(connection, &local)) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection was not valid: %s"), + local->message); + g_error_free(local); + return FALSE; + } + + config_iface = nm_connection_get_interface_name(connection); + device_iface = nm_device_get_iface(device); + if (config_iface && g_strcmp0(config_iface, device_iface) != 0) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The interface names of the device and the connection didn't match.")); + return FALSE; + } + + return TRUE; +} + +/** + * nm_device_connection_compatible: + * @device: an #NMDevice to validate @connection against + * @connection: an #NMConnection to validate against @device + * @error: return location for a #GError, or %NULL + * + * Validates a given connection for a given #NMDevice object and returns + * whether the connection may be activated with the device. For example if + * @device is a Wi-Fi device that supports only WEP encryption, the connection + * will only be valid if it is a Wi-Fi connection which describes a WEP or open + * network, and will not be valid if it describes a WPA network, or if it is + * an Ethernet, Bluetooth, WWAN, etc connection that is incompatible with the + * device. + * + * This function does the same as nm_device_connection_valid(), i.e. checking + * compatibility of the given device and connection. But, in addition, it sets + * GError when FALSE is returned. + * + * Returns: %TRUE if the connection may be activated with this device, %FALSE + * if is incompatible with the device's capabilities and characteristics. + **/ +gboolean +nm_device_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + g_return_val_if_fail(NM_IS_CONNECTION(connection), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + return NM_DEVICE_GET_CLASS(device)->connection_compatible(device, connection, error); +} + +/** + * nm_device_filter_connections: + * @device: an #NMDevice to filter connections for + * @connections: (element-type NMConnection): an array of #NMConnections to filter + * + * Filters a given array of connections for a given #NMDevice object and returns + * connections which may be activated with the device. For example if @device + * is a Wi-Fi device that supports only WEP encryption, the returned array will + * contain any Wi-Fi connections in @connections that allow connection to + * unencrypted or WEP-enabled SSIDs. The returned array will not contain + * Ethernet, Bluetooth, Wi-Fi WPA connections, or any other connection that is + * incompatible with the device. To get the full list of connections see + * nm_client_get_connections(). + * + * Returns: (transfer full) (element-type NMConnection): an array of + * #NMConnections that could be activated with the given @device. The array + * should be freed with g_ptr_array_unref() when it is no longer required. + * + * WARNING: the transfer annotation for this function may not work correctly + * with bindings. See https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/305. + * You can filter the list yourself with nm_device_connection_valid(). + **/ +GPtrArray * +nm_device_filter_connections(NMDevice *device, const GPtrArray *connections) +{ + GPtrArray *filtered; + int i; + + filtered = g_ptr_array_new_with_free_func(g_object_unref); + for (i = 0; i < connections->len; i++) { + NMConnection *candidate = connections->pdata[i]; + + /* Connection applies to this device */ + if (nm_device_connection_valid(device, candidate)) + g_ptr_array_add(filtered, g_object_ref(candidate)); + } + + return filtered; +} + +/** + * nm_device_get_setting_type: + * @device: an #NMDevice + * + * Gets the (primary) #NMSetting subtype associated with connections + * that can be used on @device. + * + * Returns: @device's associated #NMSetting type + */ +GType +nm_device_get_setting_type(NMDevice *device) +{ + g_return_val_if_fail(NM_IS_DEVICE(device), G_TYPE_INVALID); + g_return_val_if_fail(NM_DEVICE_GET_CLASS(device)->get_setting_type != NULL, G_TYPE_INVALID); + + return NM_DEVICE_GET_CLASS(device)->get_setting_type(device); +} + +/*****************************************************************************/ + +static gboolean +NM_IS_LLDP_NEIGHBOR(const NMLldpNeighbor *self) +{ + nm_assert(!self || (self->refcount > 0 && self->attrs)); + return self && self->refcount > 0; +} + +/** + * nm_lldp_neighbor_new: + * + * Creates a new #NMLldpNeighbor object. + * + * Note that #NMLldpNeighbor has no public API for mutating + * an instance. Also, libnm will not internally mutate a + * once exposed object. They are guaranteed to be immutable. + * Since 1.32, ref-counting is thread-safe. + * + * This function is not useful, as there is no public API to + * actually modify the (empty) instance. + * + * Returns: (transfer full): the new #NMLldpNeighbor object. + * + * Since: 1.2 + **/ +NMLldpNeighbor * +nm_lldp_neighbor_new(void) +{ + NMLldpNeighbor *neigh; + + neigh = g_slice_new(NMLldpNeighbor); + *neigh = (NMLldpNeighbor){ + .refcount = 1, + .attrs = g_hash_table_new_full(nm_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref), + }; + + return neigh; +} + +static NMLldpNeighbor * +_nm_lldp_neighbor_dup(NMLldpNeighbor *neighbor) +{ + /* There is no public API for mutating a NMLldpNeighbor. Nor should + * we ever add one, because immutable types (or at least, sealable types) + * are great. + * + * As such, dup is merely taking another ref. */ + nm_lldp_neighbor_ref(neighbor); + return neighbor; +} + +/** + * nm_lldp_neighbor_ref: + * @neighbor: the #NMLldpNeighbor + * + * Increases the reference count of the object. + * + * Since 1.32, ref-counting of #NMLldpNeighbor is thread-safe. + * + * Since: 1.2 + **/ +void +nm_lldp_neighbor_ref(NMLldpNeighbor *neighbor) +{ + g_return_if_fail(NM_IS_LLDP_NEIGHBOR(neighbor)); + + g_atomic_int_inc(&neighbor->refcount); +} + +/** + * nm_lldp_neighbor_unref: + * @neighbor: the #NMLldpNeighbor + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since 1.32, ref-counting of #NMLldpNeighbor is thread-safe. + * + * Since: 1.2 + **/ +void +nm_lldp_neighbor_unref(NMLldpNeighbor *neighbor) +{ + g_return_if_fail(NM_IS_LLDP_NEIGHBOR(neighbor)); + + if (g_atomic_int_dec_and_test(&neighbor->refcount)) { + g_hash_table_unref(neighbor->attrs); + nm_g_slice_free(neighbor); + } +} + +/** + * nm_lldp_neighbor_get_attr_value: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * + * Gets the value (as a GVariant) of attribute with name @name on @neighbor + * + * Returns: (transfer none): the value or %NULL if the attribute with @name was + * not found. + * + * Since: 1.18 + **/ +GVariant * +nm_lldp_neighbor_get_attr_value(NMLldpNeighbor *neighbor, const char *name) +{ + g_return_val_if_fail(NM_IS_LLDP_NEIGHBOR(neighbor), FALSE); + g_return_val_if_fail(name && name[0], FALSE); + + return g_hash_table_lookup(neighbor->attrs, name); +} + +/** + * nm_lldp_neighbor_get_attr_names: + * @neighbor: the #NMLldpNeighbor + * + * Gets an array of attribute names available for @neighbor. + * + * Returns: (transfer full): a %NULL-terminated array of attribute names. + * + * Since: 1.2 + **/ +char ** +nm_lldp_neighbor_get_attr_names(NMLldpNeighbor *neighbor) +{ + const char **keys; + + g_return_val_if_fail(NM_IS_LLDP_NEIGHBOR(neighbor), NULL); + + keys = nm_utils_strdict_get_keys(neighbor->attrs, TRUE, NULL); + + return nm_utils_strv_make_deep_copied_nonnull(keys); +} + +/** + * nm_lldp_neighbor_get_attr_string_value: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * @out_value: (out) (allow-none) (transfer none): on return, the attribute value + * + * Gets the string value of attribute with name @name on @neighbor + * + * Returns: %TRUE if a string attribute with name @name was found, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_lldp_neighbor_get_attr_string_value(NMLldpNeighbor *neighbor, + const char * name, + const char ** out_value) +{ + GVariant *variant; + + variant = nm_lldp_neighbor_get_attr_value(neighbor, name); + if (!variant || !g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) + return FALSE; + + NM_SET_OUT(out_value, g_variant_get_string(variant, NULL)); + return TRUE; +} + +/** + * nm_lldp_neighbor_get_attr_uint_value: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * @out_value: (out) (allow-none): on return, the attribute value + * + * Gets the uint32 value of attribute with name @name on @neighbor + * + * Returns: %TRUE if a uint32 attribute with name @name was found, %FALSE otherwise + * + * Since: 1.2 + **/ +gboolean +nm_lldp_neighbor_get_attr_uint_value(NMLldpNeighbor *neighbor, const char *name, guint *out_value) +{ + GVariant *variant; + + variant = nm_lldp_neighbor_get_attr_value(neighbor, name); + if (!variant || !g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32)) + return FALSE; + + NM_SET_OUT(out_value, g_variant_get_uint32(variant)); + return TRUE; +} + +/** + * nm_lldp_neighbor_get_attr_type: + * @neighbor: the #NMLldpNeighbor + * @name: the attribute name + * + * Get the type of an attribute. + * + * Returns: the #GVariantType of the attribute with name @name + * + * Since: 1.2 + **/ +const GVariantType * +nm_lldp_neighbor_get_attr_type(NMLldpNeighbor *neighbor, const char *name) +{ + GVariant *variant; + + variant = nm_lldp_neighbor_get_attr_value(neighbor, name); + if (!variant) + return NULL; + + return g_variant_get_type(variant); +} diff --git a/src/libnm-client-impl/nm-dhcp-config.c b/src/libnm-client-impl/nm-dhcp-config.c new file mode 100644 index 00000000..c7f9a87a --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp-config.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2008 - 2014 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dhcp-config.h" + +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-dbus-interface.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpConfig, PROP_FAMILY, PROP_OPTIONS, ); + +typedef struct _NMDhcpConfigPrivate { + GHashTable *options; +} NMDhcpConfigPrivate; + +G_DEFINE_ABSTRACT_TYPE(NMDhcpConfig, nm_dhcp_config, NM_TYPE_OBJECT) + +#define NM_DHCP_CONFIG_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMDhcpConfig, NM_IS_DHCP_CONFIG, NMObject) + +/*****************************************************************************/ + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_options(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMDhcpConfig * self = NM_DHCP_CONFIG(dbobj->nmobj); + NMDhcpConfigPrivate *priv = NM_DHCP_CONFIG_GET_PRIVATE(self); + + g_hash_table_remove_all(priv->options); + + if (value) { + GVariantIter iter; + const char * key; + GVariant * opt; + + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "{&sv}", &key, &opt)) { + if (g_variant_is_of_type(opt, G_VARIANT_TYPE_STRING)) + g_hash_table_insert(priv->options, g_strdup(key), g_variant_dup_string(opt, NULL)); + g_variant_unref(opt); + } + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +nm_dhcp_config_init(NMDhcpConfig *self) +{ + NMDhcpConfigPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_DHCP_CONFIG, NMDhcpConfigPrivate); + + self->_priv = priv; + + priv->options = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); +} + +static void +finalize(GObject *object) +{ + NMDhcpConfigPrivate *priv = NM_DHCP_CONFIG_GET_PRIVATE(object); + + g_hash_table_destroy(priv->options); + + G_OBJECT_CLASS(nm_dhcp_config_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMDhcpConfig *self = NM_DHCP_CONFIG(object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int(value, nm_dhcp_config_get_family(self)); + break; + case PROP_OPTIONS: + g_value_set_boxed(value, nm_dhcp_config_get_options(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dhcp4config = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DHCP4_CONFIG, + nm_dhcp4_config_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("Options", + PROP_OPTIONS, + "a{sv}", + _notify_update_prop_options), ), + .base_struct_offset = G_STRUCT_OFFSET(NMDhcpConfig, _priv), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dhcp6config = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_DHCP6_CONFIG, + nm_dhcp6_config_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("Options", + PROP_OPTIONS, + "a{sv}", + _notify_update_prop_options), ), + .base_struct_offset = G_STRUCT_OFFSET(NMDhcpConfig, _priv), ); + +static void +nm_dhcp_config_class_init(NMDhcpConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(config_class); + + g_type_class_add_private(config_class, sizeof(NMDhcpConfigPrivate)); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMDhcpConfig:family: + * + * The IP address family of the configuration; either + * AF_INET or AF_INET6. + **/ + obj_properties[PROP_FAMILY] = g_param_spec_int(NM_DHCP_CONFIG_FAMILY, + "", + "", + 0, + 255, + AF_UNSPEC, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMDhcpConfig:options: (type GHashTable(utf8,utf8)): + * + * The #GHashTable containing options of the configuration. + **/ + obj_properties[PROP_OPTIONS] = g_param_spec_boxed(NM_DHCP_CONFIG_OPTIONS, + "", + "", + G_TYPE_HASH_TABLE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_dhcp4config, + &_nml_dbus_meta_iface_nm_dhcp6config); +} + +/** + * nm_dhcp_config_get_family: + * @config: a #NMDhcpConfig + * + * Gets the IP address family of the configuration + * + * Returns: the IP address family; either AF_INET or + * AF_INET6 + **/ +int +nm_dhcp_config_get_family(NMDhcpConfig *config) +{ + g_return_val_if_fail(NM_IS_DHCP_CONFIG(config), AF_UNSPEC); + + return NM_IS_DHCP4_CONFIG(config) ? AF_INET : AF_INET6; +} + +/** + * nm_dhcp_config_get_options: + * @config: a #NMDhcpConfig + * + * Gets all the options contained in the configuration. + * + * Returns: (transfer none) (element-type utf8 utf8): the #GHashTable containing + * strings for keys and values. This is the internal copy used by the + * configuration, and must not be modified. + **/ +GHashTable * +nm_dhcp_config_get_options(NMDhcpConfig *config) +{ + g_return_val_if_fail(NM_IS_DHCP_CONFIG(config), NULL); + + return NM_DHCP_CONFIG_GET_PRIVATE(config)->options; +} + +/** + * nm_dhcp_config_get_one_option: + * @config: a #NMDhcpConfig + * @option: the option to retrieve + * + * Gets one option by option name. + * + * Returns: the configuration option's value. This is the internal string used by the + * configuration, and must not be modified. + **/ +const char * +nm_dhcp_config_get_one_option(NMDhcpConfig *config, const char *option) +{ + g_return_val_if_fail(NM_IS_DHCP_CONFIG(config), NULL); + + return g_hash_table_lookup(nm_dhcp_config_get_options(config), option); +} diff --git a/src/libnm-client-impl/nm-dhcp4-config.c b/src/libnm-client-impl/nm-dhcp4-config.c new file mode 100644 index 00000000..8ce8415f --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp4-config.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dhcp4-config.h" + +/*****************************************************************************/ + +struct _NMDhcp4Config { + NMDhcpConfig parent; +}; + +struct _NMDhcp4ConfigClass { + NMDhcpConfigClass parent; +}; + +G_DEFINE_TYPE(NMDhcp4Config, nm_dhcp4_config, NM_TYPE_DHCP_CONFIG) + +/*****************************************************************************/ + +static void +nm_dhcp4_config_init(NMDhcp4Config *config) +{} + +static void +nm_dhcp4_config_class_init(NMDhcp4ConfigClass *config_class) +{} diff --git a/src/libnm-client-impl/nm-dhcp4-config.h b/src/libnm-client-impl/nm-dhcp4-config.h new file mode 100644 index 00000000..d0ea2b46 --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp4-config.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#ifndef __NM_DHCP4_CONFIG_H__ +#define __NM_DHCP4_CONFIG_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-dhcp-config.h" + +#define NM_TYPE_DHCP4_CONFIG (nm_dhcp4_config_get_type()) +#define NM_DHCP4_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP4_CONFIG, NMDhcp4Config)) +#define NM_DHCP4_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP4_CONFIG, NMDhcp4ConfigClass)) +#define NM_IS_DHCP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP4_CONFIG)) +#define NM_IS_DHCP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP4_CONFIG)) +#define NM_DHCP4_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP4_CONFIG, NMDhcp4ConfigClass)) + +/** + * NMDhcp4Config: + */ +typedef struct _NMDhcp4Config NMDhcp4Config; +typedef struct _NMDhcp4ConfigClass NMDhcp4ConfigClass; + +GType nm_dhcp4_config_get_type(void); + +#endif /* __NM_DHCP4_CONFIG_H__ */ diff --git a/src/libnm-client-impl/nm-dhcp6-config.c b/src/libnm-client-impl/nm-dhcp6-config.c new file mode 100644 index 00000000..7db02640 --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp6-config.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dhcp6-config.h" + +/*****************************************************************************/ + +struct _NMDhcp6Config { + NMDhcpConfig parent; +}; + +struct _NMDhcp6ConfigClass { + NMDhcpConfigClass parent; +}; + +G_DEFINE_TYPE(NMDhcp6Config, nm_dhcp6_config, NM_TYPE_DHCP_CONFIG) + +/*****************************************************************************/ + +static void +nm_dhcp6_config_init(NMDhcp6Config *config) +{} + +static void +nm_dhcp6_config_class_init(NMDhcp6ConfigClass *config_class) +{} diff --git a/src/libnm-client-impl/nm-dhcp6-config.h b/src/libnm-client-impl/nm-dhcp6-config.h new file mode 100644 index 00000000..253594e6 --- /dev/null +++ b/src/libnm-client-impl/nm-dhcp6-config.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#ifndef __NM_DHCP6_CONFIG_H__ +#define __NM_DHCP6_CONFIG_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-dhcp-config.h" + +#define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type()) +#define NM_DHCP6_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP6_CONFIG, NMDhcp6Config)) +#define NM_DHCP6_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP6_CONFIG, NMDhcp6ConfigClass)) +#define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP6_CONFIG)) +#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP6_CONFIG)) +#define NM_DHCP6_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP6_CONFIG, NMDhcp6ConfigClass)) + +/** + * NMDhcp6Config: + */ +typedef struct _NMDhcp6Config NMDhcp6Config; +typedef struct _NMDhcp6ConfigClass NMDhcp6ConfigClass; + +GType nm_dhcp6_config_get_type(void); + +#endif /* __NM_DHCP6_CONFIG_H__ */ diff --git a/src/libnm-client-impl/nm-dns-manager.c b/src/libnm-client-impl/nm-dns-manager.c new file mode 100644 index 00000000..f7c564ff --- /dev/null +++ b/src/libnm-client-impl/nm-dns-manager.c @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-dns-manager.h" + +#include "nm-dbus-interface.h" +#include "nm-connection.h" +#include "nm-client.h" +#include "nm-object-private.h" +#include "nm-dbus-helpers.h" +#include "libnm-core-intern/nm-core-internal.h" + +/***************************************************************************** + * NMDnsEntry + *****************************************************************************/ + +G_DEFINE_BOXED_TYPE(NMDnsEntry, nm_dns_entry, nm_dns_entry_dup, nm_dns_entry_unref) + +struct NMDnsEntry { + guint refcount; + + char * interface; + char ** nameservers; + char ** domains; + int priority; + gboolean vpn; +}; + +/** + * nm_dns_entry_new: + * + * Creates a new #NMDnsEntry object. + * + * Returns: (transfer full): the new #NMDnsEntry object, or %NULL on error + **/ +NMDnsEntry * +nm_dns_entry_new(const char * interface, + const char *const *nameservers, + const char *const *domains, + int priority, + gboolean vpn) +{ + NMDnsEntry *entry; + guint i, len; + + entry = g_slice_new0(NMDnsEntry); + entry->refcount = 1; + + entry->interface = g_strdup(interface); + + if (nameservers) { + len = g_strv_length((char **) nameservers); + entry->nameservers = g_new(char *, len + 1); + for (i = 0; i < len + 1; i++) + entry->nameservers[i] = g_strdup(nameservers[i]); + } + + if (domains) { + len = g_strv_length((char **) domains); + entry->domains = g_new(char *, len + 1); + for (i = 0; i < len + 1; i++) + entry->domains[i] = g_strdup(domains[i]); + } + + entry->priority = priority; + entry->vpn = vpn; + + return entry; +} + +/** + * nm_dns_entry_dup: + * @entry: the #NMDnsEntry + * + * Creates a copy of @entry + * + * Returns: (transfer full): a copy of @entry + **/ +NMDnsEntry * +nm_dns_entry_dup(NMDnsEntry *entry) +{ + NMDnsEntry *copy; + + g_return_val_if_fail(entry != NULL, NULL); + g_return_val_if_fail(entry->refcount > 0, NULL); + + copy = nm_dns_entry_new(entry->interface, + (const char *const *) entry->nameservers, + (const char *const *) entry->domains, + entry->priority, + entry->vpn); + + return copy; +} + +/** + * nm_dns_entry_unref: + * @entry: the #NMDnsEntry + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.6 + **/ +void +nm_dns_entry_unref(NMDnsEntry *entry) +{ + g_return_if_fail(entry != NULL); + g_return_if_fail(entry->refcount > 0); + + entry->refcount--; + if (entry->refcount == 0) { + g_free(entry->interface); + g_strfreev(entry->nameservers); + g_strfreev(entry->domains); + g_slice_free(NMDnsEntry, entry); + } +} + +/** + * nm_dns_entry_get_interface: + * @entry: the #NMDnsEntry + * + * Gets the interface on which name servers are contacted. + * + * Returns: (transfer none): the interface name + * + * Since: 1.6 + **/ +const char * +nm_dns_entry_get_interface(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return entry->interface; +} + +/** + * nm_dns_entry_get_nameservers: + * @entry: the #NMDnsEntry + * + * Gets the list of name servers for this entry. + * + * Returns: (transfer none): the list of name servers + * + * Since: 1.6 + **/ +const char *const * +nm_dns_entry_get_nameservers(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return (const char *const *) entry->nameservers; +} + +/** + * nm_dns_entry_get_domains: + * @entry: the #NMDnsEntry + * + * Gets the list of DNS domains. + * + * Returns: (transfer none): the list of DNS domains + * + * Since: 1.6 + **/ +const char *const * +nm_dns_entry_get_domains(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return (const char *const *) entry->domains; +} + +/** + * nm_dns_entry_get_vpn: + * @entry: the #NMDnsEntry + * + * Gets whether the entry refers to VPN name servers. + * + * Returns: %TRUE if the entry refers to VPN name servers + * + * Since: 1.6 + **/ +gboolean +nm_dns_entry_get_vpn(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return entry->vpn; +} + +/** + * nm_dns_entry_get_priority: + * @entry: the #NMDnsEntry + * + * Gets the priority of the entry + * + * Returns: the priority of the entry + * + * Since: 1.6 + **/ +int +nm_dns_entry_get_priority(NMDnsEntry *entry) +{ + g_return_val_if_fail(entry, 0); + g_return_val_if_fail(entry->refcount > 0, 0); + + return entry->priority; +} diff --git a/src/libnm-client-impl/nm-dns-manager.h b/src/libnm-client-impl/nm-dns-manager.h new file mode 100644 index 00000000..8053b243 --- /dev/null +++ b/src/libnm-client-impl/nm-dns-manager.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#ifndef __NM_DNS_MANAGER_H__ +#define __NM_DNS_MANAGER_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-client.h" + +NMDnsEntry *nm_dns_entry_new(const char * interface, + const char *const *nameservers, + const char *const *domains, + int priority, + gboolean vpn); +NMDnsEntry *nm_dns_entry_dup(NMDnsEntry *entry); + +#endif /* __NM_DNS_MANAGER_H__ */ diff --git a/src/libnm-client-impl/nm-ip-config.c b/src/libnm-client-impl/nm-ip-config.c new file mode 100644 index 00000000..099dd088 --- /dev/null +++ b/src/libnm-client-impl/nm-ip-config.c @@ -0,0 +1,644 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2011 Novell, Inc. + * Copyright (C) 2008 - 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-ip-config.h" + +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-setting-ip-config.h" +#include "nm-dbus-interface.h" +#include "nm-object-private.h" +#include "nm-utils.h" +#include "libnm-core-intern/nm-core-internal.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMIPConfig, + PROP_FAMILY, + PROP_GATEWAY, + PROP_ADDRESSES, + PROP_ROUTES, + PROP_NAMESERVERS, + PROP_DOMAINS, + PROP_SEARCHES, + PROP_WINS_SERVERS, ); + +typedef struct _NMIPConfigPrivate { + GPtrArray *addresses; + GPtrArray *routes; + char ** nameservers; + char ** domains; + char ** searches; + char ** wins_servers; + char * gateway; + + bool addresses_new_style : 1; + bool routes_new_style : 1; + bool nameservers_new_style : 1; + bool wins_servers_new_style : 1; +} NMIPConfigPrivate; + +G_DEFINE_ABSTRACT_TYPE(NMIPConfig, nm_ip_config, NM_TYPE_OBJECT) + +#define NM_IP_CONFIG_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMIPConfig, NM_IS_IP_CONFIG, NMObject) + +/*****************************************************************************/ + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_addresses(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMIPConfig * self = NM_IP_CONFIG(dbobj->nmobj); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gs_unref_ptrarray GPtrArray *addresses_old = NULL; + gs_unref_ptrarray GPtrArray *addresses_new = NULL; + int addr_family = meta_iface == &_nml_dbus_meta_iface_nm_ip4config ? AF_INET : AF_INET6; + gboolean new_style; + + new_style = + (((const char *) meta_iface->dbus_properties[dbus_property_idx].dbus_type)[2] == '{'); + + if (priv->addresses_new_style) { + if (!new_style) + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } else + priv->addresses_new_style = new_style; + + if (value) { + if (new_style) + addresses_new = nm_utils_ip_addresses_from_variant(value, addr_family); + else if (addr_family == AF_INET) + addresses_new = nm_utils_ip4_addresses_from_variant(value, NULL); + else + addresses_new = nm_utils_ip6_addresses_from_variant(value, NULL); + nm_assert(addresses_new); + } + if (!addresses_new) + addresses_new = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); + + addresses_old = priv->addresses; + priv->addresses = g_steal_pointer(&addresses_new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_routes(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMIPConfig * self = NM_IP_CONFIG(dbobj->nmobj); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gs_unref_ptrarray GPtrArray *routes_old = NULL; + gs_unref_ptrarray GPtrArray *routes_new = NULL; + int addr_family = meta_iface == &_nml_dbus_meta_iface_nm_ip4config ? AF_INET : AF_INET6; + gboolean new_style; + + new_style = + (((const char *) meta_iface->dbus_properties[dbus_property_idx].dbus_type)[2] == '{'); + + if (priv->routes_new_style) { + if (!new_style) + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } else + priv->routes_new_style = new_style; + + if (value) { + if (new_style) + routes_new = nm_utils_ip_routes_from_variant(value, addr_family); + else if (addr_family == AF_INET) + routes_new = nm_utils_ip4_routes_from_variant(value); + else + routes_new = nm_utils_ip6_routes_from_variant(value); + nm_assert(routes_new); + } + if (!routes_new) + routes_new = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); + + routes_old = priv->routes; + priv->routes = g_steal_pointer(&routes_new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_nameservers(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMIPConfig * self = NM_IP_CONFIG(dbobj->nmobj); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gs_strfreev char **nameservers_new = NULL; + gboolean new_style = TRUE; + int addr_family = meta_iface == &_nml_dbus_meta_iface_nm_ip4config ? AF_INET : AF_INET6; + + if (addr_family == AF_INET) { + new_style = + (((const char *) meta_iface->dbus_properties[dbus_property_idx].dbus_type)[1] == 'a'); + + if (priv->nameservers_new_style) { + if (!new_style) + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } else + priv->nameservers_new_style = new_style; + } + + if (value) { + if (addr_family == AF_INET6) + nameservers_new = nm_utils_ip6_dns_from_variant(value); + else if (!new_style) + nameservers_new = nm_utils_ip4_dns_from_variant(value); + else { + GVariantIter iter; + GVariantIter * iter_v; + gs_unref_ptrarray GPtrArray *arr = NULL; + + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "a{sv}", &iter_v)) { + const char *key; + GVariant * val; + + while (g_variant_iter_next(iter_v, "{&sv}", &key, &val)) { + if (nm_streq(key, "address")) { + gs_free char *val_str = NULL; + + if (!g_variant_is_of_type(val, G_VARIANT_TYPE_STRING)) + goto next; + if (!nm_utils_parse_inaddr(AF_INET, + g_variant_get_string(val, NULL), + &val_str)) + goto next; + if (!arr) + arr = g_ptr_array_new(); + g_ptr_array_add(arr, g_steal_pointer(&val_str)); + goto next; + } +next: + g_variant_unref(val); + } + g_variant_iter_free(iter_v); + } + if (arr && arr->len > 0) + nameservers_new = nm_utils_strv_dup((char **) arr->pdata, arr->len, FALSE); + else + nameservers_new = g_new0(char *, 1); + } + nm_assert(nameservers_new); + } + + g_strfreev(priv->nameservers); + priv->nameservers = g_steal_pointer(&nameservers_new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_wins_servers(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value) +{ + NMIPConfig * self = NM_IP_CONFIG(dbobj->nmobj); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gs_strfreev char **wins_servers_new = NULL; + gboolean new_style; + + new_style = + (((const char *) meta_iface->dbus_properties[dbus_property_idx].dbus_type)[1] == 's'); + + if (priv->wins_servers_new_style) { + if (!new_style) + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE; + } else + priv->wins_servers_new_style = new_style; + + if (value) { + if (new_style) + wins_servers_new = g_variant_dup_strv(value, NULL); + else + wins_servers_new = nm_utils_ip4_dns_from_variant(value); + nm_assert(wins_servers_new); + } + + g_strfreev(priv->wins_servers); + priv->wins_servers = g_steal_pointer(&wins_servers_new); + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMIPConfig *self = NM_IP_CONFIG(object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int(value, nm_ip_config_get_family(self)); + break; + case PROP_GATEWAY: + g_value_set_string(value, nm_ip_config_get_gateway(self)); + break; + case PROP_ADDRESSES: + g_value_take_boxed(value, + _nm_utils_copy_array(nm_ip_config_get_addresses(self), + (NMUtilsCopyFunc) nm_ip_address_dup, + (GDestroyNotify) nm_ip_address_unref)); + break; + case PROP_ROUTES: + g_value_take_boxed(value, + _nm_utils_copy_array(nm_ip_config_get_routes(self), + (NMUtilsCopyFunc) nm_ip_route_dup, + (GDestroyNotify) nm_ip_route_unref)); + break; + case PROP_NAMESERVERS: + g_value_set_boxed(value, (char **) nm_ip_config_get_nameservers(self)); + break; + case PROP_DOMAINS: + g_value_set_boxed(value, (char **) nm_ip_config_get_domains(self)); + break; + case PROP_SEARCHES: + g_value_set_boxed(value, (char **) nm_ip_config_get_searches(self)); + break; + case PROP_WINS_SERVERS: + g_value_set_boxed(value, (char **) nm_ip_config_get_wins_servers(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_ip_config_init(NMIPConfig *self) +{ + NMIPConfigPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_IP_CONFIG, NMIPConfigPrivate); + + self->_priv = priv; + + priv->addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); + priv->routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); +} + +static void +finalize(GObject *object) +{ + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(object); + + g_free(priv->gateway); + + g_ptr_array_unref(priv->routes); + g_ptr_array_unref(priv->addresses); + + g_strfreev(priv->nameservers); + g_strfreev(priv->domains); + g_strfreev(priv->searches); + g_strfreev(priv->wins_servers); + + G_OBJECT_CLASS(nm_ip_config_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_ip4config = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_IP4_CONFIG, + nm_ip4_config_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("AddressData", + PROP_ADDRESSES, + "aa{sv}", + _notify_update_prop_addresses), + NML_DBUS_META_PROPERTY_INIT_FCN("Addresses", + PROP_ADDRESSES, + "aau", + _notify_update_prop_addresses, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_TODO("DnsOptions", "as"), + NML_DBUS_META_PROPERTY_INIT_TODO("DnsPriority", "i"), + NML_DBUS_META_PROPERTY_INIT_AS("Domains", PROP_DOMAINS, NMIPConfigPrivate, domains), + NML_DBUS_META_PROPERTY_INIT_S("Gateway", PROP_GATEWAY, NMIPConfigPrivate, gateway), + NML_DBUS_META_PROPERTY_INIT_FCN("NameserverData", + PROP_NAMESERVERS, + "aa{sv}", + _notify_update_prop_nameservers), + NML_DBUS_META_PROPERTY_INIT_FCN("Nameservers", + PROP_NAMESERVERS, + "au", + _notify_update_prop_nameservers, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_FCN("RouteData", + PROP_ROUTES, + "aa{sv}", + _notify_update_prop_routes), + NML_DBUS_META_PROPERTY_INIT_FCN("Routes", + PROP_ROUTES, + "aau", + _notify_update_prop_routes, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_AS("Searches", PROP_SEARCHES, NMIPConfigPrivate, searches), + NML_DBUS_META_PROPERTY_INIT_FCN("WinsServerData", + PROP_WINS_SERVERS, + "as", + _notify_update_prop_wins_servers), + NML_DBUS_META_PROPERTY_INIT_FCN("WinsServers", + PROP_WINS_SERVERS, + "au", + _notify_update_prop_wins_servers, + .obj_property_no_reverse_idx = TRUE), ), + .base_struct_offset = G_STRUCT_OFFSET(NMIPConfig, _priv), ); + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_ip6config = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_IP6_CONFIG, + nm_ip6_config_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_FCN("AddressData", + PROP_ADDRESSES, + "aa{sv}", + _notify_update_prop_addresses), + NML_DBUS_META_PROPERTY_INIT_FCN("Addresses", + PROP_ADDRESSES, + "a(ayuay)", + _notify_update_prop_addresses, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_TODO("DnsOptions", "as"), + NML_DBUS_META_PROPERTY_INIT_TODO("DnsPriority", "i"), + NML_DBUS_META_PROPERTY_INIT_AS("Domains", PROP_DOMAINS, NMIPConfigPrivate, domains), + NML_DBUS_META_PROPERTY_INIT_S("Gateway", PROP_GATEWAY, NMIPConfigPrivate, gateway), + NML_DBUS_META_PROPERTY_INIT_FCN("Nameservers", + PROP_NAMESERVERS, + "aay", + _notify_update_prop_nameservers), + NML_DBUS_META_PROPERTY_INIT_FCN("RouteData", + PROP_ROUTES, + "aa{sv}", + _notify_update_prop_routes), + NML_DBUS_META_PROPERTY_INIT_FCN("Routes", + PROP_ROUTES, + "a(ayuayu)", + _notify_update_prop_routes, + .obj_property_no_reverse_idx = TRUE), + NML_DBUS_META_PROPERTY_INIT_AS("Searches", PROP_SEARCHES, NMIPConfigPrivate, searches), ), + .base_struct_offset = G_STRUCT_OFFSET(NMIPConfig, _priv), ); + +static void +nm_ip_config_class_init(NMIPConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(config_class); + + g_type_class_add_private(config_class, sizeof(NMIPConfigPrivate)); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMIPConfig:family: + * + * The IP address family of the configuration; either + * AF_INET or AF_INET6. + **/ + obj_properties[PROP_FAMILY] = g_param_spec_int(NM_IP_CONFIG_FAMILY, + "", + "", + 0, + 255, + AF_UNSPEC, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:gateway: + * + * The IP gateway address of the configuration as string. + **/ + obj_properties[PROP_GATEWAY] = g_param_spec_string(NM_IP_CONFIG_GATEWAY, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:addresses: + * + * A #GPtrArray containing the addresses (#NMIPAddress) of the configuration. + **/ + obj_properties[PROP_ADDRESSES] = g_param_spec_boxed(NM_IP_CONFIG_ADDRESSES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:routes: (type GPtrArray(NMIPRoute)) + * + * A #GPtrArray containing the routes (#NMIPRoute) of the configuration. + **/ + obj_properties[PROP_ROUTES] = g_param_spec_boxed(NM_IP_CONFIG_ROUTES, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:nameservers: + * + * The array containing name server IP addresses of the configuration. + **/ + obj_properties[PROP_NAMESERVERS] = + g_param_spec_boxed(NM_IP_CONFIG_NAMESERVERS, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:domains: + * + * The array containing domain strings of the configuration. + **/ + obj_properties[PROP_DOMAINS] = g_param_spec_boxed(NM_IP_CONFIG_DOMAINS, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:searches: + * + * The array containing DNS search strings of the configuration. + **/ + obj_properties[PROP_SEARCHES] = g_param_spec_boxed(NM_IP_CONFIG_SEARCHES, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMIPConfig:wins-servers: + * + * The array containing WINS server IP addresses of the configuration. + * (This will always be empty for IPv6 configurations.) + **/ + obj_properties[PROP_WINS_SERVERS] = + g_param_spec_boxed(NM_IP_CONFIG_WINS_SERVERS, + "", + "", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config); +} + +/** + * nm_ip_config_get_family: + * @config: a #NMIPConfig + * + * Gets the IP address family + * + * Returns: the IP address family; either AF_INET or + * AF_INET6 + **/ +int +nm_ip_config_get_family(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), AF_UNSPEC); + + return NM_IS_IP4_CONFIG(config) ? AF_INET : AF_INET6; +} + +/** + * nm_ip_config_get_gateway: + * @config: a #NMIPConfig + * + * Gets the IP gateway address. + * + * Returns: (transfer none): the IP address of the gateway. + **/ +const char * +nm_ip_config_get_gateway(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_str_not_empty(NM_IP_CONFIG_GET_PRIVATE(config)->gateway); +} + +/** + * nm_ip_config_get_addresses: + * @config: a #NMIPConfig + * + * Gets the IP addresses (containing the address, prefix, and gateway). + * + * Returns: (element-type NMIPAddress) (transfer none): the #GPtrArray + * containing #NMIPAddresses. This is the internal copy used by the + * configuration and must not be modified. The library never modifies the + * returned array and thus it is safe for callers to reference and keep using it. + **/ +GPtrArray * +nm_ip_config_get_addresses(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return NM_IP_CONFIG_GET_PRIVATE(config)->addresses; +} + +/** + * nm_ip_config_get_nameservers: + * @config: a #NMIPConfig + * + * Gets the domain name servers (DNS). + * + * Returns: (transfer none): the array of nameserver IP addresses + **/ +const char *const * +nm_ip_config_get_nameservers(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_strv_not_null(NM_IP_CONFIG_GET_PRIVATE(config)->nameservers); +} + +/** + * nm_ip_config_get_domains: + * @config: a #NMIPConfig + * + * Gets the domain names. + * + * Returns: (transfer none): the array of domains. + * (This is never %NULL, though it may be 0-length). + **/ +const char *const * +nm_ip_config_get_domains(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_strv_not_null(NM_IP_CONFIG_GET_PRIVATE(config)->domains); +} + +/** + * nm_ip_config_get_searches: + * @config: a #NMIPConfig + * + * Gets the DNS searches. + * + * Returns: (transfer none): the array of DNS search strings. + * (This is never %NULL, though it may be 0-length). + **/ +const char *const * +nm_ip_config_get_searches(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_strv_not_null(NM_IP_CONFIG_GET_PRIVATE(config)->searches); +} + +/** + * nm_ip_config_get_wins_servers: + * @config: a #NMIPConfig + * + * Gets the Windows Internet Name Service servers (WINS). + * + * Returns: (transfer none): the arry of WINS server IP address strings. + * (This is never %NULL, though it may be 0-length.) + **/ +const char *const * +nm_ip_config_get_wins_servers(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return _nml_coerce_property_strv_not_null(NM_IP_CONFIG_GET_PRIVATE(config)->wins_servers); +} + +/** + * nm_ip_config_get_routes: + * @config: a #NMIPConfig + * + * Gets the routes. + * + * Returns: (element-type NMIPRoute) (transfer none): the #GPtrArray containing + * #NMIPRoutes. This is the internal copy used by the configuration, and must + * not be modified. The library never modifies the returned array and thus it is + * safe for callers to reference and keep using it. + * + **/ +GPtrArray * +nm_ip_config_get_routes(NMIPConfig *config) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(config), NULL); + + return NM_IP_CONFIG_GET_PRIVATE(config)->routes; +} diff --git a/src/libnm-client-impl/nm-ip4-config.c b/src/libnm-client-impl/nm-ip4-config.c new file mode 100644 index 00000000..0f2fdb04 --- /dev/null +++ b/src/libnm-client-impl/nm-ip4-config.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-ip4-config.h" + +/*****************************************************************************/ + +struct _NMIP4Config { + NMIPConfig parent; +}; + +struct _NMIP4ConfigClass { + NMIPConfigClass parent; +}; + +G_DEFINE_TYPE(NMIP4Config, nm_ip4_config, NM_TYPE_IP_CONFIG) + +/*****************************************************************************/ + +static void +nm_ip4_config_init(NMIP4Config *config) +{} + +static void +nm_ip4_config_class_init(NMIP4ConfigClass *config_class) +{} diff --git a/src/libnm-client-impl/nm-ip4-config.h b/src/libnm-client-impl/nm-ip4-config.h new file mode 100644 index 00000000..b9297dc0 --- /dev/null +++ b/src/libnm-client-impl/nm-ip4-config.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef __NM_IP4_CONFIG_H__ +#define __NM_IP4_CONFIG_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-ip-config.h" + +#define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type()) +#define NM_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP4_CONFIG, NMIP4Config)) +#define NM_IP4_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) +#define NM_IS_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_IP4_CONFIG)) +#define NM_IS_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_IP4_CONFIG)) +#define NM_IP4_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) + +/** + * NMIP4Config: + */ +typedef struct _NMIP4Config NMIP4Config; +typedef struct _NMIP4ConfigClass NMIP4ConfigClass; + +GType nm_ip4_config_get_type(void); + +#endif /* __NM_IP4_CONFIG_H__ */ diff --git a/src/libnm-client-impl/nm-ip6-config.c b/src/libnm-client-impl/nm-ip6-config.c new file mode 100644 index 00000000..78e0b7dc --- /dev/null +++ b/src/libnm-client-impl/nm-ip6-config.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-ip6-config.h" + +/*****************************************************************************/ + +struct _NMIP6Config { + NMIPConfig parent; +}; + +struct _NMIP6ConfigClass { + NMIPConfigClass parent; +}; + +G_DEFINE_TYPE(NMIP6Config, nm_ip6_config, NM_TYPE_IP_CONFIG) + +/*****************************************************************************/ + +static void +nm_ip6_config_init(NMIP6Config *config) +{} + +static void +nm_ip6_config_class_init(NMIP6ConfigClass *config_class) +{} diff --git a/src/libnm-client-impl/nm-ip6-config.h b/src/libnm-client-impl/nm-ip6-config.h new file mode 100644 index 00000000..0c7c2a9f --- /dev/null +++ b/src/libnm-client-impl/nm-ip6-config.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 - 2014 Red Hat, Inc. + */ + +#ifndef __NM_IP6_CONFIG_H__ +#define __NM_IP6_CONFIG_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-ip-config.h" + +#define NM_TYPE_IP6_CONFIG (nm_ip6_config_get_type()) +#define NM_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP6_CONFIG, NMIP6Config)) +#define NM_IP6_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) +#define NM_IS_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_IP6_CONFIG)) +#define NM_IS_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_IP6_CONFIG)) +#define NM_IP6_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) + +/** + * NMIP6Config: + */ +typedef struct _NMIP6Config NMIP6Config; +typedef struct _NMIP6ConfigClass NMIP6ConfigClass; + +GType nm_ip6_config_get_type(void); + +#endif /* __NM_IP6_CONFIG_H__ */ diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c new file mode 100644 index 00000000..3cc88ef4 --- /dev/null +++ b/src/libnm-client-impl/nm-libnm-utils.c @@ -0,0 +1,913 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-libnm-utils.h" + +#include "libnm-glib-aux/nm-time-utils.h" +#include "libnm-core-aux-intern/nm-common-macros.h" +#include "nm-object.h" + +/*****************************************************************************/ + +volatile int _nml_dbus_log_level = 0; + +int +_nml_dbus_log_level_init(void) +{ + const GDebugKey keys[] = { + {"trace", _NML_DBUS_LOG_LEVEL_TRACE}, + {"debug", _NML_DBUS_LOG_LEVEL_DEBUG}, + {"warning", _NML_DBUS_LOG_LEVEL_WARN}, + {"error", _NML_DBUS_LOG_LEVEL_ERROR}, + {"stdout", NML_DBUS_LOG_STDOUT}, + }; + int l; + + l = _NML_DBUS_LOG_LEVEL_INITIALIZED + | nm_utils_parse_debug_string(g_getenv("LIBNM_CLIENT_DEBUG"), keys, G_N_ELEMENTS(keys)); + + if (!g_atomic_int_compare_and_exchange(&_nml_dbus_log_level, 0, l)) + l = g_atomic_int_get(&_nml_dbus_log_level); + + nm_assert(l & _NML_DBUS_LOG_LEVEL_INITIALIZED); + return l; +} + +void +_nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...) +{ + NMLDBusLogLevel configured_log_level; + gs_free char * msg = NULL; + va_list args; + const char * prefix = ""; + gint64 ts; + pid_t pid; + + /* we only call _nml_dbus_log() after nml_dbus_log_enabled(), which already does + * an atomic access to the variable. Since the value is only initialized once and + * never changes, we can just access it without additional locking. */ + configured_log_level = _nml_dbus_log_level; + + nm_assert(level & configured_log_level); + + va_start(args, fmt); + msg = g_strdup_vprintf(fmt, args); + va_end(args); + + switch (level) { + case NML_DBUS_LOG_LEVEL_TRACE: + prefix = " "; + break; + case NML_DBUS_LOG_LEVEL_DEBUG: + prefix = " "; + break; + case NML_DBUS_LOG_LEVEL_WARN: + prefix = " "; + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_WARN)) { + g_warning("libnm-dbus: %s%s", prefix, msg); + return; + } + break; + case NML_DBUS_LOG_LEVEL_ERROR: + prefix = " "; + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_ERROR)) { + g_critical("libnm-dbus: %s%s", prefix, msg); + return; + } + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_WARN)) { + g_warning("libnm-dbus: %s%s", prefix, msg); + return; + } + break; + default: + break; + } + + ts = nm_utils_clock_gettime_nsec(CLOCK_BOOTTIME); + + pid = getpid(); + + if (use_stdout) { + g_print("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", + (long long) pid, + prefix, + ts / NM_UTILS_NSEC_PER_SEC, + (ts / (NM_UTILS_NSEC_PER_SEC / 10000)) % 10000, + msg); + } else { + g_printerr("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", + (long long) pid, + prefix, + ts / NM_UTILS_NSEC_PER_SEC, + (ts / (NM_UTILS_NSEC_PER_SEC / 10000)) % 10000, + msg); + } +} + +/*****************************************************************************/ + +/* Stolen from dbus-glib */ +char * +nm_utils_wincaps_to_dash(const char *caps) +{ + const char *p; + GString * str; + + str = g_string_new(NULL); + p = caps; + while (*p) { + if (g_ascii_isupper(*p)) { + if (str->len > 0 && (str->len < 2 || str->str[str->len - 2] != '-')) + g_string_append_c(str, '-'); + g_string_append_c(str, g_ascii_tolower(*p)); + } else + g_string_append_c(str, *p); + ++p; + } + + return g_string_free(str, FALSE); +} + +/*****************************************************************************/ + +static char * +_fixup_string(const char * desc, + const char *const *ignored_phrases, + const char *const *ignored_words, + gboolean square_brackets_sensible) +{ + char * desc_full; + gboolean in_paren = FALSE; + char * p, *q; + int i; + + if (!desc || !desc[0]) + return NULL; + + /* restore original non-UTF-8-safe text. */ + desc_full = nm_utils_str_utf8safe_unescape_cp(desc, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); + + /* replace all invalid UTF-8 bytes with space. */ + p = desc_full; + while (!g_utf8_validate(p, -1, (const char **) &q)) { + /* the byte is invalid UTF-8. Replace it with space and proceed. */ + *q = ' '; + p = q + 1; + } + + /* replace '_', ',', ASCII control characters and parentheses, with space. */ + for (p = desc_full; p[0]; p++) { + if (*p == '(') + in_paren = TRUE; + if (NM_IN_SET(*p, '_', ',') || *p < ' ' || in_paren) + *p = ' '; + if (*p == ')') + in_paren = FALSE; + } + + /* Attempt to shorten ID by ignoring certain phrases */ + for (i = 0; ignored_phrases[i]; i++) { + p = strstr(desc_full, ignored_phrases[i]); + if (p) { + const char *eow = &p[strlen(ignored_phrases[i])]; + + /* require that the phrase is delimited by space, or + * at the beginning or end of the description. */ + if ((p == desc_full || p[-1] == ' ') && NM_IN_SET(eow[0], '\0', ' ')) + memmove(p, eow, strlen(eow) + 1); /* +1 for the \0 */ + } + } + + /* Attempt to shorten ID by ignoring certain individual words. + * - word-split the description at spaces + * - coalesce multiple spaces + * - skip over ignored_words */ + p = desc_full; + q = desc_full; + for (;;) { + char *eow; + gsize l; + + /* skip leading spaces. */ + while (p[0] == ' ') + p++; + + if (!p[0]) + break; + + /* split leading word on first space */ + eow = strchr(p, ' '); + if (eow) + *eow = '\0'; + + if (nm_utils_strv_find_first((char **) ignored_words, -1, p) >= 0) + goto next; + + l = strlen(p); + if (q != p) { + if (q != desc_full) + *q++ = ' '; + memmove(q, p, l); + } + q += l; + +next: + if (!eow) + break; + p = eow + 1; + } + + *q++ = '\0'; + + p = strchr(desc_full, '['); + if (p == desc_full) { + /* All we're left with is in square brackets. + * Always prefer that to a blank string.*/ + square_brackets_sensible = TRUE; + } + if (square_brackets_sensible) { + /* If there's a [] that survived the substitution, then the string + * is a short form that is generally preferable. */ + q = strchr(desc_full, ']'); + if (p && q > p) { + p++; + memmove(desc_full, p, q - p); + desc_full[q - p] = '\0'; + } + } else { + /* [] sometimes contains the preferred human-readable name, but + * mostly it's utterly useless. Sigh. Drop it. */ + if (p) { + if (p > desc_full && p[-1] == ' ') + p--; + *p = '\0'; + } + } + + if (!desc_full[0]) { + g_free(desc_full); + return NULL; + } + + return desc_full; +} + +char * +nm_utils_fixup_vendor_string(const char *desc) +{ + static const char *const IGNORED_PHRASES[] = { + "Access Systems", + "Business Mobile Networks BV", + "Communications & Multimedia", + "Company of Japan", + "Computer Co.", + "Computer Corp.", + "Computer Corporation", + "Computer Inc.", + "Computer, Inc.", + "Information and Communication Products", + "Macao Commercial Offshore", + "Mobile Phones", + "(M) Son", + "Multimedia Internet Technology", + "Technology Group Ltd.", + "Wireless Networks", + "Wireless Solutions", + NULL, + }; + static const char *const IGNORED_WORDS[] = { + "AB", + "AG", + "A/S", + "ASA", + "B.V.", + "Chips", + "Co.", + "Co", + "Communications", + "Components", + "Computers", + "Computertechnik", + "corp.", + "Corp.", + "Corp", + "Corporation", + "Design", + "Electronics", + "Enterprise", + "Enterprises", + "Europe", + "GmbH", + "Hardware", + "[hex]", + "Holdings", + "Inc.", + "Inc", + "INC.", + "Incorporated", + "Instruments", + "International", + "Intl.", + "Labs", + "Limited.", + "Limited", + "Ltd.", + "Ltd", + "Microelectronics", + "Microsystems", + "MSM", + "Multimedia", + "Networks", + "Norway", + "Optical", + "PCS", + "Semiconductor", + "Systems", + "Systemtechnik", + "Techcenter", + "Technik", + "Technologies", + "Technology", + "TECHNOLOGY", + "Telephonics", + "USA", + "WCDMA", + NULL, + }; + char *desc_full; + char *p; + + desc_full = _fixup_string(desc, IGNORED_PHRASES, IGNORED_WORDS, TRUE); + if (!desc_full) + return NULL; + + /* Chop off everything after a slash. */ + for (p = desc_full; *p; p++) { + if ((p[0] == ' ' && p[1] == '/') || p[0] == '/') { + p[0] = '\0'; + break; + } + } + + nm_assert(g_utf8_validate(desc_full, -1, NULL)); + + return desc_full; +} + +char * +nm_utils_fixup_product_string(const char *desc) +{ + static const char *const IGNORED_PHRASES[] = { + "100/10 MBit", + "10/100 Mbps", + "1.0 GbE", + "10 GbE", + "10 Gigabit", + "10 Mbps", + "1/10 Gigabit", + "150 Mbps", + "2.5 GbE", + "54 Mbps", + "Attached Port", + "+ BT", + "\"CDC Subset\"", + "CE Media Processor", + "Controller Area Network", + "Converged Network", + "DEC-Tulip compatible", + "Dish Adapter", + "Double 108 Mbps", + "Dual Band", + "Dual Port", + "Embedded UTP", + "Ethernet Connection", + "Ethernet Pro 100", + "Express Module", + "Fabric Adapter", + "Fast Ethernet", + "for 10GBASE-T", + "for 10GbE backplane", + "for 10GbE QSFP+", + "for 10GbE SFP+", + "for 1GbE", + "for 20GbE backplane", + "for 25GbE backplane", + "for 25GbE SFP28", + "for 40GbE backplane", + "for 40GbE QSFP+", + "G Adapter", + "Gigabit Desktop Network", + "Gigabit Ethernet", + "Gigabit or", + "Host Interface", + "Host Virtual Interface", + "IEEE 802.11a/b/g", + "IEEE 802.11g", + "IEEE 802.11G", + "IEEE 802.11n", + "MAC + PHY", + "Mini Card", + "Mini Wireless", + "multicore SoC", + "Multi Function", + "N Draft 11n Wireless", + "Network Connection", + "Network Everywhere", + "N Wireless", + "N+ Wireless", + "OCT To Fast Ethernet Converter", + "PC Card", + "PCI Express", + "Platform Controller Hub", + "Plus Bluetooth", + "Quad Gigabit", + "rev 1", + "rev 17", + "rev 2", + "rev A", + "rev B", + "rev F", + "TO Ethernet", + "Turbo Wireless Adapter", + "Unified Wire", + "USB 1.1", + "USB 2.0", + "Virtual media for", + "WiFi Link", + "+ WiMAX", + "WiMAX/WiFi Link", + "Wireless G", + "Wireless G+", + "Wireless Lan", + "Wireless Mini adapter", + "Wireless Mini Adapter", + "Wireless N", + "with 1000-BASE-T interface", + "with CX4 copper interface", + "with Range Amplifier", + "with SR-XFP optical interface", + "w/ Upgradable Antenna", + NULL, + }; + static const char *const IGNORED_WORDS[] = { + "1000BaseSX", + "1000BASE-T", + "1000Base-ZX", + "100/10M", + "100baseFx", + "100Base-MII", + "100Base-T", + "100BaseT4", + "100Base-TX", + "100BaseTX", + "100GbE", + "100Mbps", + "100MBps", + "10/100", + "10/100/1000", + "10/100/1000Base-T", + "10/100/1000BASE-T", + "10/100BaseT", + "10/100baseTX", + "10/100BaseTX", + "10/100/BNC", + "10/100M", + "10/20-Gigabit", + "10/25/40/50GbE", + "10/40G", + "10base-FL", + "10BaseT", + "10BASE-T", + "10G", + "10Gb", + "10Gb/25Gb", + "10Gb/25Gb/40Gb/50Gb", + "10Gbase-T", + "10GBase-T", + "10GBASE-T", + "10GbE", + "10Gbps", + "10-Giga", + "10-Gigabit", + "10mbps", + "10Mbps", + "1/10GbE", + "1/10-Gigabit", + "11b/g/n", + "11g", + "150Mbps", + "16Gbps/10Gbps", + "1GbE", + "1x2:2", + "20GbE", + "25Gb", + "25GbE", + "2-Port", + "2x3:3", + "3G", + "3G/4G", + "3x3:3", + "40GbE", + "4G", + "54g", + "54M", + "54Mbps", + "56k", + "5G", + "802.11", + "802.11a/b/g", + "802.11abg", + "802.11a/b/g/n", + "802.11abgn", + "802.11ac", + "802.11ad", + "802.11a/g", + "802.11b", + "802.11b/g", + "802.11bg", + "802.11b/g/n", + "802.11bgn", + "802.11b/g/n-draft", + "802.11g", + "802.11n", + "802.11N", + "802.11n/b/g", + "802.11ng", + "802AIN", + "802UIG-1", + "adapter", + "Adapter", + "adaptor", + "ADSL", + "Basic", + "CAN-Bus", + "card", + "Card", + "Cardbus", + "CardBus", + "CDMA", + "CNA", + "Composite", + "controller", + "Controller", + "Copper", + "DB", + "Desktop", + "device", + "Device", + "dongle", + "driver", + "Dual-band", + "Dual-Protocol", + "EISA", + "Enhanced", + "ethernet.", + "ethernet", + "Ethernet", + "Ethernet/RNDIS", + "ExpressModule", + "family", + "Family", + "Fast/Gigabit", + "Fiber", + "gigabit", + "Gigabit", + "G-NIC", + "Hi-Gain", + "Hi-Speed", + "HSDPA", + "HSUPA", + "integrated", + "Integrated", + "interface", + "LAN", + "LAN+Winmodem", + "Laptop", + "LTE", + "LTE/UMTS/GSM", + "MAC", + "Micro", + "Mini-Card", + "Mini-USB", + "misprogrammed", + "modem", + "Modem", + "Modem/Networkcard", + "Module", + "Multimode", + "Multithreaded", + "Name:", + "net", + "network", + "Network", + "n/g/b", + "NIC", + "Notebook", + "OEM", + "PCI", + "PCI64", + "PCIe", + "PCI-E", + "PCI-Express", + "PCI-X", + "PCMCIA", + "PDA", + "PnP", + "RDMA", + "RJ-45", + "Series", + "Server", + "SoC", + "Switch", + "Technologies", + "TOE", + "USB", + "USB2.0", + "USB/Ethernet", + "UTP", + "UTP/Coax", + "v1", + "v1.1", + "v2", + "V2.0", + "v3", + "v4", + "wifi", + "Wi-Fi", + "WiFi", + "wireless", + "Wireless", + "Wireless-150N", + "Wireless-300N", + "Wireless-G", + "Wireless-N", + "WLAN", + NULL, + }; + char *desc_full; + char *p; + + desc_full = _fixup_string(desc, IGNORED_PHRASES, IGNORED_WORDS, FALSE); + if (!desc_full) + return NULL; + + /* Chop off everything after a '-'. */ + for (p = desc_full; *p; p++) { + if (p[0] == ' ' && p[1] == '-' && p[2] == ' ') { + p[0] = '\0'; + break; + } + } + + nm_assert(g_utf8_validate(desc_full, -1, NULL)); + + return desc_full; +} + +/*****************************************************************************/ + +const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = { + &_nml_dbus_meta_iface_nm, + &_nml_dbus_meta_iface_nm_accesspoint, + &_nml_dbus_meta_iface_nm_agentmanager, + &_nml_dbus_meta_iface_nm_checkpoint, + &_nml_dbus_meta_iface_nm_connection_active, + &_nml_dbus_meta_iface_nm_dhcp4config, + &_nml_dbus_meta_iface_nm_dhcp6config, + &_nml_dbus_meta_iface_nm_device, + &_nml_dbus_meta_iface_nm_device_adsl, + &_nml_dbus_meta_iface_nm_device_bluetooth, + &_nml_dbus_meta_iface_nm_device_bond, + &_nml_dbus_meta_iface_nm_device_bridge, + &_nml_dbus_meta_iface_nm_device_dummy, + &_nml_dbus_meta_iface_nm_device_generic, + &_nml_dbus_meta_iface_nm_device_iptunnel, + &_nml_dbus_meta_iface_nm_device_infiniband, + &_nml_dbus_meta_iface_nm_device_lowpan, + &_nml_dbus_meta_iface_nm_device_macsec, + &_nml_dbus_meta_iface_nm_device_macvlan, + &_nml_dbus_meta_iface_nm_device_modem, + &_nml_dbus_meta_iface_nm_device_olpcmesh, + &_nml_dbus_meta_iface_nm_device_ovsbridge, + &_nml_dbus_meta_iface_nm_device_ovsinterface, + &_nml_dbus_meta_iface_nm_device_ovsport, + &_nml_dbus_meta_iface_nm_device_ppp, + &_nml_dbus_meta_iface_nm_device_statistics, + &_nml_dbus_meta_iface_nm_device_team, + &_nml_dbus_meta_iface_nm_device_tun, + &_nml_dbus_meta_iface_nm_device_veth, + &_nml_dbus_meta_iface_nm_device_vlan, + &_nml_dbus_meta_iface_nm_device_vrf, + &_nml_dbus_meta_iface_nm_device_vxlan, + &_nml_dbus_meta_iface_nm_device_wifip2p, + &_nml_dbus_meta_iface_nm_device_wireguard, + &_nml_dbus_meta_iface_nm_device_wired, + &_nml_dbus_meta_iface_nm_device_wireless, + &_nml_dbus_meta_iface_nm_device_wpan, + &_nml_dbus_meta_iface_nm_dnsmanager, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config, + &_nml_dbus_meta_iface_nm_settings, + &_nml_dbus_meta_iface_nm_settings_connection, + &_nml_dbus_meta_iface_nm_vpn_connection, + &_nml_dbus_meta_iface_nm_wifip2ppeer, +}; + +#define COMMON_PREFIX "org.freedesktop.NetworkManager" + +static int +_strcmp_common_prefix(gconstpointer a, gconstpointer b, gpointer user_data) +{ + const NMLDBusMetaIface *iface = a; + const char * dbus_iface_name = b; + + nm_assert(g_str_has_prefix(iface->dbus_iface_name, COMMON_PREFIX)); + + return strcmp(&iface->dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], dbus_iface_name); +} + +const NMLDBusMetaIface * +nml_dbus_meta_iface_get(const char *dbus_iface_name) +{ + gssize idx; + + nm_assert(dbus_iface_name); + + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLDBusMetaIface, dbus_iface_name) == 0); + + /* we assume that NetworkManager only uses unique interface names. E.g. one + * interface name always has one particular meaning (and offers one set of + * properties, signals and methods). This is a convenient assumption, and + * we sure would never violate it when extending NM's D-Bus API. */ + + if (NM_STR_HAS_PREFIX(dbus_iface_name, COMMON_PREFIX)) { + /* optimize, that in fact all our interfaces have the same prefix. */ + idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) _nml_dbus_meta_ifaces, + G_N_ELEMENTS(_nml_dbus_meta_ifaces), + &dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], + _strcmp_common_prefix, + NULL); + } else + return NULL; + + if (idx < 0) + return NULL; + return _nml_dbus_meta_ifaces[idx]; +} + +const NMLDBusMetaProperty * +nml_dbus_meta_property_get(const NMLDBusMetaIface *meta_iface, + const char * dbus_property_name, + guint * out_idx) +{ + gssize idx; + + nm_assert(meta_iface); + nm_assert(dbus_property_name); + + idx = nm_utils_array_find_binary_search(meta_iface->dbus_properties, + sizeof(meta_iface->dbus_properties[0]), + meta_iface->n_dbus_properties, + &dbus_property_name, + nm_strcmp_p_with_data, + NULL); + if (idx < 0) { + NM_SET_OUT(out_idx, meta_iface->n_dbus_properties); + return NULL; + } + NM_SET_OUT(out_idx, idx); + return &meta_iface->dbus_properties[idx]; +} + +void +_nml_dbus_meta_class_init_with_properties_impl(GObjectClass * object_class, + const NMLDBusMetaIface *const *meta_ifaces) +{ + int i_iface; + + nm_assert(G_IS_OBJECT_CLASS(object_class)); + nm_assert(meta_ifaces); + nm_assert(meta_ifaces[0]); + + for (i_iface = 0; meta_ifaces[i_iface]; i_iface++) { + const NMLDBusMetaIface *meta_iface = meta_ifaces[i_iface]; + guint8 * reverse_idx; + guint8 i; + + nm_assert(g_type_is_a(meta_iface->get_type_fcn(), G_OBJECT_CLASS_TYPE(object_class))); + nm_assert(meta_iface->n_obj_properties > 0); + nm_assert(meta_iface->obj_properties); + nm_assert(meta_iface->obj_properties_reverse_idx[0] == 0); + nm_assert(meta_iface->obj_properties == meta_ifaces[0]->obj_properties); + + if (i_iface == 0) + g_object_class_install_properties(object_class, + meta_iface->n_obj_properties, + (GParamSpec **) meta_iface->obj_properties); + + reverse_idx = (guint8 *) meta_iface->obj_properties_reverse_idx; + + for (i = 0; i < meta_iface->n_obj_properties; i++) + reverse_idx[i] = 0xFFu; + for (i = 0; i < meta_iface->n_dbus_properties; i++) { + const NMLDBusMetaProperty *mpr = &meta_iface->dbus_properties[i]; + + if (mpr->obj_properties_idx != 0 && !mpr->obj_property_no_reverse_idx) { + nm_assert(mpr->obj_properties_idx < meta_iface->n_obj_properties); + nm_assert(reverse_idx[mpr->obj_properties_idx] == 0xFFu); + + reverse_idx[mpr->obj_properties_idx] = i; + } + } + } +} + +gboolean +nm_utils_g_param_spec_is_default(const GParamSpec *pspec) +{ + g_return_val_if_fail(pspec, FALSE); + + if (pspec->value_type == G_TYPE_BOOLEAN) + return ((((GParamSpecBoolean *) pspec)->default_value) == FALSE); + if (pspec->value_type == G_TYPE_UCHAR) + return ((((GParamSpecUChar *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_INT) + return ((((GParamSpecInt *) pspec)->default_value) == 0); + if (pspec->value_type == G_TYPE_UINT) + return ((((GParamSpecUInt *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_INT64) + return ((((GParamSpecInt64 *) pspec)->default_value) == 0); + if (pspec->value_type == G_TYPE_UINT64) + return ((((GParamSpecUInt64 *) pspec)->default_value) == 0u); + if (g_type_is_a(pspec->value_type, G_TYPE_ENUM)) + return ((((GParamSpecEnum *) pspec)->default_value) == 0); + if (g_type_is_a(pspec->value_type, G_TYPE_FLAGS)) + return ((((GParamSpecFlags *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_STRING) + return ((((GParamSpecString *) pspec)->default_value) == NULL); + if (NM_IN_SET(pspec->value_type, + G_TYPE_BYTES, + G_TYPE_PTR_ARRAY, + G_TYPE_ARRAY, + G_TYPE_HASH_TABLE, + G_TYPE_STRV)) { + /* boxed types have NULL default. */ + g_return_val_if_fail(G_IS_PARAM_SPEC_BOXED(pspec), FALSE); + g_return_val_if_fail(G_TYPE_IS_BOXED(pspec->value_type), FALSE); + return TRUE; + } + if (g_type_is_a(pspec->value_type, NM_TYPE_OBJECT)) { + /* object types have NULL default. */ + g_return_val_if_fail(G_IS_PARAM_SPEC_OBJECT(pspec), FALSE); + g_return_val_if_fail(G_TYPE_IS_OBJECT(pspec->value_type), FALSE); + return TRUE; + } + + /* This function is only used for asserting/testing. It thus + * strictly asserts and only support argument types that we expect. */ + g_return_val_if_reached(FALSE); +} + +/*****************************************************************************/ + +/** + * nm_utils_print: + * @output_mode: if 1 it uses g_print(). If 2, it uses g_printerr(). + * If 0, it uses either g_print() or g_printerr(), depending + * on LIBNM_CLIENT_DEBUG (and the "stdout" flag). + * @msg: the message to print. The function does not append + * a trailing newline. + * + * The only purpose of this function is to give access to g_print() + * or g_printerr() from pygobject. libnm can do debug logging by + * setting LIBNM_CLIENT_DEBUG and uses thereby g_printerr() or + * g_print(). A plain "print()" function in python is not in sync + * with these functions (it implements additional buffering). By + * using nm_utils_print(), the same logging mechanisms can be used. + * + * Since: 1.30 + */ +void +nm_utils_print(int output_mode, const char *msg) +{ + gboolean use_stdout; + + g_return_if_fail(msg); + + if (output_mode == 0) { + nml_dbus_log_enabled_full(NML_DBUS_LOG_LEVEL_ANY, &use_stdout); + output_mode = use_stdout ? 1 : 2; + } + + if (output_mode == 1) + g_print("%s", msg); + else if (output_mode == 2) + g_printerr("%s", msg); + else + g_return_if_reached(); +} diff --git a/src/libnm-client-impl/nm-libnm-utils.h b/src/libnm-client-impl/nm-libnm-utils.h new file mode 100644 index 00000000..3fd3b679 --- /dev/null +++ b/src/libnm-client-impl/nm-libnm-utils.h @@ -0,0 +1,1030 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017, 2018 Red Hat, Inc. + */ + +#ifndef __NM_LIBNM_UTILS_H__ +#define __NM_LIBNM_UTILS_H__ + +#include "c-list/src/c-list.h" +#include "nm-device.h" +#include "libnm-glib-aux/nm-ref-string.h" +#include "libnm-glib-aux/nm-logging-fwd.h" +#include "nm-types.h" +#include "nm-object.h" +#include "nm-client.h" + +/*****************************************************************************/ + +char *nm_utils_fixup_vendor_string(const char *desc); +char *nm_utils_fixup_product_string(const char *desc); + +char *nm_utils_wincaps_to_dash(const char *caps); + +gboolean nm_utils_g_param_spec_is_default(const GParamSpec *pspec); + +/*****************************************************************************/ + +typedef enum { + _NML_DBUS_LOG_LEVEL_NONE = 0x00, + + _NML_DBUS_LOG_LEVEL_INITIALIZED = 0x01, + + _NML_DBUS_LOG_LEVEL_TRACE = 0x02, + + _NML_DBUS_LOG_LEVEL_DEBUG = 0x04, + + /* the difference between a warning and a critical is that it results in + * g_warning() vs. g_critical() messages. Note that we want to use "warnings" + * for unknown D-Bus API that could just result because we run against a + * newer NetworkManager version (such warnings are more graceful, because + * we want that libnm can be forward compatible against newer servers). + * Critical warnings should be emitted when NetworkManager exposes something + * on D-Bus that breaks the current expectations. Usually NetworkManager + * should not break API, hence such issues are more severe. */ + _NML_DBUS_LOG_LEVEL_WARN = 0x08, + _NML_DBUS_LOG_LEVEL_ERROR = 0x10, + + /* ANY is only relevant for nml_dbus_log_enabled() to check whether any of the + * options is on. */ + NML_DBUS_LOG_LEVEL_ANY = _NML_DBUS_LOG_LEVEL_INITIALIZED, + + NML_DBUS_LOG_LEVEL_TRACE = _NML_DBUS_LOG_LEVEL_TRACE, + NML_DBUS_LOG_LEVEL_DEBUG = _NML_DBUS_LOG_LEVEL_DEBUG | NML_DBUS_LOG_LEVEL_TRACE, + NML_DBUS_LOG_LEVEL_WARN = _NML_DBUS_LOG_LEVEL_WARN | NML_DBUS_LOG_LEVEL_DEBUG, + NML_DBUS_LOG_LEVEL_ERROR = _NML_DBUS_LOG_LEVEL_ERROR | NML_DBUS_LOG_LEVEL_WARN, + + NML_DBUS_LOG_STDOUT = 0x20, +} NMLDBusLogLevel; + +#undef _LOGL_TRACE +#undef _LOGL_DEBUG +#undef _LOGL_INFO +#undef _LOGL_WARN +#undef _LOGL_ERR + +#define _LOGL_TRACE NML_DBUS_LOG_LEVEL_TRACE +#define _LOGL_DEBUG NML_DBUS_LOG_LEVEL_DEBUG +#define _LOGL_INFO NML_DBUS_LOG_LEVEL_INFO +#define _LOGL_WARN NML_DBUS_LOG_LEVEL_WARN +#define _LOGL_ERR NML_DBUS_LOG_LEVEL_ERR + +extern volatile int _nml_dbus_log_level; + +int _nml_dbus_log_level_init(void); + +static inline gboolean +nml_dbus_log_enabled_full(NMLDBusLogLevel level, gboolean *out_use_stdout) +{ + int l; + + nm_assert(NM_IN_SET(level, + NML_DBUS_LOG_LEVEL_ANY, + _NML_DBUS_LOG_LEVEL_NONE, + NML_DBUS_LOG_LEVEL_TRACE, + NML_DBUS_LOG_LEVEL_DEBUG, + NML_DBUS_LOG_LEVEL_WARN, + NML_DBUS_LOG_LEVEL_ERROR)); + + l = g_atomic_int_get(&_nml_dbus_log_level); + if (G_UNLIKELY(l == 0)) + l = _nml_dbus_log_level_init(); + + nm_assert(l & _NML_DBUS_LOG_LEVEL_INITIALIZED); + NM_SET_OUT(out_use_stdout, NM_FLAGS_HAS(l, NML_DBUS_LOG_STDOUT)); + if (level == NML_DBUS_LOG_LEVEL_ANY) + return l != _NML_DBUS_LOG_LEVEL_INITIALIZED; + return !!(((NMLDBusLogLevel) l) & level); +} + +static inline gboolean +nml_dbus_log_enabled(NMLDBusLogLevel level) +{ + return nml_dbus_log_enabled_full(level, NULL); +} + +void _nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...) + _nm_printf(3, 4); + +#define NML_DBUS_LOG(level, ...) \ + G_STMT_START \ + { \ + gboolean _use_stdout; \ + \ + G_STATIC_ASSERT((level) == _NML_DBUS_LOG_LEVEL_NONE || (level) == NML_DBUS_LOG_LEVEL_TRACE \ + || (level) == NML_DBUS_LOG_LEVEL_DEBUG \ + || (level) == NML_DBUS_LOG_LEVEL_WARN \ + || (level) == NML_DBUS_LOG_LEVEL_ERROR); \ + \ + if (nml_dbus_log_enabled_full(level, &_use_stdout)) { \ + _nml_dbus_log((level), _use_stdout, __VA_ARGS__); \ + } \ + } \ + G_STMT_END + +#define NML_DBUS_LOG_T(...) NML_DBUS_LOG(NML_DBUS_LOG_LEVEL_TRACE, __VA_ARGS__) +#define NML_DBUS_LOG_D(...) NML_DBUS_LOG(NML_DBUS_LOG_LEVEL_DEBUG, __VA_ARGS__) +#define NML_DBUS_LOG_W(...) NML_DBUS_LOG(NML_DBUS_LOG_LEVEL_WARN, __VA_ARGS__) +#define NML_DBUS_LOG_E(...) NML_DBUS_LOG(NML_DBUS_LOG_LEVEL_ERROR, __VA_ARGS__) + +/* _NML_NMCLIENT_LOG_LEVEL_COERCE is only for printf debugging. You can disable client logging by + * mapping the requested log level to a different one (or disable it altogether). + * That's useful for example if you are interested in *other* trace logging messages from + * libnm and don't want to get flooded by NMClient's trace messages. */ +#define _NML_NMCLIENT_LOG_LEVEL_COERCE(level) \ + /* for example, change condition below to suppress messages from NMClient. */ \ + ((TRUE || ((level) != NML_DBUS_LOG_LEVEL_TRACE)) ? (level) : _NML_DBUS_LOG_LEVEL_NONE) + +#define NML_NMCLIENT_LOG(level, self, ...) \ + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(level), \ + "nmclient[" NM_HASH_OBFUSCATE_PTR_FMT "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)) + +#define NML_NMCLIENT_LOG_T(self, ...) NML_NMCLIENT_LOG(NML_DBUS_LOG_LEVEL_TRACE, self, __VA_ARGS__) +#define NML_NMCLIENT_LOG_D(self, ...) NML_NMCLIENT_LOG(NML_DBUS_LOG_LEVEL_DEBUG, self, __VA_ARGS__) +#define NML_NMCLIENT_LOG_W(self, ...) NML_NMCLIENT_LOG(NML_DBUS_LOG_LEVEL_WARN, self, __VA_ARGS__) +#define NML_NMCLIENT_LOG_E(self, ...) NML_NMCLIENT_LOG(NML_DBUS_LOG_LEVEL_ERROR, self, __VA_ARGS__) + +/*****************************************************************************/ + +static inline const char * +_nml_coerce_property_str_not_null(const char *str) +{ + return str ?: ""; +} + +static inline const char * +_nml_coerce_property_str_not_empty(const char *str) +{ + return str && str[0] ? str : NULL; +} + +static inline const char * +_nml_coerce_property_object_path(NMRefString *path) +{ + if (!path) + return NULL; + return nm_dbus_path_not_empty(path->str); +} + +static inline const char *const * +_nml_coerce_property_strv_not_null(char **strv) +{ + return ((const char *const *) strv) ?: NM_PTRARRAY_EMPTY(const char *); +} + +/*****************************************************************************/ + +GQuark nm_context_busy_watcher_quark(void); + +void nm_context_busy_watcher_integrate_source(GMainContext *outer_context, + GMainContext *inner_context, + GObject * context_busy_watcher); + +/*****************************************************************************/ + +typedef struct { + GCancellable *cancellable; + GSource * cancel_on_idle_source; + gulong cancelled_id; + union { + struct { + GTask *task; + } async; + struct { + GMainLoop *main_loop; + GError ** error_location; + } sync; + } data; + bool is_sync : 1; +} NMLInitData; + +NMLInitData * +nml_init_data_new_sync(GCancellable *cancellable, GMainLoop *main_loop, GError **error_location); + +NMLInitData *nml_init_data_new_async(GCancellable *cancellable, GTask *task_take); + +void nml_init_data_return(NMLInitData *init_data, GError *error_take); + +void nml_cleanup_context_busy_watcher_on_idle(GObject * context_busy_watcher_take, + GMainContext *context); + +/*****************************************************************************/ + +typedef struct _NMLDBusObject NMLDBusObject; +typedef struct _NMLDBusObjWatcher NMLDBusObjWatcher; +typedef struct _NMLDBusMetaIface NMLDBusMetaIface; +typedef struct _NMLDBusPropertyO NMLDBusPropertyO; +typedef struct _NMLDBusPropertyAO NMLDBusPropertyAO; + +typedef enum { + /* See comments below for NMLDBusMetaIface.interface_prio. + * + * Higher numbers means more important to detect the GObject type. */ + NML_DBUS_META_INTERFACE_PRIO_NONE = 0, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT = 1, + NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE = 2, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10 = 3, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20 = 4, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30 = 5, +} NMLDBusMetaInteracePrio; + +/*****************************************************************************/ + +#define NM_CLIENT_INSTANCE_FLAGS_ALL ((NMClientInstanceFlags) 0x1) + +typedef struct { + GType (*get_o_type_fcn)(void); + + /* Ignore whether the referenced NMObject is ready or not. That means, + * the property is always ready (and if the pointed object itself is + * not yet ready, the property pretends to be %NULL for the moment. */ + bool is_always_ready : 1; + +} NMLDBusPropertVTableO; + +struct _NMLDBusPropertyO { + NMLDBusObject * owner_dbobj; + NMLDBusObjWatcher * obj_watcher; + GObject * nmobj; + const NMLDBusMetaIface *meta_iface; + guint dbus_property_idx; + bool is_ready : 1; + bool is_changed : 1; + bool block_is_changed : 1; +}; + +gpointer nml_dbus_property_o_get_obj(NMLDBusPropertyO *pr_o); + +gboolean nml_dbus_property_o_is_ready(const NMLDBusPropertyO *pr_o); + +gboolean nml_dbus_property_o_is_ready_fully(const NMLDBusPropertyO *pr_o); + +void nml_dbus_property_o_clear(NMLDBusPropertyO *pr_o, NMClient *client); + +void nml_dbus_property_o_clear_many(NMLDBusPropertyO *pr_o, guint len, NMClient *self); + +void nml_dbus_property_o_notify_changed_many(NMLDBusPropertyO *ptr, guint len, NMClient *self); + +/*****************************************************************************/ + +typedef struct { + GType (*get_o_type_fcn)(void); + + void (*notify_changed_ao)(NMLDBusPropertyAO *pr_ao, + NMClient * self, + NMObject * nmobj, + gboolean is_added /* or else removed */); + + gboolean (*check_nmobj_visible_fcn)(GObject *nmobj); + + /* Ignore whether the referenced NMObject is ready or not. That means, + * the property is always ready (and if the pointed object itself is + * not yet ready, the property pretends to be %NULL for the moment. */ + bool is_always_ready : 1; + +} NMLDBusPropertVTableAO; + +struct _NMLDBusPropertyAOData; + +struct _NMLDBusPropertyAO { + CList data_lst_head; + GHashTable * hash; + NMLDBusObject * owner_dbobj; + const NMLDBusMetaIface * meta_iface; + GPtrArray * arr; + struct _NMLDBusPropertyAOData *changed_head; + guint dbus_property_idx; + guint n_not_ready; + bool is_changed : 1; +}; + +const GPtrArray *nml_dbus_property_ao_get_objs_as_ptrarray(NMLDBusPropertyAO *pr_ao); + +gboolean nml_dbus_property_ao_is_ready(const NMLDBusPropertyAO *pr_ao); + +void nml_dbus_property_ao_clear(NMLDBusPropertyAO *pr_ao, NMClient *client); + +void nml_dbus_property_ao_clear_many(NMLDBusPropertyAO *pr_ao, guint len, NMClient *self); + +void nml_dbus_property_ao_notify_changed_many(NMLDBusPropertyAO *ptr, guint len, NMClient *self); + +/*****************************************************************************/ + +typedef enum { + NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NONE = 0, + NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY = 0x1, +} NMLDBusNotifyUpdatePropFlags; + +NMLDBusNotifyUpdatePropFlags _nml_dbus_notify_update_prop_ignore(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value); + +NMLDBusNotifyUpdatePropFlags _nml_dbus_notify_update_prop_o(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value); + +typedef struct { + const char * dbus_property_name; + const GVariantType *dbus_type; + + guint16 prop_struct_offset; + + guint8 obj_properties_idx; + + bool use_notify_update_prop : 1; + + bool obj_property_no_reverse_idx : 1; + + union { + union { + const NMLDBusPropertVTableO * property_vtable_o; + const NMLDBusPropertVTableAO *property_vtable_ao; + } extra; + + NMLDBusNotifyUpdatePropFlags (*notify_update_prop)(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value); + }; + +} NMLDBusMetaProperty; + +#define NML_DBUS_META_PROPERTY_INIT(v_dbus_property_name, v_dbus_type, v_obj_properties_idx, ...) \ + { \ + .dbus_property_name = "" v_dbus_property_name "", \ + .dbus_type = NM_G_VARIANT_TYPE("" v_dbus_type ""), \ + .obj_properties_idx = v_obj_properties_idx, ##__VA_ARGS__ \ + } + +#define _NML_DBUS_META_PROPERTY_INIT_DEFAULT(v_dbus_type, \ + v_exp_type, \ + v_dbus_property_name, \ + v_obj_properties_idx, \ + v_container, \ + v_field) \ + NML_DBUS_META_PROPERTY_INIT( \ + v_dbus_property_name, \ + v_dbus_type, \ + v_obj_properties_idx, \ + .prop_struct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(v_exp_type, v_container, v_field)) + +#define NML_DBUS_META_PROPERTY_INIT_B(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("b", bool, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_Y(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("y", guint8, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_Q(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("q", guint16, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_I(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("i", gint32, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_U(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("u", guint32, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_X(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("x", gint64, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_T(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("t", guint64, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_S(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("s", char *, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_AS(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("as", char **, __VA_ARGS__) +#define NML_DBUS_META_PROPERTY_INIT_AY(...) \ + _NML_DBUS_META_PROPERTY_INIT_DEFAULT("ay", GBytes *, __VA_ARGS__) + +#define NML_DBUS_META_PROPERTY_INIT_O(v_dbus_property_name, \ + v_obj_properties_idx, \ + v_container, \ + v_field) \ + NML_DBUS_META_PROPERTY_INIT( \ + v_dbus_property_name, \ + "o", \ + v_obj_properties_idx, \ + .prop_struct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(NMRefString *, v_container, v_field), \ + .use_notify_update_prop = TRUE, \ + .notify_update_prop = _nml_dbus_notify_update_prop_o) + +#define NML_DBUS_META_PROPERTY_INIT_O_PROP(v_dbus_property_name, \ + v_obj_properties_idx, \ + v_container, \ + v_field, \ + v_get_o_type_fcn, \ + ...) \ + NML_DBUS_META_PROPERTY_INIT( \ + v_dbus_property_name, \ + "o", \ + v_obj_properties_idx, \ + .prop_struct_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyO, v_container, v_field), \ + .extra.property_vtable_o = \ + &((const NMLDBusPropertVTableO){.get_o_type_fcn = (v_get_o_type_fcn), ##__VA_ARGS__})) + +#define NML_DBUS_META_PROPERTY_INIT_AO_PROP(v_dbus_property_name, \ + v_obj_properties_idx, \ + v_container, \ + v_field, \ + v_get_o_type_fcn, \ + ...) \ + NML_DBUS_META_PROPERTY_INIT( \ + v_dbus_property_name, \ + "ao", \ + v_obj_properties_idx, \ + .prop_struct_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyAO, v_container, v_field), \ + .extra.property_vtable_ao = &( \ + (const NMLDBusPropertVTableAO){.get_o_type_fcn = (v_get_o_type_fcn), ##__VA_ARGS__})) + +#define NML_DBUS_META_PROPERTY_INIT_FCN(v_dbus_property_name, \ + v_obj_properties_idx, \ + v_dbus_type, \ + v_notify_update_prop, \ + ...) \ + NML_DBUS_META_PROPERTY_INIT(v_dbus_property_name, \ + v_dbus_type, \ + v_obj_properties_idx, \ + .use_notify_update_prop = TRUE, \ + .notify_update_prop = (v_notify_update_prop), \ + ##__VA_ARGS__) + +#define NML_DBUS_META_PROPERTY_INIT_IGNORE(v_dbus_property_name, v_dbus_type) \ + NML_DBUS_META_PROPERTY_INIT(v_dbus_property_name, \ + v_dbus_type, \ + 0, \ + .use_notify_update_prop = TRUE, \ + .notify_update_prop = _nml_dbus_notify_update_prop_ignore) + +/* "TODO" is like "IGNORE". The difference is that we don't plan to ever implement "IGNORE", but + * "TODO" is something we should add support for. */ +#define NML_DBUS_META_PROPERTY_INIT_TODO(...) NML_DBUS_META_PROPERTY_INIT_IGNORE(__VA_ARGS__) + +struct _NMLDBusMetaIface { + const char *dbus_iface_name; + GType (*get_type_fcn)(void); + + /* Usually there is a one-to-one correspondence between the properties + * on D-Bus (dbus_properties) and the GObject properties (obj_properties). + * + * With: + * meta_iface->obj_properties[o_idx] (o_idx < n_obj_properties) + * &meta_iface->dbus_properties[d_idx] (d_idx < n_dbus_properties) + * it follows that + * assert (meta_iface->obj_properties_reverse_idx[o_idx] == d_idx) + * assert (meta_iface->dbus_properties[d_idx].obj_properties_idx == o_idx) + * if (and only if) two properties correspond. + */ + const GParamSpec *const * obj_properties; + const NMLDBusMetaProperty *dbus_properties; + const guint8 * obj_properties_reverse_idx; + + guint8 n_dbus_properties; + guint8 n_obj_properties; + + /* The offsets "prop_struct_offset" in NMLDBusMetaProperty are based on some base + * struct. If "base_struct_offset" is 0, then the base struct is the GObject pointer + * itself. + * If this is non-null, then we expect at that location a pointer to the offset. + * In this case we need to first find the base pointer via + * *((gpointer *) ((char *) nmobj + meta_iface->base_struct_offset)). + * + * This covers NMDeviceBridge._priv vs. NMDevice._priv. In the second case, + * _priv is a pointer that we first need to follow. + */ + guint8 base_struct_offset; + + /* We create the appropriate NMObject GType based on the D-Bus interfaces that + * are present. For example, if we see a "org.freedesktop.NetworkManager.Device.Bridge" + * interface, we create a NMDeviceBridge. Basically, if it looks like a certain + * object (based on the D-Bus interface), we assume it is. + * + * Some interfaces are purely additional ("org.freedesktop.NetworkManager.Device.Statistics") + * and don't determine the NMObject type (%NML_DBUS_META_INTERFACE_PRIO_NONE). + * + * Some interfaces are of a parent type ("org.freedesktop.NetworkManager.Device" for + * NMDevice), and don't determine the type either (%NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE). + * + * Some interfaces ("org.freedesktop.NetworkManager.AgentManager") belong to NMClient + * itself. Those have priority %NML_DBUS_META_INTERFACE_PRIO_NMCLIENT. + * + * In most cases, each D-Bus object is expected to have only one D-Bus interface + * to determine the type. While theoretically an object + * "/org/freedesktop/NetworkManager/Devices/3" could have interfaces "org.freedesktop.NetworkManager.Device.Bridge" + * and "org.freedesktop.NetworkManager.Device.Bond" at the same time, in practice it doesn't. + * Note that we also assume that once a D-Bus object gets a NMObject, it cannot change (*). + * NetworkManager's API does not add/remove interfaces after exporting the object the + * first time, so in practice each D-Bus object is expected to have a suitable D-Bus + * interface (and only determining interface, which doesn't change). Those interfaces have + * priority %NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30. + * + * (*) note that nothing bad would happen if a faulty NetworkManager would violate that. + * Of course, something would not work correctly, but the D-Bus interface we find is unexpected + * and wrong. + * + * One exception is "org.freedesktop.NetworkManager.Connection.Active". This can either + * be a NMActiveConnection or a NMVpnConnection. Hence, this profile has priority + * %NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10, and depending on whether there is + * a "org.freedesktop.NetworkManager.VPN.Connection" (with high priority), we create + * one or the other type. + * + * Another exception is "org.freedesktop.NetworkManager.Device.Veth". It is a NMDeviceVeth + * and the parent is NMDeviceEthernet. Therefore it contains "org.freedesktop.NetworkManager.Device.Wired" + * as it should be registered as NMDeviceVeth, the profile has priority + * %NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20. + * + */ + NMLDBusMetaInteracePrio interface_prio : 3; +}; + +#define NML_DBUS_META_IFACE_OBJ_PROPERTIES() \ + .obj_properties = (const GParamSpec *const *) (obj_properties), \ + .n_obj_properties = _PROPERTY_ENUMS_LAST, \ + .obj_properties_reverse_idx = ((guint8[_PROPERTY_ENUMS_LAST]){}) + +#define NML_DBUS_META_IFACE_DBUS_PROPERTIES(...) \ + .dbus_properties = ((const NMLDBusMetaProperty[]){__VA_ARGS__}), \ + .n_dbus_properties = \ + (sizeof((const NMLDBusMetaProperty[]){__VA_ARGS__}) / sizeof(NMLDBusMetaProperty)) + +#define NML_DBUS_META_IFACE_INIT(v_dbus_iface_name, v_get_type_fcn, v_interface_prio, ...) \ + { \ + .dbus_iface_name = "" v_dbus_iface_name "", .get_type_fcn = v_get_type_fcn, \ + .interface_prio = v_interface_prio, ##__VA_ARGS__ \ + } + +#define NML_DBUS_META_IFACE_INIT_PROP(v_dbus_iface_name, v_get_type_fcn, v_interface_prio, ...) \ + NML_DBUS_META_IFACE_INIT(v_dbus_iface_name, \ + v_get_type_fcn, \ + v_interface_prio, \ + NML_DBUS_META_IFACE_OBJ_PROPERTIES(), \ + ##__VA_ARGS__) + +extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[44]; + +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_agentmanager; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_checkpoint; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_connection_active; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_adsl; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bluetooth; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bond; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bridge; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_dummy; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_generic; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_infiniband; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_iptunnel; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_lowpan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macsec; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macvlan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_modem; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_olpcmesh; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsbridge; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsinterface; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ovsport; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_ppp; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_statistics; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_team; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_tun; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_veth; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vlan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vrf; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_vxlan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wifip2p; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wired; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wireguard; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wireless; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_wpan; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dhcp4config; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dhcp6config; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_dnsmanager; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_ip4config; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_ip6config; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings_connection; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_vpn_connection; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_wifip2ppeer; + +const NMLDBusMetaIface *nml_dbus_meta_iface_get(const char *dbus_iface_name); + +const NMLDBusMetaProperty *nml_dbus_meta_property_get(const NMLDBusMetaIface *meta_iface, + const char * dbus_property_name, + guint * out_idx); + +void _nml_dbus_meta_class_init_with_properties_impl(GObjectClass * object_class, + const NMLDBusMetaIface *const *meta_iface); +#define _nml_dbus_meta_class_init_with_properties(object_class, ...) \ + _nml_dbus_meta_class_init_with_properties_impl( \ + (object_class), \ + ((const NMLDBusMetaIface *const[]){__VA_ARGS__, NULL})) + +/*****************************************************************************/ + +typedef enum { + NML_DBUS_OBJ_STATE_UNLINKED = 0, + NML_DBUS_OBJ_STATE_WATCHED_ONLY, + NML_DBUS_OBJ_STATE_ON_DBUS, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_NOT_READY, + NML_DBUS_OBJ_STATE_WITH_NMOBJ_READY, +} NMLDBusObjState; + +typedef enum { + NML_DBUS_OBJ_CHANGED_TYPE_NONE = 0, + NML_DBUS_OBJ_CHANGED_TYPE_DBUS = (1LL << 0), + NML_DBUS_OBJ_CHANGED_TYPE_NMOBJ = (1LL << 1), +} NMLDBusObjChangedType; + +struct _NMLDBusObject { + NMRefString *dbus_path; + + /* While the object is tracked by NMClient, it is linked with this list. + * The lists are partitioned based on the NMLDBusObjState. */ + CList dbus_objects_lst; + + /* The list of D-Bus interface NMLDBusObjIfaceData. + * + * Some may be about to be removed (iface_removed) or + * unknown (!dbus_iface_is_wellknown). */ + CList iface_lst_head; + + /* The list of registered NMLDBusObjWatcher. */ + CList watcher_lst_head; + + /* When an object changes (e.g. because of new information on D-Bus), we often + * don't process the changes right away, but enqueue the object in a changed + * list. This list goes together with obj_changed_type property below, which + * tracks what changed. */ + CList obj_changed_lst; + + GObject *nmobj; + + int ref_count; + + NMLDBusObjState obj_state : 4; + + NMLDBusObjChangedType obj_changed_type : 3; +}; + +static inline gboolean +NML_IS_DBUS_OBJECT(NMLDBusObject *dbobj) +{ + nm_assert(!dbobj || (NM_IS_REF_STRING(dbobj->dbus_path) && dbobj->ref_count > 0)); + nm_assert(!dbobj || !dbobj->nmobj || NM_IS_OBJECT(dbobj->nmobj) || NM_IS_CLIENT(dbobj->nmobj)); + return !!dbobj; +} + +NMLDBusObject *nml_dbus_object_ref(NMLDBusObject *dbobj); + +void nml_dbus_object_unref(NMLDBusObject *dbobj); + +NM_AUTO_DEFINE_FCN0(NMLDBusObject *, _nm_auto_unref_nml_dbusobj, nml_dbus_object_unref); +#define nm_auto_unref_nml_dbusobj nm_auto(_nm_auto_unref_nml_dbusobj) + +gpointer nml_dbus_object_get_property_location(NMLDBusObject * dbobj, + const NMLDBusMetaIface * meta_iface, + const NMLDBusMetaProperty *meta_property); + +/*****************************************************************************/ + +/* NMClient is not an NMObject, but in some aspects we want to track it like + * an NMObject. For that, both NMClient and NMObject "implement" NMObjectBase, + * despite not actually implementing such a GObject type. */ +typedef struct { + GObject parent; + CList queue_notify_lst; + bool is_disposing : 1; +} NMObjectBase; + +typedef struct { + GObjectClass parent; +} NMObjectBaseClass; + +struct _NMObjectPrivate; + +struct _NMObject { + union { + GObject parent; + NMObjectBase obj_base; + }; + struct _NMObjectPrivate *_priv; +}; + +typedef struct _NMObjectClassFieldInfo { + const struct _NMObjectClassFieldInfo *parent; + NMObjectClass * klass; + guint16 offset; + guint16 num; +} _NMObjectClassFieldInfo; + +struct _NMObjectClass { + union { + GObjectClass parent; + NMObjectBaseClass obj_base; + }; + + void (*register_client)(NMObject *self, NMClient *client, NMLDBusObject *dbobj); + + void (*unregister_client)(NMObject *self, NMClient *client, NMLDBusObject *dbobj); + + gboolean (*is_ready)(NMObject *self); + + void (*obj_changed_notify)(NMObject *self); + + const _NMObjectClassFieldInfo *property_o_info; + const _NMObjectClassFieldInfo *property_ao_info; + + guint16 priv_ptr_offset; + + bool priv_ptr_indirect : 1; +}; + +#define _NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, type_name) \ + G_STMT_START \ + { \ + (nm_object_class)->priv_ptr_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(type_name##Private, type_name, _priv); \ + (nm_object_class)->priv_ptr_indirect = FALSE; \ + } \ + G_STMT_END + +#define _NM_OBJECT_CLASS_INIT_PRIV_PTR_INDIRECT(nm_object_class, type_name) \ + G_STMT_START \ + { \ + (nm_object_class)->priv_ptr_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(type_name##Private *, type_name, _priv); \ + (nm_object_class)->priv_ptr_indirect = TRUE; \ + } \ + G_STMT_END + +#define _NM_OBJECT_CLASS_INIT_FIELD_INFO(_nm_object_class, _field_name, _offset, _num) \ + G_STMT_START \ + { \ + (_nm_object_class)->_field_name = ({ \ + static _NMObjectClassFieldInfo _f; \ + \ + _f = (_NMObjectClassFieldInfo){ \ + .parent = (_nm_object_class)->_field_name, \ + .klass = (_nm_object_class), \ + .offset = _offset, \ + .num = _num, \ + }; \ + &_f; \ + }); \ + } \ + G_STMT_END + +#define _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_1(nm_object_class, type_name, field_name) \ + _NM_OBJECT_CLASS_INIT_FIELD_INFO( \ + nm_object_class, \ + property_o_info, \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyO, type_name, field_name), \ + 1) + +#define _NM_OBJECT_CLASS_INIT_PROPERTY_O_FIELDS_N(nm_object_class, type_name, field_name) \ + _NM_OBJECT_CLASS_INIT_FIELD_INFO( \ + nm_object_class, \ + property_o_info, \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyO *, type_name, field_name), \ + G_N_ELEMENTS(((type_name *) NULL)->field_name)) + +#define _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_1(nm_object_class, type_name, field_name) \ + _NM_OBJECT_CLASS_INIT_FIELD_INFO( \ + nm_object_class, \ + property_ao_info, \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyAO, type_name, field_name), \ + 1) + +#define _NM_OBJECT_CLASS_INIT_PROPERTY_AO_FIELDS_N(nm_object_class, type_name, field_name) \ + _NM_OBJECT_CLASS_INIT_FIELD_INFO( \ + nm_object_class, \ + property_ao_info, \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMLDBusPropertyAO *, type_name, field_name), \ + G_N_ELEMENTS(((type_name *) NULL)->field_name)) + +/*****************************************************************************/ + +struct _NMDevicePrivate; + +struct _NMDevice { + NMObject parent; + struct _NMDevicePrivate *_priv; +}; + +struct _NMDeviceClass { + struct _NMObjectClass parent; + + gboolean (*connection_compatible)(NMDevice *device, NMConnection *connection, GError **error); + + const char *(*get_type_description)(NMDevice *device); + + GType (*get_setting_type)(NMDevice *device); +}; + +/*****************************************************************************/ + +struct _NMDeviceEthernetPrivate; + +struct _NMDeviceEthernet { + NMDevice parent; + struct _NMDeviceEthernetPrivate *_priv; +}; + +struct _NMDeviceEthernetClass { + NMDeviceClass parent; +}; + +/*****************************************************************************/ + +struct _NMActiveConnectionPrivate; + +struct _NMActiveConnection { + NMObject parent; + struct _NMActiveConnectionPrivate *_priv; +}; + +struct _NMActiveConnectionClass { + struct _NMObjectClass parent; +}; + +/*****************************************************************************/ + +struct _NMDhcpConfigPrivate; + +struct _NMDhcpConfig { + NMObject parent; + struct _NMDhcpConfigPrivate *_priv; +}; + +struct _NMDhcpConfigClass { + struct _NMObjectClass parent; +}; + +/*****************************************************************************/ + +struct _NMIPConfigPrivate; + +struct _NMIPConfig { + NMObject parent; + struct _NMIPConfigPrivate *_priv; +}; + +struct _NMIPConfigClass { + struct _NMObjectClass parent; +}; + +/*****************************************************************************/ + +NMLDBusObject *_nm_object_get_dbobj(gpointer self); + +const char *_nm_object_get_path(gpointer self); + +NMClient *_nm_object_get_client(gpointer self); + +GDBusConnection *_nm_client_get_dbus_connection(NMClient *client); + +const char *_nm_client_get_dbus_name_owner(NMClient *client); + +GMainContext *_nm_client_get_context_main(NMClient *client); +GMainContext *_nm_client_get_context_dbus(NMClient *client); + +void _nm_client_queue_notify_object(NMClient *client, gpointer nmobj, const GParamSpec *pspec); + +void _nm_client_notify_object_changed(NMClient *self, NMLDBusObject *dbobj); + +struct udev *_nm_client_get_udev(NMClient *self); + +/*****************************************************************************/ + +#define NM_CLIENT_NOTIFY_EVENT_PRIO_BEFORE (-100) +#define NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP 0 +#define NM_CLIENT_NOTIFY_EVENT_PRIO_AFTER 100 + +typedef struct _NMClientNotifyEvent NMClientNotifyEvent; + +typedef void (*NMClientNotifyEventCb)(NMClient *self, gpointer notify_event); + +struct _NMClientNotifyEvent { + CList lst; + NMClientNotifyEventCb callback; + int priority; +}; + +gpointer _nm_client_notify_event_queue(NMClient * self, + int priority, + NMClientNotifyEventCb callback, + gsize event_size); + +typedef struct _NMClientNotifyEventWithPtr NMClientNotifyEventWithPtr; + +typedef void (*NMClientNotifyEventWithPtrCb)(NMClient * self, + NMClientNotifyEventWithPtr *notify_event); + +struct _NMClientNotifyEventWithPtr { + NMClientNotifyEvent parent; + gpointer user_data; +}; + +NMClientNotifyEventWithPtr * +_nm_client_notify_event_queue_with_ptr(NMClient * self, + int priority, + NMClientNotifyEventWithPtrCb callback, + gpointer user_data); + +void _nm_client_notify_event_queue_emit_obj_signal(NMClient *self, + GObject * source, + NMObject *nmobj, + gboolean is_added /* or else removed */, + int prio_offset, + guint signal_id); + +/*****************************************************************************/ + +GError *_nm_client_new_error_nm_not_running(void); +GError *_nm_client_new_error_nm_not_cached(void); + +void _nm_client_dbus_call_simple(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback callback, + gpointer user_data); + +void _nm_client_dbus_call(NMClient * self, + gpointer source_obj, + gpointer source_tag, + GCancellable * cancellable, + GAsyncReadyCallback user_callback, + gpointer user_callback_data, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GAsyncReadyCallback internal_callback); + +GVariant *_nm_client_dbus_call_sync(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError ** error); + +gboolean _nm_client_dbus_call_sync_void(NMClient * self, + GCancellable * cancellable, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + GDBusCallFlags flags, + int timeout_msec, + gboolean strip_dbus_error, + GError ** error); + +void _nm_client_set_property_sync_legacy(NMClient * self, + const char *object_path, + const char *interface, + const char *prop_name, + const char *format_string, + ...); + +/*****************************************************************************/ + +void _nm_client_get_settings_call(NMClient *self, NMLDBusObject *dbobj); + +GCancellable *_nm_remote_settings_get_settings_prepare(NMRemoteConnection *self); + +void _nm_remote_settings_get_settings_commit(NMRemoteConnection *self, GVariant *settings); + +/*****************************************************************************/ + +void +_nm_active_connection_state_changed_commit(NMActiveConnection *self, guint32 state, guint32 reason); + +void _nm_vpn_connection_state_changed_commit(NMVpnConnection *self, guint32 state, guint32 reason); + +/*****************************************************************************/ + +NMLDBusNotifyUpdatePropFlags +_nm_device_notify_update_prop_hw_address(NMClient * client, + NMLDBusObject * dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant * value); + +/*****************************************************************************/ + +#endif /* __NM_LIBNM_UTILS_H__ */ diff --git a/src/libnm-client-impl/nm-object-private.h b/src/libnm-client-impl/nm-object-private.h new file mode 100644 index 00000000..9f78304d --- /dev/null +++ b/src/libnm-client-impl/nm-object-private.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2008 - 2011 Red Hat, Inc. + */ + +#ifndef __NM_OBJECT_PRIVATE_H__ +#define __NM_OBJECT_PRIVATE_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#include "nm-object.h" + +#endif /* __NM_OBJECT_PRIVATE_H__ */ diff --git a/src/libnm-client-impl/nm-object.c b/src/libnm-client-impl/nm-object.c new file mode 100644 index 00000000..2e7de8c2 --- /dev/null +++ b/src/libnm-client-impl/nm-object.c @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-object.h" + +#include +#include + +#include "nm-utils.h" +#include "nm-dbus-interface.h" +#include "nm-object-private.h" +#include "nm-dbus-helpers.h" +#include "nm-client.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "c-list/src/c-list.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PATH, ); + +typedef struct _NMObjectPrivate { + NMClient * client; + NMLDBusObject *dbobj; +} NMObjectPrivate; + +G_DEFINE_ABSTRACT_TYPE(NMObject, nm_object, G_TYPE_OBJECT); + +#define NM_OBJECT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMObject, NM_IS_OBJECT) + +static NMObjectClass *_nm_object_class = NULL; + +/*****************************************************************************/ + +static gpointer +_nm_object_get_private(NMObjectClass *klass, NMObject *self, guint16 extra_offset) +{ + char *ptr; + + nm_assert(klass->priv_ptr_offset > 0); + + ptr = (char *) self; + ptr += klass->priv_ptr_offset; + if (klass->priv_ptr_indirect) + ptr = *((gpointer *) ptr); + return ptr + extra_offset; +} + +NMLDBusObject * +_nm_object_get_dbobj(gpointer self) +{ + return NM_OBJECT_GET_PRIVATE(self)->dbobj; +} + +const char * +_nm_object_get_path(gpointer self) +{ + return NM_OBJECT_GET_PRIVATE(self)->dbobj->dbus_path->str; +} + +NMClient * +_nm_object_get_client(gpointer self) +{ + return NM_OBJECT_GET_PRIVATE(self)->client; +} + +/** + * nm_object_get_path: + * @object: a #NMObject + * + * Gets the DBus path of the #NMObject. + * + * Returns: the object's path. This is the internal string used by the + * object, and must not be modified. + * + * Note that the D-Bus path of an NMObject never changes, even + * if the instance gets removed from the cache. To find out + * whether the object is still alive/cached, check nm_object_get_client(). + **/ +const char * +nm_object_get_path(NMObject *object) +{ + g_return_val_if_fail(NM_IS_OBJECT(object), NULL); + + return _nm_object_get_path(object); +} + +/** + * nm_object_get_client: + * @object: a #NMObject + * + * Returns the #NMClient instance in which object is cached. + * Also, if the object got removed from the client cached, + * this returns %NULL. So it can be used to check whether the + * object is still alive. + * + * Returns: (transfer none): the #NMClient cache in which the + * object can be found, or %NULL if the object is no longer + * cached. + * + * Since: 1.24 + **/ +NMClient * +nm_object_get_client(NMObject *object) +{ + g_return_val_if_fail(NM_IS_OBJECT(object), NULL); + + return _nm_object_get_client(object); +} + +/*****************************************************************************/ + +static void +clear_properties(NMObject *self, NMClient *client) +{ + NMObjectClass * klass = NM_OBJECT_GET_CLASS(self); + const _NMObjectClassFieldInfo *p; + + nm_assert(NM_IS_OBJECT(self)); + nm_assert(!client || NM_IS_CLIENT(client)); + + for (p = klass->property_o_info; p; p = p->parent) { + nml_dbus_property_o_clear_many(_nm_object_get_private(p->klass, self, p->offset), + p->num, + client); + } + + for (p = klass->property_ao_info; p; p = p->parent) { + nml_dbus_property_ao_clear_many(_nm_object_get_private(p->klass, self, p->offset), + p->num, + client); + } +} + +/*****************************************************************************/ + +static gboolean +is_ready(NMObject *self) +{ + NMObjectClass * klass = NM_OBJECT_GET_CLASS(self); + NMClient * client = _nm_object_get_client(self); + const _NMObjectClassFieldInfo *p; + guint16 i; + + nm_assert(NM_IS_CLIENT(client)); + + for (p = klass->property_o_info; p; p = p->parent) { + NMLDBusPropertyO *fields = _nm_object_get_private(p->klass, self, p->offset); + + for (i = 0; i < p->num; i++) { + if (!nml_dbus_property_o_is_ready(&fields[i])) + return FALSE; + } + } + + for (p = klass->property_ao_info; p; p = p->parent) { + NMLDBusPropertyAO *fields = _nm_object_get_private(p->klass, self, p->offset); + + for (i = 0; i < p->num; i++) { + if (!nml_dbus_property_ao_is_ready(&fields[i])) + return FALSE; + } + } + + return TRUE; +} + +static void +obj_changed_notify(NMObject *self) +{ + NMObjectClass * klass = NM_OBJECT_GET_CLASS(self); + NMClient * client = _nm_object_get_client(self); + const _NMObjectClassFieldInfo *p; + + nm_assert(NM_IS_CLIENT(client)); + + for (p = klass->property_o_info; p; p = p->parent) { + nml_dbus_property_o_notify_changed_many(_nm_object_get_private(p->klass, self, p->offset), + p->num, + client); + } + + for (p = klass->property_ao_info; p; p = p->parent) { + nml_dbus_property_ao_notify_changed_many(_nm_object_get_private(p->klass, self, p->offset), + p->num, + client); + } +} + +/*****************************************************************************/ + +static void +register_client(NMObject *self, NMClient *client, NMLDBusObject *dbobj) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE(self); + + nm_assert(!priv->client); + nm_assert(NML_IS_DBUS_OBJECT(dbobj)); + nm_assert(dbobj->nmobj == G_OBJECT(self)); + + priv->client = client; + priv->dbobj = nml_dbus_object_ref(dbobj); +} + +static void +unregister_client(NMObject *self, NMClient *client, NMLDBusObject *dbobj) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE(self); + + nm_assert(NM_IS_CLIENT(client)); + nm_assert(priv->client == client); + priv->client = NULL; + + clear_properties(self, client); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMObject *self = NM_OBJECT(object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string(value, nm_object_get_path(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_object_init(NMObject *object) +{ + NMObject * self = NM_OBJECT(object); + NMObjectPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_OBJECT, NMObjectPrivate); + + self->_priv = priv; + + c_list_init(&self->obj_base.queue_notify_lst); + + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(NML_DBUS_LOG_LEVEL_TRACE), + "nmobj[" NM_HASH_OBFUSCATE_PTR_FMT "]: creating", + NM_HASH_OBFUSCATE_PTR(self)); +} + +static void +dispose(GObject *object) +{ + NMObject * self = NM_OBJECT(object); + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE(self); + + if (!self->obj_base.is_disposing) { + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(NML_DBUS_LOG_LEVEL_TRACE), + "nmobj[" NM_HASH_OBFUSCATE_PTR_FMT "]: disposing", + NM_HASH_OBFUSCATE_PTR(self)); + } + + self->obj_base.is_disposing = TRUE; + + nm_assert(c_list_is_empty(&self->obj_base.queue_notify_lst)); + nm_assert(!priv->client); + nm_assert(!priv->dbobj || !priv->dbobj->nmobj); + + clear_properties(self, NULL); + + G_OBJECT_CLASS(nm_object_parent_class)->dispose(object); + + nm_clear_pointer(&priv->dbobj, nml_dbus_object_unref); +} + +static void +nm_object_class_init(NMObjectClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + _nm_object_class = klass; + + g_type_class_add_private(klass, sizeof(NMObjectPrivate)); + + object_class->get_property = get_property; + object_class->dispose = dispose; + + klass->register_client = register_client; + klass->unregister_client = unregister_client; + klass->is_ready = is_ready; + klass->obj_changed_notify = obj_changed_notify; + + /** + * NMObject:path: + * + * The D-Bus object path. + **/ + obj_properties[PROP_PATH] = g_param_spec_string(NM_OBJECT_PATH, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/libnm-client-impl/nm-property-infos-dbus.xml b/src/libnm-client-impl/nm-property-infos-dbus.xml new file mode 100644 index 00000000..216ed1e7 --- /dev/null +++ b/src/libnm-client-impl/nm-property-infos-dbus.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml new file mode 100644 index 00000000..df38ca79 --- /dev/null +++ b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-keyfile.xml b/src/libnm-client-impl/nm-property-infos-keyfile.xml new file mode 100644 index 00000000..486d61e9 --- /dev/null +++ b/src/libnm-client-impl/nm-property-infos-keyfile.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-nmcli.xml b/src/libnm-client-impl/nm-property-infos-nmcli.xml new file mode 100644 index 00000000..49f2e220 --- /dev/null +++ b/src/libnm-client-impl/nm-property-infos-nmcli.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libnm-client-impl/nm-remote-connection-private.h b/src/libnm-client-impl/nm-remote-connection-private.h new file mode 100644 index 00000000..3ca934bf --- /dev/null +++ b/src/libnm-client-impl/nm-remote-connection-private.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef __NM_REMOTE_CONNECTION_PRIVATE_H__ +#define __NM_REMOTE_CONNECTION_PRIVATE_H__ + +#if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE) + #error Cannot use this header. +#endif + +#define NM_REMOTE_CONNECTION_INIT_RESULT "init-result" + +typedef enum { + NM_REMOTE_CONNECTION_INIT_RESULT_UNKNOWN = 0, + NM_REMOTE_CONNECTION_INIT_RESULT_SUCCESS, + NM_REMOTE_CONNECTION_INIT_RESULT_ERROR, + NM_REMOTE_CONNECTION_INIT_RESULT_INVISIBLE, +} NMRemoteConnectionInitResult; + +#endif /* __NM_REMOTE_CONNECTION_PRIVATE__ */ diff --git a/src/libnm-client-impl/nm-remote-connection.c b/src/libnm-client-impl/nm-remote-connection.c new file mode 100644 index 00000000..8e36cf6d --- /dev/null +++ b/src/libnm-client-impl/nm-remote-connection.c @@ -0,0 +1,839 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-remote-connection.h" + +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-dbus-interface.h" +#include "nm-utils.h" +#include "nm-setting-connection.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-remote-connection-private.h" +#include "nm-object-private.h" +#include "nm-dbus-helpers.h" + +/** + * SECTION:nm-remote-connection + * @short_description: A connection managed by NetworkManager server + * + * A #NMRemoteConnection represents a connection that is exported via + * NetworkManager D-Bus interface. + **/ + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMRemoteConnection, + PROP_UNSAVED, + PROP_FLAGS, + PROP_FILENAME, + PROP_VISIBLE, ); + +typedef struct { + GCancellable *get_settings_cancellable; + + char * filename; + guint32 flags; + bool unsaved; + + bool visible : 1; + bool is_initialized : 1; +} NMRemoteConnectionPrivate; + +struct _NMRemoteConnection { + NMObject parent; + NMRemoteConnectionPrivate _priv; +}; + +struct _NMRemoteConnectionClass { + NMObjectClass parent_class; +}; + +static void nm_remote_connection_connection_iface_init(NMConnectionInterface *iface); + +G_DEFINE_TYPE_WITH_CODE(NMRemoteConnection, + nm_remote_connection, + NM_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(NM_TYPE_CONNECTION, + nm_remote_connection_connection_iface_init);) + +#define NM_REMOTE_CONNECTION_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMRemoteConnection, NM_IS_REMOTE_CONNECTION, NMObject) + +/*****************************************************************************/ + +/** + * nm_remote_connection_update2: + * @connection: the #NMRemoteConnection + * @settings: (allow-none): optional connection to update the settings. + * @flags: update-flags + * @args: (allow-none): optional arguments. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the commit operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously calls the Update2() D-Bus method. + * + * Since: 1.12 + **/ +void +nm_remote_connection_update2(NMRemoteConnection * connection, + GVariant * settings, + NMSettingsUpdate2Flags flags, + GVariant * args, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(!settings || g_variant_is_of_type(settings, NM_VARIANT_TYPE_CONNECTION)); + g_return_if_fail(!args || g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}"))); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + if (!settings) + settings = nm_g_variant_singleton_aLsaLsvII(); + if (!args) + args = nm_g_variant_singleton_aLsvI(); + + _nm_client_dbus_call(_nm_object_get_client(connection), + connection, + nm_remote_connection_update2, + cancellable, + callback, + user_data, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Update2", + g_variant_new("(@a{sa{sv}}u@a{sv})", settings, (guint32) flags, args), + G_VARIANT_TYPE("(a{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_remote_connection_update2_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_commit_changes_async(). + * + * Returns: (transfer full): on success, a #GVariant of type "a{sv}" with the result. On failure, + * %NULL. + **/ +GVariant * +nm_remote_connection_update2_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + GVariant * v_result; + + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), NULL); + g_return_val_if_fail(nm_g_task_is_valid(result, connection, nm_remote_connection_update2), + NULL); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sv})", &v_result); + + return v_result; +} + +/*****************************************************************************/ + +/** + * nm_remote_connection_commit_changes: + * @connection: the #NMRemoteConnection + * @save_to_disk: whether to persist the changes to disk + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Send any local changes to the settings and properties of @connection to + * NetworkManager. If @save_to_disk is %TRUE, the updated connection will be saved to + * disk; if %FALSE, then only the in-memory representation will be changed. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Deprecated: 1.22: Use nm_remote_connection_commit_changes_async() or GDBusConnection. + **/ +gboolean +nm_remote_connection_commit_changes(NMRemoteConnection *connection, + gboolean save_to_disk, + GCancellable * cancellable, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + ret = _nm_client_dbus_call_sync( + _nm_object_get_client(connection), + cancellable, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Update2", + g_variant_new("(@a{sa{sv}}u@a{sv})", + nm_connection_to_dbus(NM_CONNECTION(connection), NM_CONNECTION_SERIALIZE_ALL), + (guint32) (save_to_disk ? NM_SETTINGS_UPDATE2_FLAG_TO_DISK + : NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY), + nm_g_variant_singleton_aLsvI()), + G_VARIANT_TYPE("(a{sv})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return FALSE; + + return TRUE; +} + +/** + * nm_remote_connection_commit_changes_async: + * @connection: the #NMRemoteConnection + * @save_to_disk: whether to save the changes to persistent storage + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the commit operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously sends any local changes to the settings and properties of + * @connection to NetworkManager. If @save is %TRUE, the updated connection will + * be saved to disk; if %FALSE, then only the in-memory representation will be + * changed. + **/ +void +nm_remote_connection_commit_changes_async(NMRemoteConnection *connection, + gboolean save_to_disk, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + nm_remote_connection_update2( + connection, + nm_connection_to_dbus(NM_CONNECTION(connection), NM_CONNECTION_SERIALIZE_ALL), + save_to_disk ? NM_SETTINGS_UPDATE2_FLAG_TO_DISK : NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, + NULL, + cancellable, + callback, + user_data); +} + +/** + * nm_remote_connection_commit_changes_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_commit_changes_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + **/ +gboolean +nm_remote_connection_commit_changes_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + gs_unref_variant GVariant *v_result = NULL; + + v_result = nm_remote_connection_update2_finish(connection, result, error); + return !!v_result; +} + +/*****************************************************************************/ + +/** + * nm_remote_connection_save: + * @connection: the #NMRemoteConnection + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Saves the connection to disk if the connection has changes that have not yet + * been written to disk, or if the connection has never been saved. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Deprecated: 1.22: Use nm_remote_connection_save_async() or GDBusConnection. + **/ +gboolean +nm_remote_connection_save(NMRemoteConnection *connection, GCancellable *cancellable, GError **error) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(connection), + cancellable, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Save", + g_variant_new("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_remote_connection_save_async: + * @connection: the #NMRemoteConnection + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the save operation completes + * @user_data: caller-specific data passed to @callback + * + * Saves the connection to disk if the connection has changes that have not yet + * been written to disk, or if the connection has never been saved. + **/ +void +nm_remote_connection_save_async(NMRemoteConnection *connection, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(connection), + connection, + nm_remote_connection_save_async, + cancellable, + callback, + user_data, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Save", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_remote_connection_save_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_save_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + **/ +gboolean +nm_remote_connection_save_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, connection, nm_remote_connection_save_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_remote_connection_delete: + * @connection: the #NMRemoteConnection + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Deletes the connection. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + * + * Deprecated: 1.22: Use nm_remote_connection_delete_async() or GDBusConnection. + **/ +gboolean +nm_remote_connection_delete(NMRemoteConnection *connection, + GCancellable * cancellable, + GError ** error) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + + return _nm_client_dbus_call_sync_void(_nm_object_get_client(connection), + cancellable, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Delete", + g_variant_new("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); +} + +/** + * nm_remote_connection_delete_async: + * @connection: the #NMRemoteConnection + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the delete operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously deletes the connection. + **/ +void +nm_remote_connection_delete_async(NMRemoteConnection *connection, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(connection), + connection, + nm_remote_connection_delete_async, + cancellable, + callback, + user_data, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "Delete", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_void_strip_dbus_error_cb); +} + +/** + * nm_remote_connection_delete_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_delete_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + **/ +gboolean +nm_remote_connection_delete_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, connection, nm_remote_connection_delete_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_remote_connection_get_secrets: + * @connection: the #NMRemoteConnection + * @setting_name: the #NMSetting object name to get secrets for + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Request the connection's secrets. Note that this is a blocking D-Bus call, + * not a simple property accessor. + * + * Returns: (transfer full): a #GVariant of type %NM_VARIANT_TYPE_CONNECTION containing + * @connection's secrets, or %NULL on error. + * + * Deprecated: 1.22: Use nm_remote_connection_get_secrets_async() or GDBusConnection. + **/ +GVariant * +nm_remote_connection_get_secrets(NMRemoteConnection *connection, + const char * setting_name, + GCancellable * cancellable, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + GVariant * secrets; + + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), NULL); + g_return_val_if_fail(setting_name, NULL); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), NULL); + + ret = _nm_client_dbus_call_sync(_nm_object_get_client(connection), + cancellable, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "GetSecrets", + g_variant_new("(s)", setting_name), + G_VARIANT_TYPE("(a{sa{sv}})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + TRUE, + error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sa{sv}})", &secrets); + + return secrets; +} + +/** + * nm_remote_connection_get_secrets_async: + * @connection: the #NMRemoteConnection + * @setting_name: the #NMSetting object name to get secrets for + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the secret request completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously requests the connection's secrets. + **/ +void +nm_remote_connection_get_secrets_async(NMRemoteConnection *connection, + const char * setting_name, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_REMOTE_CONNECTION(connection)); + g_return_if_fail(setting_name); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + _nm_client_dbus_call(_nm_object_get_client(connection), + connection, + nm_remote_connection_get_secrets_async, + cancellable, + callback, + user_data, + _nm_object_get_path(connection), + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + "GetSecrets", + g_variant_new("(s)", setting_name), + G_VARIANT_TYPE("(a{sa{sv}})"), + G_DBUS_CALL_FLAGS_NONE, + NM_DBUS_DEFAULT_TIMEOUT_MSEC, + nm_dbus_connection_call_finish_variant_strip_dbus_error_cb); +} + +/** + * nm_remote_connection_get_secrets_finish: + * @connection: the #NMRemoteConnection + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_remote_connection_get_secrets_async(). + * + * Returns: (transfer full): a #GVariant of type %NM_VARIANT_TYPE_CONNECTION + * containing @connection's secrets, or %NULL on error. + **/ +GVariant * +nm_remote_connection_get_secrets_finish(NMRemoteConnection *connection, + GAsyncResult * result, + GError ** error) +{ + gs_unref_variant GVariant *ret = NULL; + GVariant * secrets; + + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), NULL); + g_return_val_if_fail( + nm_g_task_is_valid(result, connection, nm_remote_connection_get_secrets_async), + FALSE); + + ret = g_task_propagate_pointer(G_TASK(result), error); + if (!ret) + return NULL; + + g_variant_get(ret, "(@a{sa{sv}})", &secrets); + + return secrets; +} + +/** + * nm_remote_connection_get_unsaved: + * @connection: the #NMRemoteConnection + * + * Returns: %TRUE if the remote connection contains changes that have not + * been saved to disk, %FALSE if the connection is the same as its on-disk + * representation. + **/ +gboolean +nm_remote_connection_get_unsaved(NMRemoteConnection *connection) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + + return NM_REMOTE_CONNECTION_GET_PRIVATE(connection)->unsaved; +} + +/** + * nm_remote_connection_get_flags: + * @connection: the #NMRemoteConnection + * + * Returns: the flags of the connection of type #NMSettingsConnectionFlags. + * + * Since: 1.12 + **/ +NMSettingsConnectionFlags +nm_remote_connection_get_flags(NMRemoteConnection *connection) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + + return (NMSettingsConnectionFlags) NM_REMOTE_CONNECTION_GET_PRIVATE(connection)->flags; +} + +/** + * nm_remote_connection_get_filename: + * @connection: the #NMRemoteConnection + * + * Returns: file that stores the connection in case the connection is file-backed. + * + * Since: 1.12 + **/ +const char * +nm_remote_connection_get_filename(NMRemoteConnection *connection) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), NULL); + + return NM_REMOTE_CONNECTION_GET_PRIVATE(connection)->filename; +} + +/** + * nm_remote_connection_get_visible: + * @connection: the #NMRemoteConnection + * + * Checks if the connection is visible to the current user. If the + * connection is not visible then it is essentially useless; it will + * not contain any settings, and operations such as + * nm_remote_connection_save() and nm_remote_connection_delete() will + * always fail. (#NMRemoteSettings will not normally return + * non-visible connections to callers, but it is possible for a + * connection's visibility to change after you already have a + * reference to it.) + * + * Returns: %TRUE if the remote connection is visible to the current + * user, %FALSE if not. + **/ +gboolean +nm_remote_connection_get_visible(NMRemoteConnection *connection) +{ + g_return_val_if_fail(NM_IS_REMOTE_CONNECTION(connection), FALSE); + + return NM_REMOTE_CONNECTION_GET_PRIVATE(connection)->visible; +} + +/*****************************************************************************/ + +GCancellable * +_nm_remote_settings_get_settings_prepare(NMRemoteConnection *self) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE(self); + + nm_clear_g_cancellable(&priv->get_settings_cancellable); + priv->get_settings_cancellable = g_cancellable_new(); + return priv->get_settings_cancellable; +} + +void +_nm_remote_settings_get_settings_commit(NMRemoteConnection *self, GVariant *settings) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE(self); + GError * error = NULL; + gboolean visible = FALSE; + gboolean changed = FALSE; + + g_clear_object(&priv->get_settings_cancellable); + + if (!priv->is_initialized) { + changed = TRUE; + priv->is_initialized = TRUE; + } + + if (settings) { + if (!_nm_connection_replace_settings((NMConnection *) self, + settings, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT, + &error)) { + NML_NMCLIENT_LOG_E(_nm_object_get_client(self), + "[%s] failure to update settings: %s", + _nm_object_get_path(self), + error->message); + g_clear_error(&error); + } else + visible = TRUE; + } else + nm_connection_clear_settings(NM_CONNECTION(self)); + + if (priv->visible != visible) { + priv->visible = visible; + _nm_client_queue_notify_object(_nm_object_get_client(self), + self, + obj_properties[PROP_VISIBLE]); + changed = TRUE; + } + + if (changed) + _nm_client_notify_object_changed(_nm_object_get_client(self), _nm_object_get_dbobj(self)); +} + +/*****************************************************************************/ + +static gboolean +is_ready(NMObject *nmobj) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE(nmobj); + + if (!priv->is_initialized) + return FALSE; + + return NM_OBJECT_CLASS(nm_remote_connection_parent_class)->is_ready(nmobj); +} + +/*****************************************************************************/ + +static void +register_client(NMObject *nmobj, NMClient *client, NMLDBusObject *dbobj) +{ + NM_OBJECT_CLASS(nm_remote_connection_parent_class)->register_client(nmobj, client, dbobj); + nm_connection_set_path(NM_CONNECTION(nmobj), dbobj->dbus_path->str); + _nm_client_get_settings_call(client, dbobj); +} + +static void +unregister_client(NMObject *nmobj, NMClient *client, NMLDBusObject *dbobj) +{ + nm_clear_g_cancellable(&NM_REMOTE_CONNECTION_GET_PRIVATE(nmobj)->get_settings_cancellable); + NM_OBJECT_CLASS(nm_remote_connection_parent_class)->unregister_client(nmobj, client, dbobj); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_UNSAVED: + g_value_set_boolean(value, NM_REMOTE_CONNECTION_GET_PRIVATE(object)->unsaved); + break; + case PROP_FLAGS: + g_value_set_uint(value, NM_REMOTE_CONNECTION_GET_PRIVATE(object)->flags); + break; + case PROP_FILENAME: + g_value_set_string(value, NM_REMOTE_CONNECTION_GET_PRIVATE(object)->filename); + break; + case PROP_VISIBLE: + g_value_set_boolean(value, NM_REMOTE_CONNECTION_GET_PRIVATE(object)->visible); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_remote_connection_init(NMRemoteConnection *self) +{} + +static void +dispose(GObject *object) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE(object); + + nm_clear_g_free(&priv->filename); + + G_OBJECT_CLASS(nm_remote_connection_parent_class)->dispose(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_settings_connection = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + nm_remote_connection_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Filename", + PROP_FILENAME, + NMRemoteConnection, + _priv.filename), + NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMRemoteConnection, _priv.flags), + NML_DBUS_META_PROPERTY_INIT_B("Unsaved", + PROP_UNSAVED, + NMRemoteConnection, + _priv.unsaved), ), ); + +static void +nm_remote_connection_class_init(NMRemoteConnectionClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + object_class->dispose = dispose; + + nm_object_class->is_ready = is_ready; + nm_object_class->register_client = register_client; + nm_object_class->unregister_client = unregister_client; + + /** + * NMRemoteConnection:unsaved: + * + * %TRUE if the remote connection contains changes that have not been saved + * to disk, %FALSE if the connection is the same as its on-disk representation. + **/ + obj_properties[PROP_UNSAVED] = g_param_spec_boolean(NM_REMOTE_CONNECTION_UNSAVED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMRemoteConnection:flags: + * + * The flags of the connection as unsigned integer. The values + * correspond to the #NMSettingsConnectionFlags enum. + * + * Since: 1.12 + **/ + obj_properties[PROP_FLAGS] = g_param_spec_uint(NM_REMOTE_CONNECTION_FLAGS, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMRemoteConnection:filename: + * + * File that stores the connection in case the connection is + * file-backed. + * + * Since: 1.12 + **/ + obj_properties[PROP_FILENAME] = g_param_spec_string(NM_REMOTE_CONNECTION_FILENAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMRemoteConnection:visible: + * + * %TRUE if the remote connection is visible to the current user, %FALSE if + * not. If the connection is not visible then it is essentially useless; it + * will not contain any settings, and operations such as + * nm_remote_connection_save() and nm_remote_connection_delete() will always + * fail. (#NMRemoteSettings will not normally return non-visible connections + * to callers, but it is possible for a connection's visibility to change + * after you already have a reference to it.) + **/ + obj_properties[PROP_VISIBLE] = g_param_spec_boolean(NM_REMOTE_CONNECTION_VISIBLE, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_settings_connection); +} + +static void +nm_remote_connection_connection_iface_init(NMConnectionInterface *iface) +{} diff --git a/src/libnm-client-impl/nm-secret-agent-old.c b/src/libnm-client-impl/nm-secret-agent-old.c new file mode 100644 index 00000000..881dfd54 --- /dev/null +++ b/src/libnm-client-impl/nm-secret-agent-old.c @@ -0,0 +1,1995 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2010 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-secret-agent-old.h" + +#include "c-list/src/c-list.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-dbus-helpers.h" +#include "nm-dbus-interface.h" +#include "nm-enum-types.h" +#include "libnm-glib-aux/nm-c-list.h" +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "libnm-glib-aux/nm-time-utils.h" +#include "nm-simple-connection.h" + +#define REGISTER_RETRY_TIMEOUT_MSEC 3000 +#define _CALL_REGISTER_TIMEOUT_MSEC 15000 + +/*****************************************************************************/ + +typedef struct { + char * connection_path; + char * setting_name; + GDBusMethodInvocation *context; + CList gsi_lst; + bool is_cancelling : 1; +} GetSecretsInfo; + +NM_GOBJECT_PROPERTIES_DEFINE(NMSecretAgentOld, + PROP_IDENTIFIER, + PROP_AUTO_REGISTER, + PROP_REGISTERED, + PROP_CAPABILITIES, + PROP_DBUS_CONNECTION, ); + +typedef struct { + GDBusConnection *dbus_connection; + GMainContext * main_context; + GMainContext * dbus_context; + GObject * context_busy_watcher; + GCancellable * name_owner_cancellable; + GCancellable * registering_cancellable; + GSource * registering_retry_source; + + NMLInitData *init_data; + + CList gsi_lst_head; + + CList pending_tasks_register_lst_head; + + char *identifier; + + NMRefString *name_owner_curr; + NMRefString *name_owner_next; + + gint64 registering_timeout_msec; + + guint name_owner_changed_id; + + guint exported_id; + + guint capabilities; + + guint8 registering_try_count; + + guint8 register_state_change_reenter : 2; + + bool session_bus : 1; + + bool auto_register : 1; + + bool is_registered : 1; + + bool is_enabled : 1; + + bool registration_force_unregister : 1; + + /* This is true, if we either are in the process of RegisterWithCapabilities() or + * are already successfully registered. + * + * This is only TRUE, if the name owner was authenticated to run as root user. + * + * It also means, we should follow up with an Unregister() call during shutdown. */ + bool registered_against_server : 1; + + bool is_initialized : 1; + bool is_destroyed : 1; +} NMSecretAgentOldPrivate; + +static void nm_secret_agent_old_initable_iface_init(GInitableIface *iface); +static void nm_secret_agent_old_async_initable_iface_init(GAsyncInitableIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE( + NMSecretAgentOld, + nm_secret_agent_old, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, nm_secret_agent_old_initable_iface_init); + G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, nm_secret_agent_old_async_initable_iface_init);) + +#define NM_SECRET_AGENT_OLD_GET_PRIVATE(self) \ + (G_TYPE_INSTANCE_GET_PRIVATE((self), NM_TYPE_SECRET_AGENT_OLD, NMSecretAgentOldPrivate)) + +/*****************************************************************************/ + +#define _NMLOG(level, ...) \ + NML_DBUS_LOG((level), \ + "secret-agent[" NM_HASH_OBFUSCATE_PTR_FMT \ + "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)) + +/*****************************************************************************/ + +static const GDBusInterfaceInfo interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( + NM_DBUS_INTERFACE_SECRET_AGENT, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS( + NM_DEFINE_GDBUS_METHOD_INFO("GetSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), + NM_DEFINE_GDBUS_ARG_INFO("setting_name", "s"), + NM_DEFINE_GDBUS_ARG_INFO("hints", "as"), + NM_DEFINE_GDBUS_ARG_INFO("flags", "u"), ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("secrets", "a{sa{sv}}"), ), ), + NM_DEFINE_GDBUS_METHOD_INFO("CancelGetSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), + NM_DEFINE_GDBUS_ARG_INFO("setting_name", "s"), ), ), + NM_DEFINE_GDBUS_METHOD_INFO("SaveSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), ), ), + NM_DEFINE_GDBUS_METHOD_INFO( + "DeleteSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), ), ), ), ); + +/*****************************************************************************/ + +static void _register_state_change(NMSecretAgentOld *self); + +static void _register_dbus_call(NMSecretAgentOld *self); + +static void _init_complete(NMSecretAgentOld *self, GError *error_take); + +static void _register_state_complete(NMSecretAgentOld *self); + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_get_dbus_connection: + * @self: the #NMSecretAgentOld instance + * + * Returns: (transfer none): the #GDBusConnection used by the secret agent. + * You may either set this as construct property %NM_SECRET_AGENT_OLD_DBUS_CONNECTION, + * or it will automatically set during initialization. + * + * Since: 1.24 + */ +GDBusConnection * +nm_secret_agent_old_get_dbus_connection(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->dbus_connection; +} + +/** + * nm_secret_agent_old_get_main_context: + * @self: the #NMSecretAgentOld instance + * + * Returns: (transfer none): the #GMainContext instance associate with the + * instance. This is the g_main_context_get_thread_default() at the time + * when creating the instance. + * + * Since: 1.24 + */ +GMainContext * +nm_secret_agent_old_get_main_context(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->main_context; +} + +/** + * nm_secret_agent_old_get_context_busy_watcher: + * @self: the #NMSecretAgentOld instance + * + * Returns a #GObject that stays alive as long as there are pending + * requests in the #GDBusConnection. Such requests keep the #GMainContext + * alive, and thus you may want to keep iterating the context as long + * until a weak reference indicates that this object is gone. This is + * useful because even when you destroy the instance right away (and all + * the internally pending requests get cancelled), any pending g_dbus_connection_call() + * requests will still invoke the result on the #GMainContext. Hence, this + * allows you to know how long you must iterate the context to know + * that all remains are cleaned up. + * + * Returns: (transfer none): a #GObject that you may register a weak pointer + * to know that the #GMainContext is still kept busy by @self. + * + * Since: 1.24 + */ +GObject * +nm_secret_agent_old_get_context_busy_watcher(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->context_busy_watcher; +} + +/** + * nm_secret_agent_old_get_dbus_name_owner: + * @self: the #NMSecretAgentOld instance + * + * Returns: the current D-Bus name owner. While this property + * is set while registering, it really only makes sense when + * the nm_secret_agent_old_get_registered() indicates that + * registration is successful. + * + * Since: 1.24 + */ +const char * +nm_secret_agent_old_get_dbus_name_owner(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); + + return nm_ref_string_get_str(NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->name_owner_curr); +} + +/** + * nm_secret_agent_old_get_registered: + * @self: a #NMSecretAgentOld + * + * Note that the secret agent transparently registers and re-registers + * as the D-Bus name owner appears. Hence, this property is not really + * useful. Also, to be graceful against races during registration, the + * instance will already accept requests while being in the process of + * registering. + * If you need to avoid races and want to wait until @self is registered, + * call nm_secret_agent_old_register_async(). If that function completes + * with success, you know the instance is registered. + * + * Returns: a %TRUE if the agent is registered, %FALSE if it is not. + **/ +gboolean +nm_secret_agent_old_get_registered(NMSecretAgentOld *self) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->is_registered; +} + +/*****************************************************************************/ + +static void +get_secret_info_free(GetSecretsInfo *info) +{ + nm_assert(info); + nm_assert(!info->context); + + c_list_unlink_stale(&info->gsi_lst); + g_free(info->connection_path); + g_free(info->setting_name); + nm_g_slice_free(info); +} + +static void +get_secret_info_complete_and_free(GetSecretsInfo *info, GVariant *secrets, GError *error) +{ + if (error) { + if (secrets) + nm_g_variant_unref_floating(secrets); + g_dbus_method_invocation_return_gerror(g_steal_pointer(&info->context), error); + } else { + g_dbus_method_invocation_return_value(g_steal_pointer(&info->context), + g_variant_new("(@a{sa{sv}})", secrets)); + } + get_secret_info_free(info); +} + +static void +get_secret_info_complete_and_free_error(GetSecretsInfo *info, + GQuark error_domain, + int error_code, + const char * error_message) +{ + g_dbus_method_invocation_return_error_literal(g_steal_pointer(&info->context), + error_domain, + error_code, + error_message); + get_secret_info_free(info); +} + +/*****************************************************************************/ + +static void +_dbus_connection_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + gs_unref_object GObject *context_busy_watcher = NULL; + GAsyncReadyCallback callback; + gpointer callback_user_data; + + nm_utils_user_data_unpack(user_data, &context_busy_watcher, &callback, &callback_user_data); + callback(source, result, callback_user_data); +} + +static void +_dbus_connection_call(NMSecretAgentOld * self, + const char * bus_name, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + int timeout_msec, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + nm_assert(nm_g_main_context_is_thread_default(priv->dbus_context)); + + g_dbus_connection_call( + priv->dbus_connection, + bus_name, + object_path, + interface_name, + method_name, + parameters, + reply_type, + flags, + timeout_msec, + cancellable, + callback ? _dbus_connection_call_cb : NULL, + callback + ? nm_utils_user_data_pack(g_object_ref(priv->context_busy_watcher), callback, user_data) + : NULL); +} + +/*****************************************************************************/ + +static GetSecretsInfo * +find_get_secrets_info(NMSecretAgentOldPrivate *priv, + const char * connection_path, + const char * setting_name) +{ + GetSecretsInfo *info; + + c_list_for_each_entry (info, &priv->gsi_lst_head, gsi_lst) { + if (nm_streq(connection_path, info->connection_path) + && nm_streq(setting_name, info->setting_name)) + return info; + } + return NULL; +} + +static void +_cancel_get_secret_request(NMSecretAgentOld *self, GetSecretsInfo *info, const char *message) +{ + c_list_unlink(&info->gsi_lst); + info->is_cancelling = TRUE; + + _LOGT("cancel get-secrets request \"%s\", \"%s\": %s", + info->connection_path, + info->setting_name, + message); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->cancel_get_secrets(self, + info->connection_path, + info->setting_name); + + get_secret_info_complete_and_free_error(info, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_AGENT_CANCELED, + message); +} + +static gboolean +verify_request(NMSecretAgentOld * self, + GDBusMethodInvocation *context, + GVariant * connection_dict, + const char * connection_path, + NMConnection ** out_connection, + GError ** error) +{ + gs_unref_object NMConnection *connection = NULL; + gs_free_error GError *local = NULL; + + if (!nm_dbus_path_not_empty(connection_path)) { + g_set_error_literal(error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "Invalid connection: no connection path given."); + return FALSE; + } + + connection = _nm_simple_connection_new_from_dbus(connection_dict, + NM_SETTING_PARSE_FLAGS_BEST_EFFORT, + &local); + if (!connection) { + g_set_error(error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "Invalid connection: %s", + local->message); + return FALSE; + } + + nm_connection_set_path(connection, connection_path); + NM_SET_OUT(out_connection, g_steal_pointer(&connection)); + return TRUE; +} + +static void +get_secrets_cb(NMSecretAgentOld *self, + NMConnection * connection, + GVariant * secrets, + GError * error, + gpointer user_data) +{ + GetSecretsInfo *info = user_data; + + if (info->is_cancelling) { + if (secrets) + nm_g_variant_unref_floating(secrets); + return; + } + + _LOGT("request: get-secrets request \"%s\", \"%s\" complete with %s%s%s", + info->connection_path, + info->setting_name, + NM_PRINT_FMT_QUOTED(error, "error: ", error->message, "", "success")); + + get_secret_info_complete_and_free(info, secrets, error); +} + +static void +impl_get_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + GError * error = NULL; + gs_unref_object NMConnection *connection = NULL; + GetSecretsInfo * info; + gs_unref_variant GVariant *arg_connection = NULL; + const char * arg_connection_path; + const char * arg_setting_name; + gs_free const char ** arg_hints = NULL; + guint32 arg_flags; + + g_variant_get(parameters, + "(@a{sa{sv}}&o&s^a&su)", + &arg_connection, + &arg_connection_path, + &arg_setting_name, + &arg_hints, + &arg_flags); + + if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + _LOGT("request: get-secrets(\"%s\", \"%s\")", arg_connection_path, arg_setting_name); + + info = find_get_secrets_info(priv, arg_connection_path, arg_setting_name); + if (info) + _cancel_get_secret_request(self, info, "Request aborted due to new request"); + + info = g_slice_new(GetSecretsInfo); + *info = (GetSecretsInfo){ + .context = context, + .connection_path = g_strdup(arg_connection_path), + .setting_name = g_strdup(arg_setting_name), + }; + c_list_link_tail(&priv->gsi_lst_head, &info->gsi_lst); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->get_secrets(self, + connection, + info->connection_path, + info->setting_name, + arg_hints, + arg_flags, + get_secrets_cb, + info); +} + +static void +impl_cancel_get_secrets(NMSecretAgentOld * self, + GVariant * parameters, + GDBusMethodInvocation *context) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + GetSecretsInfo * info; + const char * arg_connection_path; + const char * arg_setting_name; + + g_variant_get(parameters, "(&o&s)", &arg_connection_path, &arg_setting_name); + + info = find_get_secrets_info(priv, arg_connection_path, arg_setting_name); + if (!info) { + g_dbus_method_invocation_return_error_literal( + context, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "No secrets request in progress for this connection."); + return; + } + + _cancel_get_secret_request(self, info, "Request cancelled by NetworkManager"); + + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +save_secrets_cb(NMSecretAgentOld *self, NMConnection *connection, GError *error, gpointer user_data) +{ + GDBusMethodInvocation *context = user_data; + + if (error) + g_dbus_method_invocation_return_gerror(context, error); + else + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_save_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *arg_connection = NULL; + const char * arg_connection_path; + GError * error = NULL; + + g_variant_get(parameters, "(@a{sa{sv}}&o)", &arg_connection, &arg_connection_path); + + if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + _LOGT("request: save-secrets(\"%s\")", arg_connection_path); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->save_secrets(self, + connection, + arg_connection_path, + save_secrets_cb, + context); +} + +static void +delete_secrets_cb(NMSecretAgentOld *self, + NMConnection * connection, + GError * error, + gpointer user_data) +{ + GDBusMethodInvocation *context = user_data; + + if (error) + g_dbus_method_invocation_return_gerror(context, error); + else + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_delete_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *arg_connection = NULL; + const char * arg_connection_path; + GError * error = NULL; + + g_variant_get(parameters, "(@a{sa{sv}}&o)", &arg_connection, &arg_connection_path); + + if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + _LOGT("request: delete-secrets(\"%s\")", arg_connection_path); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->delete_secrets(self, + connection, + arg_connection_path, + delete_secrets_cb, + context); +} + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_enable: + * @self: the #NMSecretAgentOld instance + * @enable: whether to enable or disable the listener. + * + * This has the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER + * property. + * + * Unlike most other functions, you may already call this function before + * initialization completes. + * + * Since: 1.24 + */ +void +nm_secret_agent_old_enable(NMSecretAgentOld *self, gboolean enable) +{ + NMSecretAgentOldPrivate *priv; + + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + enable = (!!enable); + + if (priv->auto_register != enable) { + priv->auto_register = enable; + priv->is_enabled = enable; + _notify(self, PROP_AUTO_REGISTER); + } + _register_state_change(self); +} + +static void +_secret_agent_old_destroy(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + priv->is_destroyed = TRUE; + + if (priv->exported_id != 0) { + g_dbus_connection_unregister_object(priv->dbus_connection, + nm_steal_int(&priv->exported_id)); + } + + _register_state_change(self); + + nm_assert(!priv->name_owner_changed_id); + nm_assert(!priv->name_owner_curr); + nm_assert(!priv->name_owner_next); + nm_assert(!priv->name_owner_cancellable); + nm_assert(!priv->registering_retry_source); + nm_assert(!priv->registering_cancellable); + nm_assert(!priv->init_data); + nm_assert(c_list_is_empty(&priv->gsi_lst_head)); + nm_assert(c_list_is_empty(&priv->pending_tasks_register_lst_head)); +} + +/** + * nm_secret_agent_old_destroy: + * @self: the #NMSecretAgentOld instance. + * + * Since 1.24, the instance will already register a D-Bus object on the + * D-Bus connection during initialization. That object will stay registered + * until @self gets unrefed (destroyed) or this function is called. This + * function performs the necessary cleanup to tear down the instance. Afterwards, + * the function can not longer be used. This is optional, but necessary to + * ensure unregistering the D-Bus object at a define point, when other users + * might still have a reference on @self. + * + * You may call this function any time and repeatedly. However, after destroying + * the instance, it is a bug to still use the instance for other purposes. The + * instance becomes defunct and cannot re-register. + * + * Since: 1.24 + */ +void +nm_secret_agent_old_destroy(NMSecretAgentOld *self) +{ + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + + _LOGT("destroying"); + + _secret_agent_old_destroy(self); +} + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_register: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Registers the #NMSecretAgentOld with the NetworkManager secret manager, + * indicating to NetworkManager that the agent is able to provide and save + * secrets for connections on behalf of its user. + * + * Returns: %TRUE if registration was successful, %FALSE on error. + * + * Since 1.24, this can no longer fail unless the @cancellable gets + * cancelled. Contrary to nm_secret_agent_old_register_async(), this also + * does not wait for the registration to succeed. You cannot synchronously + * (without iterating the caller's GMainContext) wait for registration. + * + * Since 1.24, registration is idempotent. It has the same effect as setting + * %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE or nm_secret_agent_old_enable(). + * + * Deprecated: 1.24: Use nm_secret_agent_old_enable() or nm_secret_agent_old_register_async(). + **/ +gboolean +nm_secret_agent_old_register(NMSecretAgentOld *self, GCancellable *cancellable, GError **error) +{ + NMSecretAgentOldPrivate *priv; + + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_val_if_fail(priv->is_initialized && !priv->is_destroyed, FALSE); + + priv->is_enabled = TRUE; + _register_state_change(self); + + if (g_cancellable_set_error_if_cancelled(cancellable, error)) + return FALSE; + + /* This is a synchronous function, meaning: we are not allowed to iterate + * the caller's GMainContext. This is a catch 22, because we don't want + * to perform synchronous calls that bypasses the ordering of our otherwise + * asynchronous mode of operation. Hence, we always signal success. + * That's why this function is deprecated. + * + * So despite claiming success, we might still be in the process of registering + * or NetworkManager might not be available. + * + * This is a change in behavior with respect to libnm before 1.24. + */ + return TRUE; +} + +static void +_register_cancelled_cb(GObject *object, gpointer user_data) +{ + GTask * task0 = user_data; + gs_unref_object GTask * task = NULL; + NMSecretAgentOld * self = g_task_get_source_object(task0); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gulong * p_cancelled_id; + NMCListElem * elem; + gs_free_error GError *error = NULL; + + elem = nm_c_list_elem_find_first(&priv->pending_tasks_register_lst_head, x, x == task0); + + g_return_if_fail(elem); + + task = nm_c_list_elem_free_steal(elem); + + p_cancelled_id = g_task_get_task_data(task); + if (p_cancelled_id) { + g_signal_handler_disconnect(g_task_get_cancellable(task), *p_cancelled_id); + g_task_set_task_data(task, NULL, NULL); + } + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + g_task_return_error(task, error); +} + +/** + * nm_secret_agent_old_register_async: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is registered + * @user_data: data for @callback + * + * Asynchronously registers the #NMSecretAgentOld with the NetworkManager secret + * manager, indicating to NetworkManager that the agent is able to provide and + * save secrets for connections on behalf of its user. + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE + * or nm_secret_agent_old_enable(). + * + * Since 1.24, the asynchronous result indicates whether the instance is successfully + * registered. In any case, this call enables the agent and it will automatically + * try to register and handle secret requests. A failure of this function only indicates + * that currently the instance might not be ready (but since it will automatically + * try to recover, it might be ready in a moment afterwards). Use this function if + * you want to check and ensure that the agent is registered. + **/ +void +nm_secret_agent_old_register_async(NMSecretAgentOld * self, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv; + + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_if_fail(priv->is_initialized && !priv->is_destroyed); + + if (callback) { + GTask *task; + + task = nm_g_task_new(self, + cancellable, + nm_secret_agent_old_register_async, + callback, + user_data); + + c_list_link_tail(&priv->pending_tasks_register_lst_head, + &nm_c_list_elem_new_stale(task)->lst); + + if (cancellable) { + gulong cancelled_id; + + cancelled_id = + g_cancellable_connect(cancellable, G_CALLBACK(_register_cancelled_cb), task, NULL); + if (cancelled_id != 0) { + g_task_set_task_data(task, g_memdup(&cancelled_id, sizeof(cancelled_id)), g_free); + } + } + } + + priv->is_enabled = TRUE; + _register_state_change(self); +} + +/** + * nm_secret_agent_old_register_finish: + * @self: a #NMSecretAgentOld + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_old_register_async(). + * + * Returns: %TRUE if registration was successful, %FALSE on error. + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE + * or nm_secret_agent_old_enable(). + **/ +gboolean +nm_secret_agent_old_register_finish(NMSecretAgentOld *self, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_secret_agent_old_register_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/** + * nm_secret_agent_old_unregister: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Unregisters the #NMSecretAgentOld with the NetworkManager secret manager, + * indicating to NetworkManager that the agent will no longer provide or + * store secrets on behalf of this user. + * + * Returns: %TRUE if unregistration was successful, %FALSE on error + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE + * or nm_secret_agent_old_enable(). + * + * Deprecated: 1.24: Use nm_secret_agent_old_enable(). + **/ +gboolean +nm_secret_agent_old_unregister(NMSecretAgentOld *self, GCancellable *cancellable, GError **error) +{ + NMSecretAgentOldPrivate *priv; + + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_val_if_fail(priv->is_initialized && !priv->is_destroyed, FALSE); + + priv->is_enabled = FALSE; + _register_state_change(self); + + return !g_cancellable_set_error_if_cancelled(cancellable, error); +} + +/** + * nm_secret_agent_old_unregister_async: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is unregistered + * @user_data: data for @callback + * + * Asynchronously unregisters the #NMSecretAgentOld with the NetworkManager secret + * manager, indicating to NetworkManager that the agent will no longer provide + * or store secrets on behalf of this user. + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE + * or nm_secret_agent_old_enable(). + * + * Deprecated: 1.24: Use nm_secret_agent_old_enable(). + **/ +void +nm_secret_agent_old_unregister_async(NMSecretAgentOld * self, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv; + + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_if_fail(priv->is_initialized && !priv->is_destroyed); + + if (callback) { + gs_unref_object GTask *task = NULL; + + task = nm_g_task_new(self, + cancellable, + nm_secret_agent_old_unregister_async, + callback, + user_data); + g_task_return_boolean(task, TRUE); + } + + priv->is_enabled = FALSE; + _register_state_change(self); +} + +/** + * nm_secret_agent_old_unregister_finish: + * @self: a #NMSecretAgentOld + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_old_unregister_async(). + * + * Returns: %TRUE if unregistration was successful, %FALSE on error. + * + * Since 1.24, registration cannot fail and is idempotent. It has + * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE + * or nm_secret_agent_old_enable(). + * + * Deprecated: 1.24: Use nm_secret_agent_old_enable(). + **/ +gboolean +nm_secret_agent_old_unregister_finish(NMSecretAgentOld *self, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_secret_agent_old_unregister_async), + FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +/** + * nm_secret_agent_old_get_secrets: (virtual get_secrets): + * @self: a #NMSecretAgentOld + * @connection: the #NMConnection for which we're asked secrets + * @setting_name: the name of the secret setting + * @hints: (array zero-terminated=1): hints to the agent + * @flags: flags that modify the behavior of the request + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously retrieves secrets belonging to @connection for the + * setting @setting_name. @flags indicate specific behavior that the secret + * agent should use when performing the request, for example returning only + * existing secrets without user interaction, or requesting entirely new + * secrets from the user. + */ +void +nm_secret_agent_old_get_secrets(NMSecretAgentOld * self, + NMConnection * connection, + const char * setting_name, + const char ** hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentOldGetSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(NM_IS_CONNECTION(connection)); + g_return_if_fail(nm_connection_get_path(connection)); + g_return_if_fail(setting_name && setting_name[0]); + g_return_if_fail(!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM)); + g_return_if_fail(!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS)); + g_return_if_fail(callback != NULL); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->get_secrets(self, + connection, + nm_connection_get_path(connection), + setting_name, + hints, + flags, + callback, + user_data); +} + +/** + * nm_secret_agent_old_save_secrets: (virtual save_secrets): + * @self: a #NMSecretAgentOld + * @connection: a #NMConnection + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously ensures that all secrets inside @connection are stored to + * disk. + */ +void +nm_secret_agent_old_save_secrets(NMSecretAgentOld * self, + NMConnection * connection, + NMSecretAgentOldSaveSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(NM_IS_CONNECTION(connection)); + g_return_if_fail(nm_connection_get_path(connection)); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->save_secrets(self, + connection, + nm_connection_get_path(connection), + callback, + user_data); +} + +/** + * nm_secret_agent_old_delete_secrets: (virtual delete_secrets): + * @self: a #NMSecretAgentOld + * @connection: a #NMConnection + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously asks the agent to delete all saved secrets belonging to + * @connection. + */ +void +nm_secret_agent_old_delete_secrets(NMSecretAgentOld * self, + NMConnection * connection, + NMSecretAgentOldDeleteSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); + g_return_if_fail(NM_IS_CONNECTION(connection)); + g_return_if_fail(nm_connection_get_path(connection)); + + NM_SECRET_AGENT_OLD_GET_CLASS(self)->delete_secrets(self, + connection, + nm_connection_get_path(connection), + callback, + user_data); +} + +/*****************************************************************************/ + +static gboolean +validate_identifier(const char *identifier) +{ + const char *p = identifier; + size_t id_len; + + /* Length between 3 and 255 characters inclusive */ + id_len = strlen(identifier); + if (id_len < 3 || id_len > 255) + return FALSE; + + if ((identifier[0] == '.') || (identifier[id_len - 1] == '.')) + return FALSE; + + /* FIXME: do complete validation here */ + while (p && *p) { + if (!g_ascii_isalnum(*p) && (*p != '_') && (*p != '-') && (*p != '.')) + return FALSE; + if ((*p == '.') && (*(p + 1) == '.')) + return FALSE; + p++; + } + + return TRUE; +} + +/*****************************************************************************/ + +static gboolean +_register_retry_cb(gpointer user_data) +{ + NMSecretAgentOld * self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + nm_clear_g_source_inst(&priv->registering_retry_source); + _register_dbus_call(self); + return G_SOURCE_CONTINUE; +} + +static void +_register_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOld * self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + if (nm_utils_error_is_cancelled(error)) + return; + + nm_assert(!priv->registering_retry_source); + nm_assert(!priv->is_registered); + nm_assert(priv->registering_cancellable); + + if (nm_dbus_error_is(error, NM_DBUS_ERROR_NAME_UNKNOWN_METHOD) + && nm_utils_get_monotonic_timestamp_msec() < priv->registering_timeout_msec) { + guint timeout_msec; + + timeout_msec = (2u << NM_MIN(6u, ++priv->registering_try_count)); + + _LOGT("register: registration failed with error \"%s\". Retry in %u msec...", + error->message, + timeout_msec); + + priv->registering_retry_source = + nm_g_source_attach(nm_g_timeout_source_new(timeout_msec, + G_PRIORITY_DEFAULT, + _register_retry_cb, + self, + NULL), + priv->dbus_context); + return; + } + + g_clear_object(&priv->registering_cancellable); + + if (error) { + /* registration apparently failed. However we still keep priv->registered_against_server TRUE, because + * + * - eventually we want to still make an Unregister() call. Even if it probably has no effect, + * better be sure. + * + * - we actually accept secret request (from the right name owner). We register so that + * NetworkManager knows that we are here. We don't require the registration to succeed + * for our purpose. If NetworkManager makes requests for us, despite the registration + * failing, that is fine. */ + _LOGT("register: registration failed with error \"%s\"", error->message); + goto out; + } + + _LOGT("register: registration succeeded"); + priv->is_registered = TRUE; + _notify(self, PROP_REGISTERED); + +out: + _register_state_complete(self); +} + +static void +_register_dbus_call(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + _dbus_connection_call(self, + nm_ref_string_get_str(priv->name_owner_curr), + NM_DBUS_PATH_AGENT_MANAGER, + NM_DBUS_INTERFACE_AGENT_MANAGER, + "RegisterWithCapabilities", + g_variant_new("(su)", priv->identifier, (guint32) priv->capabilities), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + _CALL_REGISTER_TIMEOUT_MSEC, + priv->registering_cancellable, + _register_call_cb, + self); +} + +static void +_get_connection_unix_user_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOld * self; + NMSecretAgentOldPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + guint32 sender_uid = 0; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = user_data; + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + nm_assert(priv->registering_cancellable); + nm_assert(!priv->registered_against_server); + + if (ret) + g_variant_get(ret, "(u)", &sender_uid); + + if (ret && sender_uid == 0) + _LOGT("register: peer %s is owned by root. Validated to accept requests.", + priv->name_owner_curr->str); + else if (ret && priv->session_bus) { + _LOGT( + "register: peer %s is owned by user %d for session bus. Validated to accept requests.", + priv->name_owner_curr->str, + sender_uid); + } else { + /* the peer is not validated. We don't actually register. */ + if (ret) + _LOGT("register: peer %s is owned by user %u. Not validated as NetworkManager service.", + priv->name_owner_curr->str, + sender_uid); + else + _LOGT("register: failed to get user id for peer %s: %s. Not validated as " + "NetworkManager service.", + priv->name_owner_curr->str, + error->message); + + /* we actually don't do anything and keep the agent unregistered. + * + * We keep priv->registering_cancellable set to not retry this again, until we loose the + * name owner. But the state of the agent is lingering and won't accept any requests. */ + return; + } + + priv->registering_timeout_msec = + nm_utils_get_monotonic_timestamp_msec() + REGISTER_RETRY_TIMEOUT_MSEC; + priv->registering_try_count = 0; + priv->registered_against_server = TRUE; + _register_dbus_call(self); +} + +/*****************************************************************************/ + +static void +_name_owner_changed(NMSecretAgentOld *self, const char *name_owner, gboolean is_event) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + if (is_event) { + if (priv->name_owner_cancellable) { + /* we are still fetching the name-owner. Ignore this event. */ + return; + } + } else + g_clear_object(&priv->name_owner_cancellable); + + nm_ref_string_unref(priv->name_owner_next); + priv->name_owner_next = nm_ref_string_new(nm_str_not_empty(name_owner)); + + _LOGT("name-owner changed: %s%s%s -> %s%s%s", + NM_PRINT_FMT_QUOTED(priv->name_owner_curr, + "\"", + priv->name_owner_curr->str, + "\"", + "(null)"), + NM_PRINT_FMT_QUOTED(priv->name_owner_next, + "\"", + priv->name_owner_next->str, + "\"", + "(null)")); + + _register_state_change(self); +} + +static void +_name_owner_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMSecretAgentOld *self = user_data; + const char * new_owner; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) + return; + + g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); + + _name_owner_changed(self, new_owner, TRUE); +} + +static void +_name_owner_get_cb(const char *name_owner, GError *error, gpointer user_data) +{ + if (name_owner || !nm_utils_error_is_cancelled(error)) + _name_owner_changed(user_data, name_owner, FALSE); +} + +/*****************************************************************************/ + +static void +_method_call(GDBusConnection * connection, + const char * sender, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + GDBusMethodInvocation *context, + gpointer user_data) +{ + NMSecretAgentOld * self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + nm_assert(nm_streq0(object_path, NM_DBUS_PATH_SECRET_AGENT)); + nm_assert(nm_streq0(interface_name, NM_DBUS_INTERFACE_SECRET_AGENT)); + nm_assert(sender); + nm_assert(nm_streq0(sender, g_dbus_method_invocation_get_sender(context))); + + if (!priv->name_owner_curr || !priv->registered_against_server) { + /* priv->registered_against_server means that we started to register, but not necessarily + * that the registration fully succeeded. However, we already authenticated the request + * and so we accept it, even if the registration is not yet complete. */ + g_dbus_method_invocation_return_error_literal(context, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_PERMISSION_DENIED, + "Request by non authenticated peer rejected"); + return; + } + + if (nm_streq(method_name, "GetSecrets")) + impl_get_secrets(self, parameters, context); + else if (nm_streq(method_name, "CancelGetSecrets")) + impl_cancel_get_secrets(self, parameters, context); + else if (nm_streq(method_name, "SaveSecrets")) + impl_save_secrets(self, parameters, context); + else if (nm_streq(method_name, "DeleteSecrets")) + impl_delete_secrets(self, parameters, context); + else + nm_assert_not_reached(); +} + +/*****************************************************************************/ + +static void +_register_state_complete(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + NMCListElem * elem; + gboolean any_tasks_to_complete = FALSE; + + if (!c_list_is_empty(&priv->pending_tasks_register_lst_head)) { + /* add a dummy sentinel. We want to complete all the task we started + * so far, but as we invoke user callbacks, the user might register + * new tasks. Those we don't complete in this run. */ + g_object_ref(self); + any_tasks_to_complete = TRUE; + c_list_link_tail(&priv->pending_tasks_register_lst_head, + &nm_c_list_elem_new_stale(&any_tasks_to_complete)->lst); + } + + _init_complete(self, NULL); + + if (any_tasks_to_complete) { + while ( + (elem = c_list_first_entry(&priv->pending_tasks_register_lst_head, NMCListElem, lst))) { + gpointer data = nm_c_list_elem_free_steal(elem); + gs_unref_object GTask *task = NULL; + + if (data == &any_tasks_to_complete) { + any_tasks_to_complete = FALSE; + break; + } + + task = data; + + if (!priv->is_registered) { + g_task_return_error(task, + g_error_new_literal(NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + _("registration failed"))); + continue; + } + g_task_return_boolean(task, TRUE); + } + nm_assert(!any_tasks_to_complete); + g_object_unref(self); + } +} + +static void +_register_state_change_do(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + if (priv->is_destroyed) + priv->is_enabled = FALSE; + + if (!priv->is_enabled || priv->registration_force_unregister + || priv->name_owner_curr != priv->name_owner_next) { + GetSecretsInfo *info; + + while ((info = c_list_first_entry(&priv->gsi_lst_head, GetSecretsInfo, gsi_lst))) { + _cancel_get_secret_request(self, info, "The secret agent is going away"); + _register_state_change(self); + return; + } + + priv->registration_force_unregister = FALSE; + + nm_clear_g_cancellable(&priv->registering_cancellable); + nm_clear_g_source_inst(&priv->registering_retry_source); + + if (priv->registered_against_server) { + priv->registered_against_server = FALSE; + if (priv->name_owner_curr) { + _LOGT("register: unregister from %s", priv->name_owner_curr->str); + _dbus_connection_call(self, + priv->name_owner_curr->str, + NM_DBUS_PATH_AGENT_MANAGER, + NM_DBUS_INTERFACE_AGENT_MANAGER, + "Unregister", + g_variant_new("()"), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NONE, + _CALL_REGISTER_TIMEOUT_MSEC, + NULL, + NULL, + NULL); + } + } + + if (!priv->is_enabled) { + nm_clear_g_cancellable(&priv->name_owner_cancellable); + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + nm_clear_pointer(&priv->name_owner_curr, nm_ref_string_unref); + nm_clear_pointer(&priv->name_owner_next, nm_ref_string_unref); + } + + if (priv->is_registered) { + priv->is_registered = FALSE; + if (!priv->is_destroyed) { + _LOGT("register: now unregistered"); + _notify(self, PROP_REGISTERED); + _register_state_change(self); + return; + } + } + + if (!priv->is_enabled) { + _register_state_complete(self); + return; + } + + if (priv->name_owner_curr != priv->name_owner_next) { + nm_ref_string_unref(priv->name_owner_curr); + priv->name_owner_curr = nm_ref_string_ref(priv->name_owner_next); + } + } + + if (priv->name_owner_changed_id == 0) { + nm_assert(!priv->name_owner_cancellable); + nm_assert(!priv->name_owner_curr); + nm_assert(!priv->name_owner_next); + priv->name_owner_cancellable = g_cancellable_new(); + priv->name_owner_changed_id = + nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, + NM_DBUS_SERVICE, + _name_owner_changed_cb, + self, + NULL); + nm_dbus_connection_call_get_name_owner(priv->dbus_connection, + NM_DBUS_SERVICE, + -1, + priv->name_owner_cancellable, + _name_owner_get_cb, + self); + return; + } + + if (priv->name_owner_cancellable) { + /* we still wait for the name owner. Nothing to do for now. */ + return; + } + + if (!priv->name_owner_curr) { + /* we don't have a name owner. We are done and wait. */ + _register_state_complete(self); + return; + } + + if (priv->registering_cancellable) { + /* we are already registering... wait longer. */ + return; + } + + nm_assert(!priv->registering_retry_source); + + if (!priv->is_registered) { + /* start registering... */ + priv->registering_cancellable = g_cancellable_new(); + _dbus_connection_call(self, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixUser", + g_variant_new("(s)", priv->name_owner_curr->str), + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + _CALL_REGISTER_TIMEOUT_MSEC, + priv->registering_cancellable, + _get_connection_unix_user_cb, + self); + return; + } + + /* we are fully registered and done. */ + _register_state_complete(self); +} + +static void +_register_state_change(NMSecretAgentOld *self) +{ + _nm_unused gs_unref_object NMSecretAgentOld *self_keep_alive = g_object_ref(self); + NMSecretAgentOldPrivate * priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + + if (priv->register_state_change_reenter == 0) { + /* We are not yet initialized. Do nothing. */ + return; + } + + if (priv->register_state_change_reenter != 1) { + /* Recursive calls are prevented. Do nothing for now, but repeat + * the state change afterwards. */ + priv->register_state_change_reenter = 3; + return; + } + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + +again: + priv->register_state_change_reenter = 2; + + _register_state_change_do(self); + + if (priv->register_state_change_reenter != 2) + goto again; + + priv->register_state_change_reenter = 1; +} + +/*****************************************************************************/ + +static void +_init_complete(NMSecretAgentOld *self, GError *error_take) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gs_free_error GError *error = g_steal_pointer(&error_take); + GError * error_cancelled = NULL; + + if (!priv->init_data) + return; + + if (g_cancellable_set_error_if_cancelled(priv->init_data->cancellable, &error_cancelled)) { + g_clear_error(&error); + g_propagate_error(&error, error_cancelled); + } + + priv->is_initialized = (!error); + + _LOGT("%s init complete with %s%s%s", + priv->init_data->is_sync ? "sync" : "async", + NM_PRINT_FMT_QUOTED(error_take, "error: ", error_take->message, "", "success")); + + nml_init_data_return(g_steal_pointer(&priv->init_data), g_steal_pointer(&error)); +} + +static void +_init_register_object(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gs_free_error GError *error = NULL; + GDBusInterfaceVTable interface_vtable = { + .method_call = _method_call, + }; + + if (g_cancellable_set_error_if_cancelled(priv->init_data->cancellable, &error)) { + _init_complete(self, g_steal_pointer(&error)); + return; + } + + priv->exported_id = g_dbus_connection_register_object(priv->dbus_connection, + NM_DBUS_PATH_SECRET_AGENT, + (GDBusInterfaceInfo *) &interface_info, + &interface_vtable, + self, + NULL, + &error); + if (priv->exported_id == 0) { + _init_complete(self, g_steal_pointer(&error)); + return; + } + + priv->register_state_change_reenter = 1; + + _register_state_change(self); +} + +static void +_init_got_bus(GObject *initable, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOld * self = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + gs_free_error GError *error = NULL; + + priv->dbus_connection = g_bus_get_finish(result, &error); + if (!priv->dbus_connection) { + _init_complete(self, g_steal_pointer(&error)); + return; + } + + _LOGT("init: got GDBusConnection"); + + _notify(self, PROP_DBUS_CONNECTION); + + _init_register_object(self); +} + +static void +_init_start(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + if (!priv->dbus_connection) { + GBusType bus_type; + + bus_type = _nm_dbus_bus_type(); + + priv->session_bus = (bus_type == G_BUS_TYPE_SESSION); + + g_bus_get(bus_type, priv->init_data->cancellable, _init_got_bus, self); + return; + } + + _init_register_object(self); +} + +static void +init_async(GAsyncInitable * initable, + int io_priority, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOld * self; + NMSecretAgentOldClass * klass; + NMSecretAgentOldPrivate *priv; + nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; + gs_unref_object GTask *task = NULL; + + g_return_if_fail(NM_IS_SECRET_AGENT_OLD(initable)); + + self = NM_SECRET_AGENT_OLD(initable); + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_if_fail(!priv->dbus_context); + g_return_if_fail(!priv->is_destroyed); + + klass = NM_SECRET_AGENT_OLD_GET_CLASS(self); + g_return_if_fail(klass->get_secrets); + g_return_if_fail(klass->cancel_get_secrets); + g_return_if_fail(klass->save_secrets); + g_return_if_fail(klass->delete_secrets); + + _LOGT("init-async starting..."); + + priv->dbus_context = g_main_context_ref(priv->main_context); + + dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); + + task = nm_g_task_new(self, cancellable, init_async, callback, user_data); + g_task_set_priority(task, io_priority); + + priv->init_data = nml_init_data_new_async(cancellable, g_steal_pointer(&task)); + + _init_start(self); +} + +static gboolean +init_finish(GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(initable), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, initable, init_async), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +static gboolean +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + gs_unref_object NMSecretAgentOld *self = NULL; + NMSecretAgentOldPrivate * priv; + NMSecretAgentOldClass * klass; + GMainLoop * main_loop; + GError * local_error = NULL; + + g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(initable), FALSE); + + self = g_object_ref(NM_SECRET_AGENT_OLD(initable)); + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + g_return_val_if_fail(!priv->dbus_context, FALSE); + g_return_val_if_fail(!priv->is_destroyed, FALSE); + + klass = NM_SECRET_AGENT_OLD_GET_CLASS(self); + g_return_val_if_fail(klass->get_secrets, FALSE); + g_return_val_if_fail(klass->cancel_get_secrets, FALSE); + g_return_val_if_fail(klass->save_secrets, FALSE); + g_return_val_if_fail(klass->delete_secrets, FALSE); + + _LOGT("init-sync"); + + /* See NMClient's sync-init method for explanation about why we create + * an internal GMainContext priv->dbus_context. */ + + priv->dbus_context = g_main_context_new(); + + g_main_context_push_thread_default(priv->dbus_context); + + main_loop = g_main_loop_new(priv->dbus_context, FALSE); + + priv->init_data = nml_init_data_new_sync(cancellable, main_loop, &local_error); + + _init_start(self); + + g_main_loop_run(main_loop); + + g_main_loop_unref(main_loop); + + g_main_context_pop_thread_default(priv->dbus_context); + + nm_context_busy_watcher_integrate_source(priv->main_context, + priv->dbus_context, + priv->context_busy_watcher); + + if (local_error) { + g_propagate_error(error, local_error); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_CONNECTION: + g_value_set_object(value, priv->dbus_connection); + break; + case PROP_IDENTIFIER: + g_value_set_string(value, priv->identifier); + break; + case PROP_AUTO_REGISTER: + g_value_set_boolean(value, priv->auto_register); + break; + case PROP_REGISTERED: + g_value_set_boolean(value, priv->is_registered); + break; + case PROP_CAPABILITIES: + g_value_set_flags(value, priv->capabilities); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMSecretAgentOld * self = NM_SECRET_AGENT_OLD(object); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + guint u; + + switch (prop_id) { + case PROP_DBUS_CONNECTION: + /* construct-only */ + priv->dbus_connection = g_value_dup_object(value); + break; + case PROP_IDENTIFIER: + /* construct-only */ + priv->identifier = g_value_dup_string(value); + g_return_if_fail(validate_identifier(priv->identifier)); + break; + case PROP_AUTO_REGISTER: + /* construct */ + priv->auto_register = g_value_get_boolean(value); + priv->is_enabled = priv->auto_register; + _register_state_change(self); + break; + case PROP_CAPABILITIES: + /* construct */ + u = g_value_get_flags(value); + if (u != priv->capabilities) { + priv->capabilities = u; + priv->registration_force_unregister = TRUE; + _register_state_change(self); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_secret_agent_old_init(NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + _LOGT("create new instance"); + + c_list_init(&priv->gsi_lst_head); + c_list_init(&priv->pending_tasks_register_lst_head); + + priv->main_context = g_main_context_ref_thread_default(); + priv->context_busy_watcher = g_object_new(G_TYPE_OBJECT, NULL); +} + +static void +dispose(GObject *object) +{ + NMSecretAgentOld *self = NM_SECRET_AGENT_OLD(object); + + _LOGT("disposing"); + + _secret_agent_old_destroy(self); + + G_OBJECT_CLASS(nm_secret_agent_old_parent_class)->dispose(object); +} + +static void +finalize(GObject *object) +{ + NMSecretAgentOld * self = NM_SECRET_AGENT_OLD(object); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); + + _LOGT("finalizing"); + + if (priv->dbus_context) { + nml_cleanup_context_busy_watcher_on_idle(g_steal_pointer(&priv->context_busy_watcher), + priv->dbus_context); + } + + g_clear_object(&priv->dbus_connection); + nm_clear_pointer(&priv->dbus_context, g_main_context_unref); + nm_clear_pointer(&priv->main_context, g_main_context_unref); + + g_clear_object(&priv->context_busy_watcher); + + g_free(priv->identifier); + + G_OBJECT_CLASS(nm_secret_agent_old_parent_class)->finalize(object); +} + +static void +nm_secret_agent_old_class_init(NMSecretAgentOldClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(class); + + g_type_class_add_private(class, sizeof(NMSecretAgentOldPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /** + * NMSecretAgentOld:dbus-connection: + * + * The #GDBusConnection used by the instance. You may either set this + * as construct-only property, or otherwise #NMSecretAgentOld will choose + * a connection via g_bus_get() during initialization. + * + * Since: 1.24 + **/ + obj_properties[PROP_DBUS_CONNECTION] = + g_param_spec_object(NM_SECRET_AGENT_OLD_DBUS_CONNECTION, + "", + "", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMSecretAgentOld:identifier: + * + * Identifies this agent; only one agent in each user session may use the + * same identifier. Identifier formatting follows the same rules as + * D-Bus bus names with the exception that the ':' character is not + * allowed. The valid set of characters is "[A-Z][a-z][0-9]_-." and the + * identifier is limited in length to 255 characters with a minimum + * of 3 characters. An example valid identifier is 'org.gnome.nm-applet' + * (without quotes). + **/ + obj_properties[PROP_IDENTIFIER] = + g_param_spec_string(NM_SECRET_AGENT_OLD_IDENTIFIER, + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMSecretAgentOld:auto-register: + * + * If %TRUE (the default), the agent will always be registered when + * NetworkManager is running; if NetworkManager exits and restarts, the + * agent will re-register itself automatically. + * + * In particular, if this property is %TRUE at construct time, then the + * agent will register itself with NetworkManager during + * construction/initialization and initialization will only complete + * after registration is completed (either successfully or unsuccessfully). + * Since 1.24, a failure to register will no longer cause initialization + * of #NMSecretAgentOld to fail. + * + * If the property is %FALSE, the agent will not automatically register with + * NetworkManager, and nm_secret_agent_old_enable() or + * nm_secret_agent_old_register_async() must be called to register it. + * + * Calling nm_secret_agent_old_enable() has the same effect as setting this + * property. + **/ + obj_properties[PROP_AUTO_REGISTER] = + g_param_spec_boolean(NM_SECRET_AGENT_OLD_AUTO_REGISTER, + "", + "", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + /** + * NMSecretAgentOld:registered: + * + * %TRUE if the agent is registered with NetworkManager, %FALSE if not. + **/ + obj_properties[PROP_REGISTERED] = + g_param_spec_boolean(NM_SECRET_AGENT_OLD_REGISTERED, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMSecretAgentOld:capabilities: + * + * A bitfield of %NMSecretAgentCapabilities. + * + * Changing this property is possible at any time. In case the secret + * agent is currently registered, this will cause a re-registration. + **/ + obj_properties[PROP_CAPABILITIES] = + g_param_spec_flags(NM_SECRET_AGENT_OLD_CAPABILITIES, + "", + "", + NM_TYPE_SECRET_AGENT_CAPABILITIES, + NM_SECRET_AGENT_CAPABILITY_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} + +static void +nm_secret_agent_old_initable_iface_init(GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_secret_agent_old_async_initable_iface_init(GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} diff --git a/src/libnm-client-impl/nm-settings-docs-gir.xml b/src/libnm-client-impl/nm-settings-docs-gir.xml new file mode 100644 index 00000000..5f8a1d9f --- /dev/null +++ b/src/libnm-client-impl/nm-settings-docs-gir.xml @@ -0,0 +1,530 @@ + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libnm-client-impl/nm-vpn-connection.c b/src/libnm-client-impl/nm-vpn-connection.c new file mode 100644 index 00000000..fb061532 --- /dev/null +++ b/src/libnm-client-impl/nm-vpn-connection.c @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-vpn-connection.h" + +#include "nm-dbus-interface.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "nm-active-connection.h" +#include "nm-dbus-helpers.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMVpnConnection, PROP_VPN_STATE, PROP_BANNER, ); + +enum { + VPN_STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef struct { + char * banner; + guint32 vpn_state; + guint32 reason; +} NMVpnConnectionPrivate; + +struct _NMVpnConnection { + NMActiveConnection parent; + NMVpnConnectionPrivate _priv; +}; + +struct _NMVpnConnectionClass { + NMActiveConnectionClass parent; +}; + +G_DEFINE_TYPE(NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION) + +#define NM_VPN_CONNECTION_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMVpnConnection, NM_IS_VPN_CONNECTION, NMObject, NMActiveConnection) + +G_STATIC_ASSERT(sizeof(NMVpnConnectionStateReason) == sizeof(NMActiveConnectionStateReason)); + +/*****************************************************************************/ + +/** + * nm_vpn_connection_get_banner: + * @vpn: a #NMVpnConnection + * + * Gets the VPN login banner of the active #NMVpnConnection. + * + * Returns: the VPN login banner of the VPN connection. This is the internal + * string used by the connection, and must not be modified. + **/ +const char * +nm_vpn_connection_get_banner(NMVpnConnection *vpn) +{ + g_return_val_if_fail(NM_IS_VPN_CONNECTION(vpn), NULL); + + return _nml_coerce_property_str_not_empty(NM_VPN_CONNECTION_GET_PRIVATE(vpn)->banner); +} + +/** + * nm_vpn_connection_get_vpn_state: + * @vpn: a #NMVpnConnection + * + * Gets the current #NMVpnConnection state. + * + * Returns: the VPN state of the active VPN connection. + **/ +NMVpnConnectionState +nm_vpn_connection_get_vpn_state(NMVpnConnection *vpn) +{ + g_return_val_if_fail(NM_IS_VPN_CONNECTION(vpn), NM_VPN_CONNECTION_STATE_UNKNOWN); + + return NM_VPN_CONNECTION_GET_PRIVATE(vpn)->vpn_state; +} + +/*****************************************************************************/ + +static void +_notify_event_state_changed(NMClient *client, NMClientNotifyEventWithPtr *notify_event) +{ + gs_unref_object NMVpnConnection *self = notify_event->user_data; + NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + /* we expose here the value cache in @priv. In practice, this is the same + * value as we received from the signal. In the unexpected case where they + * differ, the cached value of the current instance would still be more correct. */ + g_signal_emit(self, + signals[VPN_STATE_CHANGED], + 0, + (guint) priv->vpn_state, + (guint) priv->reason); +} + +void +_nm_vpn_connection_state_changed_commit(NMVpnConnection *self, guint32 state, guint32 reason) +{ + NMClient * client; + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + client = _nm_object_get_client(self); + + if (priv->vpn_state != state) { + priv->vpn_state = state; + _nm_client_queue_notify_object(client, self, obj_properties[PROP_VPN_STATE]); + } + + priv->reason = reason; + + _nm_client_notify_event_queue_with_ptr(client, + NM_CLIENT_NOTIFY_EVENT_PRIO_GPROP + 1, + _notify_event_state_changed, + g_object_ref(self)); +} + +/*****************************************************************************/ + +static void +nm_vpn_connection_init(NMVpnConnection *connection) +{} + +static void +finalize(GObject *object) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(object); + + g_free(priv->banner); + + G_OBJECT_CLASS(nm_vpn_connection_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMVpnConnection *self = NM_VPN_CONNECTION(object); + + switch (prop_id) { + case PROP_VPN_STATE: + g_value_set_enum(value, nm_vpn_connection_get_vpn_state(self)); + break; + case PROP_BANNER: + g_value_set_string(value, nm_vpn_connection_get_banner(self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_vpn_connection = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_VPN_CONNECTION, + nm_vpn_connection_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_S("Banner", PROP_BANNER, NMVpnConnection, _priv.banner), + NML_DBUS_META_PROPERTY_INIT_U("VpnState", + PROP_VPN_STATE, + NMVpnConnection, + _priv.vpn_state), ), ); + +static void +nm_vpn_connection_class_init(NMVpnConnectionClass *connection_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(connection_class); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMVpnConnection:vpn-state: + * + * The VPN state of the active VPN connection. + **/ + obj_properties[PROP_VPN_STATE] = g_param_spec_enum(NM_VPN_CONNECTION_VPN_STATE, + "", + "", + NM_TYPE_VPN_CONNECTION_STATE, + NM_VPN_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMVpnConnection:banner: + * + * The VPN login banner of the active VPN connection. + **/ + obj_properties[PROP_BANNER] = g_param_spec_string(NM_VPN_CONNECTION_BANNER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, + &_nml_dbus_meta_iface_nm_vpn_connection); + + /* TODO: the state reason should also be exposed as a property in libnm's NMVpnConnection, + * like done for NMDevice's state reason. */ + + /* TODO: the D-Bus API should also expose the state-reason as a property instead of + * a "VpnStateChanged" signal. Like done for Device's "StateReason". */ + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + signals[VPN_STATE_CHANGED] = g_signal_new("vpn-state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_UINT, + G_TYPE_UINT); + G_GNUC_END_IGNORE_DEPRECATIONS +} diff --git a/src/libnm-client-impl/nm-vpn-editor.c b/src/libnm-client-impl/nm-vpn-editor.c new file mode 100644 index 00000000..57aa8b02 --- /dev/null +++ b/src/libnm-client-impl/nm-vpn-editor.c @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2008 - 2010 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-vpn-editor.h" + +static void nm_vpn_editor_default_init(NMVpnEditorInterface *iface); + +G_DEFINE_INTERFACE(NMVpnEditor, nm_vpn_editor, G_TYPE_OBJECT) + +static void +nm_vpn_editor_default_init(NMVpnEditorInterface *iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE(iface); + + /* Signals */ + g_signal_new("changed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnEditorInterface, changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +/** + * nm_vpn_editor_get_widget: + * @editor: the #NMVpnEditor + * + * Returns: (transfer none): + */ +GObject * +nm_vpn_editor_get_widget(NMVpnEditor *editor) +{ + g_return_val_if_fail(NM_IS_VPN_EDITOR(editor), NULL); + + return NM_VPN_EDITOR_GET_INTERFACE(editor)->get_widget(editor); +} + +gboolean +nm_vpn_editor_update_connection(NMVpnEditor *editor, NMConnection *connection, GError **error) +{ + g_return_val_if_fail(NM_IS_VPN_EDITOR(editor), FALSE); + + if (error) + g_return_val_if_fail(*error == NULL, FALSE); + + return NM_VPN_EDITOR_GET_INTERFACE(editor)->update_connection(editor, connection, error); +} diff --git a/src/libnm-client-impl/nm-vpn-plugin-old.c b/src/libnm-client-impl/nm-vpn-plugin-old.c new file mode 100644 index 00000000..f60bf19c --- /dev/null +++ b/src/libnm-client-impl/nm-vpn-plugin-old.c @@ -0,0 +1,1135 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-vpn-plugin-old.h" + +#include +#include + +#include "nm-enum-types.h" +#include "nm-utils.h" +#include "nm-connection.h" +#include "nm-dbus-helpers.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-simple-connection.h" +#include "nm-vpn-service-plugin.h" + +#include "introspection/org.freedesktop.NetworkManager.VPN.Plugin.h" + +#define NM_VPN_PLUGIN_OLD_QUIT_TIMER 180 + +static void nm_vpn_plugin_old_initable_iface_init(GInitableIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE(NMVpnPluginOld, + nm_vpn_plugin_old, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, + nm_vpn_plugin_old_initable_iface_init);) + +typedef struct { + NMVpnServiceState state; + + /* DBUS-y stuff */ + GDBusConnection *connection; + NMDBusVpnPlugin *dbus_vpn_plugin_old; + char * dbus_service_name; + + /* Temporary stuff */ + guint connect_timer; + guint quit_timer; + guint fail_stop_id; + gboolean interactive; + + gboolean got_config; + gboolean has_ip4, got_ip4; + gboolean has_ip6, got_ip6; + + /* Config stuff copied from config to ip4config */ + GVariant *banner, *tundev, *gateway, *mtu; +} NMVpnPluginOldPrivate; + +#define NM_VPN_PLUGIN_OLD_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NM_TYPE_VPN_PLUGIN_OLD, NMVpnPluginOldPrivate)) + +enum { + STATE_CHANGED, + CONFIG, + IP4_CONFIG, + IP6_CONFIG, + LOGIN_BANNER, + FAILURE, + QUIT, + SECRETS_REQUIRED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DBUS_SERVICE_NAME, PROP_STATE, ); + +static GSList *active_plugins = NULL; + +static void +nm_vpn_plugin_old_set_connection(NMVpnPluginOld *plugin, GDBusConnection *connection) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + g_clear_object(&priv->connection); + + if (connection) + priv->connection = g_object_ref(connection); +} + +/** + * nm_vpn_plugin_old_get_connection: + * + * Returns: (transfer full): + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +GDBusConnection * +nm_vpn_plugin_old_get_connection(NMVpnPluginOld *plugin) +{ + GDBusConnection *connection; + + g_return_val_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin), NULL); + + connection = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin)->connection; + + if (connection) + g_object_ref(connection); + + return connection; +} + +/** + * nm_vpn_plugin_old_get_state: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +NMVpnServiceState +nm_vpn_plugin_old_get_state(NMVpnPluginOld *plugin) +{ + g_return_val_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin), NM_VPN_SERVICE_STATE_UNKNOWN); + + return NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin)->state; +} + +/** + * nm_vpn_plugin_old_set_state: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_state(NMVpnPluginOld *plugin, NMVpnServiceState state) +{ + NMVpnPluginOldPrivate *priv; + + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + + priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + if (priv->state != state) { + priv->state = state; + g_signal_emit(plugin, signals[STATE_CHANGED], 0, state); + } +} + +/** + * nm_vpn_plugin_old_set_login_banner: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_login_banner(NMVpnPluginOld *plugin, const char *banner) +{ + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + g_return_if_fail(banner != NULL); + + g_signal_emit(plugin, signals[LOGIN_BANNER], 0, banner); +} + +/** + * nm_vpn_plugin_old_failure: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_failure(NMVpnPluginOld *plugin, NMVpnPluginFailure reason) +{ + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + + g_signal_emit(plugin, signals[FAILURE], 0, reason); +} + +/** + * nm_vpn_plugin_old_disconnect: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +gboolean +nm_vpn_plugin_old_disconnect(NMVpnPluginOld *plugin, GError **err) +{ + gboolean ret = FALSE; + NMVpnServiceState state; + + g_return_val_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin), FALSE); + + state = nm_vpn_plugin_old_get_state(plugin); + switch (state) { + case NM_VPN_SERVICE_STATE_STOPPING: + g_set_error( + err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS, + "%s", + "Could not process the request because the VPN connection is already being stopped."); + break; + case NM_VPN_SERVICE_STATE_STOPPED: + g_set_error(err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED, + "%s", + "Could not process the request because no VPN connection was active."); + break; + case NM_VPN_SERVICE_STATE_STARTING: + case NM_VPN_SERVICE_STATE_STARTED: + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPING); + ret = NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->disconnect(plugin, err); + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); + break; + case NM_VPN_SERVICE_STATE_INIT: + ret = TRUE; + break; + + default: + g_warning("Unhandled VPN service state %d", state); + g_assert_not_reached(); + break; + } + + return ret; +} + +static void +nm_vpn_plugin_old_emit_quit(NMVpnPluginOld *plugin) +{ + g_signal_emit(plugin, signals[QUIT], 0); +} + +static gboolean +connect_timer_expired(gpointer data) +{ + NMVpnPluginOld *plugin = NM_VPN_PLUGIN_OLD(data); + GError * err = NULL; + + NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin)->connect_timer = 0; + g_message("Connect timer expired, disconnecting."); + nm_vpn_plugin_old_disconnect(plugin, &err); + if (err) { + g_warning("Disconnect failed: %s", err->message); + g_error_free(err); + } + + return G_SOURCE_REMOVE; +} + +static gboolean +quit_timer_expired(gpointer data) +{ + NMVpnPluginOld *self = NM_VPN_PLUGIN_OLD(data); + + NM_VPN_PLUGIN_OLD_GET_PRIVATE(self)->quit_timer = 0; + nm_vpn_plugin_old_emit_quit(self); + return G_SOURCE_REMOVE; +} + +static void +schedule_quit_timer(NMVpnPluginOld *self) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(self); + + nm_clear_g_source(&priv->quit_timer); + priv->quit_timer = + g_timeout_add_seconds(NM_VPN_PLUGIN_OLD_QUIT_TIMER, quit_timer_expired, self); +} + +static gboolean +fail_stop(gpointer data) +{ + NMVpnPluginOld *self = NM_VPN_PLUGIN_OLD(data); + + NM_VPN_PLUGIN_OLD_GET_PRIVATE(self)->fail_stop_id = 0; + nm_vpn_plugin_old_set_state(self, NM_VPN_SERVICE_STATE_STOPPED); + return G_SOURCE_REMOVE; +} + +static void +schedule_fail_stop(NMVpnPluginOld *plugin, guint timeout_secs) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + nm_clear_g_source(&priv->fail_stop_id); + if (timeout_secs) + priv->fail_stop_id = g_timeout_add_seconds(timeout_secs, fail_stop, plugin); + else + priv->fail_stop_id = g_idle_add(fail_stop, plugin); +} + +/** + * nm_vpn_plugin_old_set_config: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_config(NMVpnPluginOld *plugin, GVariant *config) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + g_return_if_fail(config != NULL); + + priv->got_config = TRUE; + + (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &priv->has_ip4); + (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &priv->has_ip6); + + g_warn_if_fail(priv->has_ip4 || priv->has_ip6); + + /* Record the items that need to also be inserted into the + * ip4config, for compatibility with older daemons. + */ + if (priv->banner) + g_variant_unref(priv->banner); + priv->banner = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_BANNER, G_VARIANT_TYPE("s")); + if (priv->tundev) + g_variant_unref(priv->tundev); + priv->tundev = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_TUNDEV, G_VARIANT_TYPE("s")); + if (priv->gateway) + g_variant_unref(priv->gateway); + priv->gateway = + g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, G_VARIANT_TYPE("u")); + if (priv->mtu) + g_variant_unref(priv->mtu); + priv->mtu = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_MTU, G_VARIANT_TYPE("u")); + + g_signal_emit(plugin, signals[CONFIG], 0, config); +} + +/** + * nm_vpn_plugin_old_set_ip4_config: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_ip4_config(NMVpnPluginOld *plugin, GVariant *ip4_config) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + GVariant * combined_config; + GVariantBuilder builder; + GVariantIter iter; + const char * key; + GVariant * value; + + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + g_return_if_fail(ip4_config != NULL); + + priv->got_ip4 = TRUE; + + /* Old plugins won't send the "config" signal and thus can't send + * NM_VPN_PLUGIN_OLD_CONFIG_HAS_IP4 either. But since they don't support IPv6, + * we can safely assume that, if we don't receive a "config" signal but do + * receive an "ip4-config" signal, the old plugin supports IPv4. + */ + if (!priv->got_config) + priv->has_ip4 = TRUE; + + /* Older NetworkManager daemons expect all config info to be in + * the ip4 config, so they won't even notice the "config" signal + * being emitted. So just copy all of that data into the ip4 + * config too. + */ + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_iter_init(&iter, ip4_config); + while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) { + g_variant_builder_add(&builder, "{sv}", key, value); + g_variant_unref(value); + } + + if (priv->banner) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner); + if (priv->tundev) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev); + if (priv->gateway) + g_variant_builder_add(&builder, + "{sv}", + NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, + &priv->gateway); + if (priv->mtu) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu); + + combined_config = g_variant_builder_end(&builder); + g_variant_ref_sink(combined_config); + g_signal_emit(plugin, signals[IP4_CONFIG], 0, combined_config); + g_variant_unref(combined_config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +/** + * nm_vpn_plugin_old_set_ip6_config: + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_set_ip6_config(NMVpnPluginOld *plugin, GVariant *ip6_config) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + g_return_if_fail(NM_IS_VPN_PLUGIN_OLD(plugin)); + g_return_if_fail(ip6_config != NULL); + + g_variant_ref_sink(ip6_config); + + priv->got_ip6 = TRUE; + g_signal_emit(plugin, signals[IP6_CONFIG], 0, ip6_config); + + g_variant_unref(ip6_config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +static void +connect_timer_start(NMVpnPluginOld *plugin) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + priv->connect_timer = g_timeout_add_seconds(60, connect_timer_expired, plugin); +} + +static void +_connect_generic(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + GVariant * details) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + NMVpnPluginOldClass * vpn_class = NM_VPN_PLUGIN_OLD_GET_CLASS(plugin); + gs_unref_object NMConnection *connection = NULL; + gboolean success = FALSE; + gs_free_error GError *error = NULL; + guint fail_stop_timeout = 0; + + if (priv->state != NM_VPN_SERVICE_STATE_STOPPED && priv->state != NM_VPN_SERVICE_STATE_INIT) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not start connection: wrong plugin state %d", + priv->state); + return; + } + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: %s", + error->message); + return; + } + + priv->interactive = FALSE; + if (details && !vpn_class->connect_interactive) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Plugin does not implement ConnectInteractive()"); + return; + } + + nm_clear_g_source(&priv->fail_stop_id); + + if (details) { + priv->interactive = TRUE; + success = vpn_class->connect_interactive(plugin, connection, details, &error); + if (g_error_matches(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) { + /* Give NetworkManager a bit of time to fall back to Connect() */ + fail_stop_timeout = 5; + } + } else + success = vpn_class->connect(plugin, connection, &error); + + if (success) { + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_STARTING); + + g_dbus_method_invocation_return_value(context, NULL); + + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start(plugin); + } else { + g_dbus_method_invocation_take_error(context, g_steal_pointer(&error)); + + /* Stop the plugin from an idle handler so that the Connect + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop(plugin, fail_stop_timeout); + } +} + +static void +impl_vpn_plugin_old_connect(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * connection, + gpointer user_data) +{ + _connect_generic(plugin, context, connection, NULL); +} + +static void +impl_vpn_plugin_old_connect_interactive(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * connection, + GVariant * details, + gpointer user_data) +{ + _connect_generic(plugin, context, connection, details); +} + +/*****************************************************************************/ + +static void +impl_vpn_plugin_old_need_secrets(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + gpointer user_data) +{ + NMConnection *connection; + const char * setting_name; + gboolean needed; + GError * error = NULL; + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION, + "The connection was invalid: %s", + error->message); + g_error_free(error); + return; + } + + if (!NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->need_secrets) { + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); + return; + } + + needed = NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->need_secrets(plugin, + connection, + &setting_name, + &error); + if (error) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + if (needed) { + /* Push back the quit timer so the VPN plugin doesn't quit in the + * middle of asking the user for secrets. + */ + schedule_quit_timer(plugin); + + g_assert(setting_name); + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", setting_name)); + } else { + /* No secrets required */ + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); + } +} + +static void +impl_vpn_plugin_old_new_secrets(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + gpointer user_data) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + NMConnection * connection; + GError * error = NULL; + gboolean success; + + if (priv->state != NM_VPN_SERVICE_STATE_STARTING) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not accept new secrets: wrong plugin state %d", + priv->state); + return; + } + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: %s", + error->message); + g_clear_error(&error); + return; + } + + if (!NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->new_secrets) { + g_dbus_method_invocation_return_error( + context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Could not accept new secrets: plugin cannot process interactive secrets"); + g_object_unref(connection); + return; + } + + success = NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->new_secrets(plugin, connection, &error); + if (success) { + g_dbus_method_invocation_return_value(context, NULL); + + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start(plugin); + } else { + g_dbus_method_invocation_take_error(context, error); + + /* Stop the plugin from and idle handler so that the NewSecrets + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop(plugin, 0); + } + + g_object_unref(connection); +} + +/** + * nm_vpn_plugin_old_secrets_required: + * @plugin: the #NMVpnPluginOld + * @message: an information message about why secrets are required, if any + * @hints: VPN specific secret names for required new secrets + * + * Called by VPN plugin implementations to signal to NetworkManager that secrets + * are required during the connection process. This signal may be used to + * request new secrets when the secrets originally provided by NetworkManager + * are insufficient, or the VPN process indicates that it needs additional + * information to complete the request. + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ +void +nm_vpn_plugin_old_secrets_required(NMVpnPluginOld *plugin, const char *message, const char **hints) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + /* Plugin must be able to accept the new secrets if it calls this method */ + g_return_if_fail(NM_VPN_PLUGIN_OLD_GET_CLASS(plugin)->new_secrets); + + /* Plugin cannot call this method if NetworkManager didn't originally call + * ConnectInteractive(). + */ + g_return_if_fail(priv->interactive == TRUE); + + /* Cancel the connect timer since secrets might take a while. It'll + * get restarted when the secrets come back via NewSecrets(). + */ + nm_clear_g_source(&priv->connect_timer); + + g_signal_emit(plugin, signals[SECRETS_REQUIRED], 0, message, hints); +} + +/*****************************************************************************/ + +/** + * nm_vpn_plugin_old_read_vpn_details: + * @fd: file descriptor to read from, usually stdin (0) + * @out_data: (out) (transfer full): on successful return, a hash table + * (mapping char*:char*) containing the key/value pairs of VPN data items + * @out_secrets: (out) (transfer full): on successful return, a hash table + * (mapping char*:char*) containing the key/value pairsof VPN secrets + * + * Parses key/value pairs from a file descriptor (normally stdin) passed by + * an applet when the applet calls the authentication dialog of the VPN plugin. + * + * Returns: %TRUE if reading values was successful, %FALSE if not + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + **/ +gboolean +nm_vpn_plugin_old_read_vpn_details(int fd, GHashTable **out_data, GHashTable **out_secrets) +{ + return nm_vpn_service_plugin_read_vpn_details(fd, out_data, out_secrets); +} + +/** + * nm_vpn_plugin_old_get_secret_flags: + * @data: hash table containing VPN key/value pair data items + * @secret_name: VPN secret key name for which to retrieve flags for + * @out_flags: (out): on success, the flags associated with @secret_name + * + * Given a VPN secret key name, attempts to find the corresponding flags data + * item in @data. If found, converts the flags data item to + * #NMSettingSecretFlags and returns it. + * + * Returns: %TRUE if the flag data item was found and successfully converted + * to flags, %FALSE if not + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + **/ +gboolean +nm_vpn_plugin_old_get_secret_flags(GHashTable * data, + const char * secret_name, + NMSettingSecretFlags *out_flags) +{ + return nm_vpn_service_plugin_get_secret_flags(data, secret_name, out_flags); +} + +/*****************************************************************************/ + +static void +impl_vpn_plugin_old_disconnect(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + gpointer user_data) +{ + GError *error = NULL; + + if (nm_vpn_plugin_old_disconnect(plugin, &error)) + g_dbus_method_invocation_return_value(context, NULL); + else + g_dbus_method_invocation_take_error(context, error); +} + +static void +impl_vpn_plugin_old_set_config(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_plugin_old_set_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_plugin_old_set_ip4_config(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_plugin_old_set_ip4_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_plugin_old_set_ip6_config(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_plugin_old_set_ip6_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_plugin_old_set_failure(NMVpnPluginOld * plugin, + GDBusMethodInvocation *context, + char * reason, + gpointer user_data) +{ + nm_vpn_plugin_old_failure(plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG); + g_dbus_method_invocation_return_value(context, NULL); +} + +/*****************************************************************************/ + +static void +_emit_quit(gpointer data, gpointer user_data) +{ + NMVpnPluginOld *plugin = data; + + nm_vpn_plugin_old_emit_quit(plugin); +} + +static void +sigterm_handler(int signum) +{ + g_slist_foreach(active_plugins, _emit_quit, NULL); +} + +static void +setup_unix_signal_handler(void) +{ + struct sigaction action; + sigset_t block_mask; + + action.sa_handler = sigterm_handler; + sigemptyset(&block_mask); + action.sa_mask = block_mask; + action.sa_flags = 0; + sigaction(SIGINT, &action, NULL); + sigaction(SIGTERM, &action, NULL); +} + +/*****************************************************************************/ + +static void +one_plugin_destroyed(gpointer data, GObject *object) +{ + active_plugins = g_slist_remove(active_plugins, object); +} + +static void +nm_vpn_plugin_old_init(NMVpnPluginOld *plugin) +{ + active_plugins = g_slist_append(active_plugins, plugin); + g_object_weak_ref(G_OBJECT(plugin), one_plugin_destroyed, NULL); +} + +static gboolean +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMVpnPluginOld * plugin = NM_VPN_PLUGIN_OLD(initable); + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + GDBusConnection * connection = NULL; + GDBusProxy * proxy; + GVariant * ret; + gboolean success = FALSE; + + if (!priv->dbus_service_name) { + g_set_error_literal(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + _("No service name specified")); + return FALSE; + } + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (!connection) + return FALSE; + + proxy = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + cancellable, + error); + if (!proxy) + goto out; + + ret = g_dbus_proxy_call_sync(proxy, + "RequestName", + g_variant_new("(su)", priv->dbus_service_name, 0), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + g_object_unref(proxy); + if (!ret) { + if (error && *error) + g_dbus_error_strip_remote_error(*error); + goto out; + } + g_variant_unref(ret); + + priv->dbus_vpn_plugin_old = nmdbus_vpn_plugin_skeleton_new(); + if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(priv->dbus_vpn_plugin_old), + connection, + NM_VPN_DBUS_PLUGIN_PATH, + error)) + goto out; + + _nm_dbus_bind_properties(plugin, priv->dbus_vpn_plugin_old); + _nm_dbus_bind_methods(plugin, + priv->dbus_vpn_plugin_old, + "Connect", + impl_vpn_plugin_old_connect, + "ConnectInteractive", + impl_vpn_plugin_old_connect_interactive, + "NeedSecrets", + impl_vpn_plugin_old_need_secrets, + "NewSecrets", + impl_vpn_plugin_old_new_secrets, + "Disconnect", + impl_vpn_plugin_old_disconnect, + "SetConfig", + impl_vpn_plugin_old_set_config, + "SetIp4Config", + impl_vpn_plugin_old_set_ip4_config, + "SetIp6Config", + impl_vpn_plugin_old_set_ip6_config, + "SetFailure", + impl_vpn_plugin_old_set_failure, + NULL); + + nm_vpn_plugin_old_set_connection(plugin, connection); + nm_vpn_plugin_old_set_state(plugin, NM_VPN_SERVICE_STATE_INIT); + + success = TRUE; + +out: + g_clear_object(&connection); + + return success; +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + /* construct-only */ + priv->dbus_service_name = g_value_dup_string(value); + break; + case PROP_STATE: + nm_vpn_plugin_old_set_state(NM_VPN_PLUGIN_OLD(object), + (NMVpnServiceState) g_value_get_enum(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + g_value_set_string(value, priv->dbus_service_name); + break; + case PROP_STATE: + g_value_set_enum(value, nm_vpn_plugin_old_get_state(NM_VPN_PLUGIN_OLD(object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +dispose(GObject *object) +{ + NMVpnPluginOld * plugin = NM_VPN_PLUGIN_OLD(object); + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + NMVpnServiceState state; + GError * err = NULL; + + nm_clear_g_source(&priv->fail_stop_id); + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->connect_timer); + + state = nm_vpn_plugin_old_get_state(plugin); + + if (state == NM_VPN_SERVICE_STATE_STARTED || state == NM_VPN_SERVICE_STATE_STARTING) + nm_vpn_plugin_old_disconnect(plugin, &err); + + if (err) { + g_warning("Error disconnecting VPN connection: %s", err->message); + g_error_free(err); + } + + G_OBJECT_CLASS(nm_vpn_plugin_old_parent_class)->dispose(object); +} + +static void +finalize(GObject *object) +{ + NMVpnPluginOld * plugin = NM_VPN_PLUGIN_OLD(object); + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + nm_vpn_plugin_old_set_connection(plugin, NULL); + g_free(priv->dbus_service_name); + + nm_clear_pointer(&priv->banner, g_variant_unref); + nm_clear_pointer(&priv->tundev, g_variant_unref); + nm_clear_pointer(&priv->gateway, g_variant_unref); + nm_clear_pointer(&priv->mtu, g_variant_unref); + + G_OBJECT_CLASS(nm_vpn_plugin_old_parent_class)->finalize(object); +} + +static void +state_changed(NMVpnPluginOld *plugin, NMVpnServiceState state) +{ + NMVpnPluginOldPrivate *priv = NM_VPN_PLUGIN_OLD_GET_PRIVATE(plugin); + + switch (state) { + case NM_VPN_SERVICE_STATE_STARTING: + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->fail_stop_id); + break; + case NM_VPN_SERVICE_STATE_STOPPED: + schedule_quit_timer(plugin); + break; + default: + /* Clean up all timers we might have set up. */ + nm_clear_g_source(&priv->connect_timer); + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->fail_stop_id); + break; + } +} + +static void +nm_vpn_plugin_old_class_init(NMVpnPluginOldClass *plugin_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(plugin_class); + + g_type_class_add_private(object_class, sizeof(NMVpnPluginOldPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + plugin_class->state_changed = state_changed; + + /** + * NMVpnPluginOld:service-name: + * + * The D-Bus service name of this plugin. + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ + obj_properties[PROP_DBUS_SERVICE_NAME] = + g_param_spec_string(NM_VPN_PLUGIN_OLD_DBUS_SERVICE_NAME, + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMVpnPluginOld:state: + * + * The state of the plugin. + * + * Deprecated: 1.2: Replaced by NMVpnServicePlugin. + */ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_VPN_PLUGIN_OLD_STATE, + "", + "", + NM_TYPE_VPN_SERVICE_STATE, + NM_VPN_SERVICE_STATE_INIT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + signals[STATE_CHANGED] = g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, state_changed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[SECRETS_REQUIRED] = g_signal_new("secrets-required", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_TYPE_STRV); + + signals[CONFIG] = g_signal_new("config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[IP4_CONFIG] = g_signal_new("ip4-config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, ip4_config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[IP6_CONFIG] = g_signal_new("ip6-config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, ip6_config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[LOGIN_BANNER] = g_signal_new("login-banner", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, login_banner), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[FAILURE] = g_signal_new("failure", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, failure), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[QUIT] = g_signal_new("quit", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnPluginOldClass, quit), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0, + G_TYPE_NONE); + + setup_unix_signal_handler(); +} + +static void +nm_vpn_plugin_old_initable_iface_init(GInitableIface *iface) +{ + iface->init = init_sync; +} diff --git a/src/libnm-client-impl/nm-vpn-service-plugin.c b/src/libnm-client-impl/nm-vpn-service-plugin.c new file mode 100644 index 00000000..18a85e91 --- /dev/null +++ b/src/libnm-client-impl/nm-vpn-service-plugin.c @@ -0,0 +1,1384 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2015 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-vpn-service-plugin.h" + +#include +#include +#include + +#include "libnm-glib-aux/nm-secret-utils.h" +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "nm-enum-types.h" +#include "nm-utils.h" +#include "nm-connection.h" +#include "nm-dbus-helpers.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "nm-simple-connection.h" + +#include "introspection/org.freedesktop.NetworkManager.VPN.Plugin.h" + +#define NM_VPN_SERVICE_PLUGIN_QUIT_TIMER 180 + +static void nm_vpn_service_plugin_initable_iface_init(GInitableIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE(NMVpnServicePlugin, + nm_vpn_service_plugin, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, + nm_vpn_service_plugin_initable_iface_init);) + +typedef struct { + NMVpnServiceState state; + + /* DBUS-y stuff */ + GDBusConnection *connection; + NMDBusVpnPlugin *dbus_vpn_service_plugin; + char * dbus_service_name; + gboolean dbus_watch_peer; + + /* Temporary stuff */ + guint connect_timer; + guint quit_timer; + guint fail_stop_id; + guint peer_watch_id; + gboolean interactive; + + gboolean got_config; + gboolean has_ip4, got_ip4; + gboolean has_ip6, got_ip6; + + /* Config stuff copied from config to ip4config */ + GVariant *banner, *tundev, *gateway, *mtu; +} NMVpnServicePluginPrivate; + +#define NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginPrivate)) + +enum { + STATE_CHANGED, + CONFIG, + IP4_CONFIG, + IP6_CONFIG, + LOGIN_BANNER, + FAILURE, + QUIT, + SECRETS_REQUIRED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DBUS_SERVICE_NAME, PROP_DBUS_WATCH_PEER, PROP_STATE, ); + +static GSList *active_plugins = NULL; + +static void +nm_vpn_service_plugin_set_connection(NMVpnServicePlugin *plugin, GDBusConnection *connection) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + g_clear_object(&priv->connection); + + if (connection) + priv->connection = g_object_ref(connection); +} + +/** + * nm_vpn_service_plugin_get_connection: + * + * Returns: (transfer full): + * + * Since: 1.2 + */ +GDBusConnection * +nm_vpn_service_plugin_get_connection(NMVpnServicePlugin *plugin) +{ + GDBusConnection *connection; + + g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), NULL); + + connection = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->connection; + + if (connection) + g_object_ref(connection); + + return connection; +} + +static NMVpnServiceState +nm_vpn_service_plugin_get_state(NMVpnServicePlugin *plugin) +{ + g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), NM_VPN_SERVICE_STATE_UNKNOWN); + + return NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->state; +} + +static void +nm_vpn_service_plugin_set_state(NMVpnServicePlugin *plugin, NMVpnServiceState state) +{ + NMVpnServicePluginPrivate *priv; + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + + priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + if (priv->state != state) { + priv->state = state; + g_signal_emit(plugin, signals[STATE_CHANGED], 0, state); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_state_changed(priv->dbus_vpn_service_plugin, state); + } +} + +void +nm_vpn_service_plugin_set_login_banner(NMVpnServicePlugin *plugin, const char *banner) +{ + NMVpnServicePluginPrivate *priv; + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + g_return_if_fail(banner != NULL); + + priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + g_signal_emit(plugin, signals[LOGIN_BANNER], 0, banner); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_login_banner(priv->dbus_vpn_service_plugin, banner); +} + +static void +_emit_failure(NMVpnServicePlugin *plugin, NMVpnPluginFailure reason) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + g_signal_emit(plugin, signals[FAILURE], 0, reason); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_failure(priv->dbus_vpn_service_plugin, reason); +} + +void +nm_vpn_service_plugin_failure(NMVpnServicePlugin *plugin, NMVpnPluginFailure reason) +{ + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + + _emit_failure(plugin, reason); + nm_vpn_service_plugin_disconnect(plugin, NULL); +} + +gboolean +nm_vpn_service_plugin_disconnect(NMVpnServicePlugin *plugin, GError **err) +{ + gboolean ret = FALSE; + NMVpnServiceState state; + + g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), FALSE); + + state = nm_vpn_service_plugin_get_state(plugin); + switch (state) { + case NM_VPN_SERVICE_STATE_STOPPING: + g_set_error( + err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS, + "%s", + "Could not process the request because the VPN connection is already being stopped."); + break; + case NM_VPN_SERVICE_STATE_STOPPED: + g_set_error(err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED, + "%s", + "Could not process the request because no VPN connection was active."); + break; + case NM_VPN_SERVICE_STATE_STARTING: + _emit_failure(plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED); + /* fall-through */ + case NM_VPN_SERVICE_STATE_STARTED: + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPING); + ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->disconnect(plugin, err); + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); + break; + case NM_VPN_SERVICE_STATE_INIT: + ret = TRUE; + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); + break; + + default: + g_warning("Unhandled VPN service state %d", state); + g_assert_not_reached(); + break; + } + + return ret; +} + +static void +nm_vpn_service_plugin_emit_quit(NMVpnServicePlugin *plugin) +{ + g_signal_emit(plugin, signals[QUIT], 0); +} + +/** + * nm_vpn_service_plugin_shutdown: + * @plugin: the #NMVpnServicePlugin instance + * + * Shutdown the @plugin and disconnect from D-Bus. After this, + * the plugin instance is dead and should no longer be used. + * It ensures to get no more requests from D-Bus. In principle, + * you don't need to shutdown the plugin, disposing the instance + * has the same effect. However, this gives a way to deactivate + * the plugin before giving up the last reference. + * + * Since: 1.12 + */ +void +nm_vpn_service_plugin_shutdown(NMVpnServicePlugin *plugin) +{ + NMVpnServicePluginPrivate *priv; + NMVpnServiceState state; + GError * error = NULL; + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + + priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + nm_clear_g_source(&priv->fail_stop_id); + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->connect_timer); + + state = nm_vpn_service_plugin_get_state(plugin); + if (state == NM_VPN_SERVICE_STATE_STARTED || state == NM_VPN_SERVICE_STATE_STARTING) { + nm_vpn_service_plugin_disconnect(plugin, &error); + + if (error) { + g_warning("Error disconnecting VPN connection: %s", error->message); + g_error_free(error); + } + } + + if (priv->dbus_vpn_service_plugin) { + g_dbus_interface_skeleton_unexport( + G_DBUS_INTERFACE_SKELETON(priv->dbus_vpn_service_plugin)); + g_clear_object(&priv->dbus_vpn_service_plugin); + } +} + +static gboolean +connect_timer_expired(gpointer data) +{ + NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN(data); + GError * err = NULL; + + NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->connect_timer = 0; + g_message("Connect timer expired, disconnecting."); + nm_vpn_service_plugin_disconnect(plugin, &err); + if (err) { + g_warning("Disconnect failed: %s", err->message); + g_error_free(err); + } + + return G_SOURCE_REMOVE; +} + +static gboolean +quit_timer_expired(gpointer data) +{ + NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN(data); + + NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self)->quit_timer = 0; + nm_vpn_service_plugin_emit_quit(self); + return G_SOURCE_REMOVE; +} + +static void +schedule_quit_timer(NMVpnServicePlugin *self) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self); + + nm_clear_g_source(&priv->quit_timer); + priv->quit_timer = + g_timeout_add_seconds(NM_VPN_SERVICE_PLUGIN_QUIT_TIMER, quit_timer_expired, self); +} + +static gboolean +fail_stop(gpointer data) +{ + NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN(data); + + NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self)->fail_stop_id = 0; + nm_vpn_service_plugin_set_state(self, NM_VPN_SERVICE_STATE_STOPPED); + return G_SOURCE_REMOVE; +} + +static void +schedule_fail_stop(NMVpnServicePlugin *plugin, guint timeout_secs) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + nm_clear_g_source(&priv->fail_stop_id); + if (timeout_secs) + priv->fail_stop_id = g_timeout_add_seconds(timeout_secs, fail_stop, plugin); + else + priv->fail_stop_id = g_idle_add(fail_stop, plugin); +} + +void +nm_vpn_service_plugin_set_config(NMVpnServicePlugin *plugin, GVariant *config) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + g_return_if_fail(config != NULL); + + priv->got_config = TRUE; + + (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &priv->has_ip4); + (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &priv->has_ip6); + + /* Record the items that need to also be inserted into the + * ip4config, for compatibility with older daemons. + */ + if (priv->banner) + g_variant_unref(priv->banner); + priv->banner = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_BANNER, G_VARIANT_TYPE("s")); + if (priv->tundev) + g_variant_unref(priv->tundev); + priv->tundev = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_TUNDEV, G_VARIANT_TYPE("s")); + if (priv->gateway) + g_variant_unref(priv->gateway); + priv->gateway = + g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, G_VARIANT_TYPE("u")); + if (priv->mtu) + g_variant_unref(priv->mtu); + priv->mtu = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_MTU, G_VARIANT_TYPE("u")); + + g_signal_emit(plugin, signals[CONFIG], 0, config); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_config(priv->dbus_vpn_service_plugin, config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +void +nm_vpn_service_plugin_set_ip4_config(NMVpnServicePlugin *plugin, GVariant *ip4_config) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + GVariant * combined_config; + GVariantBuilder builder; + GVariantIter iter; + const char * key; + GVariant * value; + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + g_return_if_fail(ip4_config != NULL); + + priv->got_ip4 = TRUE; + + /* Old plugins won't send the "config" signal and thus can't send + * NM_VPN_SERVICE_PLUGIN_CONFIG_HAS_IP4 either. But since they don't support IPv6, + * we can safely assume that, if we don't receive a "config" signal but do + * receive an "ip4-config" signal, the old plugin supports IPv4. + */ + if (!priv->got_config) + priv->has_ip4 = TRUE; + + /* Older NetworkManager daemons expect all config info to be in + * the ip4 config, so they won't even notice the "config" signal + * being emitted. So just copy all of that data into the ip4 + * config too. + */ + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_iter_init(&iter, ip4_config); + while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) { + g_variant_builder_add(&builder, "{sv}", key, value); + g_variant_unref(value); + } + + if (priv->banner) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, priv->banner); + if (priv->tundev) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, priv->tundev); + if (priv->gateway) + g_variant_builder_add(&builder, + "{sv}", + NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, + priv->gateway); + if (priv->mtu) + g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, priv->mtu); + + combined_config = g_variant_builder_end(&builder); + g_variant_ref_sink(combined_config); + g_signal_emit(plugin, signals[IP4_CONFIG], 0, combined_config); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_ip4_config(priv->dbus_vpn_service_plugin, combined_config); + g_variant_unref(combined_config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +void +nm_vpn_service_plugin_set_ip6_config(NMVpnServicePlugin *plugin, GVariant *ip6_config) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); + g_return_if_fail(ip6_config != NULL); + + g_variant_ref_sink(ip6_config); + + priv->got_ip6 = TRUE; + g_signal_emit(plugin, signals[IP6_CONFIG], 0, ip6_config); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_ip6_config(priv->dbus_vpn_service_plugin, ip6_config); + + g_variant_unref(ip6_config); + + if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +static void +connect_timer_start(NMVpnServicePlugin *plugin) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + nm_clear_g_source(&priv->connect_timer); + priv->connect_timer = g_timeout_add_seconds(60, connect_timer_expired, plugin); +} + +static void +peer_vanished(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + nm_vpn_service_plugin_disconnect(NM_VPN_SERVICE_PLUGIN(user_data), NULL); +} + +static guint +watch_peer(NMVpnServicePlugin *plugin, GDBusMethodInvocation *context) +{ + GDBusConnection *connection = g_dbus_method_invocation_get_connection(context); + const char *peer = g_dbus_message_get_sender(g_dbus_method_invocation_get_message(context)); + + return nm_dbus_connection_signal_subscribe_name_owner_changed(connection, + peer, + peer_vanished, + plugin, + NULL); +} + +static void +_connect_generic(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + GVariant * details) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + NMVpnServicePluginClass * vpn_class = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin); + NMConnection * connection; + gboolean success = FALSE; + GError * error = NULL; + guint fail_stop_timeout = 0; + + if (priv->state != NM_VPN_SERVICE_STATE_STOPPED && priv->state != NM_VPN_SERVICE_STATE_INIT) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not start connection: wrong plugin state %d", + priv->state); + return; + } + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: %s", + error->message); + g_clear_error(&error); + return; + } + + priv->interactive = FALSE; + if (details && !vpn_class->connect_interactive) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Plugin does not implement ConnectInteractive()"); + return; + } + + nm_clear_g_source(&priv->fail_stop_id); + + if (priv->dbus_watch_peer) + priv->peer_watch_id = watch_peer(plugin, context); + + if (details) { + priv->interactive = TRUE; + success = vpn_class->connect_interactive(plugin, connection, details, &error); + if (g_error_matches(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) { + /* Give NetworkManager a bit of time to fall back to Connect() */ + fail_stop_timeout = 5; + } + } else + success = vpn_class->connect(plugin, connection, &error); + + if (success) { + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTING); + + g_dbus_method_invocation_return_value(context, NULL); + + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start(plugin); + } else { + g_dbus_method_invocation_take_error(context, error); + + /* Stop the plugin from an idle handler so that the Connect + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop(plugin, fail_stop_timeout); + } + + g_object_unref(connection); +} + +static void +impl_vpn_service_plugin_connect(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * connection, + gpointer user_data) +{ + _connect_generic(plugin, context, connection, NULL); +} + +static void +impl_vpn_service_plugin_connect_interactive(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * connection, + GVariant * details, + gpointer user_data) +{ + _connect_generic(plugin, context, connection, details); +} + +/*****************************************************************************/ + +static void +impl_vpn_service_plugin_need_secrets(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + gpointer user_data) +{ + NMConnection *connection; + const char * setting_name; + gboolean needed; + GError * error = NULL; + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION, + "The connection was invalid: %s", + error->message); + g_error_free(error); + return; + } + + if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->need_secrets) { + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); + return; + } + + needed = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->need_secrets(plugin, + connection, + &setting_name, + &error); + if (error) { + g_dbus_method_invocation_take_error(context, error); + return; + } + + if (needed) { + /* Push back the quit timer so the VPN plugin doesn't quit in the + * middle of asking the user for secrets. + */ + schedule_quit_timer(plugin); + + g_assert(setting_name); + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", setting_name)); + } else { + /* No secrets required */ + g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); + } +} + +static void +impl_vpn_service_plugin_new_secrets(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * properties, + gpointer user_data) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + NMConnection * connection; + GError * error = NULL; + gboolean success; + + if (priv->state != NM_VPN_SERVICE_STATE_STARTING) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not accept new secrets: wrong plugin state %d", + priv->state); + return; + } + + connection = + _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); + if (!connection) { + g_dbus_method_invocation_return_error(context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: %s", + error->message); + g_clear_error(&error); + return; + } + + if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets) { + g_dbus_method_invocation_return_error( + context, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Could not accept new secrets: plugin cannot process interactive secrets"); + g_object_unref(connection); + return; + } + + success = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets(plugin, connection, &error); + if (success) { + g_dbus_method_invocation_return_value(context, NULL); + + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start(plugin); + } else { + g_dbus_method_invocation_take_error(context, error); + + /* Stop the plugin from and idle handler so that the NewSecrets + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop(plugin, 0); + } + + g_object_unref(connection); +} + +/** + * nm_vpn_service_plugin_secrets_required: + * @plugin: the #NMVpnServicePlugin + * @message: an information message about why secrets are required, if any + * @hints: VPN specific secret names for required new secrets + * + * Called by VPN plugin implementations to signal to NetworkManager that secrets + * are required during the connection process. This signal may be used to + * request new secrets when the secrets originally provided by NetworkManager + * are insufficient, or the VPN process indicates that it needs additional + * information to complete the request. + * + * Since: 1.2 + */ +void +nm_vpn_service_plugin_secrets_required(NMVpnServicePlugin *plugin, + const char * message, + const char ** hints) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + /* Plugin must be able to accept the new secrets if it calls this method */ + g_return_if_fail(NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets); + + /* Plugin cannot call this method if NetworkManager didn't originally call + * ConnectInteractive(). + */ + g_return_if_fail(priv->interactive == TRUE); + + /* Cancel the connect timer since secrets might take a while. It'll + * get restarted when the secrets come back via NewSecrets(). + */ + nm_clear_g_source(&priv->connect_timer); + + g_signal_emit(plugin, signals[SECRETS_REQUIRED], 0, message, hints); + if (priv->dbus_vpn_service_plugin) + nmdbus_vpn_plugin_emit_secrets_required(priv->dbus_vpn_service_plugin, message, hints); +} + +/*****************************************************************************/ + +typedef struct { + char *buf; + gsize n_buf; + int fd; + bool eof : 1; + char buf_full[1024]; +} ReadFdBuf; + +static inline gboolean +_read_fd_buf_c(ReadFdBuf *read_buf, char *ch) +{ + gssize n_read; + + if (read_buf->n_buf > 0) + goto out_data; + if (read_buf->eof) + return FALSE; + +again: + n_read = read(read_buf->fd, read_buf->buf_full, sizeof(read_buf->buf_full)); + if (n_read <= 0) { + if (n_read < 0 && errno == EAGAIN) { + struct pollfd pfd; + int r; + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = read_buf->fd; + pfd.events = POLLIN; + + r = poll(&pfd, 1, -1); + if (r > 0) + goto again; + /* error or timeout. Fall through and set EOF. */ + } + read_buf->eof = TRUE; + return FALSE; + } + + read_buf->buf = read_buf->buf_full; + read_buf->n_buf = n_read; + +out_data: + read_buf->n_buf--; + *ch = read_buf->buf[0]; + read_buf->buf++; + return TRUE; +} + +#define DATA_KEY_TAG "DATA_KEY=" +#define DATA_VAL_TAG "DATA_VAL=" +#define SECRET_KEY_TAG "SECRET_KEY=" +#define SECRET_VAL_TAG "SECRET_VAL=" + +/** + * nm_vpn_service_plugin_read_vpn_details: + * @fd: file descriptor to read from, usually stdin (0) + * @out_data: (out) (transfer full): on successful return, a hash table + * (mapping char*:char*) containing the key/value pairs of VPN data items + * @out_secrets: (out) (transfer full): on successful return, a hash table + * (mapping char*:char*) containing the key/value pairsof VPN secrets + * + * Parses key/value pairs from a file descriptor (normally stdin) passed by + * an applet when the applet calls the authentication dialog of the VPN plugin. + * + * Returns: %TRUE if reading values was successful, %FALSE if not + * + * Since: 1.2 + **/ +gboolean +nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable **out_secrets) +{ + gs_unref_hashtable GHashTable *data = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + gboolean success = FALSE; + GHashTable * hash = NULL; + nm_auto_free_gstring GString *key = NULL; + nm_auto_free_gstring GString *val = NULL; + nm_auto_free_gstring GString *line = NULL; + GString * str = NULL; + ReadFdBuf read_buf; + + if (out_data) + g_return_val_if_fail(*out_data == NULL, FALSE); + if (out_secrets) + g_return_val_if_fail(*out_secrets == NULL, FALSE); + + data = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); + secrets = + g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) nm_free_secret); + + read_buf.buf = NULL; + read_buf.n_buf = 0; + read_buf.fd = fd; + read_buf.eof = FALSE; + + line = g_string_new(NULL); + + /* Read stdin for data and secret items until we get a DONE */ + while (1) { + gboolean eof; + char c = '\0'; + + eof = !_read_fd_buf_c(&read_buf, &c); + + if (!eof && c == '\0') { + /* On the first '\0' char, we also assume the data is finished. Abort. */ + read_buf.eof = TRUE; + eof = TRUE; + } + + if (!eof && c != '\n') { + g_string_append_c(line, c); + if (line->len > 512 * 1024) { + /* we are about to read a huge line. That is not right, abort. */ + break; + } + continue; + } + + if (str && *line->str == '=') { + /* continuation */ + g_string_append_c(str, '\n'); + g_string_append(str, line->str + 1); + goto next; + } + + if (key && val) { + /* done a line */ + g_return_val_if_fail(hash, FALSE); + g_hash_table_insert(hash, g_string_free(key, FALSE), g_string_free(val, FALSE)); + key = NULL; + val = NULL; + hash = NULL; + success = TRUE; /* Got at least one value */ + } + + if (nm_streq(line->str, "DONE")) { + /* finish marker */ + break; + } else if (NM_STR_HAS_PREFIX(line->str, DATA_KEY_TAG)) { + nm_clear_g_string(&key); + key = g_string_new(line->str + strlen(DATA_KEY_TAG)); + str = key; + hash = data; + } else if (NM_STR_HAS_PREFIX(line->str, DATA_VAL_TAG)) { + if (val || !key || hash != data) + break; + val = g_string_new(line->str + strlen(DATA_VAL_TAG)); + str = val; + } else if (NM_STR_HAS_PREFIX(line->str, SECRET_KEY_TAG)) { + nm_clear_g_string(&key); + key = g_string_new(line->str + strlen(SECRET_KEY_TAG)); + str = key; + hash = secrets; + } else if (NM_STR_HAS_PREFIX(line->str, SECRET_VAL_TAG)) { + if (val || !key || hash != secrets) + break; + val = g_string_new(line->str + strlen(SECRET_VAL_TAG)); + str = val; + } + +next: + g_string_truncate(line, 0); + + if (eof) + break; + } + + if (success) { + NM_SET_OUT(out_data, g_steal_pointer(&data)); + NM_SET_OUT(out_secrets, g_steal_pointer(&secrets)); + } + return success; +} + +/** + * nm_vpn_service_plugin_get_secret_flags: + * @data: hash table containing VPN key/value pair data items + * @secret_name: VPN secret key name for which to retrieve flags for + * @out_flags: (out): on success, the flags associated with @secret_name + * + * Given a VPN secret key name, attempts to find the corresponding flags data + * item in @data. If found, converts the flags data item to + * #NMSettingSecretFlags and returns it. + * + * Returns: %TRUE if the flag data item was found and successfully converted + * to flags, %FALSE if not + * + * Since: 1.2 + **/ +gboolean +nm_vpn_service_plugin_get_secret_flags(GHashTable * data, + const char * secret_name, + NMSettingSecretFlags *out_flags) +{ + gs_free char * flag_name_free = NULL; + const char * s; + gint64 t1; + NMSettingSecretFlags t0; + + g_return_val_if_fail(data, FALSE); + g_return_val_if_fail(out_flags && *out_flags == NM_SETTING_SECRET_FLAG_NONE, FALSE); + if (!secret_name || !*secret_name) + g_return_val_if_reached(FALSE); + + s = g_hash_table_lookup(data, nm_construct_name_a("%s-flags", secret_name, &flag_name_free)); + if (!s) + return FALSE; + t1 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXINT64, -1); + if (t1 == -1) + return FALSE; + t0 = (NMSettingSecretFlags) t1; + if ((gint64) t0 != t1) + return FALSE; + NM_SET_OUT(out_flags, t0); + return TRUE; +} + +/*****************************************************************************/ + +static void +impl_vpn_service_plugin_disconnect(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + gpointer user_data) +{ + GError *error = NULL; + + if (nm_vpn_service_plugin_disconnect(plugin, &error)) + g_dbus_method_invocation_return_value(context, NULL); + else + g_dbus_method_invocation_take_error(context, error); +} + +static void +impl_vpn_service_plugin_set_config(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_service_plugin_set_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_service_plugin_set_ip4_config(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_service_plugin_set_ip4_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_service_plugin_set_ip6_config(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + GVariant * config, + gpointer user_data) +{ + nm_vpn_service_plugin_set_ip6_config(plugin, config); + g_dbus_method_invocation_return_value(context, NULL); +} + +static void +impl_vpn_service_plugin_set_failure(NMVpnServicePlugin * plugin, + GDBusMethodInvocation *context, + char * reason, + gpointer user_data) +{ + nm_vpn_service_plugin_failure(plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG); + g_dbus_method_invocation_return_value(context, NULL); +} + +/*****************************************************************************/ + +static void +_emit_quit(gpointer data, gpointer user_data) +{ + NMVpnServicePlugin *plugin = data; + + nm_vpn_service_plugin_emit_quit(plugin); +} + +static void +sigterm_handler(int signum) +{ + g_slist_foreach(active_plugins, _emit_quit, NULL); +} + +static void +setup_unix_signal_handler(void) +{ + struct sigaction action; + sigset_t block_mask; + + action.sa_handler = sigterm_handler; + sigemptyset(&block_mask); + action.sa_mask = block_mask; + action.sa_flags = 0; + sigaction(SIGINT, &action, NULL); + sigaction(SIGTERM, &action, NULL); +} + +/*****************************************************************************/ + +static void +one_plugin_destroyed(gpointer data, GObject *object) +{ + active_plugins = g_slist_remove(active_plugins, object); +} + +static void +nm_vpn_service_plugin_init(NMVpnServicePlugin *plugin) +{ + active_plugins = g_slist_append(active_plugins, plugin); + g_object_weak_ref(G_OBJECT(plugin), one_plugin_destroyed, NULL); +} + +static gboolean +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMVpnServicePlugin * plugin = NM_VPN_SERVICE_PLUGIN(initable); + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + gs_unref_object GDBusConnection *connection = NULL; + gs_unref_object GDBusProxy *proxy = NULL; + GVariant * ret; + + if (!priv->dbus_service_name) { + g_set_error_literal(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + _("No service name specified")); + return FALSE; + } + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (!connection) + return FALSE; + + proxy = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + cancellable, + error); + if (!proxy) + return FALSE; + + priv->dbus_vpn_service_plugin = nmdbus_vpn_plugin_skeleton_new(); + + _nm_dbus_bind_properties(plugin, priv->dbus_vpn_service_plugin); + _nm_dbus_bind_methods(plugin, + priv->dbus_vpn_service_plugin, + "Connect", + impl_vpn_service_plugin_connect, + "ConnectInteractive", + impl_vpn_service_plugin_connect_interactive, + "NeedSecrets", + impl_vpn_service_plugin_need_secrets, + "NewSecrets", + impl_vpn_service_plugin_new_secrets, + "Disconnect", + impl_vpn_service_plugin_disconnect, + "SetConfig", + impl_vpn_service_plugin_set_config, + "SetIp4Config", + impl_vpn_service_plugin_set_ip4_config, + "SetIp6Config", + impl_vpn_service_plugin_set_ip6_config, + "SetFailure", + impl_vpn_service_plugin_set_failure, + NULL); + + if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(priv->dbus_vpn_service_plugin), + connection, + NM_VPN_DBUS_PLUGIN_PATH, + error)) + return FALSE; + + nm_vpn_service_plugin_set_connection(plugin, connection); + nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_INIT); + + ret = g_dbus_proxy_call_sync(proxy, + "RequestName", + g_variant_new("(su)", priv->dbus_service_name, 0), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (!ret) { + if (error && *error) + g_dbus_error_strip_remote_error(*error); + return FALSE; + } + g_variant_unref(ret); + + return TRUE; +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + /* construct-only */ + priv->dbus_service_name = g_value_dup_string(value); + break; + case PROP_DBUS_WATCH_PEER: + /* construct-only */ + priv->dbus_watch_peer = g_value_get_boolean(value); + break; + case PROP_STATE: + nm_vpn_service_plugin_set_state(NM_VPN_SERVICE_PLUGIN(object), + (NMVpnServiceState) g_value_get_enum(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + g_value_set_string(value, priv->dbus_service_name); + break; + case PROP_DBUS_WATCH_PEER: + g_value_set_boolean(value, priv->dbus_watch_peer); + break; + case PROP_STATE: + g_value_set_enum(value, nm_vpn_service_plugin_get_state(NM_VPN_SERVICE_PLUGIN(object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +dispose(GObject *object) +{ + nm_vpn_service_plugin_shutdown(NM_VPN_SERVICE_PLUGIN(object)); + G_OBJECT_CLASS(nm_vpn_service_plugin_parent_class)->dispose(object); +} + +static void +finalize(GObject *object) +{ + NMVpnServicePlugin * plugin = NM_VPN_SERVICE_PLUGIN(object); + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + nm_vpn_service_plugin_set_connection(plugin, NULL); + g_free(priv->dbus_service_name); + + nm_clear_pointer(&priv->banner, g_variant_unref); + nm_clear_pointer(&priv->tundev, g_variant_unref); + nm_clear_pointer(&priv->gateway, g_variant_unref); + nm_clear_pointer(&priv->mtu, g_variant_unref); + + G_OBJECT_CLASS(nm_vpn_service_plugin_parent_class)->finalize(object); +} + +static void +state_changed(NMVpnServicePlugin *plugin, NMVpnServiceState state) +{ + NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); + + switch (state) { + case NM_VPN_SERVICE_STATE_STARTING: + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->fail_stop_id); + break; + case NM_VPN_SERVICE_STATE_STOPPED: + if (priv->dbus_watch_peer) + nm_vpn_service_plugin_emit_quit(plugin); + else + schedule_quit_timer(plugin); + nm_clear_g_dbus_connection_signal(nm_vpn_service_plugin_get_connection(plugin), + &priv->peer_watch_id); + break; + default: + /* Clean up all timers we might have set up. */ + nm_clear_g_source(&priv->connect_timer); + nm_clear_g_source(&priv->quit_timer); + nm_clear_g_source(&priv->fail_stop_id); + break; + } +} + +static void +nm_vpn_service_plugin_class_init(NMVpnServicePluginClass *plugin_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(plugin_class); + + g_type_class_add_private(object_class, sizeof(NMVpnServicePluginPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + plugin_class->state_changed = state_changed; + + /** + * NMVpnServicePlugin:service-name: + * + * The D-Bus service name of this plugin. + * + * Since: 1.2 + */ + obj_properties[PROP_DBUS_SERVICE_NAME] = + g_param_spec_string(NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMVpnServicePlugin:watch-peer: + * + * Whether to watch for D-Bus peer's changes. + * + * Since: 1.2 + */ + obj_properties[PROP_DBUS_WATCH_PEER] = + g_param_spec_boolean(NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER, + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * NMVpnServicePlugin:state: + * + * The state of the plugin. + * + * Since: 1.2 + */ + obj_properties[PROP_STATE] = g_param_spec_enum(NM_VPN_SERVICE_PLUGIN_STATE, + "", + "", + NM_TYPE_VPN_SERVICE_STATE, + NM_VPN_SERVICE_STATE_INIT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + signals[STATE_CHANGED] = g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, state_changed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[SECRETS_REQUIRED] = g_signal_new("secrets-required", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_TYPE_STRV); + + signals[CONFIG] = g_signal_new("config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[IP4_CONFIG] = g_signal_new("ip4-config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, ip4_config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[IP6_CONFIG] = g_signal_new("ip6-config", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, ip6_config), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_VARIANT); + + signals[LOGIN_BANNER] = g_signal_new("login-banner", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, login_banner), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[FAILURE] = g_signal_new("failure", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, failure), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + signals[QUIT] = g_signal_new("quit", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NMVpnServicePluginClass, quit), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0, + G_TYPE_NONE); + + setup_unix_signal_handler(); +} + +static void +nm_vpn_service_plugin_initable_iface_init(GInitableIface *iface) +{ + iface->init = init_sync; +} diff --git a/src/libnm-client-impl/nm-wifi-p2p-peer.c b/src/libnm-client-impl/nm-wifi-p2p-peer.c new file mode 100644 index 00000000..1d6b7c95 --- /dev/null +++ b/src/libnm-client-impl/nm-wifi-p2p-peer.c @@ -0,0 +1,575 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 - 2019 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-wifi-p2p-peer.h" + +#include "nm-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-wifi-p2p.h" +#include "nm-utils.h" +#include "nm-dbus-interface.h" +#include "nm-object-private.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_FLAGS, + PROP_NAME, + PROP_MANUFACTURER, + PROP_MODEL, + PROP_MODEL_NUMBER, + PROP_SERIAL, + PROP_WFD_IES, + PROP_HW_ADDRESS, + PROP_STRENGTH, + PROP_LAST_SEEN, ); + +typedef struct { + GBytes *wfd_ies; + char * name; + char * manufacturer; + char * model; + char * model_number; + char * serial; + char * hw_address; + gint32 last_seen; + guint32 flags; + guint8 strength; +} NMWifiP2PPeerPrivate; + +struct _NMWifiP2PPeer { + NMObject parent; + NMWifiP2PPeerPrivate _priv; +}; + +struct _NMWifiP2PPeerClass { + NMObjectClass parent; +}; + +G_DEFINE_TYPE(NMWifiP2PPeer, nm_wifi_p2p_peer, NM_TYPE_OBJECT) + +#define NM_WIFI_P2P_PEER_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMWifiP2PPeer, NM_IS_WIFI_P2P_PEER, NMObject) + +/*****************************************************************************/ + +/** + * nm_wifi_p2p_peer_get_flags: + * @peer: a #NMWifiP2PPeer + * + * Gets the flags of the P2P peer. + * + * Returns: the flags + * + * Since: 1.16 + **/ +NM80211ApFlags +nm_wifi_p2p_peer_get_flags(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NM_802_11_AP_FLAGS_NONE); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->flags; +} + +/** + * nm_wifi_p2p_peer_get_name: + * @peer: a #NMWifiP2PPeer + * + * Gets the name of the P2P peer. + * + * Returns: the name + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_name(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->name; +} + +/** + * nm_wifi_p2p_peer_get_manufacturer: + * @peer: a #NMWifiP2PPeer + * + * Gets the manufacturer of the P2P peer. + * + * Returns: the manufacturer + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_manufacturer(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->manufacturer; +} + +/** + * nm_wifi_p2p_peer_get_model: + * @peer: a #NMWifiP2PPeer + * + * Gets the model of the P2P peer. + * + * Returns: the model + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_model(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->model; +} + +/** + * nm_wifi_p2p_peer_get_model_number: + * @peer: a #NMWifiP2PPeer + * + * Gets the model number of the P2P peer. + * + * Returns: the model number + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_model_number(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->model_number; +} + +/** + * nm_wifi_p2p_peer_get_serial: + * @peer: a #NMWifiP2PPeer + * + * Gets the serial number of the P2P peer. + * + * Returns: the serial number + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_serial(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->serial; +} + +/** + * nm_wifi_p2p_peer_get_wfd_ies: + * @peer: a #NMWifiP2PPeer + * + * Gets the WFD information elements of the P2P peer. + * + * Returns: (transfer none): the #GBytes containing the WFD IEs, or %NULL. + * + * Since: 1.16 + **/ +GBytes * +nm_wifi_p2p_peer_get_wfd_ies(NMWifiP2PPeer *peer) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer); + if (!priv->wfd_ies || g_bytes_get_size(priv->wfd_ies) == 0) + return NULL; + + return priv->wfd_ies; +} + +/** + * nm_wifi_p2p_peer_get_hw_address: + * @peer: a #NMWifiP2PPeer + * + * Gets the hardware address of the P2P peer. + * + * Returns: the hardware address + * + * Since: 1.16 + **/ +const char * +nm_wifi_p2p_peer_get_hw_address(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->hw_address; +} + +/** + * nm_wifi_p2p_peer_get_strength: + * @peer: a #NMWifiP2PPeer + * + * Gets the current signal strength of the P2P peer as a percentage. + * + * Returns: the signal strength (0 to 100) + * + * Since: 1.16 + **/ +guint8 +nm_wifi_p2p_peer_get_strength(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), 0); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->strength; +} + +/** + * nm_wifi_p2p_peer_get_last_seen: + * @peer: a #NMWifiP2PPeer + * + * Returns the timestamp (in CLOCK_BOOTTIME seconds) for the last time the + * P2P peer was seen. A value of -1 means the P2P peer has never been seen. + * + * Returns: the last seen time in seconds + * + * Since: 1.16 + **/ +int +nm_wifi_p2p_peer_get_last_seen(NMWifiP2PPeer *peer) +{ + g_return_val_if_fail(NM_IS_WIFI_P2P_PEER(peer), -1); + + return NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->last_seen; +} + +/** + * nm_wifi_p2p_peer_connection_valid: + * @peer: an #NMWifiP2PPeer to validate @connection against + * @connection: an #NMConnection to validate against @peer + * + * Validates a given connection against a given Wi-Fi P2P peer to ensure that + * the connection may be activated with that peer. The connection must match the + * @peer's address and in the future possibly other attributes. + * + * Returns: %TRUE if the connection may be activated with this Wi-Fi P2P Peer, + * %FALSE if it cannot be. + * + * Since: 1.16 + **/ +gboolean +nm_wifi_p2p_peer_connection_valid(NMWifiP2PPeer *peer, NMConnection *connection) +{ + NMSettingConnection *s_con; + NMSettingWifiP2P * s_wifi_p2p; + const char * ctype; + const char * hw_address; + const char * setting_peer; + + s_wifi_p2p = + (NMSettingWifiP2P *) nm_connection_get_setting(connection, NM_TYPE_SETTING_WIFI_P2P); + if (!s_wifi_p2p) + return FALSE; + + s_con = nm_connection_get_setting_connection(connection); + if (!s_con) + return FALSE; + + ctype = nm_setting_connection_get_connection_type(s_con); + if (!ctype || !nm_streq(ctype, NM_SETTING_WIFI_P2P_SETTING_NAME)) + return FALSE; + + /* HW Address check */ + hw_address = nm_wifi_p2p_peer_get_hw_address(peer); + if (!hw_address) + return FALSE; + + setting_peer = nm_setting_wifi_p2p_get_peer(s_wifi_p2p); + if (!setting_peer || !nm_streq(hw_address, setting_peer)) + return FALSE; + + return TRUE; +} + +/** + * nm_wifi_p2p_peer_filter_connections: + * @peer: an #NMWifiP2PPeer to filter connections for + * @connections: (element-type NMConnection): an array of #NMConnections to + * filter + * + * Filters a given array of connections for a given #NMWifiP2PPeer object and + * returns connections which may be activated with the P2P peer. Any + * returned connections will match the @peers's HW address and in the future + * possibly other attributes. + * + * To obtain the list of connections that are compatible with this P2P peer, + * use nm_client_get_connections() and then filter the returned list for a given + * #NMDevice using nm_device_filter_connections() and finally filter that list + * with this function. + * + * Returns: (transfer container) (element-type NMConnection): an array of + * #NMConnections that could be activated with the given @peer. The array should + * be freed with g_ptr_array_unref() when it is no longer required. + * + * Since: 1.16 + **/ +GPtrArray * +nm_wifi_p2p_peer_filter_connections(NMWifiP2PPeer *peer, const GPtrArray *connections) +{ + GPtrArray *filtered; + guint i; + + filtered = g_ptr_array_new_with_free_func(g_object_unref); + for (i = 0; i < connections->len; i++) { + NMConnection *candidate = connections->pdata[i]; + + if (nm_wifi_p2p_peer_connection_valid(peer, candidate)) + g_ptr_array_add(filtered, g_object_ref(candidate)); + } + + return filtered; +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMWifiP2PPeer *peer = NM_WIFI_P2P_PEER(object); + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_flags(value, nm_wifi_p2p_peer_get_flags(peer)); + break; + case PROP_NAME: + g_value_set_string(value, nm_wifi_p2p_peer_get_name(peer)); + break; + case PROP_MANUFACTURER: + g_value_set_string(value, nm_wifi_p2p_peer_get_manufacturer(peer)); + break; + case PROP_MODEL: + g_value_set_string(value, nm_wifi_p2p_peer_get_model(peer)); + break; + case PROP_MODEL_NUMBER: + g_value_set_string(value, nm_wifi_p2p_peer_get_model_number(peer)); + break; + case PROP_SERIAL: + g_value_set_string(value, nm_wifi_p2p_peer_get_serial(peer)); + break; + case PROP_WFD_IES: + g_value_set_boxed(value, nm_wifi_p2p_peer_get_wfd_ies(peer)); + break; + case PROP_HW_ADDRESS: + g_value_set_string(value, nm_wifi_p2p_peer_get_hw_address(peer)); + break; + case PROP_STRENGTH: + g_value_set_uchar(value, nm_wifi_p2p_peer_get_strength(peer)); + break; + case PROP_LAST_SEEN: + g_value_set_int(value, nm_wifi_p2p_peer_get_last_seen(peer)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_wifi_p2p_peer_init(NMWifiP2PPeer *peer) +{ + NM_WIFI_P2P_PEER_GET_PRIVATE(peer)->last_seen = -1; +} + +static void +finalize(GObject *object) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE(object); + + g_free(priv->name); + g_free(priv->manufacturer); + g_free(priv->model); + g_free(priv->model_number); + g_free(priv->serial); + g_free(priv->hw_address); + + g_bytes_unref(priv->wfd_ies); + + G_OBJECT_CLASS(nm_wifi_p2p_peer_parent_class)->finalize(object); +} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_wifip2ppeer = NML_DBUS_META_IFACE_INIT_PROP( + NM_DBUS_INTERFACE_WIFI_P2P_PEER, + nm_wifi_p2p_peer_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + NML_DBUS_META_IFACE_DBUS_PROPERTIES( + NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMWifiP2PPeer, _priv.flags), + NML_DBUS_META_PROPERTY_INIT_IGNORE("Groups", "as"), + NML_DBUS_META_PROPERTY_INIT_S("HwAddress", + PROP_HW_ADDRESS, + NMWifiP2PPeer, + _priv.hw_address), + NML_DBUS_META_PROPERTY_INIT_I("LastSeen", PROP_LAST_SEEN, NMWifiP2PPeer, _priv.last_seen), + NML_DBUS_META_PROPERTY_INIT_S("Manufacturer", + PROP_MANUFACTURER, + NMWifiP2PPeer, + _priv.manufacturer), + NML_DBUS_META_PROPERTY_INIT_S("Model", PROP_MODEL, NMWifiP2PPeer, _priv.model), + NML_DBUS_META_PROPERTY_INIT_S("ModelNumber", + PROP_MODEL_NUMBER, + NMWifiP2PPeer, + _priv.model_number), + NML_DBUS_META_PROPERTY_INIT_S("Name", PROP_NAME, NMWifiP2PPeer, _priv.name), + NML_DBUS_META_PROPERTY_INIT_S("Serial", PROP_SERIAL, NMWifiP2PPeer, _priv.serial), + NML_DBUS_META_PROPERTY_INIT_Y("Strength", PROP_STRENGTH, NMWifiP2PPeer, _priv.strength), + NML_DBUS_META_PROPERTY_INIT_AY("WfdIEs", PROP_WFD_IES, NMWifiP2PPeer, _priv.wfd_ies), ), ); + +static void +nm_wifi_p2p_peer_class_init(NMWifiP2PPeerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + /** + * NMWifiP2PPeer:flags: + * + * The flags of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_FLAGS] = g_param_spec_flags(NM_WIFI_P2P_PEER_FLAGS, + "", + "", + NM_TYPE_802_11_AP_FLAGS, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:name: + * + * The name of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_NAME] = g_param_spec_string(NM_WIFI_P2P_PEER_NAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:manufacturer: + * + * The manufacturer of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_MANUFACTURER] = + g_param_spec_string(NM_WIFI_P2P_PEER_MANUFACTURER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:model: + * + * The model of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_MODEL] = g_param_spec_string(NM_WIFI_P2P_PEER_MODEL, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:model-number: + * + * The hardware address of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_MODEL_NUMBER] = + g_param_spec_string(NM_WIFI_P2P_PEER_MODEL_NUMBER, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:serial: + * + * The serial number of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_SERIAL] = g_param_spec_string(NM_WIFI_P2P_PEER_SERIAL, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:wfd-ies: + * + * The WFD information elements of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_WFD_IES] = g_param_spec_boxed(NM_WIFI_P2P_PEER_WFD_IES, + "", + "", + G_TYPE_BYTES, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * NMWifiP2PPeer:hw-address: + * + * The hardware address of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string(NM_WIFI_P2P_PEER_HW_ADDRESS, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:strength: + * + * The current signal strength of the P2P peer. + * + * Since: 1.16 + **/ + obj_properties[PROP_STRENGTH] = g_param_spec_uchar(NM_WIFI_P2P_PEER_STRENGTH, + "", + "", + 0, + G_MAXUINT8, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWifiP2PPeer:last-seen: + * + * The timestamp (in CLOCK_BOOTTIME seconds) for the last time the + * P2P peer was found. A value of -1 means the peer has never been seen. + * + * Since: 1.16 + **/ + obj_properties[PROP_LAST_SEEN] = g_param_spec_int(NM_WIFI_P2P_PEER_LAST_SEEN, + "", + "", + -1, + G_MAXINT, + -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_wifip2ppeer); +} diff --git a/src/libnm-client-impl/nm-wimax-nsp.c b/src/libnm-client-impl/nm-wimax-nsp.c new file mode 100644 index 00000000..62181d6b --- /dev/null +++ b/src/libnm-client-impl/nm-wimax-nsp.c @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-wimax-nsp.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_NAME, PROP_SIGNAL_QUALITY, PROP_NETWORK_TYPE, ); + +struct _NMWimaxNsp { + NMObject parent; +}; + +struct _NMWimaxNspClass { + NMObjectClass parent; +}; + +G_DEFINE_TYPE(NMWimaxNsp, nm_wimax_nsp, NM_TYPE_OBJECT) + +#define NM_WIMAX_NSP_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMWimaxNsp, NM_IS_WIMAX_NSP, NMObject) + +/*****************************************************************************/ + +/** + * nm_wimax_nsp_get_name: + * @nsp: a #NMWimaxNsp + * + * Gets the name of the wimax NSP + * + * Returns: the name + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +const char * +nm_wimax_nsp_get_name(NMWimaxNsp *nsp) +{ + g_return_val_if_reached(NULL); +} + +/** + * nm_wimax_nsp_get_signal_quality: + * @nsp: a #NMWimaxNsp + * + * Gets the WPA signal quality of the wimax NSP. + * + * Returns: the signal quality + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +guint32 +nm_wimax_nsp_get_signal_quality(NMWimaxNsp *nsp) +{ + g_return_val_if_reached(0); +} + +/** + * nm_wimax_nsp_get_network_type: + * @nsp: a #NMWimaxNsp + * + * Gets the network type of the wimax NSP. + * + * Returns: the network type + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +NMWimaxNspNetworkType +nm_wimax_nsp_get_network_type(NMWimaxNsp *nsp) +{ + g_return_val_if_reached(NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN); +} + +/** + * nm_wimax_nsp_connection_valid: + * @nsp: an #NMWimaxNsp to validate @connection against + * @connection: an #NMConnection to validate against @nsp + * + * Validates a given connection against a given WiMAX NSP to ensure that the + * connection may be activated with that NSP. The connection must match the + * @nsp's network name and other attributes. + * + * Returns: %TRUE if the connection may be activated with this WiMAX NSP, + * %FALSE if it cannot be. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +gboolean +nm_wimax_nsp_connection_valid(NMWimaxNsp *nsp, NMConnection *connection) +{ + g_return_val_if_reached(FALSE); +} + +/** + * nm_wimax_nsp_filter_connections: + * @nsp: an #NMWimaxNsp to filter connections for + * @connections: (element-type NMConnection): an array of #NMConnections to + * filter + * + * Filters a given array of connections for a given #NMWimaxNsp object and + * return connections which may be activated with the NSP. Any returned + * connections will match the @nsp's network name and other attributes. + * + * Returns: (transfer full) (element-type NMConnection): an array of + * #NMConnections that could be activated with the given @nsp. The array should + * be freed with g_ptr_array_unref() when it is no longer required. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ +GPtrArray * +nm_wimax_nsp_filter_connections(NMWimaxNsp *nsp, const GPtrArray *connections) +{ + g_return_val_if_reached(NULL); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + g_return_if_reached(); +} + +static void +nm_wimax_nsp_init(NMWimaxNsp *nsp) +{ + g_return_if_reached(); +} + +static void +nm_wimax_nsp_class_init(NMWimaxNspClass *nsp_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(nsp_class); + + object_class->get_property = get_property; + + /** + * NMWimaxNsp:name: + * + * The name of the WiMAX NSP. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ + obj_properties[PROP_NAME] = g_param_spec_string(NM_WIMAX_NSP_NAME, + "", + "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWimaxNsp:signal-quality: + * + * The signal quality of the WiMAX NSP. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ + obj_properties[PROP_SIGNAL_QUALITY] = + g_param_spec_uint(NM_WIMAX_NSP_SIGNAL_QUALITY, + "", + "", + 0, + 100, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * NMWimaxNsp:network-type: + * + * The network type of the WiMAX NSP. + * + * Deprecated: 1.22: WiMAX is no longer supported by NetworkManager since 1.2.0. + **/ + obj_properties[PROP_NETWORK_TYPE] = + g_param_spec_enum(NM_WIMAX_NSP_NETWORK_TYPE, + "", + "", + NM_TYPE_WIMAX_NSP_NETWORK_TYPE, + NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/libnm-client-impl/tests/meson.build b/src/libnm-client-impl/tests/meson.build new file mode 100644 index 00000000..3f2e78a7 --- /dev/null +++ b/src/libnm-client-impl/tests/meson.build @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +test_units = [ + 'test-libnm', + 'test-nm-client', + 'test-remote-settings-client', + 'test-secret-agent', +] + +foreach test_unit: test_units + exe = executable( + test_unit, + [ test_unit + '.c' ] + (test_unit == 'test-libnm' ? src_contrib_nm_compat_source : []), + include_directories: [ + libnm_client_public_inc, + ], + dependencies: [ + libnm_core_public_dep, + libnm_client_public_dep, + glib_dep, + libudev_dep, + ], + link_with: [ + libnm_client_impl, + libnm_client_test, + libnm_core_aux_intern, + libnm_core_impl, + libnm_crypto, + libnm_base, + libnm_udev_aux, + libnm_systemd_shared, + libnm_log_null, + libnm_glib_aux, + libnm_std_aux, + libc_siphash, + libnmdbus, + ], + ) + + test( + 'src/libnm-client-impl/tests/' + test_unit, + test_script, + timeout: 90, + args: test_args + [exe.full_path()], + ) +endforeach diff --git a/src/libnm-client-impl/tests/test-libnm.c b/src/libnm-client-impl/tests/test-libnm.c new file mode 100644 index 00000000..6cb8076e --- /dev/null +++ b/src/libnm-client-impl/tests/test-libnm.c @@ -0,0 +1,3502 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#if defined(HAVE_DECL_MEMFD_CREATE) && HAVE_DECL_MEMFD_CREATE + #include +#endif + +#include + +#include "NetworkManager.h" +#include "nm-access-point.h" +#include "nm-checkpoint.h" +#include "libnm-client-impl/nm-dhcp4-config.h" +#include "libnm-client-impl/nm-dhcp6-config.h" +#include "libnm-client-impl/nm-dns-manager.h" +#include "libnm-client-impl/nm-ip4-config.h" +#include "libnm-client-impl/nm-ip6-config.h" +#include "libnm-client-impl/nm-libnm-utils.h" +#include "nm-object.h" +#include "nm-vpn-service-plugin.h" +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" + +#include "libnm-glib-aux/nm-test-utils.h" + +/*****************************************************************************/ + +typedef struct { + const char *desc; + const char *expected; + int line; +} TestFixupData; + +static void +_test_fixup_string(const TestFixupData *data, guint n_data, char *(*func)(const char *) ) +{ + guint i; + + g_assert(data); + g_assert(n_data > 0); + g_assert(func); + + for (i = 0; i < n_data; i++, data++) { + gs_free char *value = func(data->desc); + + if (!nm_streq0(value, data->expected)) { + g_error("Error comparing value: %s:%i: expected %s%s%s for %s%s%s, but got %s%s%s", + __FILE__, + data->line, + NM_PRINT_FMT_QUOTE_STRING(data->expected), + NM_PRINT_FMT_QUOTE_STRING(data->desc), + NM_PRINT_FMT_QUOTE_STRING(value)); + } + } +} + +#define T_DATA(_desc, _expected) \ + { \ + .desc = _desc, .expected = _expected, .line = __LINE__, \ + } + +static void +test_fixup_vendor_string(void) +{ + static const TestFixupData data[] = { + T_DATA("3Com", "3Com"), + T_DATA("3Com Corp.", "3Com"), + T_DATA("3Com Corporation", "3Com"), + T_DATA("Abocom Systems Inc", "Abocom"), + T_DATA("AboCom Systems Inc", "AboCom"), + T_DATA("Accton Technology Corp.", "Accton"), + T_DATA("Accton Technology Corporation", "Accton"), + T_DATA("Acer Communications & Multimedia", "Acer"), + T_DATA("Actiontec Electronics, Inc. [hex]", "Actiontec"), + T_DATA("Adaptec", "Adaptec"), + T_DATA("Addtron Technology Co, Inc.", "Addtron"), + T_DATA("ADMtek", "ADMtek"), + T_DATA("ADMtek, Inc.", "ADMtek"), + T_DATA("ADS Technologies, Inc.", "ADS"), + T_DATA("Advanced Micro Devices, Inc. [AMD]", "AMD"), + T_DATA("Advance Multimedia Internet Technology Inc. (AMIT)", "Advance"), + T_DATA("AEI", "AEI"), + T_DATA("Airprime, Incorporated", "Airprime"), + T_DATA("AirTies Wireless Networks", "AirTies"), + T_DATA("AirVast", "AirVast"), + T_DATA("Alcatel Telecom", "Alcatel Telecom"), + T_DATA("ALi Corp.", "ALi"), + T_DATA("Allied Telesis", "Allied Telesis"), + T_DATA("Allied Telesyn International", "Allied Telesyn"), + T_DATA("Alteon Networks Inc.", "Alteon"), + T_DATA("Altima (nee Broadcom)", "Altima"), + T_DATA("A-Max Technology Macao Commercial Offshore Co. Ltd.", "A-Max"), + T_DATA("Amigo Technology Inc.", "Amigo"), + T_DATA("AMIT Technology, Inc.", "AMIT"), + T_DATA("Anchor Chips, Inc.", "Anchor"), + T_DATA("AnyDATA Corporation", "AnyDATA"), + T_DATA("Apple Inc.", "Apple"), + T_DATA("Apple, Inc.", "Apple"), + T_DATA("ARC International", "ARC"), + T_DATA("ASIX Electronics Corp.", "ASIX"), + T_DATA("Asix Electronics Corporation", "Asix"), + T_DATA("Askey Computer Corp. [hex]", "Askey"), + T_DATA("ASUSTek Computer, Inc.", "ASUSTek Computer"), + T_DATA("ASUSTek Computer, Inc. (wrong ID)", "ASUSTek Computer"), + T_DATA("ATEN International Co., Ltd", "ATEN"), + T_DATA("Atheros Communications", "Atheros"), + T_DATA("Atheros Communications, Inc.", "Atheros"), + T_DATA("AVM GmbH", "AVM"), + T_DATA("AzureWave", "AzureWave"), + T_DATA("Belkin", "Belkin"), + T_DATA("Belkin Components", "Belkin"), + T_DATA("Billionton Systems, Inc.", "Billionton"), + T_DATA("Broadcom Corp.", "Broadcom"), + T_DATA("Broadcom Limited", "Broadcom"), + T_DATA("Brocade Communications Systems, Inc.", "Brocade"), + T_DATA("BUFFALO INC. (formerly MelCo., Inc.)", "BUFFALO"), + T_DATA("CACE Technologies Inc.", "CACE"), + T_DATA("Cadence Design Systems, Inc.", "Cadence"), + T_DATA("Chelsio Communications Inc", "Chelsio"), + T_DATA("Chicony Electronics Co., Ltd", "Chicony"), + T_DATA("Chu Yuen Enterprise Co., Ltd", "Chu Yuen"), + T_DATA("Cisco Systems Inc", "Cisco"), + T_DATA("Cisco Systems, Inc.", "Cisco"), + T_DATA("CMOTECH Co., Ltd.", "CMOTECH"), + T_DATA("CNet Technology Inc.", "CNet"), + T_DATA("CNet Technology Inc", "CNet"), + T_DATA("Comneon", "Comneon"), + T_DATA("Compaq Computer Corp.", "Compaq"), + T_DATA("Compaq Computer Corporation", "Compaq"), + T_DATA("Compex", "Compex"), + T_DATA("Computer Access Technology Corp.", "Computer Access"), + T_DATA("Conexant Systems, Inc.", "Conexant"), + T_DATA("Conexant Systems (Rockwell), Inc.", "Conexant"), + T_DATA("Corega K.K.", "Corega K.K."), + T_DATA("Curitel Communications, Inc.", "Curitel"), + T_DATA("CyberTAN Technology", "CyberTAN"), + T_DATA("Cypress Semiconductor Corp.", "Cypress"), + T_DATA("Davicom Semiconductor, Inc.", "Davicom"), + T_DATA("Dell Computer Corp.", "Dell"), + T_DATA("DELTA Electronics, Inc", "DELTA"), + T_DATA("Digital Equipment Corporation", "Digital Equipment"), + T_DATA("D-Link Corp.", "D-Link"), + T_DATA("D-Link System", "D-Link System"), + T_DATA("D-Link System Inc", "D-Link System"), + T_DATA("DrayTek Corp.", "DrayTek"), + T_DATA("d'TV", "d'TV"), + T_DATA("DVICO", "DVICO"), + T_DATA("Edimax Computer Co.", "Edimax"), + T_DATA("Edimax Technology Co., Ltd", "Edimax"), + T_DATA("Efar Microsystems", "Efar"), + T_DATA("Efficient Networks, Inc.", "Efficient"), + T_DATA("ELCON Systemtechnik", "ELCON"), + T_DATA("Elecom Co., Ltd", "Elecom"), + T_DATA("ELSA AG", "ELSA"), + T_DATA("Emulex Corporation", "Emulex"), + T_DATA("Encore Electronics Inc.", "Encore"), + T_DATA("EndPoints, Inc.", "EndPoints"), + T_DATA("Entrega [hex]", "Entrega"), + T_DATA("Ericsson Business Mobile Networks BV", "Ericsson"), + T_DATA("eTEK Labs", "eTEK"), + T_DATA("Exar Corp.", "Exar"), + T_DATA("Fiberline", "Fiberline"), + T_DATA("Fujitsu Limited.", "Fujitsu"), + T_DATA("Fujitsu Siemens Computers", "Fujitsu Siemens"), + T_DATA("Gateway, Inc.", "Gateway"), + T_DATA("Gemtek", "Gemtek"), + T_DATA("Genesys Logic, Inc.", "Genesys Logic"), + T_DATA("Global Sun Technology", "Global Sun"), + T_DATA("Global Sun Technology, Inc.", "Global Sun"), + T_DATA("GlobeSpan, Inc.", "GlobeSpan"), + T_DATA("Gmate, Inc.", "Gmate"), + T_DATA("Good Way Technology", "Good Way"), + T_DATA("Guillemot Corp.", "Guillemot"), + T_DATA("Hangzhou Silan Microelectronics Co., Ltd.", "Hangzhou Silan"), + T_DATA("Hawking Technologies", "Hawking"), + T_DATA("Hewlett-Packard", "Hewlett-Packard"), + T_DATA("Hirose Electric", "Hirose Electric"), + T_DATA("Holtek Microelectronics Inc", "Holtek"), + T_DATA("Huawei-3Com", "Huawei-3Com"), + T_DATA("Huawei Technologies Co., Ltd.", "Huawei"), + T_DATA("ICS Advent", "ICS Advent"), + T_DATA("IMC Networks", "IMC"), + T_DATA("Intel Corp.", "Intel"), + T_DATA("Intel Corporation", "Intel"), + T_DATA("Intellon Corp.", "Intellon"), + T_DATA("InterBiometrics", "InterBiometrics"), + T_DATA("Intersil Corp.", "Intersil"), + T_DATA("Intersil Corporation", "Intersil"), + T_DATA("I-O Data Device, Inc.", "I-O Data Device"), + T_DATA("Jaton Corp.", "Jaton"), + T_DATA("JMicron Technology Corp.", "JMicron"), + T_DATA("Kawasaki LSI", "Kawasaki LSI"), + T_DATA("KC Technology, Inc.", "KC"), + T_DATA("Kingston Technology", "Kingston"), + T_DATA("KTI", "KTI"), + T_DATA("Kvaser AB", "Kvaser"), + T_DATA("LapLink, Inc.", "LapLink"), + T_DATA("Lenovo", "Lenovo"), + T_DATA("LevelOne", "LevelOne"), + T_DATA("LG Electronics, Inc.", "LG"), + T_DATA("LG Electronics USA, Inc.", "LG"), + T_DATA("Linksys, Inc.", "Linksys"), + T_DATA("Linksys (?)", "Linksys"), + T_DATA("Linksys", "Linksys"), + T_DATA("Lite-On Communications Inc", "Lite-On"), + T_DATA("Lite-On Technology Corp.", "Lite-On"), + T_DATA("Logitec Corp.", "Logitec"), + T_DATA("Logitech, Inc.", "Logitech"), + T_DATA("LSI Corporation", "LSI"), + T_DATA("LSI Logic / Symbios Logic", "LSI Logic"), + T_DATA("Macronix, Inc. [MXIC]", "MXIC"), + T_DATA("Marvell Semiconductor, Inc.", "Marvell"), + T_DATA("Marvell Technology Group Ltd.", "Marvell"), + T_DATA("MediaTek Inc.", "MediaTek"), + T_DATA("Mellanox Technologies", "Mellanox"), + T_DATA("Memorex", "Memorex"), + T_DATA("Micrel-Kendin", "Micrel-Kendin"), + T_DATA("Microchip Technology, Inc.", "Microchip"), + T_DATA("Microcomputer Systems (M) Son", "Microcomputer"), + T_DATA("Microsoft Corp.", "Microsoft"), + T_DATA("Microsoft Corporation", "Microsoft"), + T_DATA("Micro-Star International Co., Ltd. [MSI]", "MSI"), + T_DATA("Micro Star International", "Micro Star"), + T_DATA("Mobility", "Mobility"), + T_DATA("MosChip Semiconductor", "MosChip"), + T_DATA("Motorola PCS", "Motorola"), + T_DATA("MYRICOM Inc.", "MYRICOM"), + T_DATA("MYSON Technology Inc", "MYSON"), + T_DATA("National Instruments Corp.", "National"), + T_DATA("National Semiconductor Corporation", "National"), + T_DATA("NEC Corp.", "NEC"), + T_DATA("Netchip Technology, Inc.", "Netchip"), + T_DATA("Netgear, Inc", "Netgear"), + T_DATA("NetGear, Inc.", "NetGear"), + T_DATA("Netgear", "Netgear"), + T_DATA("Netopia, Inc.", "Netopia"), + T_DATA("Netronome Systems, Inc.", "Netronome"), + T_DATA("NetVin", "NetVin"), + T_DATA("NetXen Incorporated", "NetXen"), + T_DATA("Nordic Semiconductor ASA", "Nordic"), + T_DATA("Northern Telecom", "Northern Telecom"), + T_DATA("NovaTech", "NovaTech"), + T_DATA("Novatel Wireless", "Novatel Wireless"), + T_DATA("NVIDIA Corp.", "NVIDIA"), + T_DATA("NVIDIA Corporation", "NVIDIA"), + T_DATA("Olicom", "Olicom"), + T_DATA("Olivetti Techcenter", "Olivetti"), + T_DATA("Olympus Optical Co., Ltd", "Olympus"), + T_DATA("OMEGA TECHNOLOGY", "OMEGA"), + T_DATA("Omnidirectional Control Technology, Inc.", "Omnidirectional Control"), + T_DATA("OpenMoko, Inc.", "OpenMoko"), + T_DATA("Option", "Option"), + T_DATA("OQO", "OQO"), + T_DATA("Oracle/SUN", "Oracle"), + T_DATA("Ovislink Corp.", "Ovislink"), + T_DATA("Packet Engines Inc.", "Packet Engines"), + T_DATA("Panasonic (Matsushita)", "Panasonic"), + T_DATA("PEAK System", "PEAK System"), + T_DATA("PEAK-System Technik GmbH", "PEAK-System"), + T_DATA("PEGATRON CORPORATION", "PEGATRON CORPORATION"), + T_DATA("Peppercon AG", "Peppercon"), + T_DATA("Peracom Networks, Inc.", "Peracom"), + T_DATA("Philips (or NXP)", "Philips"), + T_DATA("Planex Communications, Inc", "Planex"), + T_DATA("Planex Communications", "Planex"), + T_DATA("Planex", "Planex"), + T_DATA("PLANEX", "PLANEX"), + T_DATA("Portsmith", "Portsmith"), + T_DATA("Prolific Technology, Inc.", "Prolific"), + T_DATA("Qcom", "Qcom"), + T_DATA("Qi Hardware", "Qi"), + T_DATA("QinHeng Electronics", "QinHeng"), + T_DATA("QLogic Corp.", "QLogic"), + T_DATA("Qualcomm Atheros Communications", "Qualcomm Atheros"), + T_DATA("Qualcomm Atheros", "Qualcomm Atheros"), + T_DATA("Qualcomm, Inc.", "Qualcomm"), + T_DATA("Qualcomm / Option", "Qualcomm"), + T_DATA("Quanta Computer, Inc.", "Quanta Computer"), + T_DATA("Quanta Microsystems, Inc.", "Quanta"), + T_DATA("Quantenna Communications, Inc.", "Quantenna"), + T_DATA("RadioShack Corp. (Tandy)", "RadioShack"), + T_DATA("Ralink corp.", "Ralink"), + T_DATA("Ralink Technology, Corp.", "Ralink"), + T_DATA("RDC Semiconductor, Inc.", "RDC"), + T_DATA("Realtek Semiconductor Co., Ltd.", "Realtek"), + T_DATA("Realtek Semiconductor Corp.", "Realtek"), + T_DATA("Red Hat, Inc.", "Red Hat"), + T_DATA("SafeNet (wrong ID)", "SafeNet"), + T_DATA("Sagem", "Sagem"), + T_DATA("Samsung Electronics Co., Ltd", "Samsung"), + T_DATA("Sega Enterprises Ltd", "Sega"), + T_DATA("Senao", "Senao"), + T_DATA("Shark Multimedia", "Shark"), + T_DATA("Sharp Corp.", "Sharp"), + T_DATA("Siemens Information and Communication Products", "Siemens"), + T_DATA("Sierra Wireless, Inc.", "Sierra Wireless"), + T_DATA("Silicom", "Silicom"), + T_DATA("Silicon Graphics Intl. Corp.", "Silicon Graphics"), + T_DATA("Silicon Integrated Systems [SiS]", "SiS"), + T_DATA("Sitecom Europe B.V.", "Sitecom"), + T_DATA("Sitecom", "Sitecom"), + T_DATA("smartBridges, Inc.", "smartBridges"), + T_DATA("SohoWare", "SohoWare"), + T_DATA("Solarflare Communications", "Solarflare"), + T_DATA("Sony Corp.", "Sony"), + T_DATA("SpeedStream", "SpeedStream"), + T_DATA("Sphairon Access Systems GmbH", "Sphairon"), + T_DATA("Standard Microsystems Corp [SMC]", "SMC"), + T_DATA("Standard Microsystems Corp.", "Standard"), + T_DATA("STMicroelectronics", "STMicroelectronics"), + T_DATA("Sundance Technology Inc / IC Plus Corp", "Sundance"), + T_DATA("Surecom Technology Corp.", "Surecom"), + T_DATA("Surecom Technology", "Surecom"), + T_DATA("Sweex", "Sweex"), + T_DATA("SysKonnect", "SysKonnect"), + T_DATA("T & A Mobile Phones", "T & A"), + T_DATA("TDK Semiconductor Corp.", "TDK"), + T_DATA("Tehuti Networks Ltd.", "Tehuti"), + T_DATA("Tekram Technology Co., Ltd", "Tekram"), + T_DATA("Telit Wireless Solutions", "Telit"), + T_DATA("Texas Instruments, Inc.", "Texas"), + T_DATA("Thales Norway A/S", "Thales"), + T_DATA("TMT Technology, Inc.", "TMT"), + T_DATA("Toshiba Corp.", "Toshiba"), + T_DATA("TRENDnet", "TRENDnet"), + T_DATA("Trident Microsystems", "Trident"), + T_DATA("Trust International B.V.", "Trust"), + T_DATA("TTTech Computertechnik AG (Wrong ID)", "TTTech"), + T_DATA("TwinMOS", "TwinMOS"), + T_DATA("U-Blox AG", "U-Blox"), + T_DATA("ULi Electronics Inc.", "ULi"), + T_DATA("U.S. Robotics", "U.S. Robotics"), + T_DATA("Vaillant", "Vaillant"), + T_DATA("VIA Technologies, Inc.", "VIA"), + T_DATA("Victor Company of Japan, Ltd", "Victor"), + T_DATA("VMware", "VMware"), + T_DATA("VTech Holdings, Ltd", "VTech"), + T_DATA("Wavecom", "Wavecom"), + T_DATA("Westell", "Westell"), + T_DATA("Western Digital Technologies, Inc.", "Western Digital"), + T_DATA("Wilocity Ltd.", "Wilocity"), + T_DATA("Winbond Electronics Corp", "Winbond"), + T_DATA("Winbond", "Winbond"), + T_DATA("Wistron NeWeb", "Wistron NeWeb"), + T_DATA("Xircom", "Xircom"), + T_DATA("Z-Com", "Z-Com"), + T_DATA("Zinwell", "Zinwell"), + T_DATA("Zoom Telephonics, Inc.", "Zoom"), + T_DATA("ZTE WCDMA Technologies MSM", "ZTE"), + T_DATA("ZyDAS", "ZyDAS"), + T_DATA("ZyXEL Communications Corp.", "ZyXEL"), + }; + + _test_fixup_string(data, G_N_ELEMENTS(data), nm_utils_fixup_vendor_string); +} + +static void +test_fixup_product_string(void) +{ + static const TestFixupData data[] = { + T_DATA("10/100BaseTX [RTL81xx]", "RTL81xx"), + T_DATA("10/100 Ethernet", NULL), + T_DATA("10/100 Ethernet [pegasus]", "pegasus"), + T_DATA("10/100 USB Ethernet", NULL), + T_DATA("10/100 USB NIC", NULL), + T_DATA("1010/1020/1007/1741 10Gbps CNA", "1010/1020/1007/1741"), + T_DATA("1012 PCMCIA 10/100 Ethernet Card [RTL81xx]", "1012"), + T_DATA("101 Ethernet [klsi]", "101"), + T_DATA("10GbE Converged Network Adapter (TCP/IP Networking)", NULL), + T_DATA("10GbE Ethernet Adapter", NULL), + T_DATA("10 Gigabit BR KX4 Dual Port Network Connection", "BR KX4"), + T_DATA("10-Giga TOE SmartNIC 2-Port", "SmartNIC"), + T_DATA("10-Giga TOE SmartNIC", "SmartNIC"), + T_DATA("10Mbps Ethernet [klsi]", "klsi"), + T_DATA("1860 16Gbps/10Gbps Fabric Adapter", "1860"), + T_DATA("190 Ethernet Adapter", "190"), + T_DATA("191 Gigabit Ethernet Adapter", "191"), + T_DATA("21145 Fast Ethernet", "21145"), + T_DATA("21x4x DEC-Tulip compatible 10/100 Ethernet", "21x4x"), + T_DATA("21x4x DEC-Tulip compatible Fast Ethernet", "21x4x"), + T_DATA("2202 Ethernet [klsi]", "2202"), + T_DATA("2202 Ethernet [pegasus]", "2202"), + T_DATA("3C19250 Ethernet [klsi]", "3C19250"), + T_DATA("3c450 HomePNA [Tornado]", "3c450 HomePNA"), + T_DATA("3C460B 10/100 Ethernet Adapter", "3C460B"), + T_DATA("3c555 Laptop Hurricane", "3c555 Hurricane"), + T_DATA("3c556B CardBus [Tornado]", "3c556B"), + T_DATA("3c556 Hurricane CardBus [Cyclone]", "3c556 Hurricane"), + T_DATA("3c575 Megahertz 10/100 LAN CardBus [Boomerang]", "3c575 Megahertz"), + T_DATA("3c590 10BaseT [Vortex]", "3c590"), + T_DATA("3c592 EISA 10mbps Demon/Vortex", "3c592 Demon/Vortex"), + T_DATA("3c595 100Base-MII [Vortex]", "3c595"), + T_DATA("3c595 100BaseT4 [Vortex]", "3c595"), + T_DATA("3c595 100BaseTX [Vortex]", "3c595"), + T_DATA("3c595 Megahertz 10/100 LAN CardBus [Boomerang]", "3c595 Megahertz"), + T_DATA("3c597 EISA Fast Demon/Vortex", "3c597 Fast Demon/Vortex"), + T_DATA("3c900 10BaseT [Boomerang]", "3c900"), + T_DATA("3c900 10Mbps Combo [Boomerang]", "3c900 Combo"), + T_DATA("3c900B-Combo Etherlink XL [Cyclone]", "3c900B-Combo Etherlink XL"), + T_DATA("3c900B-FL 10base-FL [Cyclone]", "3c900B-FL"), + T_DATA("3c900B-TPC Etherlink XL [Cyclone]", "3c900B-TPC Etherlink XL"), + T_DATA("3c900B-TPO Etherlink XL [Cyclone]", "3c900B-TPO Etherlink XL"), + T_DATA("3c905 100BaseT4 [Boomerang]", "3c905"), + T_DATA("3c905 100BaseTX [Boomerang]", "3c905"), + T_DATA("3c905B 100BaseTX [Cyclone]", "3c905B"), + T_DATA("3c905B Deluxe Etherlink 10/100/BNC [Cyclone]", "3c905B Deluxe Etherlink"), + T_DATA("3c905B-FX Fast Etherlink XL FX 100baseFx [Cyclone]", + "3c905B-FX Fast Etherlink XL FX"), + T_DATA("3c905B-T4 Fast EtherLink XL [Cyclone]", "3c905B-T4 Fast EtherLink XL"), + T_DATA("3C905B-TX Fast Etherlink XL PCI", "3C905B-TX Fast Etherlink XL"), + T_DATA("3c905C-TX/TX-M [Tornado]", "3c905C-TX/TX-M"), + T_DATA("3C920B-EMB Integrated Fast Ethernet Controller [Tornado]", "3C920B-EMB"), + T_DATA("3C920B-EMB-WNM Integrated Fast Ethernet Controller", "3C920B-EMB-WNM"), + T_DATA("3c940 10/100/1000Base-T [Marvell]", "3c940"), + T_DATA("3c940B 10/100/1000Base-T", "3c940B"), + T_DATA("3c980-C 10/100baseTX NIC [Python-T]", "3c980-C"), + T_DATA("3c980-TX Fast Etherlink XL Server Adapter [Cyclone]", "3c980-TX Fast Etherlink XL"), + T_DATA("3c982-TXM 10/100baseTX Dual Port A [Hydra]", "3c982-TXM A"), + T_DATA("3c982-TXM 10/100baseTX Dual Port B [Hydra]", "3c982-TXM B"), + T_DATA("3c985 1000BaseSX (SX/TX)", "3c985"), + T_DATA("3C990B-TX-M/3C990BSVR [Typhoon2]", "3C990B-TX-M/3C990BSVR"), + T_DATA("3C990SVR [Typhoon Server]", "3C990SVR"), + T_DATA("3C990-TX [Typhoon]", "3C990-TX"), + T_DATA("3cCFE575BT Megahertz 10/100 LAN CardBus [Cyclone]", "3cCFE575BT Megahertz"), + T_DATA("3cCFE575CT CardBus [Cyclone]", "3cCFE575CT"), + T_DATA("3cCFE656 CardBus [Cyclone]", "3cCFE656"), + T_DATA("3cCFEM656B 10/100 LAN+Winmodem CardBus [Cyclone]", "3cCFEM656B"), + T_DATA("3Com 3C920B-EMB-WNM Integrated Fast Ethernet Controller", "3Com 3C920B-EMB-WNM"), + T_DATA("3Com 3CRUSBN275 802.11abgn Wireless Adapter [Atheros AR9170]", "3Com 3CRUSBN275"), + T_DATA("3com 3CRWE154G72 [Office Connect Wireless LAN Adapter]", "3com 3CRWE154G72"), + T_DATA("3CR990-FX-95/97/95 [Typhon Fiber]", "3CR990-FX-95/97/95"), + T_DATA("3CR990SVR95 [Typhoon Server 56-bit]", "3CR990SVR95"), + T_DATA("3CR990SVR97 [Typhoon Server 168-bit]", "3CR990SVR97"), + T_DATA("3CR990-TX-95 [Typhoon 56-bit]", "3CR990-TX-95"), + T_DATA("3CR990-TX-97 [Typhoon 168-bit]", "3CR990-TX-97"), + T_DATA("3CRPAG175 Wireless PC Card", "3CRPAG175"), + T_DATA("3CRUSB10075 802.11bg [ZyDAS ZD1211]", "3CRUSB10075"), + T_DATA("3CRWE254G72 802.11g Adapter", "3CRWE254G72"), + T_DATA("3CSOHO100B-TX 910-A01 [tulip]", "3CSOHO100B-TX 910-A01"), + T_DATA("3cSOHO100-TX Hurricane", "3cSOHO100-TX Hurricane"), + T_DATA("3cXFEM656C 10/100 LAN+Winmodem CardBus [Tornado]", "3cXFEM656C"), + T_DATA("4410a Wireless-G Adapter [Intersil ISL3887]", "4410a"), + T_DATA("4DWave DX", "4DWave DX"), + T_DATA("4G LTE adapter", NULL), + T_DATA("54g USB Network Adapter", NULL), + T_DATA("570x 10/100 Integrated Controller", "570x"), + T_DATA("79c970 [PCnet32 LANCE]", "79c970"), + T_DATA("79c978 [HomePNA]", "79c978"), + T_DATA("80003ES2LAN Gigabit Ethernet Controller (Copper)", "80003ES2LAN"), + T_DATA("80003ES2LAN Gigabit Ethernet Controller (Serdes)", "80003ES2LAN"), + T_DATA("802.11a/b/g/n USB Wireless LAN Card", NULL), + T_DATA("802.11 Adapter", NULL), + T_DATA("802.11bgn 1T1R Mini Card Wireless Adapter", "1T1R"), + T_DATA("802.11bg", NULL), + T_DATA("802.11b/g/n USB Wireless LAN Card", NULL), + T_DATA("802.11b/g/n USB Wireless Network Adapter", NULL), + T_DATA("802.11b/g/n Wireless Network Adapter", NULL), + T_DATA("802.11b/g Turbo Wireless Adapter", NULL), + T_DATA("802.11b/g Wireless Network Adapter", NULL), + T_DATA("802.11g Wireless Adapter [Intersil ISL3886]", "Intersil ISL3886"), + T_DATA("802.11n adapter", NULL), + T_DATA("802.11n/b/g Mini Wireless LAN USB2.0 Adapter", NULL), + T_DATA("802.11n/b/g Wireless LAN USB2.0 Adapter", NULL), + T_DATA("802.11 n/g/b Wireless LAN Adapter", NULL), + T_DATA("802.11 n/g/b Wireless LAN USB Adapter", NULL), + T_DATA("802.11 n/g/b Wireless LAN USB Mini-Card", NULL), + T_DATA("802.11n Network Adapter", NULL), + T_DATA("802.11n Network Adapter (wrong ID - swapped vendor and device)", NULL), + T_DATA("802.11n USB Wireless Card", NULL), + T_DATA("802.11n Wireless Adapter", NULL), + T_DATA("802.11n Wireless LAN Card", NULL), + T_DATA("802.11n Wireless USB Card", NULL), + T_DATA("802AIN Wireless N Network Adapter [Atheros AR9170+AR9101]", + "Atheros AR9170+AR9101"), + T_DATA("802UIG-1 802.11g Wireless Mini Adapter [Intersil ISL3887]", "Intersil ISL3887"), + T_DATA("82540EM Gigabit Ethernet Controller", "82540EM"), + T_DATA("82540EM Gigabit Ethernet Controller (LOM)", "82540EM"), + T_DATA("82540EP Gigabit Ethernet Controller", "82540EP"), + T_DATA("82540EP Gigabit Ethernet Controller (Mobile)", "82540EP"), + T_DATA("82541EI Gigabit Ethernet Controller", "82541EI"), + T_DATA("82541ER Gigabit Ethernet Controller", "82541ER"), + T_DATA("82541GI Gigabit Ethernet Controller", "82541GI"), + T_DATA("82541PI Gigabit Ethernet Controller", "82541PI"), + T_DATA("82542 Gigabit Ethernet Controller (Fiber)", "82542"), + T_DATA("82543GC Gigabit Ethernet Controller (Copper)", "82543GC"), + T_DATA("82543GC Gigabit Ethernet Controller (Fiber)", "82543GC"), + T_DATA("82544EI Gigabit Ethernet Controller (Copper)", "82544EI"), + T_DATA("82544EI Gigabit Ethernet Controller (Fiber)", "82544EI"), + T_DATA("82544GC Gigabit Ethernet Controller (Copper)", "82544GC"), + T_DATA("82544GC Gigabit Ethernet Controller (LOM)", "82544GC"), + T_DATA("82545EM Gigabit Ethernet Controller (Copper)", "82545EM"), + T_DATA("82545EM Gigabit Ethernet Controller (Fiber)", "82545EM"), + T_DATA("82545GM Gigabit Ethernet Controller", "82545GM"), + T_DATA("82546EB Gigabit Ethernet Controller", "82546EB"), + T_DATA("82546EB Gigabit Ethernet Controller (Copper)", "82546EB"), + T_DATA("82546EB Gigabit Ethernet Controller (Fiber)", "82546EB"), + T_DATA("82546GB Gigabit Ethernet Controller", "82546GB"), + T_DATA("82546GB Gigabit Ethernet Controller (Copper)", "82546GB"), + T_DATA("82547EI Gigabit Ethernet Controller", "82547EI"), + T_DATA("82547EI Gigabit Ethernet Controller (Mobile)", "82547EI"), + T_DATA("82547GI Gigabit Ethernet Controller", "82547GI"), + T_DATA("82551QM Ethernet Controller", "82551QM"), + T_DATA("82552 10/100 Network Connection", "82552"), + T_DATA("82557/8/9/0/1 Ethernet Pro 100", "82557/8/9/0/1"), + T_DATA("82559 Ethernet Controller", "82559"), + T_DATA("82559 InBusiness 10/100", "82559 InBusiness"), + T_DATA("8255xER/82551IT Fast Ethernet Controller", "8255xER/82551IT"), + T_DATA("82562 EM/EX/GX - PRO/100 VM Ethernet Controller", "82562 EM/EX/GX"), + T_DATA("82562 EM/EX/GX - PRO/100 VM (LOM) Ethernet Controller", "82562 EM/EX/GX"), + T_DATA("82562EM/EX/GX - PRO/100 VM (LOM) Ethernet Controller Mobile", "82562EM/EX/GX"), + T_DATA("82562ET/EZ/GT/GZ - PRO/100 VE Ethernet Controller", "82562ET/EZ/GT/GZ"), + T_DATA("82562ET/EZ/GT/GZ - PRO/100 VE (LOM) Ethernet Controller", "82562ET/EZ/GT/GZ"), + T_DATA("82562ET/EZ/GT/GZ - PRO/100 VE (LOM) Ethernet Controller Mobile", + "82562ET/EZ/GT/GZ"), + T_DATA("82562EZ 10/100 Ethernet Controller", "82562EZ"), + T_DATA("82562G 10/100 Network Connection", "82562G"), + T_DATA("82562G-2 10/100 Network Connection", "82562G-2"), + T_DATA("82562G - PRO/100 VE Ethernet Controller Mobile", "82562G"), + T_DATA("82562G - PRO/100 VE (LOM) Ethernet Controller", "82562G"), + T_DATA("82562GT 10/100 Network Connection", "82562GT"), + T_DATA("82562GT-2 10/100 Network Connection", "82562GT-2"), + T_DATA("82562V 10/100 Network Connection", "82562V"), + T_DATA("82562V-2 10/100 Network Connection", "82562V-2"), + T_DATA("82566DC-2 Gigabit Network Connection", "82566DC-2"), + T_DATA("82566DC Gigabit Network Connection", "82566DC"), + T_DATA("82566DM-2 Gigabit Network Connection", "82566DM-2"), + T_DATA("82566DM Gigabit Network Connection", "82566DM"), + T_DATA("82566MC Gigabit Network Connection", "82566MC"), + T_DATA("82566MM Gigabit Network Connection", "82566MM"), + T_DATA("82567LF-2 Gigabit Network Connection", "82567LF-2"), + T_DATA("82567LF-3 Gigabit Network Connection", "82567LF-3"), + T_DATA("82567LF Gigabit Network Connection", "82567LF"), + T_DATA("82567LM-2 Gigabit Network Connection", "82567LM-2"), + T_DATA("82567LM-3 Gigabit Network Connection", "82567LM-3"), + T_DATA("82567LM-4 Gigabit Network Connection", "82567LM-4"), + T_DATA("82567LM Gigabit Network Connection", "82567LM"), + T_DATA("82567V-2 Gigabit Network Connection", "82567V-2"), + T_DATA("82567V-3 Gigabit Network Connection", "82567V-3"), + T_DATA("82567V-4 Gigabit Network Connection", "82567V-4"), + T_DATA("82567V Gigabit Network Connection", "82567V"), + T_DATA("82571EB Dual Port Gigabit Mezzanine Adapter", "82571EB Mezzanine"), + T_DATA("82571EB Gigabit Ethernet Controller", "82571EB"), + T_DATA("82571EB Gigabit Ethernet Controller (Copper)", "82571EB"), + T_DATA("82571EB Gigabit Ethernet Controller (Fiber)", "82571EB"), + T_DATA("82571EB Quad Port Gigabit Mezzanine Adapter", "82571EB Quad Port Mezzanine"), + T_DATA("82571PT Gigabit PT Quad Port Server ExpressModule", "82571PT PT Quad Port"), + T_DATA("82572EI Gigabit Ethernet Controller", "82572EI"), + T_DATA("82572EI Gigabit Ethernet Controller (Copper)", "82572EI"), + T_DATA("82572EI Gigabit Ethernet Controller (Fiber)", "82572EI"), + T_DATA("82573E Gigabit Ethernet Controller (Copper)", "82573E"), + T_DATA("82573L Gigabit Ethernet Controller", "82573L"), + T_DATA("82573V Gigabit Ethernet Controller (Copper)", "82573V"), + T_DATA("82574L Gigabit Network Connection", "82574L"), + T_DATA("82575EB Gigabit Backplane Connection", "82575EB Backplane Connection"), + T_DATA("82575EB Gigabit Network Connection", "82575EB"), + T_DATA("82575GB Gigabit Network Connection", "82575GB"), + T_DATA("82576 Gigabit Backplane Connection", "82576 Backplane Connection"), + T_DATA("82576 Gigabit Network Connection", "82576"), + T_DATA("82576NS Gigabit Network Connection", "82576NS"), + T_DATA("82576NS SerDes Gigabit Network Connection", "82576NS SerDes"), + T_DATA("82576 Virtual Function", "82576 Virtual Function"), + T_DATA("82577LC Gigabit Network Connection", "82577LC"), + T_DATA("82577LM Gigabit Network Connection", "82577LM"), + T_DATA("82578DC Gigabit Network Connection", "82578DC"), + T_DATA("82578DM Gigabit Network Connection", "82578DM"), + T_DATA("82579LM Gigabit Network Connection (Lewisville)", "82579LM"), + T_DATA("82579V Gigabit Network Connection", "82579V"), + T_DATA("82580 Gigabit Backplane Connection", "82580 Backplane Connection"), + T_DATA("82580 Gigabit Fiber Network Connection", "82580"), + T_DATA("82580 Gigabit Network Connection", "82580"), + T_DATA("82580 Gigabit SFP Connection", "82580 SFP Connection"), + T_DATA("82583V Gigabit Network Connection", "82583V"), + T_DATA("82597EX 10GbE Ethernet Controller", "82597EX"), + T_DATA("82598 10GbE PCI-Express Ethernet Controller", "82598"), + T_DATA("82598EB 10-Gigabit AF Dual Port Network Connection", "82598EB AF"), + T_DATA("82598EB 10-Gigabit AF Network Connection", "82598EB AF"), + T_DATA("82598EB 10-Gigabit AT2 Server Adapter", "82598EB AT2"), + T_DATA("82598EB 10-Gigabit AT CX4 Network Connection", "82598EB AT CX4"), + T_DATA("82598EB 10-Gigabit AT Network Connection", "82598EB AT"), + T_DATA("82598EB 10-Gigabit Dual Port Network Connection", "82598EB"), + T_DATA("82598EB Gigabit BX Network Connection", "82598EB BX"), + T_DATA("82599 10 Gigabit Dual Port Backplane Connection", "82599 Backplane Connection"), + T_DATA("82599 10 Gigabit Dual Port Backplane Connection with FCoE", + "82599 Backplane Connection with FCoE"), + T_DATA("82599 10 Gigabit Dual Port Network Connection", "82599"), + T_DATA("82599 10 Gigabit Dual Port Network Connection with FCoE", "82599 with FCoE"), + T_DATA("82599 10 Gigabit Network Connection", "82599"), + T_DATA("82599 10 Gigabit TN Network Connection", "82599 TN"), + T_DATA("82599ES 10 Gigabit Network Connection", "82599ES"), + T_DATA("82599ES 10-Gigabit SFI/SFP+ Network Connection", "82599ES SFI/SFP+"), + T_DATA("82599 Ethernet Controller Virtual Function", "82599 Virtual Function"), + T_DATA("82599 Virtual Function", "82599 Virtual Function"), + T_DATA("82801BA/BAM/CA/CAM Ethernet Controller", "82801BA/BAM/CA/CAM"), + T_DATA("82801CAM (ICH3) PRO/100 VE Ethernet Controller", "82801CAM"), + T_DATA("82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller", "82801CAM"), + T_DATA("82801CAM (ICH3) PRO/100 VM Ethernet Controller", "82801CAM"), + T_DATA("82801CAM (ICH3) PRO/100 VM (KM) Ethernet Controller", "82801CAM"), + T_DATA("82801CAM (ICH3) PRO/100 VM (LOM) Ethernet Controller", "82801CAM"), + T_DATA("82801DB PRO/100 VE (CNR) Ethernet Controller", "82801DB PRO/100 VE"), + T_DATA("82801DB PRO/100 VE (LOM) Ethernet Controller", "82801DB PRO/100 VE"), + T_DATA("82801DB PRO/100 VE (MOB) Ethernet Controller", "82801DB PRO/100 VE"), + T_DATA("82801DB PRO/100 VM (CNR) Ethernet Controller", "82801DB PRO/100 VM"), + T_DATA("82801DB PRO/100 VM (LOM) Ethernet Controller", "82801DB PRO/100 VM"), + T_DATA("82801DB PRO/100 VM (MOB) Ethernet Controller", "82801DB PRO/100 VM"), + T_DATA("82801EB/ER (ICH5/ICH5R) integrated LAN Controller", "82801EB/ER"), + T_DATA("82801E Ethernet Controller 0", "82801E 0"), + T_DATA("82801E Ethernet Controller 1", "82801E 1"), + T_DATA("83c170 EPIC/100 Fast Ethernet Adapter", "83c170 EPIC/100"), + T_DATA("83c175 EPIC/100 Fast Ethernet Adapter", "83c175 EPIC/100"), + T_DATA("83C885 NT50 DigitalScape Fast Ethernet", "83C885 NT50 DigitalScape"), + T_DATA("88E8001 Gigabit Ethernet Controller", "88E8001"), + T_DATA("88E8021 PCI-X IPMI Gigabit Ethernet Controller", "88E8021 IPMI"), + T_DATA("88E8022 PCI-X IPMI Gigabit Ethernet Controller", "88E8022 IPMI"), + T_DATA("88E8035 PCI-E Fast Ethernet Controller", "88E8035"), + T_DATA("88E8036 PCI-E Fast Ethernet Controller", "88E8036"), + T_DATA("88E8038 PCI-E Fast Ethernet Controller", "88E8038"), + T_DATA("88E8039 PCI-E Fast Ethernet Controller", "88E8039"), + T_DATA("88E8040 PCI-E Fast Ethernet Controller", "88E8040"), + T_DATA("88E8040T PCI-E Fast Ethernet Controller", "88E8040T"), + T_DATA("88E8042 PCI-E Fast Ethernet Controller", "88E8042"), + T_DATA("88E8048 PCI-E Fast Ethernet Controller", "88E8048"), + T_DATA("88E8050 PCI-E ASF Gigabit Ethernet Controller", "88E8050 ASF"), + T_DATA("88E8052 PCI-E ASF Gigabit Ethernet Controller", "88E8052 ASF"), + T_DATA("88E8053 PCI-E Gigabit Ethernet Controller", "88E8053"), + T_DATA("88E8055 PCI-E Gigabit Ethernet Controller", "88E8055"), + T_DATA("88E8056 PCI-E Gigabit Ethernet Controller", "88E8056"), + T_DATA("88E8057 PCI-E Gigabit Ethernet Controller", "88E8057"), + T_DATA("88E8058 PCI-E Gigabit Ethernet Controller", "88E8058"), + T_DATA("88E8061 PCI-E IPMI Gigabit Ethernet Controller", "88E8061 IPMI"), + T_DATA("88E8062 PCI-E IPMI Gigabit Ethernet Controller", "88E8062 IPMI"), + T_DATA("88E8070 based Ethernet Controller", "88E8070 based"), + T_DATA("88E8071 PCI-E Gigabit Ethernet Controller", "88E8071"), + T_DATA("88E8072 PCI-E Gigabit Ethernet Controller", "88E8072"), + T_DATA("88E8075 PCI-E Gigabit Ethernet Controller", "88E8075"), + T_DATA("88EC032 Ethernet Controller", "88EC032"), + T_DATA("88EC033 PCI-E Fast Ethernet Controller", "88EC033"), + T_DATA("88EC034 Ethernet Controller", "88EC034"), + T_DATA("88EC036 PCI-E Gigabit Ethernet Controller", "88EC036"), + T_DATA("88EC042 Ethernet Controller", "88EC042"), + T_DATA("88W8363 [TopDog] 802.11n Wireless", "88W8363"), + T_DATA("88W8366 [TopDog] 802.11n Wireless", "88W8366"), + T_DATA("88W8388 802.11a/b/g WLAN", "88W8388"), + T_DATA("88W8687 [TopDog] 802.11b/g Wireless", "88W8687"), + T_DATA("88W8764 [Avastar] 802.11n Wireless", "88W8764"), + T_DATA("88W8897 [AVASTAR] 802.11ac Wireless", "88W8897"), + T_DATA("A90-211WG-01 802.11g Adapter [Intersil ISL3887]", "A90-211WG-01"), + T_DATA("A9T wireless 802.11bg", "A9T"), + T_DATA("AboCom Systems Inc [WN2001 Prolink Wireless-N Nano Adapter]", "AboCom Systems Inc"), + T_DATA("AC1000 Gigabit Ethernet", "AC1000"), + T_DATA("AC1001 Gigabit Ethernet", "AC1001"), + T_DATA("AC1003 Gigabit Ethernet", "AC1003"), + T_DATA("AC9100 Gigabit Ethernet", "AC9100"), + T_DATA("AceNIC Gigabit Ethernet", "AceNIC"), + T_DATA("AceNIC Gigabit Ethernet (Copper)", "AceNIC"), + T_DATA("Acer Gobi 2000 Wireless Modem", "Acer Gobi 2000"), + T_DATA("Acer Gobi Wireless Modem", "Acer Gobi"), + T_DATA("ADM8511 Pegasus II Ethernet", "ADM8511 Pegasus II"), + T_DATA("ADMtek ADM8515 NIC", "ADMtek ADM8515"), + T_DATA("ADMtek Centaur-C rev 17 [D-Link DFE-680TX] CardBus Fast Ethernet Adapter", + "ADMtek Centaur-C"), + T_DATA("ADSL Modem", NULL), + T_DATA("AE1000 v1 802.11n [Ralink RT3572]", "AE1000"), + T_DATA("AE1200 802.11bgn Wireless Adapter [Broadcom BCM43235]", "AE1200"), + T_DATA("AE3000 802.11abgn (3x3) Wireless Adapter [Ralink RT3573]", "AE3000"), + T_DATA("AG-225H 802.11bg", "AG-225H"), + T_DATA("Air2210 54 Mbps Wireless Adapter", "Air2210"), + T_DATA("Air2310 150 Mbps Wireless Adapter", "Air2310"), + T_DATA("Airlink101 AWLL6070 802.11bgn Wireless Adapter [Ralink RT2770]", + "Airlink101 AWLL6070"), + T_DATA("Airlink101 AWLL6080 802.11bgn Wireless Adapter [Ralink RT2870]", + "Airlink101 AWLL6080"), + T_DATA("AirLive WL-1600USB 802.11g Adapter [Realtek RTL8187L]", "AirLive WL-1600USB"), + T_DATA("AirLive WN-200USB wireless 11b/g/n dongle", "AirLive WN-200USB"), + T_DATA("AirLive WN-360USB adapter", "AirLive WN-360USB"), + T_DATA("AirLive X.USB 802.11abgn [Atheros AR9170+AR9104]", "AirLive X.USB"), + T_DATA("AirPcap NX [Atheros AR9001U-(2)NG]", "AirPcap NX"), + T_DATA("AirPlus G DWL-G120 Wireless Adapter(rev.C) [Intersil ISL3887]", + "AirPlus G DWL-G120"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.A1) [Intersil ISL3880]", + "AirPlus G DWL-G122"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.A2) [Intersil ISL3887]", + "AirPlus G DWL-G122"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.B1) [Ralink RT2571]", "AirPlus G DWL-G122"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.C1) [Ralink RT2571W]", + "AirPlus G DWL-G122"), + T_DATA("AirPlus G DWL-G122 Wireless Adapter(rev.E1) [Ralink RT2070]", "AirPlus G DWL-G122"), + T_DATA("Alcatel One Touch L100V / Telekom Speedstick LTE II", + "Alcatel One Touch L100V / Telekom Speedstick II"), + T_DATA("Allnet ALL0283 [AR5523]", "Allnet ALL0283"), + T_DATA("Allnet ALL0283 [AR5523](no firmware)", "Allnet ALL0283"), + T_DATA("Allnet ALL0298 v2 802.11bg", "Allnet ALL0298"), + T_DATA("AM10 v1 802.11n [Ralink RT3072]", "AM10"), + T_DATA("AMD-8111 Ethernet", "AMD-8111"), + T_DATA("AN2720 USB-USB Bridge", "AN2720 USB-USB Bridge"), + T_DATA("AN8513 Ethernet", "AN8513"), + T_DATA("AN8515 Ethernet", "AN8515"), + T_DATA("AN986A Ethernet", "AN986A"), + T_DATA("AN986 Pegasus Ethernet", "AN986 Pegasus"), + T_DATA("ANA620xx/ANA69011A", "ANA620xx/ANA69011A"), + T_DATA("AN-WF500 802.11abgn + BT Wireless Adapter [Broadcom BCM43242]", "AN-WF500"), + T_DATA("Aolynk WUB320g", "Aolynk WUB320g"), + T_DATA("AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg]", "AR2413/AR2414"), + T_DATA("AR2417 Wireless Network Adapter [AR5007G 802.11bg]", "AR2417"), + T_DATA("AR2425 Wireless Network Adapter [AR5007EG 802.11bg]", "AR2425"), + T_DATA("AR2427 802.11bg Wireless Network Adapter (PCI-Express)", "AR2427"), + T_DATA("AR242x / AR542x Wireless Network Adapter (PCI-Express)", "AR242x / AR542x"), + T_DATA("AR5210 Wireless Network Adapter [AR5000 802.11a]", "AR5210"), + T_DATA("AR5211 Wireless Network Adapter [AR5001A 802.11a]", "AR5211"), + T_DATA("AR5211 Wireless Network Adapter [AR5001X 802.11ab]", "AR5211"), + T_DATA("AR5212/5213/2414 Wireless Network Adapter", "AR5212/5213/2414"), + T_DATA("AR5212 802.11abg NIC (3CRDAG675)", "AR5212"), + T_DATA("AR5212 802.11abg NIC", "AR5212"), + T_DATA("AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg]", "AR5413/AR5414"), + T_DATA("AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn]", "AR5416"), + T_DATA("AR5418 Wireless Network Adapter [AR5008E 802.11(a)bgn] (PCI-Express)", "AR5418"), + T_DATA("AR5523", "AR5523"), + T_DATA("AR5523 driver (no firmware)", "AR5523"), + T_DATA("AR5523 (no firmware)", "AR5523"), + T_DATA("AR7010 (no firmware)", "AR7010"), + T_DATA("AR8121/AR8113/AR8114 Gigabit or Fast Ethernet", "AR8121/AR8113/AR8114"), + T_DATA("AR8131 Gigabit Ethernet", "AR8131"), + T_DATA("AR8132 Fast Ethernet", "AR8132"), + T_DATA("AR8151 v1.0 Gigabit Ethernet", "AR8151 v1.0"), + T_DATA("AR8151 v2.0 Gigabit Ethernet", "AR8151 v2.0"), + T_DATA("AR8152 v1.1 Fast Ethernet", "AR8152"), + T_DATA("AR8152 v2.0 Fast Ethernet", "AR8152 v2.0"), + T_DATA("AR8161 Gigabit Ethernet", "AR8161"), + T_DATA("AR8162 Fast Ethernet", "AR8162"), + T_DATA("AR9160 Wireless Network Adapter [AR9001 802.11(a)bgn]", "AR9160"), + T_DATA("AR9170 802.11n", "AR9170"), + T_DATA("AR9170+AR9104 802.11abgn Wireless Adapter", "AR9170+AR9104"), + T_DATA("AR9227 Wireless Network Adapter", "AR9227"), + T_DATA("AR922X Wireless Network Adapter", "AR922X"), + T_DATA("AR922X Wireless Network Adapter (Compex WLM200NX / Wistron DNMA-92)", "AR922X"), + T_DATA("AR9271 802.11n", "AR9271"), + T_DATA("AR9285 Wireless Network Adapter (PCI-Express)", "AR9285"), + T_DATA("AR9285 Wireless Network Adapter (PCI-Express) (AW-NB037H 802.11bgn Wireless " + "Half-size Mini PCIe Card [AR9002WB-1NGCD])", + "AR9285"), + T_DATA("AR9287 Wireless Network Adapter (PCI-Express)", "AR9287"), + T_DATA("AR928X Wireless Network Adapter (PCI-Express)", "AR928X"), + T_DATA("AR928X Wireless Network Adapter (PCI-Express) (EM306 802.11bgn Wireless Half-size " + "Mini PCIe Card [AR9283])", + "AR928X"), + T_DATA("AR928X Wireless Network Adapter (PCI-Express) (T77H047.31 802.11bgn Wireless " + "Half-size Mini PCIe Card [AR9283])", + "AR928X"), + T_DATA("AR93xx Wireless Network Adapter", "AR93xx"), + T_DATA("AR93xx Wireless Network Adapter (Killer Wireless-N 1102 Half-size Mini PCIe Card " + "[AR9382])", + "AR93xx"), + T_DATA("AR93xx Wireless Network Adapter (Killer Wireless-N 1103 Half-size Mini PCIe Card " + "[AR9380])", + "AR93xx"), + T_DATA("AR9462 Wireless Network Adapter", "AR9462"), + T_DATA("AR9462 Wireless Network Adapter (Wireless 1601 802.11abgn Adapter)", "AR9462"), + T_DATA("AR9462 Wireless Network Adapter (Wireless 1802 802.11abgn Adapter)", "AR9462"), + T_DATA("AR9485 Wireless Network Adapter", "AR9485"), + T_DATA("AR9485 Wireless Network Adapter (AR9485WB-EG 802.11b/g/n mini-PCIe card on a " + "series 3 laptop)", + "AR9485"), + T_DATA("AR9485 Wireless Network Adapter (AW-NE186H)", "AR9485"), + T_DATA("AR958x 802.11abgn Wireless Network Adapter", "AR958x"), + T_DATA("Arcadyan 802.11N Wireless Adapter", "Arcadyan"), + T_DATA("Arcadyan WN4501 802.11b/g", "Arcadyan WN4501"), + T_DATA("Arcadyan WN7512 802.11n", "Arcadyan WN7512"), + T_DATA("Asus Gobi 2000 Wireless Modem", "Asus Gobi 2000"), + T_DATA("Aterm PA-WL54GU", "Aterm PA-WL54GU"), + T_DATA("Aterm WL300NU-AG", "Aterm WL300NU-AG"), + T_DATA("Aterm WL300NU-G", "Aterm WL300NU-G"), + T_DATA("Attansic L1 Gigabit Ethernet", "Attansic L1"), + T_DATA("Attansic L2c Gigabit Ethernet", "Attansic L2c"), + T_DATA("Attansic L2 Fast Ethernet", "Attansic L2"), + T_DATA("AT-USB100", "AT-USB100"), + T_DATA("AX88141 Fast Ethernet Controller", "AX88141"), + T_DATA("AX88178", "AX88178"), + T_DATA("AX88179 Gigabit Ethernet", "AX88179"), + T_DATA("AX88179 Gigabit Ethernet [Sitecom]", "AX88179"), + T_DATA("AX88179 Gigabit Ethernet [ThinkPad OneLink GigaLAN]", "AX88179"), + T_DATA("AX88772A Fast Ethernet", "AX88772A"), + T_DATA("AX88772", "AX88772"), + T_DATA("AX88772B", "AX88772B"), + T_DATA("AX88772B Fast Ethernet Controller", "AX88772B"), + T_DATA("B404-BT Unified Wire Ethernet Controller", "B404-BT"), + T_DATA("B404-BT Unified Wire Ethernet Controller [VF]", "B404-BT"), + T_DATA("B420-SR Unified Wire Ethernet Controller", "B420-SR"), + T_DATA("B420-SR Unified Wire Ethernet Controller [VF]", "B420-SR"), + T_DATA("B504-BT Unified Wire Ethernet Controller", "B504-BT"), + T_DATA("B504-BT Unified Wire Ethernet Controller [VF]", "B504-BT"), + T_DATA("B520-SR Unified Wire Ethernet Controller", "B520-SR"), + T_DATA("B520-SR Unified Wire Ethernet Controller [VF]", "B520-SR"), + T_DATA("BCM43142 802.11b/g/n", "BCM43142"), + T_DATA("BCM43143 802.11bgn (1x1) Wireless Adapter", "BCM43143"), + T_DATA("BCM43143 WLAN card", "BCM43143"), + T_DATA("BCM43236 802.11abgn Wireless Adapter", "BCM43236"), + T_DATA("BCM43241 WLAN card", "BCM43241"), + T_DATA("BCM43242 802.11abgn Wireless Adapter", "BCM43242"), + T_DATA("BCM4329 WLAN card", "BCM4329"), + T_DATA("BCM4330 WLAN card", "BCM4330"), + T_DATA("BCM43340 WLAN card", "BCM43340"), + T_DATA("BCM43341 WLAN card", "BCM43341"), + T_DATA("BCM4334 WLAN card", "BCM4334"), + T_DATA("BCM4335/BCM4339 WLAN card", "BCM4335/BCM4339"), + T_DATA("BCM43362 WLAN card", "BCM43362"), + T_DATA("BCM4350 802.11ac Wireless Network Adapter", "BCM4350"), + T_DATA("BCM4354 WLAN card", "BCM4354"), + T_DATA("BCM43567 802.11ac Wireless Network Adapter", "BCM43567"), + T_DATA("BCM4356 802.11ac Wireless Network Adapter", "BCM4356"), + T_DATA("BCM43570 802.11ac Wireless Network Adapter", "BCM43570"), + T_DATA("BCM4358 802.11ac Wireless LAN SoC", "BCM4358"), + T_DATA("BCM43602 802.11ac Wireless LAN SoC", "BCM43602"), + T_DATA("BCM4401 100Base-T", "BCM4401"), + T_DATA("BCM4401-B0 100Base-TX", "BCM4401-B0"), + T_DATA("BCM4402 Integrated 10/100BaseT", "BCM4402"), + T_DATA("BCM57301 NetXtreme-C 10Gb Ethernet Controller", "BCM57301 NetXtreme-C"), + T_DATA("BCM57302 NetXtreme-C 10Gb/25Gb Ethernet Controller", "BCM57302 NetXtreme-C"), + T_DATA("BCM57304 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet Controller", + "BCM57304 NetXtreme-C"), + T_DATA("BCM57311 NetXtreme-C 10Gb RDMA Ethernet Controller", "BCM57311 NetXtreme-C"), + T_DATA("BCM57312 NetXtreme-C 10Gb/25Gb RDMA Ethernet Controller", "BCM57312 NetXtreme-C"), + T_DATA("BCM57314 NetXtreme-C 10Gb/25Gb/40Gb/50Gb RDMA Ethernet Controller", + "BCM57314 NetXtreme-C"), + T_DATA("BCM57402 NetXtreme-E 10Gb Ethernet Controller", "BCM57402 NetXtreme-E"), + T_DATA("BCM57402 NetXtreme-E Ethernet Partition", "BCM57402 NetXtreme-E Partition"), + T_DATA("BCM57404 NetXtreme-E 10Gb/25Gb Ethernet Controller", "BCM57404 NetXtreme-E"), + T_DATA("BCM57404 NetXtreme-E Ethernet Partition", "BCM57404 NetXtreme-E Partition"), + T_DATA("BCM57406 NetXtreme-E 10GBASE-T Ethernet Controller", "BCM57406 NetXtreme-E"), + T_DATA("BCM57406 NetXtreme-E Ethernet Partition", "BCM57406 NetXtreme-E Partition"), + T_DATA("BCM57407 NetXtreme-E 10GBase-T Ethernet Controller", "BCM57407 NetXtreme-E"), + T_DATA("BCM57407 NetXtreme-E 25Gb Ethernet Controller", "BCM57407 NetXtreme-E"), + T_DATA("BCM57412 NetXtreme-E 10Gb RDMA Ethernet Controller", "BCM57412 NetXtreme-E"), + T_DATA("BCM57412 NetXtreme-E Ethernet Partition", "BCM57412 NetXtreme-E Partition"), + T_DATA("BCM57414 NetXtreme-E 10Gb/25Gb RDMA Ethernet Controller", "BCM57414 NetXtreme-E"), + T_DATA("BCM57414 NetXtreme-E Ethernet Partition", "BCM57414 NetXtreme-E Partition"), + T_DATA("BCM57414 NetXtreme-E RDMA Partition", "BCM57414 NetXtreme-E Partition"), + T_DATA("BCM57416 NetXtreme-E 10GBase-T RDMA Ethernet Controller", "BCM57416 NetXtreme-E"), + T_DATA("BCM57416 NetXtreme-E 10Gb RDMA Ethernet Controller", "BCM57416 NetXtreme-E"), + T_DATA("BCM57416 NetXtreme-E Ethernet Partition", "BCM57416 NetXtreme-E Partition"), + T_DATA("BCM57416 NetXtreme-E RDMA Partition", "BCM57416 NetXtreme-E Partition"), + T_DATA("BCM57417 NetXtreme-E 10Gb/25Gb RDMA Ethernet Controller", "BCM57417 NetXtreme-E"), + T_DATA("BCM57417 NetXtreme-E 10GBASE-T RDMA Ethernet Controller", "BCM57417 NetXtreme-E"), + T_DATA("BCM57417 NetXtreme-E Ethernet Partition", "BCM57417 NetXtreme-E Partition"), + T_DATA("BCM57840 NetXtreme II 10/20-Gigabit Ethernet", "BCM57840 NetXtreme II"), + T_DATA("BCM57840 NetXtreme II 10 Gigabit Ethernet", "BCM57840 NetXtreme II"), + T_DATA("BCM57840 NetXtreme II Ethernet Multi Function", "BCM57840 NetXtreme II"), + T_DATA("Belkin F5D5005 Gigabit Desktop Network PCI Card", "Belkin F5D5005"), + T_DATA("ben-wpan, AT86RF230-based", "ben-wpan AT86RF230-based"), + T_DATA("BladeCenter-H 10-Gigabit Ethernet High Speed Daughter Card", + "BladeCenter-H High Speed Daughter"), + T_DATA("BladeEngine2 10Gb Gen2 PCIe Network Adapter", "BladeEngine2 Gen2"), + T_DATA("BladeEngine3 10Gb Gen2 PCIe Network Adapter", "BladeEngine3 Gen2"), + T_DATA("BLOB boot loader firmware", "BLOB boot loader firmware"), + T_DATA("Broadcom NetXtreme BCM5701 Gigabit Ethernet", "Broadcom NetXtreme BCM5701"), + T_DATA("BWIFI-USB54AR 802.11bg", "BWIFI-USB54AR"), + T_DATA("Cardbus Ethernet 10/100", NULL), + T_DATA("Cassini 10/100/1000", "Cassini"), + T_DATA("CE Media Processor Gigabit Ethernet Controller", NULL), + T_DATA("Centrino Advanced-N 6200", "Centrino Advanced-N 6200"), + T_DATA("Centrino Advanced-N 6205 [Taylor Peak]", "Centrino Advanced-N 6205"), + T_DATA("Centrino Advanced-N 6230 [Rainbow Peak]", "Centrino Advanced-N 6230"), + T_DATA("Centrino Advanced-N 6235", "Centrino Advanced-N 6235"), + T_DATA("Centrino Advanced-N + WiMAX 6250 [Kilmer Peak]", "Centrino Advanced-N 6250"), + T_DATA("Centrino Ultimate-N 6300", "Centrino Ultimate-N 6300"), + T_DATA("Centrino Wireless-N 1000 [Condor Peak]", "Centrino 1000"), + T_DATA("Centrino Wireless-N 100", "Centrino 100"), + T_DATA("Centrino Wireless-N 1030 [Rainbow Peak]", "Centrino 1030"), + T_DATA("Centrino Wireless-N 105", "Centrino 105"), + T_DATA("Centrino Wireless-N 130", "Centrino 130"), + T_DATA("Centrino Wireless-N 135", "Centrino 135"), + T_DATA("Centrino Wireless-N 2200", "Centrino 2200"), + T_DATA("Centrino Wireless-N 2230", "Centrino 2230"), + T_DATA("Centrino Wireless-N + WiMAX 6150", "Centrino 6150"), + T_DATA("CG-WLUSB10 Corega Wireless USB Adapter", "CG-WLUSB10 Corega"), + T_DATA("CG-WLUSB2GNL", "CG-WLUSB2GNL"), + T_DATA("CG-WLUSB2GNR Corega Wireless USB Adapter", "CG-WLUSB2GNR Corega"), + T_DATA("CG-WLUSB2GO", "CG-WLUSB2GO"), + T_DATA("CG-WLUSB2GPX [Ralink RT2571W]", "CG-WLUSB2GPX"), + T_DATA("CG-WLUSB2GT 802.11g Wireless Adapter [Intersil ISL3880]", "CG-WLUSB2GT"), + T_DATA("CG-WLUSB2GTST 802.11g Wireless Adapter [Intersil ISL3887]", "CG-WLUSB2GTST"), + T_DATA("CG-WLUSB300AGN", "CG-WLUSB300AGN"), + T_DATA("CG-WLUSB300GNM", "CG-WLUSB300GNM"), + T_DATA("CG-WLUSB300GNS", "CG-WLUSB300GNS"), + T_DATA("CK804 Ethernet Controller", "CK804"), + T_DATA("CK8S Ethernet Controller", "CK8S"), + T_DATA("cLOM8214 1/10GbE Controller", "cLOM8214"), + T_DATA("CMOTECH CDMA Technologies modem", "CMOTECH"), + T_DATA("Cohiba 802.11g Wireless Mini adapter [Intersil ISL3887]", "Cohiba"), + T_DATA("Conceptronic C300RU v1 802.11bgn Wireless Adapter [Ralink RT2870]", + "Conceptronic C300RU"), + T_DATA("Conceptronic C300RU v2 802.11bgn Wireless Adapter [Ralink RT2770]", + "Conceptronic C300RU"), + T_DATA("Conceptronic C54RU v2 802.11bg Wireless Adapter [Ralink RT2571]", + "Conceptronic C54RU"), + T_DATA("Conceptronic C54RU v3 802.11bg Wireless Adapter [Ralink RT2571W]", + "Conceptronic C54RU"), + T_DATA("Connect2Air E-5400 802.11g Wireless Adapter", "Connect2Air E-5400"), + T_DATA("Connect2Air E-5400 D1700 802.11g Wireless Adapter [Intersil ISL3887]", + "Connect2Air E-5400 D1700"), + T_DATA("CPWUE001 USB/Ethernet Adapter", "CPWUE001"), + T_DATA("CWD-854 rev F", "CWD-854"), + T_DATA("CWD-854 [RT2573]", "CWD-854"), + T_DATA("CWD-854 Wireless 802.11g 54Mbps Network Adapter [RTL8187]", "CWD-854"), + T_DATA("DECchip 21040 [Tulip]", "DECchip 21040"), + T_DATA("DECchip 21041 [Tulip Pass 3]", "DECchip 21041"), + T_DATA("DECchip 21140 [FasterNet]", "DECchip 21140"), + T_DATA("DECchip 21142/43", "DECchip 21142/43"), + T_DATA("DFE-680TXD CardBus PC Card", "DFE-680TXD"), + T_DATA("DFE-690TXD CardBus PC Card", "DFE-690TXD"), + T_DATA("DGE-528T Gigabit Ethernet Adapter", "DGE-528T"), + T_DATA("DGE-528T Gigabit Ethernet Adapter (DGE-560T PCI Express (x1) Gigabit Ethernet " + "Adapter)", + "DGE-528T"), + T_DATA("DGE-530T Gigabit Ethernet Adapter (rev 11)", "DGE-530T"), + T_DATA("DGE-530T Gigabit Ethernet Adapter (rev.C1) [Realtek RTL8169]", "DGE-530T"), + T_DATA("DGE-550SX PCI-X Gigabit Ethernet Adapter", "DGE-550SX"), + T_DATA("DGE-550T Gigabit Ethernet Adapter V.B1", "DGE-550T V.B1"), + T_DATA("DGE-560SX PCI Express Gigabit Ethernet Adapter", "DGE-560SX"), + T_DATA("DGE-560T PCI Express Gigabit Ethernet Adapter", "DGE-560T"), + T_DATA("DH8900CC Series Gigabit Backplane Network Connection", "DH8900CC Backplane"), + T_DATA("DH8900CC Series Gigabit Fiber Network Connection", "DH8900CC"), + T_DATA("DH8900CC Series Gigabit Network Connection", "DH8900CC"), + T_DATA("DH8900CC Series Gigabit SFP Network Connection", "DH8900CC SFP"), + T_DATA("Direct Connect", "Direct Connect"), + T_DATA("DL10050 Sundance Ethernet (DFE-550TX/FX)", "DL10050 Sundance"), + T_DATA("DL10050 Sundance Ethernet (DFE-580TX)", "DL10050 Sundance"), + T_DATA("DL10050 Sundance Ethernet", "DL10050 Sundance"), + T_DATA("DL2000-based Gigabit Ethernet", "DL2000-based"), + T_DATA("DM9000E Fast Ethernet Adapter", "DM9000E"), + T_DATA("DM9601 Fast Ethernet Adapter", "DM9601"), + T_DATA("DP83065 [Saturn] 10/100/1000 Ethernet Controller", "DP83065"), + T_DATA("DP83815 (MacPhyter) Ethernet Controller (Aculab E1/T1 PMXc cPCI carrier card)", + "DP83815"), + T_DATA("DP83815 (MacPhyter) Ethernet Controller", "DP83815"), + T_DATA("DP83820 10/100/1000 Ethernet Controller", "DP83820"), + T_DATA("DrayTek Vigor N61 802.11bgn Wireless Adapter [Ralink RT2870]", "DrayTek Vigor N61"), + T_DATA("DRP-32TXD Cardbus PC Card", "DRP-32TXD"), + T_DATA("DSB-650 10Mbps Ethernet [klsi]", "DSB-650"), + T_DATA("DSB-650C Ethernet [klsi]", "DSB-650C"), + T_DATA("DSB-650 Ethernet [pegasus]", "DSB-650"), + T_DATA("DSB-650TX Ethernet [pegasus]", "DSB-650TX"), + T_DATA("DSB-650TX-PNA Ethernet [pegasus]", "DSB-650TX-PNA"), + T_DATA("Dual Band Wireless-AC 3165 Plus Bluetooth", "Wireless-AC 3165"), + T_DATA("DUB-E100 Fast Ethernet Adapter(rev.A) [ASIX AX88172]", "DUB-E100"), + T_DATA("DUB-E100 Fast Ethernet Adapter(rev.B1) [ASIX AX88772]", "DUB-E100"), + T_DATA("DUB-E100 Fast Ethernet Adapter(rev.C1) [ASIX AX88772]", "DUB-E100"), + T_DATA("DU-E100 Ethernet [pegasus]", "DU-E100"), + T_DATA("DU-E10 Ethernet [klsi]", "DU-E10"), + T_DATA("DU-E10 Ethernet [pegasus]", "DU-E10"), + T_DATA("DWA-110 Wireless G Adapter(rev.A1) [Ralink RT2571W]", "DWA-110"), + T_DATA("DWA-110 Wireless G Adapter(rev.B) [Ralink RT2870]", "DWA-110"), + T_DATA("DWA-111 802.11bg Wireless Adapter [Ralink RT2571W]", "DWA-111"), + T_DATA("DWA-121 802.11n Wireless N 150 Pico Adapter [Realtek RTL8188CUS]", + "DWA-121 150 Pico"), + T_DATA("DWA-123 Wireless N 150 Adapter(rev.A1) [Ralink RT3370]", "DWA-123 150"), + T_DATA("DWA-125 Wireless N 150 Adapter(rev.A1) [Ralink RT3070]", "DWA-125 150"), + T_DATA("DWA-125 Wireless N 150 Adapter(rev.A2) [Ralink RT3070]", "DWA-125 150"), + T_DATA("DWA-125 Wireless N 150 Adapter(rev.A3) [Ralink RT5370]", "DWA-125 150"), + T_DATA("DWA-126 802.11n Wireless Adapter [Atheros AR9271]", "DWA-126"), + T_DATA("DWA-127 Wireless N 150 High-Gain Adapter(rev.A1) [Ralink RT3070]", + "DWA-127 150 High-Gain"), + T_DATA("DWA-130 802.11n Wireless N Adapter(rev.B) [Ralink RT2870]", "DWA-130"), + T_DATA("DWA-130 802.11n Wireless N Adapter(rev.D) [Atheros AR9170+AR9102]", "DWA-130"), + T_DATA("DWA-133 802.11n Wireless N Adapter [Realtek RTL8192CU]", "DWA-133"), + T_DATA("DWA-135 802.11n Wireless N Adapter(rev.A1) [Realtek RTL8192CU]", "DWA-135"), + T_DATA("DWA-140 RangeBooster N Adapter(rev.B1) [Ralink RT2870]", "DWA-140 RangeBooster N"), + T_DATA("DWA-140 RangeBooster N Adapter(rev.B2) [Ralink RT3072]", "DWA-140 RangeBooster N"), + T_DATA("DWA-140 RangeBooster N Adapter(rev.B3) [Ralink RT2870]", "DWA-140 RangeBooster N"), + T_DATA("DWA-140 RangeBooster N Adapter(rev.B3) [Ralink RT5372]", "DWA-140 RangeBooster N"), + T_DATA("DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.A1) [Atheros AR9170+AR9104]", + "DWA-160 Xtreme N"), + T_DATA("DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.A2) [Atheros AR9170+AR9104]", + "DWA-160 Xtreme N"), + T_DATA("DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.B2) [Ralink RT5572]", + "DWA-160 Xtreme N"), + T_DATA("DWA-160 Xtreme N Dual Band USB Adapter(rev.B) [Ralink RT2870]", "DWA-160 Xtreme N"), + T_DATA("DWL-510 / DWL-610 802.11b [Realtek RTL8180L]", "DWL-510 / DWL-610"), + T_DATA("DWL-AG122 [Atheros AR5523]", "DWL-AG122"), + T_DATA("DWL-AG122 (no firmware) [Atheros AR5523]", "DWL-AG122"), + T_DATA("DWL-AG132 [Atheros AR5523]", "DWL-AG132"), + T_DATA("DWL-AG132 (no firmware) [Atheros AR5523]", "DWL-AG132"), + T_DATA("DWL-G120 Spinnaker 802.11g [Intersil ISL3886]", "DWL-G120 Spinnaker"), + T_DATA("DWL-G132 [Atheros AR5523]", "DWL-G132"), + T_DATA("DWL-G132 (no firmware) [Atheros AR5523]", "DWL-G132"), + T_DATA("DY-WL10 802.11abgn Adapter [Broadcom BCM4323]", "DY-WL10"), + T_DATA("E180v", "E180v"), + T_DATA("E45 Ethernet [klsi]", "E45"), + T_DATA("E815", "E815"), + T_DATA("EA101 10 Mbps 10BASE-T Ethernet [Kawasaki LSI KL5KLUSB101B]", "EA101"), + T_DATA("EasiDock Ethernet", "EasiDock"), + T_DATA("EH103 Wireless G Adapter", "EH103"), + T_DATA("Eminent EM4045 [Broadcom 4320 USB]", "Eminent EM4045"), + T_DATA("EN-1216 Ethernet Adapter", "EN-1216"), + T_DATA("EN-1217 Ethernet Adapter", "EN-1217"), + T_DATA("Enet2 Ethernet [klsi]", "Enet2"), + T_DATA("Enet Ethernet [klsi]", "Enet"), + T_DATA("EnGenius 802.11n Wireless USB Adapter", "EnGenius"), + T_DATA("ENUWI-N3 [802.11n Wireless N150 Adapter]", "ENUWI-N3"), + T_DATA("EP-1427X-2 Ethernet Adapter [Acer]", "EP-1427X-2"), + T_DATA("EP-9001-g 802.11g 54M WLAN Adapter", "EP-9001-g"), + T_DATA("ET-131x PCI-E Ethernet Controller", "ET-131x"), + T_DATA("ET32P2", "ET32P2"), + T_DATA("ETG-US2", "ETG-US2"), + T_DATA("Ethernet 100/10 MBit", NULL), + T_DATA("Ethernet 10G 2P X520 Adapter", "2P X520"), + T_DATA("Ethernet Adapter [A1277]", "A1277"), + T_DATA("Ethernet Adapter", NULL), + T_DATA("Ethernet adapter [U2L 100P-Y1]", "U2L 100P-Y1"), + T_DATA("Ethernet Adaptive Virtual Function", "Adaptive Virtual Function"), + T_DATA("Ethernet Connection (2) I218-LM", NULL), + T_DATA("Ethernet Connection (2) I218-V", NULL), + T_DATA("Ethernet Connection (2) I219-LM", NULL), + T_DATA("Ethernet Connection (2) I219-V", NULL), + T_DATA("Ethernet Connection (3) I218-LM", NULL), + T_DATA("Ethernet Connection (3) I218-V", NULL), + T_DATA("Ethernet Connection (3) I219-LM", NULL), + T_DATA("Ethernet Connection (4) I219-LM", NULL), + T_DATA("Ethernet Connection (4) I219-V", NULL), + T_DATA("Ethernet Connection (5) I219-LM", NULL), + T_DATA("Ethernet Connection (5) I219-V", NULL), + T_DATA("Ethernet Connection (6) I219-LM", NULL), + T_DATA("Ethernet Connection (6) I219-V", NULL), + T_DATA("Ethernet Connection (7) I219-LM", NULL), + T_DATA("Ethernet Connection (7) I219-V", NULL), + T_DATA("Ethernet Connection (8) I219-LM", NULL), + T_DATA("Ethernet Connection (8) I219-V", NULL), + T_DATA("Ethernet Connection (9) I219-LM", NULL), + T_DATA("Ethernet Connection (9) I219-V", NULL), + T_DATA("Ethernet Connection I217-LM", "I217-LM"), + T_DATA("Ethernet Connection I217-V", "I217-V"), + T_DATA("Ethernet Connection I218-LM", "I218-LM"), + T_DATA("Ethernet Connection I218-V", "I218-V"), + T_DATA("Ethernet Connection I219-LM", "I219-LM"), + T_DATA("Ethernet Connection I219-V", "I219-V"), + T_DATA("Ethernet Connection I354 1.0 GbE Backplane", "I354 Backplane"), + T_DATA("Ethernet Connection I354 2.5 GbE Backplane", "I354 Backplane"), + T_DATA("Ethernet Connection I354", "I354"), + T_DATA("Ethernet Connection X552 1000BASE-T", "X552"), + T_DATA("Ethernet Connection X552 10 GbE Backplane", "X552 Backplane"), + T_DATA("Ethernet Connection X552 10 GbE SFP+", "X552 SFP+"), + T_DATA("Ethernet Connection X552 Backplane", "X552 Backplane"), + T_DATA("Ethernet Connection X552 Virtual Function", "X552 Virtual Function"), + T_DATA("Ethernet Connection X552/X557-AT 10GBASE-T", "X552/X557-AT"), + T_DATA("Ethernet Connection X553 10 GbE SFP+", "X553 SFP+"), + T_DATA("Ethernet Connection X553 1GbE", "X553"), + T_DATA("Ethernet Connection X553 Backplane", "X553 Backplane"), + T_DATA("Ethernet Connection X553/X557-AT 10GBASE-T", "X553/X557-AT"), + T_DATA("Ethernet Connection X722 for 10GBASE-T", "X722"), + T_DATA("Ethernet Connection X722 for 10GbE backplane", "X722"), + T_DATA("Ethernet Connection X722 for 10GbE QSFP+", "X722"), + T_DATA("Ethernet Connection X722 for 10GbE SFP+", "X722"), + T_DATA("Ethernet Connection X722 for 1GbE", "X722"), + T_DATA("Ethernet Controller 10-Gigabit X540-AT2", "X540-AT2"), + T_DATA("Ethernet Controller 10G X550T", "X550T"), + T_DATA("Ethernet Controller X540", "X540"), + T_DATA("Ethernet Controller X710 for 10GBASE-T", "X710"), + T_DATA("Ethernet Controller X710 for 10GbE backplane", "X710"), + T_DATA("Ethernet Controller X710 for 10GbE QSFP+", "X710"), + T_DATA("Ethernet Controller X710 for 10GbE SFP+", "X710"), + T_DATA("Ethernet Controller X710/X557-AT 10GBASE-T", "X710/X557-AT"), + T_DATA("Ethernet Controller XL710 for 20GbE backplane", "XL710"), + T_DATA("Ethernet Controller XL710 for 40GbE backplane", "XL710"), + T_DATA("Ethernet Controller XL710 for 40GbE QSFP+", "XL710"), + T_DATA("Ethernet Controller XXV710 for 25GbE backplane", "XXV710"), + T_DATA("Ethernet Controller XXV710 for 25GbE SFP28", "XXV710"), + T_DATA("Ethernet Converged Network Adapter X520-Q1", "X520-Q1"), + T_DATA("Ethernet Express Module X520-P2", "X520-P2"), + T_DATA("Ethernet HN210E", "HN210E"), + T_DATA("Ethernet", NULL), + T_DATA("Ethernet Server Adapter X520-4", "X520-4"), + T_DATA("Ethernet Switch FM10000 Host Interface", "FM10000"), + T_DATA("Ethernet Switch FM10000 Host Virtual Interface", "FM10000"), + T_DATA("Ethernet X520 10GbE Dual Port KX4 Mezz", "X520 KX4 Mezz"), + T_DATA("Ether USB-T Ethernet [klsi]", "Ether USB-T"), + T_DATA("ET/TX Ethernet [pegasus]", "ET/TX"), + T_DATA("ET/TX-S Ethernet [pegasus2]", "ET/TX-S"), + T_DATA("EUB-3701 EXT 802.11g Wireless Adapter [Ralink RT2571W]", "EUB-3701 EXT"), + T_DATA("EUB600v1 802.11abgn Wireless Adapter [Ralink RT3572]", "EUB600v1"), + T_DATA("EUB9706 802.11n Wireless Adapter [Ralink RT3072]", "EUB9706"), + T_DATA("EUB9801 802.11abgn Wireless Adapter [Ralink RT3572]", "EUB9801"), + T_DATA("EW-7711UTn nLite Wireless Adapter [Ralink RT2870]", "EW-7711UTn nLite"), + T_DATA("EW-7717UN 802.11n Wireless Adapter [Ralink RT2870]", "EW-7717UN"), + T_DATA("EW-7718UN 802.11n Wireless Adapter [Ralink RT2870]", "EW-7718UN"), + T_DATA("EW-7722UTn 802.11n Wireless Adapter [Ralink RT307x]", "EW-7722UTn"), + T_DATA("EW-7811Un 802.11n Wireless Adapter [Realtek RTL8188CUS]", "EW-7811Un"), + T_DATA("Expedite E362", "Expedite E362"), + T_DATA("Express Ethernet", "Express"), + T_DATA("EZ Connect USB Ethernet", "EZ Connect"), + T_DATA("F5D5050 100Mbps Ethernet", "F5D5050"), + T_DATA("F5D5055 Gigabit Network Adapter [AX88xxx]", "F5D5055"), + T_DATA("F5D6001 Wireless PCI Card [Realtek RTL8180]", "F5D6001"), + T_DATA("F5D6020 v3000 Wireless PCMCIA Card [Realtek RTL8180]", "F5D6020 v3000"), + T_DATA("F5D7000 v7000 Wireless G Desktop Card [Realtek RTL8185]", "F5D7000 v7000"), + T_DATA("F5D7010 v7000 Wireless G Notebook Card [Realtek RTL8185]", "F5D7010 v7000"), + T_DATA("F5D7050 Wireless G Adapter v1000/v2000 [Intersil ISL3887]", "F5D7050 v1000/v2000"), + T_DATA("F5D7050 Wireless G Adapter v3000 [Ralink RT2571W]", "F5D7050 v3000"), + T_DATA("F5D7050 Wireless G Adapter v4000 [Zydas ZD1211B]", "F5D7050 v4000"), + T_DATA("F5D7050 Wireless G Adapter v5000 [Realtek RTL8187B]", "F5D7050 v5000"), + T_DATA("F5D7051 802.11g Adapter v1000 [Broadcom 4320 USB]", "F5D7051 v1000"), + T_DATA("F5D8053 N Wireless Adapter v3000 [Ralink RT2870]", "F5D8053 v3000"), + T_DATA("F5D8053 N Wireless USB Adapter v1000/v4000 [Ralink RT2870]", "F5D8053 v1000/v4000"), + T_DATA("F5D8053 N Wireless USB Adapter v3000 [Ralink RT2870]", "F5D8053 v3000"), + T_DATA("F5D8055 N+ Wireless Adapter v1000 [Ralink RT2870]", "F5D8055 v1000"), + T_DATA("F5D8055 N+ Wireless Adapter v2000 [Ralink RT3072]", "F5D8055 v2000"), + T_DATA("F5D9050 Wireless G+ MIMO Network Adapter v3000 [Ralink RT2573]", + "F5D9050 MIMO v3000"), + T_DATA("F5D9050 Wireless G+ MIMO Network Adapter v4000 [Ralink RT2573]", + "F5D9050 MIMO v4000"), + T_DATA("F5U258 Host to Host cable", "F5U258 Host to Host cable"), + T_DATA("F6D4050 N150 Enhanced Wireless Network Adapter v1000 [Ralink RT3070]", + "F6D4050 N150 v1000"), + T_DATA("F6D4050 N150 Enhanced Wireless Network Adapter v2000 [Ralink RT3070]", + "F6D4050 N150 v2000"), + T_DATA("F7D1101 v2 Basic Wireless Adapter [Ralink RT3370]", "F7D1101"), + T_DATA("F7D1102 N150/Surf Micro Wireless Adapter v1000 [Realtek RTL8188CUS]", + "F7D1102 N150/Surf v1000"), + T_DATA("F7D2102 802.11n N300 Micro Wireless Adapter v3000 [Realtek RTL8192CU]", + "F7D2102 N300 v3000"), + T_DATA("F9L1004 802.11n Surf N300 XR Wireless Adapter [Realtek RTL8192CU]", + "F9L1004 Surf N300 XR"), + T_DATA("F9L1103 N750 DB 802.11abgn 2x3:3 [Ralink RT3573]", "F9L1103 N750"), + T_DATA("FA101 Fast Ethernet USB 1.1", "FA101"), + T_DATA("FA120 Fast Ethernet USB 2.0 [Asix AX88172 / AX8817x]", "FA120"), + T_DATA("Farallon PN9000SX Gigabit Ethernet", "Farallon PN9000SX"), + T_DATA("Farallon PN9100-T Gigabit Ethernet", "Farallon PN9100-T"), + T_DATA("Fast Ethernet", NULL), + T_DATA("FastLinQ QL41000 Series 10/25/40/50GbE Controller", "FastLinQ QL41000"), + T_DATA("FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF)", + "FastLinQ QL41000"), + T_DATA("FastLinQ QL45000 Series 100GbE Controller", "FastLinQ QL45000"), + T_DATA("FastLinQ QL45000 Series 25GbE Controller", "FastLinQ QL45000"), + T_DATA("FastLinQ QL45000 Series 40GbE Controller", "FastLinQ QL45000"), + T_DATA("FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF)", + "FastLinQ QL45000"), + T_DATA("FEther USB2-TX", "FEther USB2-TX"), + T_DATA("FEther USB-TXC", "FEther USB-TXC"), + T_DATA("FEther USB-TX Ethernet [pegasus]", "FEther USB-TX"), + T_DATA("FEther USB-TXS", "FEther USB-TXS"), + T_DATA("FNW-3602-TX CardBus Fast Ethernet", "FNW-3602-TX"), + T_DATA("FNW-3603-TX CardBus Fast Ethernet", "FNW-3603-TX"), + T_DATA("FPC-0106TX misprogrammed [RTL81xx]", "FPC-0106TX"), + T_DATA("Fritz!WLAN N 2.4 [Atheros AR9001U]", "Fritz!WLAN N 2.4"), + T_DATA("Fritz!WLAN N [Atheros AR9001U]", "Fritz!WLAN N"), + T_DATA("Fritz!WLAN N v2 [Atheros AR9271]", "Fritz!WLAN N"), + T_DATA("FRITZ WLAN N v2 [RT5572/rt2870.bin]", "FRITZ N"), + T_DATA("G-200 v2 802.11bg", "G-200"), + T_DATA("G-210H 802.11g Wireless Adapter", "G-210H"), + T_DATA("G-220 v2 802.11bg", "G-220"), + T_DATA("G240 802.11bg", "G240"), + T_DATA("GA620 Gigabit Ethernet", "GA620"), + T_DATA("GA630 Gigabit Ethernet", "GA630"), + T_DATA("GEM 10/100/1000 Ethernet [ge]", "GEM"), + T_DATA("Gigabit Ethernet Adapter", NULL), + T_DATA("Gigabit Network Adapter", NULL), + T_DATA("GigaCard Network Adapter", "GigaCard"), + T_DATA("Gigaset USB Adapter 300", "Gigaset 300"), + T_DATA("GL620USB-A GeneLink USB-USB Bridge", "GL620USB-A GeneLink USB-USB Bridge"), + T_DATA("GlobeTrotter Express 7.2 v2", "GlobeTrotter Express 7.2"), + T_DATA("Globetrotter GI0505 [iCON 505]", "Globetrotter GI0505"), + T_DATA("Globetrotter HSDPA Modem", "Globetrotter"), + T_DATA("Globetrotter HSUPA Modem (aka icon 451)", "Globetrotter"), + T_DATA("Globetrotter HSUPA Modem (aka iCON HSUPA E)", "Globetrotter"), + T_DATA("Globetrotter HSUPA Modem (icon 411 aka \"Vodafone K3760\")", "Globetrotter"), + T_DATA("Globetrotter MO40x 3G Modem (GTM 382)", "Globetrotter MO40x"), + T_DATA("GN-54G", "GN-54G"), + T_DATA("GN-BR402W", "GN-BR402W"), + T_DATA("GNIC-II PCI Gigabit Ethernet [Hamachi]", "GNIC-II"), + T_DATA("GN-WB01GS", "GN-WB01GS"), + T_DATA("GN-WB30N 802.11n WLAN Card", "GN-WB30N"), + T_DATA("GN-WB31N 802.11n USB WLAN Card", "GN-WB31N"), + T_DATA("GN-WB32L 802.11n USB WLAN Card", "GN-WB32L"), + T_DATA("GN-WBKG", "GN-WBKG"), + T_DATA("GN-WI05GS", "GN-WI05GS"), + T_DATA("Gobi 2000", "Gobi 2000"), + T_DATA("Gobi 2000 Wireless Modem", "Gobi 2000"), + T_DATA("Gobi 3000 HSPA+ Modem", "Gobi 3000 HSPA+"), + T_DATA("Gobi 9x15 Multimode 3G/4G LTE Modem (IP passthrough mode)", "Gobi 9x15"), + T_DATA("Gobi 9x15 Multimode 3G/4G LTE Modem (NAT mode)", "Gobi 9x15"), + T_DATA("Gobi Wireless Modem", "Gobi"), + T_DATA("Goldpfeil P-LAN", "Goldpfeil P-LAN"), + T_DATA("GT-B3730 Composite LTE device (Commercial)", "GT-B3730"), + T_DATA("GU-1000T", "GU-1000T"), + T_DATA("GWUS300 802.11n", "GWUS300"), + T_DATA("GW-US300MiniS", "GW-US300MiniS"), + T_DATA("GW-US300MiniW 802.11bgn Wireless Adapter", "GW-US300MiniW"), + T_DATA("GW-US54GXS 802.11bg", "GW-US54GXS"), + T_DATA("GW-US54GZ", "GW-US54GZ"), + T_DATA("GW-US54HP", "GW-US54HP"), + T_DATA("GW-US54Mini2", "GW-US54Mini2"), + T_DATA("GW-US54Mini 802.11bg", "GW-US54Mini"), + T_DATA("GW-US54ZGL 802.11bg", "GW-US54ZGL"), + T_DATA("GWUSB2E", "GWUSB2E"), + T_DATA("GW-USEco300 802.11bgn Wireless Adapter [Realtek RTL8192CU]", "GW-USEco300"), + T_DATA("GW-USMicro300", "GW-USMicro300"), + T_DATA("GW-USMini2N 802.11n Wireless Adapter [Ralink RT2870]", "GW-USMini2N"), + T_DATA("GW-USNano2 802.11n Wireless Adapter [Realtek RTL8188CUS]", "GW-USNano2"), + T_DATA("GW-USValue-EZ 802.11n Wireless Adapter [Realtek RTL8188CUS]", "GW-USValue-EZ"), + T_DATA("Happy Meal 10/100 Ethernet [hme]", "Happy Meal"), + T_DATA("Harmony 900/1100 Remote", "Harmony 900/1100 Remote"), + T_DATA("HAWNU1 Hi-Gain Wireless-150N Network Adapter with Range Amplifier [Ralink RT3070]", + "HAWNU1"), + T_DATA("HCF 56k Modem", "HCF"), + T_DATA("Hercules HWNUp-150 802.11n Wireless N Pico [Realtek RTL8188CUS]", + "Hercules HWNUp-150 Pico"), + T_DATA("HNE-300 (RealTek RTL8139c) [iPaq Networking]", "HNE-300"), + T_DATA("HomeConnect 3C460", "HomeConnect 3C460"), + T_DATA("@Home Networks Ethernet [klsi]", "@Home Networks"), + T_DATA("HU200TS Wireless Adapter", "HU200TS"), + T_DATA("HWDN1 Hi-Gain Wireless-300N Dish Adapter [Ralink RT2870]", "HWDN1"), + T_DATA("HWDN2 Hi-Gain Wireless-150N Dish Adapter [Ralink RT2770]", "HWDN2"), + T_DATA("HWGUSB2-54-LB", "HWGUSB2-54-LB"), + T_DATA("HWGUSB2-54V2-AP", "HWGUSB2-54V2-AP"), + T_DATA("HWGUSB2-54 WLAN", "HWGUSB2-54"), + T_DATA("HWU54DM", "HWU54DM"), + T_DATA("HWUN1 Hi-Gain Wireless-300N Adapter w/ Upgradable Antenna [Ralink RT2870]", + "HWUN1"), + T_DATA("HWUN2 Hi-Gain Wireless-150N Adapter w/ Upgradable Antenna [Ralink RT2770]", + "HWUN2"), + T_DATA("HWUN3 Hi-Gain Wireless-N Adapter [Ralink RT3070]", "HWUN3"), + T_DATA("I210 Gigabit Backplane Connection", "I210 Backplane Connection"), + T_DATA("I210 Gigabit Fiber Network Connection", "I210"), + T_DATA("I210 Gigabit Network Connection", "I210"), + T_DATA("I211 Gigabit Network Connection", "I211"), + T_DATA("I350 Ethernet Controller Virtual Function", "I350 Virtual Function"), + T_DATA("I350 Gigabit Backplane Connection", "I350 Backplane Connection"), + T_DATA("I350 Gigabit Connection", "I350 Connection"), + T_DATA("I350 Gigabit Fiber Network Connection", "I350"), + T_DATA("I350 Gigabit Network Connection", "I350"), + T_DATA("IC Plus IP100A Integrated 10/100 Ethernet MAC + PHY", "IC Plus IP100A"), + T_DATA("IEEE 802.11g Wireless Network Adapter", NULL), + T_DATA("IFU-WLM2 USB Wireless LAN Module (Wireless Mode)", "IFU-WLM2"), + T_DATA("Integrated NetFlex-3/P", "NetFlex-3/P"), + T_DATA("Intrepid2 GMAC (Sun GEM)", "Intrepid2 GMAC"), + T_DATA("IOGear GWU513 v2 802.11bg Wireless Adapter [Intersil ISL3887]", "IOGear GWU513"), + T_DATA("IP1000 Family Gigabit Ethernet", "IP1000"), + T_DATA("iPad 2 (3G; 64GB)", "iPad 2"), + T_DATA("iPad 3 (3G, 16 GB)", "iPad 3"), + T_DATA("iPad 4/Mini1", "iPad 4/Mini1"), + T_DATA("iPad", "iPad"), + T_DATA("iPAQ Networking 10/100 Ethernet [pegasus2]", "iPAQ Networking"), + T_DATA("iPhone 3G", "iPhone"), + T_DATA("iPhone 3GS", "iPhone 3GS"), + T_DATA("iPhone 4(CDMA)", "iPhone 4"), + T_DATA("iPhone 4", "iPhone 4"), + T_DATA("iPhone 4S", "iPhone 4S"), + T_DATA("iPhone5/5C/5S/6", "iPhone5/5C/5S/6"), + T_DATA("iPhone", "iPhone"), + T_DATA("iRex Technologies Gobi 2000 Wireless Modem", "iRex Gobi 2000"), + T_DATA("ISL3877 [Prism Indigo]", "ISL3877"), + T_DATA("ISL3886IK", "ISL3886IK"), + T_DATA("ISL3886 [Prism Javelin/Prism Xbow]", "ISL3886"), + T_DATA("ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow]", "ISL3890"), + T_DATA("ISP4022-based Ethernet NIC", "ISP4022-based"), + T_DATA("ISP4032-based Ethernet IPv6 NIC", "ISP4032-based IPv6"), + T_DATA("ISP8324 1/10GbE Converged Network Controller", "ISP8324"), + T_DATA("ISP8324 1/10GbE Converged Network Controller (NIC VF)", "ISP8324"), + T_DATA("ISY Wireless Micro Adapter IWL 2000 [RTL8188CUS]", "ISY IWL 2000"), + T_DATA("JMC250 PCI Express Gigabit Ethernet Controller", "JMC250"), + T_DATA("JMC260 PCI Express Fast Ethernet Controller", "JMC260"), + T_DATA("K2 GMAC (Sun GEM)", "K2 GMAC"), + T_DATA("K3565-Z HSDPA", "K3565-Z"), + T_DATA("K3570-Z", "K3570-Z"), + T_DATA("K3571-Z", "K3571-Z"), + T_DATA("K4505-Z", "K4505-Z"), + T_DATA("K5006-Z vodafone LTE/UMTS/GSM Modem/Networkcard", "K5006-Z vodafone"), + T_DATA("KC2190 USB Host-to-Host cable", "KC2190 Host-to-Host cable"), + T_DATA("Keebox W150NU 802.11bgn Wireless Adapter [Ralink RT3070]", "Keebox W150NU"), + T_DATA("Killer E220x Gigabit Ethernet Controller", "Killer E220x"), + T_DATA("Killer E2400 Gigabit Ethernet Controller", "Killer E2400"), + T_DATA("KL5KUSB101B Ethernet [klsi]", "KL5KUSB101B"), + T_DATA("KNU101TX 100baseTX Ethernet", "KNU101TX 100baseTX"), + T_DATA("KSZ8842-PMQL 2-Port Ethernet Switch", "KSZ8842-PMQL"), + T_DATA("KwikLink Host-Host Connector", "KwikLink Host-Host Connector"), + T_DATA("LAN7500 Ethernet 10/100/1000 Adapter", "LAN7500"), + T_DATA("LAN9420/LAN9420i", "LAN9420/LAN9420i"), + T_DATA("LAN9512/LAN9514 Ethernet 10/100 Adapter (SAL10)", "LAN9512/LAN9514"), + T_DATA("Laneed 100Mbps Ethernet LD-USB/TX [pegasus]", "Laneed LD-USB/TX"), + T_DATA("LAN-GTJ/U2A", "LAN-GTJ/U2A"), + T_DATA("LAN-W150N/U2 Wireless LAN Adapter", "LAN-W150N/U2"), + T_DATA("LAN-W150/U2M Wireless LAN Adapter", "LAN-W150/U2M"), + T_DATA("LAN-W300AN/U2 Wireless LAN Adapter", "LAN-W300AN/U2"), + T_DATA("LAN-W300N/U2 Wireless LAN Adapter", "LAN-W300N/U2"), + T_DATA("LAN-WN12/U2 Wireless LAN Adapter", "LAN-WN12/U2"), + T_DATA("LAN-WN22/U2 Wireless LAN Adapter", "LAN-WN22/U2"), + T_DATA("LapLink Gold USB-USB Bridge [net1080]", "LapLink Gold USB-USB Bridge"), + T_DATA("LD-USB20", "LD-USB20"), + T_DATA("LD-USBL/TX", "LD-USBL/TX"), + T_DATA("LD-USB/TX", "LD-USB/TX"), + T_DATA("LE920", "LE920"), + T_DATA("Leaf Light HS", "Leaf Light HS"), + T_DATA("Leaf SemiPro HS", "Leaf SemiPro HS"), + T_DATA("LevelOne WUA-0605 N_Max Wireless USB Adapter", "LevelOne WUA-0605 N Max"), + T_DATA("LevelOne WUA-0615 N_Max Wireless USB Adapter", "LevelOne WUA-0615 N Max"), + T_DATA("Libertas", "Libertas"), + T_DATA("Linksys WUSB54GP v1 OEM 802.11g Adapter [Intersil ISL3886]", "Linksys WUSB54GP"), + T_DATA("Linksys WUSB54G v1 OEM 802.11g Adapter [Intersil ISL3886]", "Linksys WUSB54G"), + T_DATA("Linux-USB \"CDC Subset\" Device, or Itsy (experimental)", "Linux-USB or Itsy"), + T_DATA("Linux-USB Ethernet/RNDIS Gadget", "Linux-USB Gadget"), + T_DATA("LN-028 Network USB 2.0 Adapter", "LN-028"), + T_DATA("LN-031 10/100/1000 Ethernet Adapter", "LN-031"), + T_DATA("LNE100TX [Linksys EtherFast 10/100]", "LNE100TX"), + T_DATA("LNE100TX", "LNE100TX"), + T_DATA("lt4112 Gobi 4G Module Network Device", "lt4112 Gobi"), + T_DATA("LTE4G O2 ZTE MF821D LTE/UMTS/GSM Modem/Networkcard", "LTE4G O2 ZTE MF821D"), + T_DATA("LTE Storage Driver [CMC2xx]", "Storage Driver"), + T_DATA("LUA2-TX Ethernet", "LUA2-TX"), + T_DATA("LUA-KTX Ethernet", "LUA-KTX"), + T_DATA("LUA-TX Ethernet", "LUA-TX"), + T_DATA("LUA-TX Ethernet [pegasus]", "LUA-TX"), + T_DATA("LUA-U2-GT 10/100/1000 Ethernet Adapter", "LUA-U2-GT"), + T_DATA("LUA-U2-KTX Ethernet", "LUA-U2-KTX"), + T_DATA("LW153 802.11n Adapter [ralink rt3070]", "LW153"), + T_DATA("LW313 802.11n Adapter [ralink rt2770 + rt2720]", "LW313"), + T_DATA("M-202 802.11bg", "M-202"), + T_DATA("M5261 Ethernet Controller", "M5261"), + T_DATA("M5632 Host-to-Host Link", "M5632 Host-to-Host Link"), + T_DATA("Marvell 88W8388 802.11a/b/g WLAN", "Marvell 88W8388"), + T_DATA("MC8700 Modem", "MC8700"), + T_DATA("MCP04 Ethernet Controller", "MCP04"), + T_DATA("MCP2A Ethernet Controller", "MCP2A"), + T_DATA("MCP51 Ethernet Controller", "MCP51"), + T_DATA("MCP55 Ethernet", "MCP55"), + T_DATA("MCP61 Ethernet", "MCP61"), + T_DATA("MCP65 Ethernet", "MCP65"), + T_DATA("MCP67 Ethernet", "MCP67"), + T_DATA("MCP73 Ethernet", "MCP73"), + T_DATA("MCP77 Ethernet", "MCP77"), + T_DATA("MCP79 Ethernet", "MCP79"), + T_DATA("MCP89 Ethernet", "MCP89"), + T_DATA("MCS7730 10/100 Mbps Ethernet adapter", "MCS7730"), + T_DATA("MCS7830 10/100 Mbps Ethernet adapter", "MCS7830"), + T_DATA("MCS7832 10/100 Mbps Ethernet adapter", "MCS7832"), + T_DATA("Metronic 495257 wifi 802.11ng", "Metronic 495257"), + T_DATA("MF110/MF627/MF636", "MF110/MF627/MF636"), + T_DATA("MF632/ONDA ET502HS/MT505UP", "MF632/ONDA ET502HS/MT505UP"), + T_DATA("MF820 4G LTE", "MF820"), + T_DATA("Micolink USB2Ethernet [pegasus]", "Micolink USB2Ethernet"), + T_DATA("MicroLink dLAN", "MicroLink dLAN"), + T_DATA("MN-120 (ADMtek Centaur-C based)", "MN-120"), + T_DATA("MN-130 (ADMtek Centaur-P based)", "MN-130"), + T_DATA("MN-710 802.11g Wireless Adapter [Intersil ISL3886]", "MN-710"), + T_DATA("model 01 Ethernet interface", "model 01"), + T_DATA("model 01+ Ethernet", "model 01+"), + T_DATA("Motorola 802.11n 5G USB Wireless Adapter", "Motorola"), + T_DATA("Motorola 802.11n Dualband USB Wireless Adapter", "Motorola Dualband"), + T_DATA("MP-PRX1 Ethernet", "MP-PRX1"), + T_DATA("MS-3870 802.11bgn Wireless Module [Ralink RT3070]", "MS-3870"), + T_DATA("MS-3871 802.11bgn Wireless Module [Ralink RT8070]", "MS-3871"), + T_DATA("MSI-6861 802.11g WiFi adapter", "MSI-6861"), + T_DATA("MT25400 Family [ConnectX-2 Virtual Function]", "MT25400"), + T_DATA("MT25408 [ConnectX EN 10GigE 10GBaseT, PCIe 2.0 2.5GT/s]", "MT25408"), + T_DATA("MT25408 [ConnectX EN 10GigE 10GBaseT, PCIe Gen2 5GT/s]", "MT25408"), + T_DATA("MT25408 [ConnectX VPI - IB SDR / 10GigE]", "MT25408"), + T_DATA("MT25418 [ConnectX VPI PCIe 2.0 2.5GT/s - IB DDR / 10GigE]", "MT25418"), + T_DATA("MT25448 [ConnectX EN 10GigE, PCIe 2.0 2.5GT/s]", "MT25448"), + T_DATA("MT26418 [ConnectX VPI PCIe 2.0 5GT/s - IB DDR / 10GigE]", "MT26418"), + T_DATA("MT26428 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE]", "MT26428"), + T_DATA("MT26438 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE Virtualization+]", + "MT26438"), + T_DATA("MT26448 [ConnectX EN 10GigE, PCIe 2.0 5GT/s]", "MT26448"), + T_DATA("MT26468 [ConnectX EN 10GigE, PCIe 2.0 5GT/s Virtualization+]", "MT26468"), + T_DATA("MT26478 [ConnectX EN 40GigE, PCIe 2.0 5GT/s]", "MT26478"), + T_DATA("MT27500 Family [ConnectX-3]", "MT27500"), + T_DATA("MT27500/MT27520 Family [ConnectX-3/ConnectX-3 Pro Virtual Function]", + "MT27500/MT27520"), + T_DATA("MT27510 Family", "MT27510"), + T_DATA("MT27511 Family", "MT27511"), + T_DATA("MT27520 Family [ConnectX-3 Pro]", "MT27520"), + T_DATA("MT27530 Family", "MT27530"), + T_DATA("MT27531 Family", "MT27531"), + T_DATA("MT27540 Family", "MT27540"), + T_DATA("MT27541 Family", "MT27541"), + T_DATA("MT27550 Family", "MT27550"), + T_DATA("MT27551 Family", "MT27551"), + T_DATA("MT27560 Family", "MT27560"), + T_DATA("MT27561 Family", "MT27561"), + T_DATA("MT27600 [Connect-IB]", "MT27600"), + T_DATA("MT27600 Family [Connect-IB Virtual Function]", "MT27600"), + T_DATA("MT27700 Family [ConnectX-4]", "MT27700"), + T_DATA("MT27700 Family [ConnectX-4 Virtual Function]", "MT27700"), + T_DATA("MT27710 Family [ConnectX-4 Lx]", "MT27710"), + T_DATA("MT27710 Family [ConnectX-4 Lx Virtual Function]", "MT27710"), + T_DATA("MT27800 Family [ConnectX-5]", "MT27800"), + T_DATA("MT27800 Family [ConnectX-5 Virtual Function]", "MT27800"), + T_DATA("MT28800 Family [ConnectX-5 Ex]", "MT28800"), + T_DATA("MT28800 Family [ConnectX-5 Ex Virtual Function]", "MT28800"), + T_DATA("MT28908 Family [ConnectX-6]", "MT28908"), + T_DATA("MT28908 Family [ConnectX-6 Virtual Function]", "MT28908"), + T_DATA("MT416842 BlueField integrated ConnectX-5 network controller", + "MT416842 BlueField ConnectX-5"), + T_DATA("MT416842 BlueField multicore SoC family VF", "MT416842 BlueField VF"), + T_DATA("MT51136", "MT51136"), + T_DATA("MT52100", "MT52100"), + T_DATA("MT53236", "MT53236"), + T_DATA("MT7601U Wireless Adapter", "MT7601U"), + T_DATA("MTD-8xx 100/10M Ethernet PCI Adapter", "MTD-8xx"), + T_DATA("Multithreaded 10-Gigabit Ethernet Network Controller", NULL), + T_DATA("MX98713", "MX98713"), + T_DATA("MX987x5", "MX987x5"), + T_DATA("Myri-10G Dual-Protocol NIC", "Myri-10G"), + T_DATA("N10 Nano 802.11n Network Adapter [Realtek RTL8192CU]", "N10 Nano"), + T_DATA("N220 802.11bgn Wireless Adapter", "N220"), + T_DATA("N320-G2-CR 10GbE Dual Port Adapter", "N320-G2-CR"), + T_DATA("N5HBZ0000055 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", "N5HBZ0000055"), + T_DATA("Name: Voyager 1055 Laptop 802.11g Adapter [Broadcom 4320]", "Voyager 1055"), + T_DATA("NC100 Network Everywhere Fast Ethernet 10/100", "NC100"), + T_DATA("NE-34", "NE-34"), + T_DATA("NET1080 USB-USB Bridge", "NET1080 USB-USB Bridge"), + T_DATA("Netelligent 10/100 TX Embedded UTP", "Netelligent TX"), + T_DATA("Netelligent 10/100 TX PCI UTP", "Netelligent TX"), + T_DATA("Netelligent 10/100 TX UTP", "Netelligent TX"), + T_DATA("Netelligent 10 T/2 PCI UTP/Coax", "Netelligent 10 T/2"), + T_DATA("Netelligent 10 T PCI UTP", "Netelligent 10 T"), + T_DATA("Netelligent Dual 10/100 TX PCI UTP", "Netelligent Dual TX"), + T_DATA("Netelligent Integrated 10/100 TX UTP", "Netelligent TX"), + T_DATA("NetFlex-3/P ThunderLAN 1.0", "NetFlex-3/P ThunderLAN 1.0"), + T_DATA("NetFlex-3/P ThunderLAN 2.3", "NetFlex-3/P ThunderLAN 2.3"), + T_DATA("NetLink BCM57780 Gigabit Ethernet PCIe", "NetLink BCM57780"), + T_DATA("NetLink BCM57781 Gigabit Ethernet PCIe", "NetLink BCM57781"), + T_DATA("NetLink BCM57785 Gigabit Ethernet PCIe", "NetLink BCM57785"), + T_DATA("NetLink BCM57788 Gigabit Ethernet PCIe", "NetLink BCM57788"), + T_DATA("NetLink BCM57790 Gigabit Ethernet PCIe", "NetLink BCM57790"), + T_DATA("NetLink BCM57791 Gigabit Ethernet PCIe", "NetLink BCM57791"), + T_DATA("NetLink BCM57795 Gigabit Ethernet PCIe", "NetLink BCM57795"), + T_DATA("NetLink BCM5781 Gigabit Ethernet PCI Express", "NetLink BCM5781"), + T_DATA("NetLink BCM5784M Gigabit Ethernet PCIe", "NetLink BCM5784M"), + T_DATA("NetLink BCM5785 Fast Ethernet", "NetLink BCM5785"), + T_DATA("NetLink BCM5785 Gigabit Ethernet", "NetLink BCM5785"), + T_DATA("NetLink BCM5786 Gigabit Ethernet PCI Express", "NetLink BCM5786"), + T_DATA("NetLink BCM5787F Fast Ethernet PCI Express", "NetLink BCM5787F"), + T_DATA("NetLink BCM5787 Gigabit Ethernet PCI Express", "NetLink BCM5787"), + T_DATA("NetLink BCM5787M Gigabit Ethernet PCI Express", "NetLink BCM5787M"), + T_DATA("NetLink BCM5789 Gigabit Ethernet PCI Express", "NetLink BCM5789"), + T_DATA("NetLink BCM5906 Fast Ethernet PCI Express", "NetLink BCM5906"), + T_DATA("NetLink BCM5906M Fast Ethernet PCI Express", "NetLink BCM5906M"), + T_DATA("NetMate2 Ethernet", "NetMate2"), + T_DATA("NetMate Ethernet", "NetMate"), + T_DATA("NetXen Dual Port 10GbE Multifunction Adapter for c-Class", + "NetXen Multifunction for c-Class"), + T_DATA("NetXtreme 5714S Gigabit Ethernet", "NetXtreme 5714S"), + T_DATA("NetXtreme BCM5700 Gigabit Ethernet", "NetXtreme BCM5700"), + T_DATA("NetXtreme BCM5701 Gigabit Ethernet", "NetXtreme BCM5701"), + T_DATA("NetXtreme BCM5702A3 Gigabit Ethernet", "NetXtreme BCM5702A3"), + T_DATA("NetXtreme BCM5702FE Gigabit Ethernet", "NetXtreme BCM5702FE"), + T_DATA("NetXtreme BCM5702 Gigabit Ethernet", "NetXtreme BCM5702"), + T_DATA("NetXtreme BCM5702X Gigabit Ethernet", "NetXtreme BCM5702X"), + T_DATA("NetXtreme BCM5703 Gigabit Ethernet", "NetXtreme BCM5703"), + T_DATA("NetXtreme BCM5703X Gigabit Ethernet", "NetXtreme BCM5703X"), + T_DATA("NetXtreme BCM5704 Gigabit Ethernet", "NetXtreme BCM5704"), + T_DATA("NetXtreme BCM5704S_2 Gigabit Ethernet", "NetXtreme BCM5704S 2"), + T_DATA("NetXtreme BCM5704S Gigabit Ethernet", "NetXtreme BCM5704S"), + T_DATA("NetXtreme BCM5705_2 Gigabit Ethernet", "NetXtreme BCM5705 2"), + T_DATA("NetXtreme BCM5705 Gigabit Ethernet", "NetXtreme BCM5705"), + T_DATA("NetXtreme BCM5705M_2 Gigabit Ethernet", "NetXtreme BCM5705M 2"), + T_DATA("NetXtreme BCM5705M Gigabit Ethernet", "NetXtreme BCM5705M"), + T_DATA("NetXtreme BCM5714 Gigabit Ethernet", "NetXtreme BCM5714"), + T_DATA("NetXtreme BCM5715 Gigabit Ethernet", "NetXtreme BCM5715"), + T_DATA("NetXtreme BCM5715S Gigabit Ethernet", "NetXtreme BCM5715S"), + T_DATA("NetXtreme BCM5717 Gigabit Ethernet PCIe", "NetXtreme BCM5717"), + T_DATA("NetXtreme BCM5718 Gigabit Ethernet PCIe", "NetXtreme BCM5718"), + T_DATA("NetXtreme BCM5719 Gigabit Ethernet PCIe", "NetXtreme BCM5719"), + T_DATA("NetXtreme BCM5720 Gigabit Ethernet PCIe", "NetXtreme BCM5720"), + T_DATA("NetXtreme BCM5721 Gigabit Ethernet PCI Express", "NetXtreme BCM5721"), + T_DATA("NetXtreme BCM5722 Gigabit Ethernet PCI Express", "NetXtreme BCM5722"), + T_DATA("NetXtreme BCM5723 Gigabit Ethernet PCIe", "NetXtreme BCM5723"), + T_DATA("NetXtreme BCM5725 Gigabit Ethernet PCIe", "NetXtreme BCM5725"), + T_DATA("NetXtreme BCM5727 Gigabit Ethernet PCIe", "NetXtreme BCM5727"), + T_DATA("NetXtreme BCM5751F Fast Ethernet PCI Express", "NetXtreme BCM5751F"), + T_DATA("NetXtreme BCM5751 Gigabit Ethernet PCI Express", "NetXtreme BCM5751"), + T_DATA("NetXtreme BCM5751M Gigabit Ethernet PCI Express", "NetXtreme BCM5751M"), + T_DATA("NetXtreme BCM5752 Gigabit Ethernet PCI Express", "NetXtreme BCM5752"), + T_DATA("NetXtreme BCM5752M Gigabit Ethernet PCI Express", "NetXtreme BCM5752M"), + T_DATA("NetXtreme BCM5753F Fast Ethernet PCI Express", "NetXtreme BCM5753F"), + T_DATA("NetXtreme BCM5753 Gigabit Ethernet PCI Express", "NetXtreme BCM5753"), + T_DATA("NetXtreme BCM5753M Gigabit Ethernet PCI Express", "NetXtreme BCM5753M"), + T_DATA("NetXtreme BCM5754 Gigabit Ethernet PCI Express", "NetXtreme BCM5754"), + T_DATA("NetXtreme BCM5754M Gigabit Ethernet PCI Express", "NetXtreme BCM5754M"), + T_DATA("NetXtreme BCM5755 Gigabit Ethernet PCI Express", "NetXtreme BCM5755"), + T_DATA("NetXtreme BCM5755M Gigabit Ethernet PCI Express", "NetXtreme BCM5755M"), + T_DATA("NetXtreme BCM5756ME Gigabit Ethernet PCI Express", "NetXtreme BCM5756ME"), + T_DATA("NetXtreme BCM5761 10/100/1000BASE-T Ethernet", "NetXtreme BCM5761"), + T_DATA("NetXtreme BCM5761e Gigabit Ethernet PCIe", "NetXtreme BCM5761e"), + T_DATA("NetXtreme BCM5761 Gigabit Ethernet PCIe", "NetXtreme BCM5761"), + T_DATA("NetXtreme BCM5762 Gigabit Ethernet PCIe", "NetXtreme BCM5762"), + T_DATA("NetXtreme BCM5764M Gigabit Ethernet PCIe", "NetXtreme BCM5764M"), + T_DATA("NetXtreme BCM57760 Gigabit Ethernet PCIe", "NetXtreme BCM57760"), + T_DATA("NetXtreme BCM57761 Gigabit Ethernet PCIe", "NetXtreme BCM57761"), + T_DATA("NetXtreme BCM57762 Gigabit Ethernet PCIe", "NetXtreme BCM57762"), + T_DATA("NetXtreme BCM57764 Gigabit Ethernet PCIe", "NetXtreme BCM57764"), + T_DATA("NetXtreme BCM57765 Gigabit Ethernet PCIe", "NetXtreme BCM57765"), + T_DATA("NetXtreme BCM57766 Gigabit Ethernet PCIe", "NetXtreme BCM57766"), + T_DATA("NetXtreme BCM57767 Gigabit Ethernet PCIe", "NetXtreme BCM57767"), + T_DATA("NetXtreme BCM57782 Gigabit Ethernet PCIe", "NetXtreme BCM57782"), + T_DATA("NetXtreme BCM57786 Gigabit Ethernet PCIe", "NetXtreme BCM57786"), + T_DATA("NetXtreme BCM57787 Gigabit Ethernet PCIe", "NetXtreme BCM57787"), + T_DATA("NetXtreme BCM5780 Gigabit Ethernet", "NetXtreme BCM5780"), + T_DATA("NetXtreme BCM5780S Gigabit Ethernet", "NetXtreme BCM5780S"), + T_DATA("NetXtreme BCM5782 Gigabit Ethernet", "NetXtreme BCM5782"), + T_DATA("NetXtreme BCM5788 Gigabit Ethernet", "NetXtreme BCM5788"), + T_DATA("NetXtreme BCM5901 100Base-TX", "NetXtreme BCM5901"), + T_DATA("NetXtreme-C Ethernet Virtual Function", "NetXtreme-C Virtual Function"), + T_DATA("NetXtreme-C RDMA Virtual Function", "NetXtreme-C Virtual Function"), + T_DATA("NetXtreme-E Ethernet Virtual Function", "NetXtreme-E Virtual Function"), + T_DATA("NetXtreme-E RDMA Virtual Function", "NetXtreme-E Virtual Function"), + T_DATA( + "NetXtreme II BCM5706 Gigabit Ethernet (NC370i Multifunction Gigabit Server Adapter)", + "NetXtreme II BCM5706"), + T_DATA( + "NetXtreme II BCM5706 Gigabit Ethernet (NC370T MultifuNCtion Gigabit Server Adapter)", + "NetXtreme II BCM5706"), + T_DATA("NetXtreme II BCM5706 Gigabit Ethernet", "NetXtreme II BCM5706"), + T_DATA( + "NetXtreme II BCM5706S Gigabit Ethernet (NC370F MultifuNCtion Gigabit Server Adapter)", + "NetXtreme II BCM5706S"), + T_DATA("NetXtreme II BCM5706S Gigabit Ethernet", "NetXtreme II BCM5706S"), + T_DATA("NetXtreme II BCM5708 Gigabit Ethernet", "NetXtreme II BCM5708"), + T_DATA("NetXtreme II BCM5708S Gigabit Ethernet", "NetXtreme II BCM5708S"), + T_DATA("NetXtreme II BCM5709 Gigabit Ethernet", "NetXtreme II BCM5709"), + T_DATA("NetXtreme II BCM5709S Gigabit Ethernet", "NetXtreme II BCM5709S"), + T_DATA("NetXtreme II BCM5716 Gigabit Ethernet", "NetXtreme II BCM5716"), + T_DATA("NetXtreme II BCM5716S Gigabit Ethernet", "NetXtreme II BCM5716S"), + T_DATA("NetXtreme II BCM57710 10-Gigabit PCIe [Everest]", "NetXtreme II BCM57710"), + T_DATA("NetXtreme II BCM57711 10-Gigabit PCIe", "NetXtreme II BCM57711"), + T_DATA("NetXtreme II BCM57711E 10-Gigabit PCIe", "NetXtreme II BCM57711E"), + T_DATA("NetXtreme II BCM57712 10 Gigabit Ethernet Multi Function", "NetXtreme II BCM57712"), + T_DATA("NetXtreme II BCM57712 10 Gigabit Ethernet", "NetXtreme II BCM57712"), + T_DATA("NetXtreme II BCM57712 10 Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57712 Virtual Function"), + T_DATA("NetXtreme II BCM57800 1/10 Gigabit Ethernet Multi Function", + "NetXtreme II BCM57800"), + T_DATA("NetXtreme II BCM57800 1/10 Gigabit Ethernet", "NetXtreme II BCM57800"), + T_DATA("NetXtreme II BCM57800 1/10 Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57800 Virtual Function"), + T_DATA("NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function", "NetXtreme II BCM57810"), + T_DATA("NetXtreme II BCM57810 10 Gigabit Ethernet", "NetXtreme II BCM57810"), + T_DATA("NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57810 Virtual Function"), + T_DATA("NetXtreme II BCM57811 10 Gigabit Ethernet Multi Function", "NetXtreme II BCM57811"), + T_DATA("NetXtreme II BCM57811 10-Gigabit Ethernet", "NetXtreme II BCM57811"), + T_DATA("NetXtreme II BCM57811 10-Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57811 Virtual Function"), + T_DATA("NetXtreme II BCM57840 10/20 Gigabit Ethernet Multi Function", + "NetXtreme II BCM57840 10/20"), + T_DATA("NetXtreme II BCM57840 10/20 Gigabit Ethernet", "NetXtreme II BCM57840 10/20"), + T_DATA("NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function", + "NetXtreme II BCM57840 10/20 Virtual Function"), + T_DATA("nForce2 Ethernet Controller", "nForce2"), + T_DATA("nForce3 Ethernet", "nForce3"), + T_DATA("nForce Ethernet Controller", "nForce"), + T_DATA("Nintendo Wi-Fi", "Nintendo"), + T_DATA("NM10/ICH7 Family LAN Controller", "NM10/ICH7"), + T_DATA("NovaTech NV-902W", "NovaTech NV-902W"), + T_DATA("NUB100 Ethernet [pegasus]", "NUB100"), + T_DATA("NUB-350 802.11g Wireless Adapter [Intersil ISL3887]", "NUB-350"), + T_DATA("NUB-8301 802.11bg", "NUB-8301"), + T_DATA("NV5000SC", "NV5000SC"), + T_DATA("NW-3100 802.11b/g 54Mbps Wireless Network Adapter [zd1211]", "NW-3100"), + T_DATA("NWD2105 802.11bgn Wireless Adapter [Ralink RT3070]", "NWD2105"), + T_DATA("NWD-210N 802.11b/g/n-draft wireless adapter", "NWD-210N"), + T_DATA("NWD211AN 802.11abgn Wireless Adapter [Ralink RT2870]", "NWD211AN"), + T_DATA("NWD2205 802.11n Wireless N Adapter [Realtek RTL8192CU]", "NWD2205"), + T_DATA("NWD-270N Wireless N-lite USB Adapter", "NWD-270N N-lite"), + T_DATA("NWD271N 802.11n Wireless Adapter [Atheros AR9001U-(2)NG]", "NWD271N"), + T_DATA("NX3031 Multifunction 1/10-Gigabit Server Adapter", "NX3031 Multifunction"), + T_DATA("NXB-10GCX4 10-Gigabit Ethernet PCIe Adapter with CX4 copper interface", + "NXB-10GCX4"), + T_DATA("NXB-10GXSR 10-Gigabit Ethernet PCIe Adapter with SR-XFP optical interface", + "NXB-10GXSR"), + T_DATA("NXB-4GCU Quad Gigabit Ethernet PCIe Adapter with 1000-BASE-T interface", + "NXB-4GCU Quad"), + T_DATA("OC-2183/2185", "OC-2183/2185"), + T_DATA("OC-2325", "OC-2325"), + T_DATA("OC-2326", "OC-2326"), + T_DATA("OCT To Fast Ethernet Converter", "OCT To Converter"), + T_DATA("Olicard 100", "Olicard 100"), + T_DATA("OneConnect 10Gb NIC (be3)", "OneConnect"), + T_DATA("OneConnect NIC (Lancer)", "OneConnect"), + T_DATA("OneConnect NIC (Skyhawk)", "OneConnect"), + T_DATA("OneConnect NIC (Skyhawk-VF)", "OneConnect"), + T_DATA("OneConnect OCe10100/OCe10102 Series 10 GbE", "OneConnect OCe10100/OCe10102"), + T_DATA("On Networks N300MA 802.11bgn [Realtek RTL8192CU]", "On Networks N300MA"), + T_DATA("Ovation MC551", "Ovation MC551"), + T_DATA("PCAN-PCI CAN-Bus controller", "PCAN-PCI"), + T_DATA("PCAN Pro", "PCAN Pro"), + T_DATA("PCAN-USB", "PCAN-USB"), + T_DATA("PCI NE2K Ethernet", "NE2K"), + T_DATA("PCI Rocker Ethernet switch device", "Rocker switch"), + T_DATA("PL2301 USB-USB Bridge", "PL2301 USB-USB Bridge"), + T_DATA("PL2302 USB-USB Bridge", "PL2302 USB-USB Bridge"), + T_DATA("PL25A1 Host-Host Bridge", "PL25A1 Host-Host Bridge"), + T_DATA("Platform Controller Hub EG20T Controller Area Network (CAN) Controller", "EG20T"), + T_DATA("PN672TX 10/100 Ethernet", "PN672TX"), + T_DATA("Pocket Ethernet [klsi]", "Pocket"), + T_DATA("Prism GT 802.11b/g Adapter", "Prism GT"), + T_DATA("PRO/100 VE Network Connection", "PRO/100 VE"), + T_DATA("PRO/100 VM Network Connection", "PRO/100 VM"), + T_DATA("PRO/Wireless 2200BG [Calexico2] Network Connection", "PRO/Wireless 2200BG"), + T_DATA("PRO/Wireless 2915ABG [Calexico2] Network Connection", "PRO/Wireless 2915ABG"), + T_DATA("PRO/Wireless 3945ABG [Golan] Network Connection", "PRO/Wireless 3945ABG"), + T_DATA("PRO/Wireless 4965 AG or AGN [Kedron] Network Connection", + "PRO/Wireless 4965 AG or AGN"), + T_DATA("PRO/Wireless 5100 AGN [Shiloh] Network Connection", "PRO/Wireless 5100 AGN"), + T_DATA("PRO/Wireless 5350 AGN [Echo Peak] Network Connection", "PRO/Wireless 5350 AGN"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter (Dell Latitude D800)", + "PRO/Wireless 2100 3B Mini"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter (MIM2000/Centrino)", + "PRO/Wireless 2100 3B Mini"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter", "PRO/Wireless 2100 3B Mini"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter (Samsung X10/P30 integrated WLAN)", + "PRO/Wireless 2100 3B Mini"), + T_DATA("PRO/Wireless LAN 2100 3B Mini PCI Adapter (Toshiba Satellite M10)", + "PRO/Wireless 2100 3B Mini"), + T_DATA("Psion Gold Port Ethernet", "Psion Gold Port"), + T_DATA("PTA01 Wireless Adapter", "PTA01"), + T_DATA("QCA6164 802.11ac Wireless Network Adapter", "QCA6164"), + T_DATA("QCA6174 802.11ac Wireless Network Adapter", "QCA6174"), + T_DATA("QCA8171 Gigabit Ethernet", "QCA8171"), + T_DATA("QCA8172 Fast Ethernet", "QCA8172"), + T_DATA("QCA9377 802.11ac Wireless Network Adapter", "QCA9377"), + T_DATA("QCA9565 / AR9565 Wireless Network Adapter", "QCA9565 / AR9565"), + T_DATA("QCA986x/988x 802.11ac Wireless Network Adapter", "QCA986x/988x"), + T_DATA("QCA9887 802.11ac Wireless Network Adapter", "QCA9887"), + T_DATA("QCA9980/9990 802.11ac Wireless Network Adapter", "QCA9980/9990"), + T_DATA("Qualcomm HSUSB Device", "Qualcomm HSUSB"), + T_DATA("Quectel UC20", "Quectel UC20"), + T_DATA("QuickWLAN 802.11bg", "QuickWLAN"), + T_DATA("R6040 MAC Controller", "R6040"), + T_DATA("Ralink RT2770/2720 802.11b/g/n Wireless LAN Mini-USB Device", "Ralink RT2770/2720"), + T_DATA("Ralink RT3070 802.11b/g/n Wireless Lan USB Device", "Ralink RT3070"), + T_DATA("ReadyLink 2000", "ReadyLink 2000"), + T_DATA("Realtek RTL8187 Wireless 802.11g 54Mbps Network Adapter", "Realtek RTL8187"), + T_DATA("RIO 10/100 Ethernet [eri]", "RIO"), + T_DATA("RL100-ATX 10/100", "RL100-ATX"), + T_DATA("RL100TX Fast Ethernet", "RL100TX"), + T_DATA("ROL/F-100 Fast Ethernet Adapter with ROL", "ROL/F-100 with ROL"), + T_DATA("RT2070 Wireless Adapter", "RT2070"), + T_DATA("RT2500USB Wireless Adapter", "RT2500USB"), + T_DATA("RT2500 Wireless 802.11bg", "RT2500"), + T_DATA("RT2501/RT2573 Wireless Adapter", "RT2501/RT2573"), + T_DATA("RT2501USB Wireless Adapter", "RT2501USB"), + T_DATA("RT2561/RT61 802.11g PCI", "RT2561/RT61"), + T_DATA("RT2561/RT61 rev B 802.11g", "RT2561/RT61"), + T_DATA("RT2570", "RT2570"), + T_DATA("RT2570 Wireless Adapter", "RT2570"), + T_DATA("RT2573", "RT2573"), + T_DATA("RT2600 802.11 MIMO", "RT2600 MIMO"), + T_DATA("RT2601/RT2671 Wireless Adapter", "RT2601/RT2671"), + T_DATA("RT2760 Wireless 802.11n 1T/2R", "RT2760 1T/2R"), + T_DATA("RT2770 Wireless Adapter", "RT2770"), + T_DATA("RT2790 Wireless 802.11n 1T/2R PCIe", "RT2790 1T/2R"), + T_DATA("RT2800 802.11n PCI", "RT2800"), + T_DATA("RT2870/RT3070 Wireless Adapter", "RT2870/RT3070"), + T_DATA("RT2870 Wireless Adapter", "RT2870"), + T_DATA("RT2890 Wireless 802.11n PCIe", "RT2890"), + T_DATA("RT3060 Wireless 802.11n 1T/1R", "RT3060 1T/1R"), + T_DATA("RT3062 Wireless 802.11n 2T/2R", "RT3062 2T/2R"), + T_DATA("RT3071 Wireless Adapter", "RT3071"), + T_DATA("RT3072 Wireless Adapter", "RT3072"), + T_DATA("RT3090 Wireless 802.11n 1T/1R PCIe", "RT3090 1T/1R"), + T_DATA("RT3091 Wireless 802.11n 1T/2R PCIe", "RT3091 1T/2R"), + T_DATA("RT3092 Wireless 802.11n 2T/2R PCIe", "RT3092 2T/2R"), + T_DATA("RT3290 Wireless 802.11n 1T/1R PCIe", "RT3290 1T/1R"), + T_DATA("RT3370 Wireless Adapter", "RT3370"), + T_DATA("RT3572 Wireless Adapter", "RT3572"), + T_DATA("RT3573 Wireless Adapter", "RT3573"), + T_DATA("RT3592 PCIe Wireless Network Adapter", "RT3592"), + T_DATA("RT3592 Wireless 802.11abgn 2T/2R PCIe", "RT3592 2T/2R"), + T_DATA("RT5360 Wireless 802.11n 1T/1R", "RT5360 1T/1R"), + T_DATA("RT5362 PCI 802.11n Wireless Network Adapter", "RT5362"), + T_DATA("RT5370 Wireless Adapter", "RT5370"), + T_DATA("RT5372 Wireless Adapter", "RT5372"), + T_DATA("RT5390 [802.11 b/g/n 1T1R G-band PCI Express Single Chip]", "RT5390"), + T_DATA("RT5390R 802.11bgn PCIe Wireless Network Adapter", "RT5390R"), + T_DATA("RT5390 Wireless 802.11n 1T/1R PCIe", "RT5390 1T/1R"), + T_DATA("RT5392 PCIe Wireless Network Adapter", "RT5392"), + T_DATA("RT5572 Wireless Adapter", "RT5572"), + T_DATA("RT8139 (B/C) Cardbus Fast Ethernet Adapter", "RT8139"), + T_DATA("RTL-8029(AS)", "RTL-8029"), + T_DATA("RTL-8100/8101L/8139 PCI Fast Ethernet Adapter", "RTL-8100/8101L/8139"), + T_DATA("RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller", "RTL8101/2/6E"), + T_DATA("RTL-8110SC/8169SC Gigabit Ethernet", "RTL-8110SC/8169SC"), + T_DATA("RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller", "RTL8111/8168/8411"), + T_DATA("RTL-8129", "RTL-8129"), + T_DATA("RTL8139D [Realtek] PCI 10/100BaseTX ethernet adaptor", "RTL8139D"), + T_DATA("RTL8139 Ethernet", "RTL8139"), + T_DATA("RTL8139 [FE2000VX] CardBus Fast Ethernet Attached Port Adapter", "RTL8139"), + T_DATA("RTL8150 Fast Ethernet Adapter", "RTL8150"), + T_DATA("RTL8151", "RTL8151"), + T_DATA("RTL8152 Fast Ethernet Adapter", "RTL8152"), + T_DATA("RTL8153 Gigabit Ethernet Adapter", "RTL8153"), + T_DATA("RTL8169 PCI Gigabit Ethernet Controller", "RTL8169"), + T_DATA("RTL8180L 802.11b MAC", "RTL8180L"), + T_DATA("RTL-8185 IEEE 802.11a/b/g Wireless LAN Controller", "RTL-8185"), + T_DATA("RTL8187B Wireless 802.11g 54Mbps Network Adapter", "RTL8187B"), + T_DATA("RTL8187B Wireless Adapter", "RTL8187B"), + T_DATA("RTL8187SE Wireless LAN Controller", "RTL8187SE"), + T_DATA("RTL8187 Wireless Adapter", "RTL8187"), + T_DATA("RTL8188CE 802.11b/g/n WiFi Adapter", "RTL8188CE"), + T_DATA("RTL8188CUS 802.11n WLAN Adapter", "RTL8188CUS"), + T_DATA("RTL8188EE Wireless Network Adapter", "RTL8188EE"), + T_DATA("RTL8188RU 802.11n WLAN Adapter", "RTL8188RU"), + T_DATA("RTL8191CE PCIe Wireless Network Adapter", "RTL8191CE"), + T_DATA("RTL8191SEvA Wireless LAN Controller", "RTL8191SEvA"), + T_DATA("RTL8191SEvB Wireless LAN Controller", "RTL8191SEvB"), + T_DATA("RTL8192CE PCIe Wireless Network Adapter", "RTL8192CE"), + T_DATA("RTL8192CU 802.11n WLAN Adapter", "RTL8192CU"), + T_DATA("RTL8192DE Wireless LAN Controller", "RTL8192DE"), + T_DATA("RTL8192EE PCIe Wireless Network Adapter", "RTL8192EE"), + T_DATA("RTL8192E/RTL8192SE Wireless LAN Controller", "RTL8192E/RTL8192SE"), + T_DATA("RTL8192EU 802.11b/g/n WLAN Adapter", "RTL8192EU"), + T_DATA("RTL8192SE Wireless LAN Controller", "RTL8192SE"), + T_DATA("RTL81xx Fast Ethernet", "RTL81xx"), + T_DATA("RTL81xx RealTek Ethernet", "RTL81xx RealTek"), + T_DATA("RTL8723AE PCIe Wireless Network Adapter", "RTL8723AE"), + T_DATA("RTL8723AU 802.11n WLAN Adapter", "RTL8723AU"), + T_DATA("RTL8723BE PCIe Wireless Network Adapter", "RTL8723BE"), + T_DATA("RTL8812AE 802.11ac PCIe Wireless Network Adapter", "RTL8812AE"), + T_DATA("RTL8821AE 802.11ac PCIe Wireless Network Adapter", "RTL8821AE"), + T_DATA("S310-CR 10GbE Single Port Adapter", "S310-CR Single Port"), + T_DATA("S320-LP-CR 10GbE Dual Port Adapter", "S320-LP-CR"), + T_DATA("Samsung Gobi 2000 Wireless Modem", "Samsung Gobi 2000"), + T_DATA("SC92031 PCI Fast Ethernet Adapter", "SC92031"), + T_DATA("SD8688 WLAN", "SD8688"), + T_DATA("SD8786 WLAN", "SD8786"), + T_DATA("SD8787 WLAN", "SD8787"), + T_DATA("SD8797 WLAN", "SD8797"), + T_DATA("SD8897 WLAN", "SD8897"), + T_DATA("SFC4000 rev A net [Solarstorm]", "SFC4000"), + T_DATA("SFC4000 rev B [Solarstorm]", "SFC4000"), + T_DATA("SFC9020 10G Ethernet Controller", "SFC9020"), + T_DATA("SFC9120 10G Ethernet Controller", "SFC9120"), + T_DATA("SFC9120 10G Ethernet Controller (Virtual Function)", "SFC9120"), + T_DATA("SFC9140 10/40G Ethernet Controller", "SFC9140"), + T_DATA("SFC9140 10/40G Ethernet Controller (Virtual Function)", "SFC9140"), + T_DATA("SFC9220 10/40G Ethernet Controller", "SFC9220"), + T_DATA("SFC9220 10/40G Ethernet Controller (Virtual Function)", "SFC9220"), + T_DATA("SFL9021 10GBASE-T Ethernet Controller", "SFL9021"), + T_DATA("Shasta (Sun GEM)", "Shasta"), + T_DATA("Siemens S30853-S1016-R107 802.11g Wireless Adapter [Intersil ISL3886]", + "Siemens S30853-S1016-R107"), + T_DATA("Siemens S30853-S1031-R351 802.11g Wireless Adapter [Atheros AR5523]", + "Siemens S30853-S1031-R351"), + T_DATA("Siemens S30853-S1038-R351 802.11g Wireless Adapter [Atheros AR5523]", + "Siemens S30853-S1038-R351"), + T_DATA("Siemens S30863-S1016-R107-2 802.11g Wireless Adapter [Intersil ISL3887]", + "Siemens S30863-S1016-R107-2"), + T_DATA("Siemens SpeedStream 100MBps Ethernet", "Siemens SpeedStream"), + T_DATA("Sierra Wireless Gobi 3000 Modem device (MC8355)", "Sierra Gobi 3000"), + T_DATA("SIMCom SIM5218 modem", "SIMCom SIM5218"), + T_DATA("SiS7016 PCI Fast Ethernet Adapter", "SiS7016"), + T_DATA("SiS900 PCI Fast Ethernet", "SiS900"), + T_DATA("SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC", + "SK-9871 ZX/SC"), + T_DATA("SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link)", "SK-9872"), + T_DATA("SK-9Dxx Gigabit Ethernet Adapter", "SK-9Dxx"), + T_DATA("SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45", "SK-9E21D"), + T_DATA("SK-9E21M 10/100/1000Base-T Adapter", "SK-9E21M"), + T_DATA("SK-9Mxx Gigabit Ethernet Adapter", "SK-9Mxx"), + T_DATA("SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45", "SK-9S21"), + T_DATA("smartNIC 2 PnP Ethernet", "smartNIC 2"), + T_DATA("smartNIC Ethernet [catc]", "smartNIC"), + T_DATA("SMC2-1211TX", "SMC2-1211TX"), + T_DATA("SMC2862W-G v1 EZ Connect 802.11g Adapter [Intersil ISL3886]", + "SMC2862W-G EZ Connect"), + T_DATA("SMC2862W-G v2 EZ Connect 802.11g Adapter [Intersil ISL3887]", + "SMC2862W-G EZ Connect"), + T_DATA("SMC2862W-G v3 EZ Connect 802.11g Adapter [Intersil ISL3887]", + "SMC2862W-G EZ Connect"), + T_DATA("SMC SMCWUSB-N 802.11bgn 2x2:2 Wireless Adapter [Ralink RT2870]", + "SMC SMCWUSB-N 2x2:2"), + T_DATA("SMCWUSB-G 802.11bg", "SMCWUSB-G"), + T_DATA("SMCWUSBS-N2 EZ Connect N Wireless Adapter [Ralink RT2870]", + "SMCWUSBS-N2 EZ Connect"), + T_DATA("SMCWUSBS-N3 EZ Connect N Wireless Adapter [Ralink RT3070]", + "SMCWUSBS-N3 EZ Connect"), + T_DATA("SMCWUSBS-N EZ Connect N Draft 11n Wireless Adapter [Ralink RT2870]", + "SMCWUSBS-N EZ Connect"), + T_DATA("SMCWUSBT-G (no firmware)", "SMCWUSBT-G"), + T_DATA("SMCWUSBT-G", "SMCWUSBT-G"), + T_DATA("SMSC9512/9514 Fast Ethernet Adapter", "SMSC9512/9514"), + T_DATA("SNU5600 802.11bg", "SNU5600"), + T_DATA("SoftGate 802.11 Adapter", "SoftGate"), + T_DATA("Sony 10Mbps Ethernet [pegasus]", "Sony"), + T_DATA("Sony Gobi 2000 Wireless Modem", "Sony Gobi 2000"), + T_DATA("Sony UWA-BR100 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", + "Sony UWA-BR100"), + T_DATA("SparkLAN WL-682 802.11bg Wireless Adapter [Intersil ISL3887]", "SparkLAN WL-682"), + T_DATA("Speedport W 102 Stick IEEE 802.11n USB 2.0 Adapter", "Speedport W 102 Stick"), + T_DATA("SpeedStream 10/100 Ethernet [pegasus]", "SpeedStream"), + T_DATA("SpeedTouch 120g 802.11g Wireless Adapter [Intersil ISL3886]", "SpeedTouch 120g"), + T_DATA("SpeedTouch 121g Wireless Dongle", "SpeedTouch 121g Dongle"), + T_DATA("Sphairon Homelink 1202 802.11n Wireless Adapter [Atheros AR9170]", + "Sphairon Homelink 1202"), + T_DATA("ST201 Sundance Ethernet", "ST201 Sundance"), + T_DATA("ST268", "ST268"), + T_DATA("SURECOM EP-320X-S 100/10M Ethernet PCI Adapter", "SURECOM EP-320X-S"), + T_DATA("T210 Protocol Engine", "T210 Protocol Engine"), + T_DATA("T302 1GbE Dual Port Adapter", "T302"), + T_DATA("T310 10GbE Single Port Adapter", "T310 Single Port"), + T_DATA("T320 10GbE Dual Port Adapter", "T320"), + T_DATA("T404-BT Unified Wire Ethernet Controller", "T404-BT"), + T_DATA("T404-BT Unified Wire Ethernet Controller [VF]", "T404-BT"), + T_DATA("T420-4082 Unified Wire Ethernet Controller", "T420-4082"), + T_DATA("T420-4082 Unified Wire Ethernet Controller [VF]", "T420-4082"), + T_DATA("T420-4085 SFP+ Unified Wire Ethernet Controller", "T420-4085 SFP+"), + T_DATA("T420-4085 SFP+ Unified Wire Ethernet Controller [VF]", "T420-4085 SFP+"), + T_DATA("T420-BCH Unified Wire Ethernet Controller", "T420-BCH"), + T_DATA("T420-BCH Unified Wire Ethernet Controller [VF]", "T420-BCH"), + T_DATA("T420-BT Unified Wire Ethernet Controller", "T420-BT"), + T_DATA("T420-BT Unified Wire Ethernet Controller [VF]", "T420-BT"), + T_DATA("T420-CR Unified Wire Ethernet Controller", "T420-CR"), + T_DATA("T420-CR Unified Wire Ethernet Controller [VF]", "T420-CR"), + T_DATA("T420-CX Unified Wire Ethernet Controller", "T420-CX"), + T_DATA("T420-CX Unified Wire Ethernet Controller [VF]", "T420-CX"), + T_DATA("T420-SO Unified Wire Ethernet Controller", "T420-SO"), + T_DATA("T420-SO Unified Wire Ethernet Controller [VF]", "T420-SO"), + T_DATA("T420X-4083 Unified Wire Ethernet Controller", "T420X-4083"), + T_DATA("T420X-4083 Unified Wire Ethernet Controller [VF]", "T420X-4083"), + T_DATA("T422-CR Unified Wire Ethernet Controller", "T422-CR"), + T_DATA("T422-CR Unified Wire Ethernet Controller [VF]", "T422-CR"), + T_DATA("T440-4084 Unified Wire Ethernet Controller", "T440-4084"), + T_DATA("T440-4084 Unified Wire Ethernet Controller [VF]", "T440-4084"), + T_DATA("T440-4086 10Gbase-T Unified Wire Ethernet Controller", "T440-4086"), + T_DATA("T440-4086 10Gbase-T Unified Wire Ethernet Controller [VF]", "T440-4086"), + T_DATA("T440-4088 Unified Wire Ethernet Controller", "T440-4088"), + T_DATA("T440-4088 Unified Wire Ethernet Controller [VF]", "T440-4088"), + T_DATA("T440-BCH Unified Wire Ethernet Controller", "T440-BCH"), + T_DATA("T440-BCH Unified Wire Ethernet Controller [VF]", "T440-BCH"), + T_DATA("T440-CH Unified Wire Ethernet Controller", "T440-CH"), + T_DATA("T440-CH Unified Wire Ethernet Controller [VF]", "T440-CH"), + T_DATA("T440-CR Unified Wire Ethernet Controller", "T440-CR"), + T_DATA("T440-CR Unified Wire Ethernet Controller [VF]", "T440-CR"), + T_DATA("T440F-4081 T440-FCoE Unified Wire Ethernet Controller", "T440F-4081 T440-FCoE"), + T_DATA("T440F-4081 T440-FCoE Unified Wire Ethernet Controller [VF]", + "T440F-4081 T440-FCoE"), + T_DATA("T440-LP-CR Unified Wire Ethernet Controller", "T440-LP-CR"), + T_DATA("T440-LP-CR Unified Wire Ethernet Controller [VF]", "T440-LP-CR"), + T_DATA("T440T-4087 Unified Wire Ethernet Controller", "T440T-4087"), + T_DATA("T440T-4087 Unified Wire Ethernet Controller [VF]", "T440T-4087"), + T_DATA("T480-4080 T480 Unified Wire Ethernet Controller", "T480-4080 T480"), + T_DATA("T480-4080 T480 Unified Wire Ethernet Controller [VF]", "T480-4080 T480"), + T_DATA("T480 Unified Wire Ethernet Controller", "T480"), + T_DATA("T480 Unified Wire Ethernet Controller [VF]", "T480"), + T_DATA("T502-BT Unified Wire Ethernet Controller", "T502-BT"), + T_DATA("T502-BT Unified Wire Ethernet Controller [VF]", "T502-BT"), + T_DATA("T504-5082 Unified Wire Ethernet Controller", "T504-5082"), + T_DATA("T504-5082 Unified Wire Ethernet Controller [VF]", "T504-5082"), + T_DATA("T504-BT Unified Wire Ethernet Controller", "T504-BT"), + T_DATA("T504-BT Unified Wire Ethernet Controller [VF]", "T504-BT"), + T_DATA("T520-5089 Unified Wire Ethernet Controller", "T520-5089"), + T_DATA("T520-5089 Unified Wire Ethernet Controller [VF]", "T520-5089"), + T_DATA("T520-5092 Unified Wire Ethernet Controller", "T520-5092"), + T_DATA("T520-5092 Unified Wire Ethernet Controller [VF]", "T520-5092"), + T_DATA("T520-5097 Unified Wire Ethernet Controller", "T520-5097"), + T_DATA("T520-5097 Unified Wire Ethernet Controller [VF]", "T520-5097"), + T_DATA("T520-509A Unified Wire Ethernet Controller", "T520-509A"), + T_DATA("T520-509A Unified Wire Ethernet Controller [VF]", "T520-509A"), + T_DATA("T520-509C Unified Wire Ethernet Controller", "T520-509C"), + T_DATA("T520-509C Unified Wire Ethernet Controller [VF]", "T520-509C"), + T_DATA("T520-509E Unified Wire Ethernet Controller", "T520-509E"), + T_DATA("T520-509E Unified Wire Ethernet Controller [VF]", "T520-509E"), + T_DATA("T520-BCH Unified Wire Ethernet Controller", "T520-BCH"), + T_DATA("T520-BCH Unified Wire Ethernet Controller [VF]", "T520-BCH"), + T_DATA("T520-BT Unified Wire Ethernet Controller", "T520-BT"), + T_DATA("T520-BT Unified Wire Ethernet Controller [VF]", "T520-BT"), + T_DATA("T520-CR Unified Wire Ethernet Controller", "T520-CR"), + T_DATA("T520-CR Unified Wire Ethernet Controller [VF]", "T520-CR"), + T_DATA("T520-CX Unified Wire Ethernet Controller", "T520-CX"), + T_DATA("T520-CX Unified Wire Ethernet Controller [VF]", "T520-CX"), + T_DATA("T520-LL-CR Unified Wire Ethernet Controller", "T520-LL-CR"), + T_DATA("T520-LL-CR Unified Wire Ethernet Controller [VF]", "T520-LL-CR"), + T_DATA("T520-OCP-SO Unified Wire Ethernet Controller", "T520-OCP-SO"), + T_DATA("T520-OCP-SO Unified Wire Ethernet Controller [VF]", "T520-OCP-SO"), + T_DATA("T520-SO Unified Wire Ethernet Controller", "T520-SO"), + T_DATA("T520-SO Unified Wire Ethernet Controller [VF]", "T520-SO"), + T_DATA("T522-5091 Unified Wire Ethernet Controller", "T522-5091"), + T_DATA("T522-5091 Unified Wire Ethernet Controller [VF]", "T522-5091"), + T_DATA("T522-CR Unified Wire Ethernet Controller", "T522-CR"), + T_DATA("T522-CR Unified Wire Ethernet Controller [VF]", "T522-CR"), + T_DATA("T540-5080 Unified Wire Ethernet Controller", "T540-5080"), + T_DATA("T540-5080 Unified Wire Ethernet Controller [VF]", "T540-5080"), + T_DATA("T540-5081 Unified Wire Ethernet Controller", "T540-5081"), + T_DATA("T540-5081 Unified Wire Ethernet Controller [VF]", "T540-5081"), + T_DATA("T540-5083 Unified Wire Ethernet Controller", "T540-5083"), + T_DATA("T540-5083 Unified Wire Ethernet Controller [VF]", "T540-5083"), + T_DATA("T540-5084 Unified Wire Ethernet Controller", "T540-5084"), + T_DATA("T540-5084 Unified Wire Ethernet Controller [VF]", "T540-5084"), + T_DATA("T540-5090 Unified Wire Ethernet Controller", "T540-5090"), + T_DATA("T540-5090 Unified Wire Ethernet Controller [VF]", "T540-5090"), + T_DATA("T540-5094 Unified Wire Ethernet Controller", "T540-5094"), + T_DATA("T540-5094 Unified Wire Ethernet Controller [VF]", "T540-5094"), + T_DATA("T540-5095 Unified Wire Ethernet Controller", "T540-5095"), + T_DATA("T540-5095 Unified Wire Ethernet Controller [VF]", "T540-5095"), + T_DATA("T540-509B Unified Wire Ethernet Controller", "T540-509B"), + T_DATA("T540-509B Unified Wire Ethernet Controller [VF]", "T540-509B"), + T_DATA("T540-509D Unified Wire Ethernet Controller", "T540-509D"), + T_DATA("T540-509D Unified Wire Ethernet Controller [VF]", "T540-509D"), + T_DATA("T540-509F Unified Wire Ethernet Controller", "T540-509F"), + T_DATA("T540-509F Unified Wire Ethernet Controller [VF]", "T540-509F"), + T_DATA("T540-50A0 Unified Wire Ethernet Controller", "T540-50A0"), + T_DATA("T540-50A0 Unified Wire Ethernet Controller [VF]", "T540-50A0"), + T_DATA("T540-50A1 Unified Wire Ethernet Controller", "T540-50A1"), + T_DATA("T540-50A1 Unified Wire Ethernet Controller [VF]", "T540-50A1"), + T_DATA("T540-BCH Unified Wire Ethernet Controller", "T540-BCH"), + T_DATA("T540-BCH Unified Wire Ethernet Controller [VF]", "T540-BCH"), + T_DATA("T540-BT Unified Wire Ethernet Controller", "T540-BT"), + T_DATA("T540-BT Unified Wire Ethernet Controller [VF]", "T540-BT"), + T_DATA("T540-CH Unified Wire Ethernet Controller", "T540-CH"), + T_DATA("T540-CH Unified Wire Ethernet Controller [VF]", "T540-CH"), + T_DATA("T540-CR Unified Wire Ethernet Controller", "T540-CR"), + T_DATA("T540-CR Unified Wire Ethernet Controller [VF]", "T540-CR"), + T_DATA("T540-LP-CR Unified Wire Ethernet Controller", "T540-LP-CR"), + T_DATA("T540-LP-CR Unified Wire Ethernet Controller [VF]", "T540-LP-CR"), + T_DATA("T560-CR Unified Wire Ethernet Controller", "T560-CR"), + T_DATA("T560-CR Unified Wire Ethernet Controller [VF]", "T560-CR"), + T_DATA("T570-5088 Unified Wire Ethernet Controller", "T570-5088"), + T_DATA("T570-5088 Unified Wire Ethernet Controller [VF]", "T570-5088"), + T_DATA("T580-5085 Unified Wire Ethernet Controller", "T580-5085"), + T_DATA("T580-5085 Unified Wire Ethernet Controller [VF]", "T580-5085"), + T_DATA("T580-5086 Unified Wire Ethernet Controller", "T580-5086"), + T_DATA("T580-5086 Unified Wire Ethernet Controller [VF]", "T580-5086"), + T_DATA("T580-5087 Unified Wire Ethernet Controller", "T580-5087"), + T_DATA("T580-5087 Unified Wire Ethernet Controller [VF]", "T580-5087"), + T_DATA("T580-5093 Unified Wire Ethernet Controller", "T580-5093"), + T_DATA("T580-5093 Unified Wire Ethernet Controller [VF]", "T580-5093"), + T_DATA("T580-5096 Unified Wire Ethernet Controller", "T580-5096"), + T_DATA("T580-5096 Unified Wire Ethernet Controller [VF]", "T580-5096"), + T_DATA("T580-5098 Unified Wire Ethernet Controller", "T580-5098"), + T_DATA("T580-5098 Unified Wire Ethernet Controller [VF]", "T580-5098"), + T_DATA("T580-5099 Unified Wire Ethernet Controller", "T580-5099"), + T_DATA("T580-5099 Unified Wire Ethernet Controller [VF]", "T580-5099"), + T_DATA("T580-50A2 Unified Wire Ethernet Controller", "T580-50A2"), + T_DATA("T580-50A2 Unified Wire Ethernet Controller [VF]", "T580-50A2"), + T_DATA("T580-CHR Unified Wire Ethernet Controller", "T580-CHR"), + T_DATA("T580-CHR Unified Wire Ethernet Controller [VF]", "T580-CHR"), + T_DATA("T580-CR Unified Wire Ethernet Controller", "T580-CR"), + T_DATA("T580-CR Unified Wire Ethernet Controller [VF]", "T580-CR"), + T_DATA("T580-LP-CR Unified Wire Ethernet Controller", "T580-LP-CR"), + T_DATA("T580-LP-CR Unified Wire Ethernet Controller [VF]", "T580-LP-CR"), + T_DATA("T580-OCP-SO Unified Wire Ethernet Controller", "T580-OCP-SO"), + T_DATA("T580-OCP-SO Unified Wire Ethernet Controller [VF]", "T580-OCP-SO"), + T_DATA("T580-SO-CR Unified Wire Ethernet Controller", "T580-SO-CR"), + T_DATA("T580-SO-CR Unified Wire Ethernet Controller [VF]", "T580-SO-CR"), + T_DATA("T61100-OCP-SO Unified Wire Ethernet Controller", "T61100-OCP-SO"), + T_DATA("T61100-OCP-SO Unified Wire Ethernet Controller [VF]", "T61100-OCP-SO"), + T_DATA("T6201-BT Unified Wire Ethernet Controller", "T6201-BT"), + T_DATA("T6201-BT Unified Wire Ethernet Controller [VF]", "T6201-BT"), + T_DATA("T62100-6081 Unified Wire Ethernet Controller", "T62100-6081"), + T_DATA("T62100-6081 Unified Wire Ethernet Controller [VF]", "T62100-6081"), + T_DATA("T62100-6083 Unified Wire Ethernet Controller", "T62100-6083"), + T_DATA("T62100-6083 Unified Wire Ethernet Controller [VF]", "T62100-6083"), + T_DATA("T62100-CR Unified Wire Ethernet Controller", "T62100-CR"), + T_DATA("T62100-CR Unified Wire Ethernet Controller [VF]", "T62100-CR"), + T_DATA("T62100-LP-CR Unified Wire Ethernet Controller", "T62100-LP-CR"), + T_DATA("T62100-LP-CR Unified Wire Ethernet Controller [VF]", "T62100-LP-CR"), + T_DATA("T62100-OCP-SO Unified Wire Ethernet Controller", "T62100-OCP-SO"), + T_DATA("T62100-OCP-SO Unified Wire Ethernet Controller [VF]", "T62100-OCP-SO"), + T_DATA("T62100-SO-CR Unified Wire Ethernet Controller", "T62100-SO-CR"), + T_DATA("T62100-SO-CR Unified Wire Ethernet Controller [VF]", "T62100-SO-CR"), + T_DATA("T6210-BT Unified Wire Ethernet Controller", "T6210-BT"), + T_DATA("T6210-BT Unified Wire Ethernet Controller [VF]", "T6210-BT"), + T_DATA("T6225-6080 Unified Wire Ethernet Controller", "T6225-6080"), + T_DATA("T6225-6080 Unified Wire Ethernet Controller [VF]", "T6225-6080"), + T_DATA("T6225-6082 Unified Wire Ethernet Controller", "T6225-6082"), + T_DATA("T6225-6082 Unified Wire Ethernet Controller [VF]", "T6225-6082"), + T_DATA("T6225-CR Unified Wire Ethernet Controller", "T6225-CR"), + T_DATA("T6225-CR Unified Wire Ethernet Controller [VF]", "T6225-CR"), + T_DATA("T6225-LL-CR Unified Wire Ethernet Controller", "T6225-LL-CR"), + T_DATA("T6225-LL-CR Unified Wire Ethernet Controller [VF]", "T6225-LL-CR"), + T_DATA("T6225-OCP-SO Unified Wire Ethernet Controller", "T6225-OCP-SO"), + T_DATA("T6225-OCP-SO Unified Wire Ethernet Controller [VF]", "T6225-OCP-SO"), + T_DATA("T6225-SO-CR Unified Wire Ethernet Controller", "T6225-SO-CR"), + T_DATA("T6225-SO-CR Unified Wire Ethernet Controller [VF]", "T6225-SO-CR"), + T_DATA("T64100-6084 Unified Wire Ethernet Controller", "T64100-6084"), + T_DATA("T64100-6084 Unified Wire Ethernet Controller [VF]", "T64100-6084"), + T_DATA("T6425-CR Unified Wire Ethernet Controller", "T6425-CR"), + T_DATA("T6425-CR Unified Wire Ethernet Controller [VF]", "T6425-CR"), + T_DATA("T6425-SO-CR Unified Wire Ethernet Controller", "T6425-SO-CR"), + T_DATA("T6425-SO-CR Unified Wire Ethernet Controller [VF]", "T6425-SO-CR"), + T_DATA("TalkTalk SNU5630NS/05 802.11bg", "TalkTalk SNU5630NS/05"), + T_DATA("TC902x Gigabit Ethernet", "TC902x"), + T_DATA("T-Com Sinus 154 data II [Intersil ISL3887]", "T-Com Sinus 154 data II"), + T_DATA("TEW-429UB 802.11bg", "TEW-429UB"), + T_DATA("TEW-429UB C1 802.11bg", "TEW-429UB C1"), + T_DATA("TEW-444UB EU (no firmware)", "TEW-444UB EU"), + T_DATA("TEW-444UB EU [TRENDnet]", "TEW-444UB EU"), + T_DATA("TEW-509UB A1 802.11abg Wireless Adapter [ZyDAS ZD1211]", "TEW-509UB A1"), + T_DATA("TEW-645UB 802.11bgn 1x2:2 Wireless Adapter [Ralink RT2770]", "TEW-645UB"), + T_DATA("TEW-648UBM 802.11n 150Mbps Micro Wireless N Adapter [Realtek RTL8188CUS]", + "TEW-648UBM"), + T_DATA("TG54USB 802.11bg", "TG54USB"), + T_DATA("Thomson TG121N [Atheros AR9001U-(2)NG]", "Thomson TG121N"), + T_DATA("Top Global Gobi 2000 Wireless Modem", "Top Global Gobi 2000"), + T_DATA("TP-Link TL-WN322G v3 / TL-WN422G v2 802.11g [Atheros AR9271]", + "TP-Link TL-WN322G / TL-WN422G"), + T_DATA("TP-Link TL-WN821N v2 / TL-WN822N v1 802.11n [Atheros AR9170]", + "TP-Link TL-WN821N / TL-WN822N"), + T_DATA("TP-Link TL-WN821N v3 / TL-WN822N v2 802.11n [Atheros AR7010+AR9287]", + "TP-Link TL-WN821N / TL-WN822N"), + T_DATA("TrueMobile 1300 802.11g Wireless Adapter [Intersil ISL3880]", "TrueMobile 1300"), + T_DATA("T-Sinus 154data", "T-Sinus 154data"), + T_DATA("TTP-Monitoring Card V2.0", "TTP-Monitoring"), + T_DATA("Turbolink UB801RE Wireless 802.11g 54Mbps Network Adapter [RTL8187]", + "Turbolink UB801RE"), + T_DATA("Turbolink UB801R WLAN Adapter", "Turbolink UB801R"), + T_DATA("U2E", "U2E"), + T_DATA("U5 802.11g Adapter", "U5"), + T_DATA("UB81 802.11bgn", "UB81"), + T_DATA("UB82 802.11abgn", "UB82"), + T_DATA("Ubiquiti WiFiStation 802.11n [Atheros AR9271]", "Ubiquiti WiFiStation"), + T_DATA("Ubiquiti WiFiStationEXT 802.11n [Atheros AR9271]", "Ubiquiti WiFiStationEXT"), + T_DATA("UBS-10BT Ethernet [klsi]", "UBS-10BT"), + T_DATA("UBS-10BT Ethernet", "UBS-10BT"), + T_DATA("UC-110T 100Mbps Ethernet [pegasus]", "UC-110T"), + T_DATA("UC-210T Ethernet", "UC-210T"), + T_DATA("UF100 Ethernet [pegasus2]", "UF100"), + T_DATA("UF200 Ethernet", "UF200"), + T_DATA("ULi 1689,1573 integrated ethernet.", "ULi 1689 1573"), + T_DATA("Ultimate N WiFi Link 5300", "Ultimate N 5300"), + T_DATA("un2400 Gobi Wireless Modem", "un2400 Gobi"), + T_DATA("UniNorth 2 GMAC (Sun GEM)", "UniNorth 2 GMAC"), + T_DATA("UniNorth GMAC (Sun GEM)", "UniNorth GMAC"), + T_DATA("UniNorth/Pangea GMAC (Sun GEM)", "UniNorth/Pangea GMAC"), + T_DATA("UR054g 802.11g Wireless Adapter [Intersil ISL3887]", "UR054g"), + T_DATA("UR055G 802.11bg", "UR055G"), + T_DATA("USB1000 Gigabit Notebook Adapter", "USB1000"), + T_DATA("USB-100N Ethernet [pegasus]", "USB-100N"), + T_DATA("USB100TX Ethernet [pegasus]", "USB100TX"), + T_DATA("USB100TX HomePNA Ethernet [pegasus]", "USB100TX HomePNA"), + T_DATA("USB10TX Ethernet [pegasus]", "USB10TX"), + T_DATA("USB10TX", "USB10TX"), + T_DATA("USB 1.1 10/100M Fast Ethernet Adapter", NULL), + T_DATA("USB200M 100baseTX Adapter", "USB200M 100baseTX"), + T_DATA("USB200M 10/100 Ethernet Adapter", "USB200M"), + T_DATA("USB 2.0 Ethernet", NULL), + T_DATA("USB2AR Ethernet", "USB2AR"), + T_DATA("USBcan II", "USBcan II"), + T_DATA("USBE-100 Ethernet [pegasus2]", "USBE-100"), + T_DATA("USBEL-100 Ethernet [pegasus]", "USBEL-100"), + T_DATA("USB Ethernet [pegasus]", "pegasus"), + T_DATA("USB ETT", "ETT"), + T_DATA("USBLAN", "USBLAN"), + T_DATA("USBLP-100 HomePNA Ethernet [pegasus]", "USBLP-100 HomePNA"), + T_DATA("USB-N10 v2 802.11b/g/n Wireless Adapter [MediaTek MT7601U]", "USB-N10"), + T_DATA("USB-N11 802.11n Network Adapter [Ralink RT2870]", "USB-N11"), + T_DATA("USB-N13 802.11n Network Adapter (rev. A1) [Ralink RT3072]", "USB-N13"), + T_DATA("USB-N13 802.11n Network Adapter (rev. B1) [Realtek RTL8192CU]", "USB-N13"), + T_DATA("USB-N14 802.11b/g/n (2x2) Wireless Adapter [Ralink RT5372]", "USB-N14"), + T_DATA("USB-N53 802.11abgn Network Adapter [Ralink RT3572]", "USB-N53"), + T_DATA("USB TO Ethernet", NULL), + T_DATA("USR5420 802.11g Adapter [Broadcom 4320 USB]", "USR5420"), + T_DATA("USR5423 802.11bg Wireless Adapter [ZyDAS ZD1211B]", "USR5423"), + T_DATA("USR997902 10/100/1000 Mbps PCI Network Card", "USR997902 Mbps"), + T_DATA("VIC Ethernet NIC Dynamic", "VIC Dynamic"), + T_DATA("VIC Ethernet NIC", "VIC"), + T_DATA("VIC SR-IOV VF", "VIC SR-IOV VF"), + T_DATA("Vigor530 IEEE 802.11G Adapter (ISL3880+NET2280)", "Vigor530"), + T_DATA("Virtual media for 802.11bg", NULL), + T_DATA("VMXNET3 Ethernet Controller", "VMXNET3"), + T_DATA("VT6102/VT6103 [Rhine-II]", "VT6102/VT6103"), + T_DATA("VT6105M [Rhine-III]", "VT6105M"), + T_DATA("VT6105/VT6106S [Rhine-III]", "VT6105/VT6106S"), + T_DATA("VT6120/VT6121/VT6122 Gigabit Ethernet Adapter", "VT6120/VT6121/VT6122"), + T_DATA("VT82C926 [Amazon]", "VT82C926"), + T_DATA("VT86C100A [Rhine]", "VT86C100A"), + T_DATA("W89C840", "W89C840"), + T_DATA("W89C940F", "W89C940F"), + T_DATA("W89C940 misprogrammed [ne2k]", "W89C940"), + T_DATA("W89C940", "W89C940"), + T_DATA("WG111T (no firmware)", "WG111T"), + T_DATA("WG111T", "WG111T"), + T_DATA("WG111U Double 108 Mbps Wireless [Atheros AR5004X / AR5005UX]", "WG111U"), + T_DATA("WG111U (no firmware) Double 108 Mbps Wireless [Atheros AR5004X / AR5005UX]", + "WG111U"), + T_DATA("WG111(v1) 54 Mbps Wireless [Intersil ISL3886]", "WG111"), + T_DATA("WG111(v1) rev 2 54 Mbps Wireless [Intersil ISL3887]", "WG111"), + T_DATA("WG111v2 54 Mbps Wireless [RealTek RTL8187L]", "WG111v2"), + T_DATA("WG111v3 54 Mbps Wireless [realtek RTL8187B]", "WG111v3"), + T_DATA("WG121(v1) 54 Mbps Wireless [Intersil ISL3886]", "WG121"), + T_DATA("WG121(v2) 54 Mbps Wireless [Intersil ISL3886]", "WG121"), + T_DATA("WGU-210 802.11g Adapter [Intersil ISL3886]", "WGU-210"), + T_DATA("WHG-AGDN/US Wireless LAN Adapter", "WHG-AGDN/US"), + T_DATA("Wi-Fi 11g adapter", NULL), + T_DATA("WiFi Link 5100", "5100"), + T_DATA("Wil6200 802.11ad Wireless Network Adapter", "Wil6200"), + T_DATA("WiMAX/WiFi Link 5150", "5150"), + T_DATA("Wireless 11n USB Adapter", "11n"), + T_DATA("Wireless 1450 Dual-band (802.11a/b/g) Adapter [Intersil ISL3887]", "1450"), + T_DATA("Wireless 3160", "3160"), + T_DATA("Wireless 3165", "3165"), + T_DATA("Wireless 7260", "7260"), + T_DATA("Wireless 7265", "7265"), + T_DATA("Wireless 802.11g 54Mbps Network Adapter [RTL8187]", "RTL8187"), + T_DATA("Wireless 8260", "8260"), + T_DATA("Wireless 8265 / 8275", "8265 / 8275"), + T_DATA("Wireless Adapter 11g", NULL), + T_DATA("Wireless LAN USB Mini-Card", NULL), + T_DATA("Wireless MAXg Adapter [Broadcom 4320]", "MAXg"), + T_DATA("Wireless Network Adapter", NULL), + T_DATA("Wireless-N Network Adapter [Ralink RT2870]", "Ralink RT2870"), + T_DATA("Wireless PCI Adapter RT2400 / RT2460", "RT2400 / RT2460"), + T_DATA("WIS09ABGN LinkStick Wireless LAN Adapter", "WIS09ABGN LinkStick"), + T_DATA("WL-113 rev 1 Wireless Network USB Adapter", "WL-113"), + T_DATA("WL-113 rev 2 Wireless Network USB Adapter", "WL-113"), + T_DATA("WL-117 Hi-Speed USB Adapter", "WL-117"), + T_DATA("WL1271", "WL1271"), + T_DATA("WL-159g 802.11bg [ZyDAS ZD1211B+AL2230]", "WL-159g"), + T_DATA("WL-167G v1 802.11g Adapter [Ralink RT2571]", "WL-167G"), + T_DATA("WL-167G v2 802.11g Adapter [Ralink RT2571W]", "WL-167G"), + T_DATA("WL-168 Wireless Network Adapter 54g", "WL-168"), + T_DATA("WL169gE 802.11g Adapter [Broadcom 4320 USB]", "WL169gE"), + T_DATA("WL-172 Wireless Network USB Adapter 54g Turbo", "WL-172 Turbo"), + T_DATA("WL-182 Wireless-N Network USB Card", "WL-182"), + T_DATA("WL-188 Wireless Network 300N USB Adapter", "WL-188 300N"), + T_DATA("WL-301 Wireless Network 300N USB Adapter", "WL-301 300N"), + T_DATA("WL-302 Wireless Network 300N USB dongle", "WL-302 300N"), + T_DATA("WL-315 Wireless-N USB Adapter", "WL-315"), + T_DATA("WL-321 Wireless USB Gaming Adapter 300N", "WL-321 Gaming 300N"), + T_DATA("WL-323 Wireless-N USB Adapter", "WL-323"), + T_DATA("WL-324 Wireless USB Adapter 300N", "WL-324 300N"), + T_DATA("WL-329 Wireless Dualband USB adapter 300N", "WL-329 Dualband 300N"), + T_DATA("WL-343 Wireless USB Adapter 150N X1", "WL-343 150N X1"), + T_DATA("WL-344 Wireless Adapter 300N X2 [Ralink RT3071]", "WL-344 300N X2"), + T_DATA("WL-345 Wireless USB adapter 300N X3", "WL-345 300N X3"), + T_DATA("WL-349v1 Wireless Adapter 150N 002 [Ralink RT3070]", "WL-349v1 150N 002"), + T_DATA("WL-349v4 Wireless Micro Adapter 150N X1 [Ralink RT3370]", "WL-349v4 150N X1"), + T_DATA("WL-352v1 Wireless USB Adapter 300N 002", "WL-352v1 300N 002"), + T_DATA("WL-358v1 Wireless Micro USB Adapter 300N X3 002", "WL-358v1 300N X3 002"), + T_DATA("WL-430U 802.11bg", "WL-430U"), + T_DATA("WL532U 802.11g Adapter", "WL532U"), + T_DATA("WL-603 Wireless Adapter", "WL-603"), + T_DATA("WL-608 Wireless USB Adapter 54g", "WL-608"), + T_DATA("WLA3310 Wireless Adapter [Intersil ISL3887]", "WLA3310"), + T_DATA("WLA-4000 802.11bgn [Ralink RT3072]", "WLA-4000"), + T_DATA("WLA-5000 802.11abgn [Ralink RT3572]", "WLA-5000"), + T_DATA("WLA-5100", "WLA-5100"), + T_DATA("WLI2-USB2-G54 Wireless LAN Adapter", "WLI2-USB2-G54"), + T_DATA("WLI-U2-G54HP", "WLI-U2-G54HP"), + T_DATA("WLI-U2-KG125S 802.11g Adapter [Broadcom 4320 USB]", "WLI-U2-KG125S"), + T_DATA("WLI-U2-KG54-AI WLAN", "WLI-U2-KG54-AI"), + T_DATA("WLI-U2-KG54-BB", "WLI-U2-KG54-BB"), + T_DATA("WLI-U2-KG54L 802.11bg [ZyDAS ZD1211B]", "WLI-U2-KG54L"), + T_DATA("WLI-U2-KG54 WLAN", "WLI-U2-KG54"), + T_DATA("WLI-U2-KG54-YB WLAN", "WLI-U2-KG54-YB"), + T_DATA("WLI-U2-SG54HP", "WLI-U2-SG54HP"), + T_DATA("WLI-UC-AG300N Wireless LAN Adapter", "WLI-UC-AG300N"), + T_DATA("WLI-UC-G300HP Wireless LAN Adapter", "WLI-UC-G300HP"), + T_DATA("WLI-UC-G300N Wireless LAN Adapter [Ralink RT2870]", "WLI-UC-G300N"), + T_DATA("WLI-UC-G301N Wireless LAN Adapter [Ralink RT3072]", "WLI-UC-G301N"), + T_DATA("WLI-UC-G450 Wireless LAN Adapter", "WLI-UC-G450"), + T_DATA("WLI-UC-GNHP Wireless LAN Adapter", "WLI-UC-GNHP"), + T_DATA("WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]", "WLI-UC-GNM2"), + T_DATA("WLI-UC-GNM Wireless LAN Adapter [Ralink RT8070]", "WLI-UC-GNM"), + T_DATA("WLI-UC-GN Wireless LAN Adapter [Ralink RT3070]", "WLI-UC-GN"), + T_DATA("WLI-USB-G54 802.11g Adapter [Broadcom 4320 USB]", "WLI-USB-G54"), + T_DATA("WLM-10U1 802.11abgn Wireless Adapter [Ralink RT3572]", "WLM-10U1"), + T_DATA("WLM-20U2/GN-1080 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", + "WLM-20U2/GN-1080"), + T_DATA("WLP-UC-AG300 Wireless LAN Adapter", "WLP-UC-AG300"), + T_DATA("WM168g 802.11bg Wireless Adapter [Intersil ISL3886]", "WM168g"), + T_DATA("WN111(v2) RangeMax Next Wireless [Atheros AR9170+AR9101]", "WN111"), + T_DATA("WNA1000M 802.11bgn [Realtek RTL8188CUS]", "WNA1000M"), + T_DATA("WNA1000Mv2 802.11bgn [Realtek RTL8188CUS?]", "WNA1000Mv2"), + T_DATA("WNA1000 Wireless-N 150 [Atheros AR9170+AR9101]", "WNA1000 150"), + T_DATA("WNA1100 Wireless-N 150 [Atheros AR9271]", "WNA1100 150"), + T_DATA("WNA3100M(v1) Wireless-N 300 [Realtek RTL8192CU]", "WNA3100M"), + T_DATA("WNDA3100v1 802.11abgn [Atheros AR9170+AR9104]", "WNDA3100v1"), + T_DATA("WNDA3200 802.11abgn Wireless Adapter [Atheros AR7010+AR9280]", "WNDA3200"), + T_DATA("WNDA4100 802.11abgn 3x3:3 [Ralink RT3573]", "WNDA4100"), + T_DATA("WN-G150U Wireless LAN Adapter", "WN-G150U"), + T_DATA("WN-G300U Wireless LAN Adapter", "WN-G300U"), + T_DATA("WNGDNUS2 802.11n", "WNGDNUS2"), + T_DATA("WN-GDN/US3 Wireless LAN Adapter", "WN-GDN/US3"), + T_DATA("WPN111 802.11g Wireless Adapter [Atheros AR5523]", "WPN111"), + T_DATA("WPN111 (no firmware)", "WPN111"), + T_DATA("WPN111 RangeMax(TM) Wireless USB 2.0 Adapter", "WPN111 RangeMax"), + T_DATA("WUA-1340", "WUA-1340"), + T_DATA("WUA-2340 RangeBooster G Adapter(rev.A) [Atheros AR5523]", "WUA-2340 RangeBooster"), + T_DATA("WUA-2340 RangeBooster G Adapter(rev.A) (no firmware) [Atheros AR5523]", + "WUA-2340 RangeBooster"), + T_DATA("WUA-2340 RangeBooster G Adapter(rev.B) [Ralink RT2070]", "WUA-2340 RangeBooster"), + T_DATA("WUBR-177G [Ralink RT2571W]", "WUBR-177G"), + T_DATA("WUBR-208N 802.11abgn Wireless Adapter [Ralink RT2870]", "WUBR-208N"), + T_DATA("WUG2690 802.11bg Wireless Module [ZyDAS ZD1211+AL2230]", "WUG2690"), + T_DATA("WUG2700", "WUG2700"), + T_DATA("WUS-201 802.11bg", "WUS-201"), + T_DATA("WUSB100 v1 RangePlus Wireless Network Adapter [Ralink RT2870]", + "WUSB100 RangePlus"), + T_DATA("WUSB100 v2 RangePlus Wireless Network Adapter [Ralink RT3070]", + "WUSB100 RangePlus"), + T_DATA("WUSB200 802.11g Adapter [Ralink RT2671]", "WUSB200"), + T_DATA("WUSB54AG 802.11a/g Adapter [Intersil ISL3887]", "WUSB54AG"), + T_DATA("WUSB54GC v1 802.11g Adapter [Ralink RT73]", "WUSB54GC"), + T_DATA("WUSB54GC v2 802.11g Adapter [Realtek RTL8187B]", "WUSB54GC"), + T_DATA("WUSB54GC v3 802.11g Adapter [Ralink RT2070L]", "WUSB54GC"), + T_DATA("WUSB54GP v1 802.11g Adapter [Intersil ISL3886]", "WUSB54GP"), + T_DATA("WUSB54GP v4.0 802.11g Adapter [Ralink RT2500USB]", "WUSB54GP v4.0"), + T_DATA("WUSB54GR", "WUSB54GR"), + T_DATA("WUSB54GSC v1 802.11g Adapter [Broadcom 4320 USB]", "WUSB54GSC"), + T_DATA("WUSB54GS v1 802.11g Adapter [Broadcom 4320 USB]", "WUSB54GS"), + T_DATA("WUSB54GS v2 802.11g Adapter [Broadcom 4320 USB]", "WUSB54GS"), + T_DATA("WUSB54G v1 802.11g Adapter [Intersil ISL3886]", "WUSB54G"), + T_DATA("WUSB54G v2 802.11g Adapter [Intersil ISL3887]", "WUSB54G"), + T_DATA("WUSB54G v4 802.11g Adapter [Ralink RT2500USB]", "WUSB54G"), + T_DATA("WUSB600N v1 Dual-Band Wireless-N Network Adapter [Ralink RT2870]", + "WUSB600N Dual-Band"), + T_DATA("WUSB600N v2 Dual-Band Wireless-N Network Adapter [Ralink RT3572]", + "WUSB600N Dual-Band"), + T_DATA("WUSBF54G 802.11bg", "WUSBF54G"), + T_DATA("WUSBF54G v1.1 802.11bg", "WUSBF54G"), + T_DATA("X3100 Series 10 Gigabit Ethernet PCIe", "X3100"), + T_DATA("X540 Ethernet Controller Virtual Function", "X540 Virtual Function"), + T_DATA("X540 Virtual Function", "X540 Virtual Function"), + T_DATA("X550 Virtual Function", "X550 Virtual Function"), + T_DATA("X552 Virtual Function", "X552 Virtual Function"), + T_DATA("X553 Virtual Function", "X553 Virtual Function"), + T_DATA("X722 Virtual Function", "X722 Virtual Function"), + T_DATA("Xframe 10-Gigabit Ethernet PCI-X", "Xframe"), + T_DATA("Xframe II 10-Gigabit Ethernet PCI-X 2.0", "Xframe II 2.0"), + T_DATA("XG-300 802.11b Adapter", "XG-300"), + T_DATA("XG-703A 802.11g Wireless Adapter [Intersil ISL3887]", "XG-703A"), + T_DATA("XG-705A 802.11g Wireless Adapter [Intersil ISL3887]", "XG-705A"), + T_DATA("XG-760A 802.11bg", "XG-760A"), + T_DATA("XG-76NA 802.11bg", "XG-76NA"), + T_DATA("XG Mgmt", "XG Mgmt"), + T_DATA("Xircom PGUNET USB-USB Bridge", "Xircom PGUNET USB-USB Bridge"), + T_DATA("XL710/X710 Virtual Function", "XL710/X710 Virtual Function"), + T_DATA("XX1", "XX1"), + T_DATA("XX2", "XX2"), + T_DATA("XX4", "XX4"), + T_DATA("XX5", "XX5"), + T_DATA("XX6", "XX6"), + T_DATA("XX7", "XX7"), + T_DATA("XX9", "XX9"), + T_DATA("Yellowfin G-NIC gigabit ethernet", "Yellowfin"), + T_DATA("YP3X00 PDA", "YP3X00"), + T_DATA("Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB]", + "Yukon Optima 88E8059"), + T_DATA("Zaurus A-300", "Zaurus A-300"), + T_DATA("Zaurus C-700 PDA", "Zaurus C-700"), + T_DATA("Zaurus C-750/C-760/C-860/SL-C3000 PDA", "Zaurus C-750/C-760/C-860/SL-C3000"), + T_DATA("Zaurus C-860 PDA", "Zaurus C-860"), + T_DATA("Zaurus SL-5000D/SL-5500 PDA", "Zaurus SL-5000D/SL-5500"), + T_DATA("Zaurus SL-6000", "Zaurus SL-6000"), + T_DATA("Zaurus SL-B500/SL-5600 PDA", "Zaurus SL-B500/SL-5600"), + T_DATA("ZD1211 802.11b/g Wireless Adapter", "ZD1211"), + T_DATA("ZD1211 802.11g", "ZD1211"), + T_DATA("ZD1211B 802.11g", "ZD1211B"), + T_DATA("ZD1211B", "ZD1211B"), + T_DATA("ZD1221 802.11n", "ZD1221"), + T_DATA("Zoom 4410 Wireless-G [Intersil ISL3887]", "Zoom 4410"), + T_DATA("ZT6688 Fast Ethernet Adapter", "ZT6688"), + T_DATA("ZyAIR AG-225H v2 802.11bg", "ZyAIR AG-225H"), + T_DATA("ZyAIR G-202 802.11bg", "ZyAIR G-202"), + T_DATA("ZyAIR G-220 802.11bg", "ZyAIR G-220"), + T_DATA("ZyAIR G-220F 802.11bg", "ZyAIR G-220F"), + }; + + _test_fixup_string(data, G_N_ELEMENTS(data), nm_utils_fixup_product_string); +} + +/*****************************************************************************/ + +static int +_memfd_create(const char *name) +{ +#if defined(HAVE_DECL_MEMFD_CREATE) && HAVE_DECL_MEMFD_CREATE + return memfd_create(name, MFD_CLOEXEC); +#endif + return -1; +} + +typedef struct { + const char *key; + const char *val; +} ReadVpnDetailData; + +#define READ_VPN_DETAIL_DATA(...) ((ReadVpnDetailData[]){__VA_ARGS__}) + +static gboolean +_do_read_vpn_details_impl1(const char * file, + int line, + int memfd, + char * mem, + gsize len, + const ReadVpnDetailData *expected_data, + guint expected_data_len, + const ReadVpnDetailData *expected_secrets, + guint expected_secrets_len) +{ + gssize written; + off_t lseeked; + gs_unref_hashtable GHashTable *data = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + + written = write(memfd, mem, len); + g_assert_cmpint(written, ==, (gssize) len); + + lseeked = lseek(memfd, 0, SEEK_SET); + g_assert_cmpint(lseeked, ==, 0); + + if (!nm_vpn_service_plugin_read_vpn_details(memfd, &data, &secrets)) { + g_assert(!data); + g_assert(!secrets); + g_assert_cmpint(expected_data_len, ==, 0); + g_assert_cmpint(expected_secrets_len, ==, 0); + return TRUE; + } + +#define _assert_hash(hash, expected, expected_len) \ + G_STMT_START \ + { \ + GHashTable * _hash = (hash); \ + guint _expected_len = (expected_len); \ + const ReadVpnDetailData *_expected = (expected); \ + GHashTableIter _iter; \ + const char * _k, *_v; \ + guint _i; \ + \ + g_assert(_hash); \ + \ + g_hash_table_iter_init(&_iter, _hash); \ + while (g_hash_table_iter_next(&_iter, (gpointer *) &_k, (gpointer *) &_v)) { \ + for (_i = 0; _i < _expected_len; _i++) { \ + if (nm_streq(_expected[_i].key, _k)) \ + break; \ + } \ + if (_i >= _expected_len) \ + g_error("%s:%d: hash '%s' contains unexpected data key '%s' with value '%s'", \ + file, \ + line, \ + G_STRINGIFY(hash), \ + _k, \ + _v); \ + } \ + \ + for (_i = 0; _i < _expected_len; _i++) { \ + const ReadVpnDetailData *_d = &_expected[_i]; \ + \ + g_assert(_d->key); \ + g_assert(_d->val); \ + _v = g_hash_table_lookup(_hash, _d->key); \ + if (!nm_streq0(_v, _d->val)) \ + g_error("%s:%d: hash '%s' contains data key '%s' with value %s%s%s but we " \ + "expected '%s'", \ + file, \ + line, \ + G_STRINGIFY(hash), \ + _d->key, \ + NM_PRINT_FMT_QUOTE_STRING(_v), \ + _d->val); \ + } \ + \ + g_assert_cmpint(g_hash_table_size(_hash), ==, _expected_len); \ + } \ + G_STMT_END + + _assert_hash(data, expected_data, expected_data_len); + _assert_hash(secrets, expected_secrets, expected_secrets_len); + +#undef _assert_hash + return TRUE; +} + +#define _do_read_vpn_details_impl0(str, \ + expected_data, \ + expected_data_len, \ + expected_secrets, \ + expected_secrets_len, \ + pre_setup_cmd) \ + G_STMT_START \ + { \ + nm_auto_close int _memfd = _memfd_create("libnm-test-read-vpn-details"); \ + \ + if (_memfd < 0) \ + g_test_skip("cannot create memfd"); \ + else { \ + { \ + pre_setup_cmd; \ + } \ + _do_read_vpn_details_impl1(__FILE__, \ + __LINE__, \ + _memfd, \ + "" str "", \ + NM_STRLEN(str), \ + expected_data, \ + expected_data_len, \ + expected_secrets, \ + expected_secrets_len); \ + } \ + } \ + G_STMT_END + +#define _do_read_vpn_details_empty(str) _do_read_vpn_details_impl0(str, NULL, 0, NULL, 0, {}) + +#define _do_read_vpn_details(str, expected_data, expected_secrets, pre_setup_cmd) \ + _do_read_vpn_details_impl0(str, \ + expected_data, \ + G_N_ELEMENTS(expected_data), \ + expected_secrets, \ + G_N_ELEMENTS(expected_secrets), \ + pre_setup_cmd) + +static void +test_nm_vpn_service_plugin_read_vpn_details(void) +{ + _do_read_vpn_details_empty(""); + _do_read_vpn_details_empty("hallo"); + _do_read_vpn_details_empty("DONE"); + _do_read_vpn_details_empty("DONE\n"); + _do_read_vpn_details_empty("DONE\0"); + _do_read_vpn_details_empty("\0DONE\0"); + + _do_read_vpn_details("" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "\n" + "DATA_KEY=some-other-key\n" + "DATA_VAL=val2\n" + "\n" + "SECRET_KEY=some-secret\n" + "SECRET_VAL=val3\n" + "\n" + "DONE\n" + "\n" + "", + READ_VPN_DETAIL_DATA({"some-key", "string"}, {"some-other-key", "val2"}, ), + READ_VPN_DETAIL_DATA({"some-secret", "val3"}, ), ); + + _do_read_vpn_details("" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "DONE\n", + READ_VPN_DETAIL_DATA({"some-key", "string"}, ), + READ_VPN_DETAIL_DATA(), ); + + _do_read_vpn_details( + "" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "=continued after a line break\n" + "SECRET_KEY=key names\n" + "=can have\n" + "=continuations too\n" + "bogus1=\n" + "SECRET_VAL=value\n" + "bogus=value\n" + "bogus=\n" + "DATA_VAL=x\n" + "DATA_KEY=\n" + "DATA_VAL=\n" + "DATA_VAL=y\n" + "DATA_KEY=y\n" + "DATA_KEY=y\n" + "DATA_KEY=z\n" + "SECRET_KEY=s1\n" + "DATA_VAL=z\n" + "SECRET_VAL=S1\n" + "\n" + "DONE\n" + "", + READ_VPN_DETAIL_DATA({"some-key", "string\ncontinued after a line break"}, ), + READ_VPN_DETAIL_DATA({"key names\ncan have\ncontinuations too", "value"}, ), ); + + _do_read_vpn_details( + "" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "=continued after a line break\n" + "SECRET_KEY=key names\n" + "=can have\n" + "=continuations too\n" + "SECRET_VAL=value\n" + "", + READ_VPN_DETAIL_DATA({"some-key", "string\ncontinued after a line break"}, ), + READ_VPN_DETAIL_DATA({"key names\ncan have\ncontinuations too", "value"}, ), ); + + _do_read_vpn_details("" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "\n" + "DATA_KEY=some\n" + "=key-2\n" + "DATA_VAL=val2\n" + "\n" + "DATA_KEY=key3\n" + "=key-2\n" + "DATA_VAL=val3\n" + "\n" + "SECRET_KEY=some-secret\n" + "SECRET_VAL=val3\n" + "\n" + "SECRET_KEY=\n" + "SECRET_VAL=val3\n" + "\n" + "SECRET_KEY=keyx\n" + "SECRET_VAL=\n" + "\n" + "SECRET_KEY=ke\xc0yx\n" + "SECRET_VAL=inval\n" + "\n" + "SECRET_KEY=key-inval\n" + "SECRET_VAL=in\xc1val\n" + "\n" + "DONE\n" + "\n" + "", + READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"}, + {"some-key", "string"}, + {"key3\nkey-2", "val3"}, ), + READ_VPN_DETAIL_DATA({"some-secret", "val3"}, + {"", "val3"}, + {"keyx", ""}, + {"ke\xc0yx", "inval"}, + {"key-inval", "in\xc1val"}, ), ); + + _do_read_vpn_details("" + "DATA_KEY=some-key\n" + "DATA_VAL=string\n" + "\n" + "DATA_KEY=some\n" + "=key-2\n" + "DATA_VAL=val2\n" + "\n" + "DATA_KEY=key3\0" + "=key-2\n" + "DATA_VAL=val3\n" + "\n" + "SECRET_KEY=some-secret\n" + "SECRET_VAL=val3\n" + "\n" + "SECRET_KEY=\n" + "SECRET_VAL=val3\n" + "\n" + "SECRET_KEY=keyx\n" + "SECRET_VAL=\n" + "\n" + "SECRET_KEY=ke\xc0yx\n" + "SECRET_VAL=inval\n" + "\n" + "SECRET_KEY=key-inval\n" + "SECRET_VAL=in\xc1val\n" + "\n" + "DONE\n" + "\n" + "", + READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"}, {"some-key", "string"}, ), + READ_VPN_DETAIL_DATA(), ); +} + +/*****************************************************************************/ + +static void +test_types(void) +{ +#define G(get_type_fcn) \ + ({ \ + GType get_type_fcn(void); \ + \ + get_type_fcn; \ + }) + GType (*get_type_fcns[])(void) = { + G(nm_802_11_ap_flags_get_type), + G(nm_802_11_ap_security_flags_get_type), + G(nm_802_11_mode_get_type), + G(nm_access_point_get_type), + G(nm_activation_state_flags_get_type), + G(nm_active_connection_get_type), + G(nm_active_connection_state_get_type), + G(nm_active_connection_state_reason_get_type), + G(nm_agent_manager_error_get_type), + G(nm_bluetooth_capabilities_get_type), + G(nm_bridge_vlan_get_type), + G(nm_capability_get_type), + G(nm_checkpoint_create_flags_get_type), + G(nm_checkpoint_get_type), + G(nm_client_error_get_type), + G(nm_client_get_type), + G(nm_client_permission_get_type), + G(nm_client_permission_result_get_type), + G(nm_connection_error_get_type), + G(nm_connection_get_type), + G(nm_connection_multi_connect_get_type), + G(nm_connection_serialization_flags_get_type), + G(nm_connectivity_state_get_type), + G(nm_crypto_error_get_type), + G(nm_device_6lowpan_get_type), + G(nm_device_adsl_get_type), + G(nm_device_bond_get_type), + G(nm_device_bridge_get_type), + G(nm_device_bt_get_type), + G(nm_device_capabilities_get_type), + G(nm_device_dummy_get_type), + G(nm_device_error_get_type), + G(nm_device_ethernet_get_type), + G(nm_device_generic_get_type), + G(nm_device_get_type), + G(nm_device_infiniband_get_type), + G(nm_device_ip_tunnel_get_type), + G(nm_device_macsec_get_type), + G(nm_device_macvlan_get_type), + G(nm_device_modem_capabilities_get_type), + G(nm_device_modem_get_type), + G(nm_device_olpc_mesh_get_type), + G(nm_device_ovs_bridge_get_type), + G(nm_device_ovs_interface_get_type), + G(nm_device_ovs_port_get_type), + G(nm_device_ppp_get_type), + G(nm_device_state_get_type), + G(nm_device_state_reason_get_type), + G(nm_device_team_get_type), + G(nm_device_tun_get_type), + G(nm_device_type_get_type), + G(nm_device_vlan_get_type), + G(nm_device_vxlan_get_type), + G(nm_device_wifi_capabilities_get_type), + G(nm_device_wifi_get_type), + G(nm_device_wifi_p2p_get_type), + G(nm_device_wimax_get_type), + G(nm_device_wireguard_get_type), + G(nm_device_wpan_get_type), + G(nm_dhcp4_config_get_type), + G(nm_dhcp6_config_get_type), + G(nm_dhcp_config_get_type), + G(nm_dns_entry_get_type), + G(nm_ip4_config_get_type), + G(nm_ip6_config_get_type), + G(nm_ip_address_get_type), + G(nm_ip_config_get_type), + G(nm_ip_route_get_type), + G(nm_ip_routing_rule_as_string_flags_get_type), + G(nm_ip_routing_rule_get_type), + G(nm_ip_tunnel_flags_get_type), + G(nm_ip_tunnel_mode_get_type), + G(nm_lldp_neighbor_get_type), + G(nm_manager_error_get_type), + G(nm_manager_reload_flags_get_type), + G(nm_metered_get_type), + G(nm_object_get_type), + G(nm_remote_connection_get_type), + G(nm_secret_agent_capabilities_get_type), + G(nm_secret_agent_error_get_type), + G(nm_secret_agent_get_secrets_flags_get_type), + G(nm_secret_agent_old_get_type), + G(nm_setting_6lowpan_get_type), + G(nm_setting_802_1x_auth_flags_get_type), + G(nm_setting_802_1x_ck_format_get_type), + G(nm_setting_802_1x_ck_scheme_get_type), + G(nm_setting_802_1x_get_type), + G(nm_setting_adsl_get_type), + G(nm_setting_bluetooth_get_type), + G(nm_setting_bond_get_type), + G(nm_setting_bridge_get_type), + G(nm_setting_bridge_port_get_type), + G(nm_setting_cdma_get_type), + G(nm_setting_compare_flags_get_type), + G(nm_setting_connection_autoconnect_slaves_get_type), + G(nm_setting_connection_get_type), + G(nm_setting_connection_lldp_get_type), + G(nm_setting_connection_llmnr_get_type), + G(nm_setting_connection_mdns_get_type), + G(nm_setting_dcb_flags_get_type), + G(nm_setting_dcb_get_type), + G(nm_setting_diff_result_get_type), + G(nm_setting_dummy_get_type), + G(nm_setting_ethtool_get_type), + G(nm_setting_generic_get_type), + G(nm_setting_get_type), + G(nm_setting_gsm_get_type), + G(nm_setting_infiniband_get_type), + G(nm_setting_ip4_config_get_type), + G(nm_setting_ip6_config_addr_gen_mode_get_type), + G(nm_setting_ip6_config_get_type), + G(nm_setting_ip6_config_privacy_get_type), + G(nm_setting_ip_config_get_type), + G(nm_setting_ip_tunnel_get_type), + G(nm_setting_mac_randomization_get_type), + G(nm_setting_macsec_get_type), + G(nm_setting_macsec_mode_get_type), + G(nm_setting_macsec_validation_get_type), + G(nm_setting_macvlan_get_type), + G(nm_setting_macvlan_mode_get_type), + G(nm_setting_match_get_type), + G(nm_setting_olpc_mesh_get_type), + G(nm_setting_ovs_bridge_get_type), + G(nm_setting_ovs_dpdk_get_type), + G(nm_setting_ovs_interface_get_type), + G(nm_setting_ovs_patch_get_type), + G(nm_setting_ovs_port_get_type), + G(nm_setting_ppp_get_type), + G(nm_setting_pppoe_get_type), + G(nm_setting_proxy_get_type), + G(nm_setting_proxy_method_get_type), + G(nm_settings_add_connection2_flags_get_type), + G(nm_settings_connection_flags_get_type), + G(nm_setting_secret_flags_get_type), + G(nm_setting_serial_get_type), + G(nm_setting_serial_parity_get_type), + G(nm_settings_error_get_type), + G(nm_setting_sriov_get_type), + G(nm_settings_update2_flags_get_type), + G(nm_setting_tc_config_get_type), + G(nm_setting_team_get_type), + G(nm_setting_team_port_get_type), + G(nm_setting_tun_get_type), + G(nm_setting_tun_mode_get_type), + G(nm_setting_user_get_type), + G(nm_setting_vlan_get_type), + G(nm_setting_vpn_get_type), + G(nm_setting_vxlan_get_type), + G(nm_setting_wifi_p2p_get_type), + G(nm_setting_wimax_get_type), + G(nm_setting_wired_get_type), + G(nm_setting_wired_wake_on_lan_get_type), + G(nm_setting_wireguard_get_type), + G(nm_setting_wireless_get_type), + G(nm_setting_wireless_powersave_get_type), + G(nm_setting_wireless_security_fils_get_type), + G(nm_setting_wireless_security_get_type), + G(nm_setting_wireless_security_pmf_get_type), + G(nm_setting_wireless_security_wps_method_get_type), + G(nm_setting_wireless_wake_on_wlan_get_type), + G(nm_setting_wpan_get_type), + G(nm_simple_connection_get_type), + G(nm_sriov_vf_get_type), + G(nm_sriov_vf_vlan_protocol_get_type), + G(nm_state_get_type), + G(nm_tc_action_get_type), + G(nm_tc_qdisc_get_type), + G(nm_tc_tfilter_get_type), + G(nm_team_link_watcher_arp_ping_flags_get_type), + G(nm_team_link_watcher_get_type), + G(nm_ternary_get_type), + G(nm_utils_security_type_get_type), + G(nm_vlan_flags_get_type), + G(nm_vlan_priority_map_get_type), + G(nm_vpn_connection_get_type), + G(nm_vpn_connection_state_get_type), + G(nm_vpn_connection_state_reason_get_type), + G(nm_vpn_editor_get_type), + G(nm_vpn_editor_plugin_capability_get_type), + G(nm_vpn_editor_plugin_get_type), + G(nm_vpn_plugin_error_get_type), + G(nm_vpn_plugin_failure_get_type), + G(nm_vpn_plugin_info_get_type), + G(nm_vpn_plugin_old_get_type), + G(nm_vpn_service_plugin_get_type), + G(nm_vpn_service_state_get_type), + G(nm_wep_key_type_get_type), + G(nm_wifi_p2p_peer_get_type), + G(nm_wimax_nsp_get_type), + G(nm_wimax_nsp_network_type_get_type), + G(nm_wireguard_peer_get_type), + }; + guint i_type; + + for (i_type = 0; i_type < G_N_ELEMENTS(get_type_fcns); i_type++) { + nm_auto_unref_gtypeclass GObjectClass *klass_unref = NULL; + GType gtype = (get_type_fcns[i_type]) (); + GObjectClass * klass; + + g_assert(g_str_has_prefix(g_type_name(gtype), "NM")); + + if (G_TYPE_IS_INTERFACE(gtype)) { + if (!NM_IN_STRSET(g_type_name(gtype), + "NMConnection", + "NMVpnEditor", + "NMVpnEditorPlugin")) + g_error("unexpected interface type %s", g_type_name(gtype)); + continue; + } + + if (g_type_is_a(gtype, G_TYPE_BOXED)) + continue; + + /* We only test parts of the types, and avoid initializing all the types. + * That is so that other unit tests in this process randomly run with either + * the class instance already initialized or not. */ + if ((nmtst_get_rand_uint() % 5) == 0) { + klass = (klass_unref = g_type_class_ref(gtype)); + g_assert(klass); + } else { + klass = g_type_class_peek(gtype); + if (!klass) + continue; + } + + if (g_type_is_a(gtype, G_TYPE_ENUM)) + continue; + + if (g_type_is_a(gtype, G_TYPE_FLAGS)) + continue; + + g_assert(g_type_is_a(gtype, G_TYPE_OBJECT)); + g_assert(G_IS_OBJECT_CLASS(klass)); + } +} + +/*****************************************************************************/ + +static void +test_nml_dbus_meta(void) +{ + const NMLDBusMetaIface * meta_iface; + const NMLDBusMetaProperty *meta_property; + guint prop_idx; + gsize i, j; + guint l, m; + + for (i = 0; i < G_N_ELEMENTS(_nml_dbus_meta_ifaces); i++) { + const NMLDBusMetaIface * mif = _nml_dbus_meta_ifaces[i]; + nm_auto_unref_gtypeclass GObjectClass *klass_unref = NULL; + GObjectClass * klass; + GType gtype; + +#define COMMON_PREFIX "org.freedesktop.NetworkManager" + + g_assert(mif); + g_assert(mif->dbus_iface_name); + g_assert(g_str_has_prefix(mif->dbus_iface_name, COMMON_PREFIX) + && !g_str_has_suffix(mif->dbus_iface_name, ".") + && NM_IN_SET(mif->dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], '\0', '.')); + for (j = i + 1; j < G_N_ELEMENTS(_nml_dbus_meta_ifaces); j++) + g_assert(mif != _nml_dbus_meta_ifaces[j]); + if (i > 0) { + if (strcmp(_nml_dbus_meta_ifaces[i - 1]->dbus_iface_name, mif->dbus_iface_name) >= 0) { + g_error("meta-ifaces are not properly sorted: [%zu] \"%s\" should be after [%zu] " + "\"%s\"", + i - 1, + _nml_dbus_meta_ifaces[i - 1]->dbus_iface_name, + i, + mif->dbus_iface_name); + } + } + + g_assert((mif->n_dbus_properties > 0) == (!!mif->dbus_properties)); + + if (mif->interface_prio == NML_DBUS_META_INTERFACE_PRIO_NONE) { + g_assert(!mif->get_type_fcn); + g_assert(!mif->obj_properties); + g_assert(mif->n_obj_properties == 0); + g_assert(!mif->obj_properties_reverse_idx); + if (!NM_IN_STRSET(mif->dbus_iface_name, + NM_DBUS_INTERFACE_AGENT_MANAGER, + NM_DBUS_INTERFACE_DEVICE_STATISTICS, + NM_DBUS_INTERFACE_DEVICE_VETH)) + g_error("D-Bus interface \"%s\" is unexpectedly empty", mif->dbus_iface_name); + if (mif->n_dbus_properties == 0) + continue; + gtype = G_TYPE_NONE; + klass = NULL; + goto check_dbus_properties; + } + + g_assert(NM_IN_SET((NMLDBusMetaInteracePrio) mif->interface_prio, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + NML_DBUS_META_INTERFACE_PRIO_PARENT_TYPE, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30)); + + g_assert(mif->get_type_fcn); + gtype = mif->get_type_fcn(); + g_assert(g_type_is_a(gtype, G_TYPE_OBJECT)); + + if (mif->interface_prio == NML_DBUS_META_INTERFACE_PRIO_NMCLIENT) + g_assert(gtype == NM_TYPE_CLIENT); + else + g_assert(g_type_is_a(gtype, NM_TYPE_OBJECT)); + + /* We only test parts of the types, and avoid initializing all the types. + * That is so that other unit tests in this process randomly run with either + * the class instance already initialized or not. */ + if ((nmtst_get_rand_uint() % 5) == 0) { + klass = (klass_unref = g_type_class_ref(gtype)); + g_assert(klass); + } else + klass = g_type_class_peek(gtype); + + if (klass) { + if (NM_IS_OBJECT_CLASS(klass)) { + NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass); + + if (nm_object_class->property_o_info || nm_object_class->property_ao_info) { + int ii; + + for (ii = 0; ii < 2; ii++) { + const _NMObjectClassFieldInfo *p_prev = NULL; + const _NMObjectClassFieldInfo *p0 = ii == 0 + ? nm_object_class->property_o_info + : nm_object_class->property_ao_info; + const _NMObjectClassFieldInfo *p; + + for (p = p0; p; p = p->parent) { + GType parent_gtype; + NMObjectClass *parent_klass; + + g_assert(p->num > 0); + g_assert(NM_IS_OBJECT_CLASS(p->klass)); + g_assert(g_type_is_a(gtype, G_TYPE_FROM_CLASS(p->klass))); + if (ii == 0) + g_assert(p->klass->property_o_info == p); + else + g_assert(p->klass->property_ao_info == p); + g_assert_cmpint(p->klass->priv_ptr_offset, >, 0); + if (p_prev) { + g_assert(g_type_is_a(G_TYPE_FROM_CLASS(p_prev->klass), + G_TYPE_FROM_CLASS(p->klass))); + g_assert(p_prev->klass != p->klass); + g_assert_cmpint(p_prev->klass->priv_ptr_offset, + >, + p->klass->priv_ptr_offset); + g_assert_cmpint(p->klass->priv_ptr_indirect, ==, TRUE); + } + + parent_gtype = g_type_parent(G_TYPE_FROM_CLASS(p->klass)); + g_assert(g_type_is_a(parent_gtype, NM_TYPE_OBJECT)); + parent_klass = g_type_class_peek(parent_gtype); + g_assert(NM_IS_OBJECT_CLASS(parent_klass)); + if (parent_gtype == NM_TYPE_OBJECT) { + g_assert_cmpint(parent_klass->priv_ptr_offset, ==, 0); + g_assert_cmpint(parent_klass->priv_ptr_indirect, ==, FALSE); + g_assert(!p->parent); + } else { + if (parent_klass->priv_ptr_offset == 0) { + g_assert(!parent_klass->property_o_info); + g_assert(!parent_klass->property_ao_info); + g_assert_cmpint(parent_klass->priv_ptr_indirect, ==, FALSE); + g_assert(!p->parent); + } else if (p->klass->priv_ptr_offset + == parent_klass->priv_ptr_offset) { + g_assert(p->klass->property_o_info + == parent_klass->property_o_info); + g_assert(p->klass->property_ao_info + == parent_klass->property_ao_info); + g_assert(p->klass->priv_ptr_indirect + == parent_klass->priv_ptr_indirect); + } else { + g_assert_cmpint(parent_klass->priv_ptr_offset, >, 0); + g_assert_cmpint(parent_klass->priv_ptr_offset, + <, + p->klass->priv_ptr_offset); + g_assert_cmpint(parent_klass->priv_ptr_indirect, ==, TRUE); + g_assert(p->klass->property_o_info + != parent_klass->property_o_info + || p->klass->property_ao_info + != parent_klass->property_ao_info); + } + } + + p_prev = p; + } + } + + g_assert_cmpint(nm_object_class->priv_ptr_offset, >, 0); + } else { + g_assert_cmpint(nm_object_class->priv_ptr_offset, ==, 0); + g_assert_cmpint(nm_object_class->priv_ptr_indirect, ==, FALSE); + } + + } else + g_assert(NM_IS_CLIENT_CLASS(klass)); + } + + if (!mif->obj_properties) { + g_assert_cmpint(mif->n_obj_properties, ==, 0); + g_assert(!mif->obj_properties_reverse_idx); + } else { + g_assert(mif->obj_properties); + g_assert(mif->obj_properties[0] == 0); + g_assert_cmpint(mif->n_obj_properties, >, 1); + if (klass) { + for (l = 1; l < mif->n_obj_properties; l++) { + const GParamSpec *sp = mif->obj_properties[l]; + + g_assert(sp); + g_assert(sp->name); + g_assert(strlen(sp->name) > 0); + } + } + + g_assert(mif->obj_properties_reverse_idx); + if (klass) { + g_assert(mif->obj_properties_reverse_idx[0] == 0xFFu); + for (l = 0; l < mif->n_obj_properties; l++) { + guint8 ridx = mif->obj_properties_reverse_idx[l]; + + if (ridx != 0xFFu) { + g_assert_cmpint(ridx, <=, mif->n_dbus_properties); + for (m = l + 1; m < mif->n_obj_properties; m++) + g_assert_cmpint(ridx, !=, mif->obj_properties_reverse_idx[m]); + } + } + } + } + +check_dbus_properties: + for (l = 0; l < mif->n_dbus_properties; l++) { + const NMLDBusMetaProperty *mpr = &mif->dbus_properties[l]; + gs_free char * obj_property_name = NULL; + const struct { + const char *dbus_type; + GType default_gtype; + } * p_expected_type, *p_expected_type_2, + expected_types[] = { + {"b", G_TYPE_BOOLEAN}, {"q", G_TYPE_UINT}, + {"y", G_TYPE_UCHAR}, {"i", G_TYPE_INT}, + {"u", G_TYPE_UINT}, {"x", G_TYPE_INT64}, + {"t", G_TYPE_UINT64}, {"s", G_TYPE_STRING}, + {"o", G_TYPE_STRING}, {"ay", G_TYPE_BYTES}, + {"as", G_TYPE_STRV}, {"ao", G_TYPE_PTR_ARRAY}, + {"a{sv}", G_TYPE_HASH_TABLE}, {"aa{sv}", G_TYPE_PTR_ARRAY}, + + {"(uu)", G_TYPE_NONE}, {"aau", G_TYPE_NONE}, + {"au", G_TYPE_NONE}, {"a(ayuay)", G_TYPE_NONE}, + {"aay", G_TYPE_NONE}, {"a(ayuayu)", G_TYPE_NONE}, + + {"u", G_TYPE_FLAGS}, {"u", G_TYPE_ENUM}, + {"o", NM_TYPE_OBJECT}, + }; + const GParamSpec *pspec = NULL; + + g_assert(mpr->dbus_property_name); + g_assert(g_variant_type_string_is_valid((const char *) mpr->dbus_type)); + if (l > 0) { + if (strcmp(mif->dbus_properties[l - 1].dbus_property_name, mpr->dbus_property_name) + >= 0) { + g_error("meta-ifaces[%s] must have property #%u \"%s\" after #%u \"%s\"", + mif->dbus_iface_name, + l - 1, + mif->dbus_properties[l - 1].dbus_property_name, + l, + mpr->dbus_property_name); + } + } + + obj_property_name = nm_utils_wincaps_to_dash(mpr->dbus_property_name); + g_assert(obj_property_name); + + for (p_expected_type = &expected_types[0]; TRUE;) { + if (nm_streq((const char *) mpr->dbus_type, p_expected_type->dbus_type)) + break; + p_expected_type++; + if (p_expected_type >= &expected_types[G_N_ELEMENTS(expected_types)]) { + g_error("D-Bus type \"%s\" is not implemented (in property %s.%s)", + (const char *) mpr->dbus_type, + mif->dbus_iface_name, + mpr->dbus_property_name); + } + } + + if (klass && mpr->obj_properties_idx > 0) { + g_assert_cmpint(mpr->obj_properties_idx, <, mif->n_obj_properties); + if (!mpr->obj_property_no_reverse_idx) + g_assert_cmpint(mif->obj_properties_reverse_idx[mpr->obj_properties_idx], + ==, + l); + else { + g_assert_cmpint(mif->obj_properties_reverse_idx[mpr->obj_properties_idx], + !=, + l); + g_assert_cmpint(mif->obj_properties_reverse_idx[mpr->obj_properties_idx], + !=, + 0xFFu); + } + pspec = mif->obj_properties[mpr->obj_properties_idx]; + } + + if (mpr->use_notify_update_prop) { + g_assert(mpr->notify_update_prop); + } else { + if (klass) + g_assert(pspec); + } + + if (pspec) { + const char *expected_property_name; + + if (mif == &_nml_dbus_meta_iface_nm_connection_active + && nm_streq(pspec->name, NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT_PATH)) { + g_assert_cmpstr(obj_property_name, ==, "specific-object"); + expected_property_name = NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT_PATH; + } else if (mif == &_nml_dbus_meta_iface_nm_accesspoint + && nm_streq(pspec->name, NM_ACCESS_POINT_BSSID)) { + g_assert_cmpstr(obj_property_name, ==, "hw-address"); + expected_property_name = NM_ACCESS_POINT_BSSID; + } else if (mif == &_nml_dbus_meta_iface_nm_device_wireguard + && nm_streq(pspec->name, NM_DEVICE_WIREGUARD_FWMARK)) { + g_assert_cmpstr(obj_property_name, ==, "fw-mark"); + expected_property_name = NM_DEVICE_WIREGUARD_FWMARK; + } else if (NM_IN_SET(mif, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config) + && nm_streq(pspec->name, NM_IP_CONFIG_ADDRESSES)) { + g_assert(NM_IN_STRSET(obj_property_name, "addresses", "address-data")); + expected_property_name = NM_IP_CONFIG_ADDRESSES; + } else if (NM_IN_SET(mif, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config) + && nm_streq(pspec->name, NM_IP_CONFIG_ROUTES)) { + g_assert(NM_IN_STRSET(obj_property_name, "routes", "route-data")); + expected_property_name = NM_IP_CONFIG_ROUTES; + } else if (NM_IN_SET(mif, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config) + && nm_streq(pspec->name, NM_IP_CONFIG_NAMESERVERS)) { + g_assert(NM_IN_STRSET(obj_property_name, "nameservers", "nameserver-data")); + expected_property_name = NM_IP_CONFIG_NAMESERVERS; + } else if (mif == &_nml_dbus_meta_iface_nm_ip4config + && nm_streq(pspec->name, NM_IP_CONFIG_WINS_SERVERS)) { + g_assert(NM_IN_STRSET(obj_property_name, "wins-servers", "wins-server-data")); + expected_property_name = NM_IP_CONFIG_WINS_SERVERS; + } else if (mif == &_nml_dbus_meta_iface_nm_dnsmanager + && nm_streq(pspec->name, NM_CLIENT_DNS_CONFIGURATION)) { + g_assert_cmpstr(obj_property_name, ==, "configuration"); + expected_property_name = NM_CLIENT_DNS_CONFIGURATION; + } else if (mif == &_nml_dbus_meta_iface_nm_dnsmanager + && nm_streq(pspec->name, NM_CLIENT_DNS_MODE)) { + g_assert_cmpstr(obj_property_name, ==, "mode"); + expected_property_name = NM_CLIENT_DNS_MODE; + } else if (mif == &_nml_dbus_meta_iface_nm_dnsmanager + && nm_streq(pspec->name, NM_CLIENT_DNS_RC_MANAGER)) { + g_assert_cmpstr(obj_property_name, ==, "rc-manager"); + expected_property_name = NM_CLIENT_DNS_RC_MANAGER; + } else + expected_property_name = obj_property_name; + + g_assert_cmpstr(expected_property_name, ==, pspec->name); + + if (!mpr->use_notify_update_prop) { + for (p_expected_type_2 = &expected_types[0]; + p_expected_type_2 < &expected_types[G_N_ELEMENTS(expected_types)]; + p_expected_type_2++) { + if (!nm_streq((const char *) mpr->dbus_type, p_expected_type_2->dbus_type)) + continue; + if (pspec->value_type == p_expected_type_2->default_gtype + || (p_expected_type_2->default_gtype == G_TYPE_ENUM + && g_type_is_a(pspec->value_type, G_TYPE_ENUM)) + || (p_expected_type_2->default_gtype == G_TYPE_FLAGS + && g_type_is_a(pspec->value_type, G_TYPE_FLAGS)) + || (p_expected_type_2->default_gtype == NM_TYPE_OBJECT + && nm_streq((const char *) mpr->dbus_type, "o") + && g_type_is_a(pspec->value_type, NM_TYPE_OBJECT))) + break; + } + if (p_expected_type_2 >= &expected_types[G_N_ELEMENTS(expected_types)]) { + g_error("D-Bus property \"%s.%s\" (type \"%s\") maps to property \"%s\", " + "but that has an unexpected property type %s (expected %s)", + mif->dbus_iface_name, + mpr->dbus_property_name, + (const char *) mpr->dbus_type, + pspec->name, + g_type_name(pspec->value_type), + g_type_name(p_expected_type->default_gtype)); + } + } + + if (!nm_utils_g_param_spec_is_default(pspec)) { + /* We expect our properties to have a default value of zero/NULL. + * Except those whitelisted here: */ + if ((mif == &_nml_dbus_meta_iface_nm_accesspoint + && nm_streq(pspec->name, NM_ACCESS_POINT_LAST_SEEN)) + || (mif == &_nml_dbus_meta_iface_nm_device_vxlan + && nm_streq(pspec->name, NM_DEVICE_VXLAN_LEARNING)) + || (mif == &_nml_dbus_meta_iface_nm_device_wireless + && nm_streq(pspec->name, NM_DEVICE_WIFI_LAST_SCAN)) + || (mif == &_nml_dbus_meta_iface_nm_wifip2ppeer + && nm_streq(pspec->name, NM_WIFI_P2P_PEER_LAST_SEEN)) + || (mif == &_nml_dbus_meta_iface_nm_device_tun + && NM_IN_STRSET(pspec->name, + NM_DEVICE_TUN_GROUP, + NM_DEVICE_TUN_OWNER))) { + /* pass */ + } else { + g_error("property %s.%s (%s.%s) does not have a default value of zero", + mif->dbus_iface_name, + mpr->dbus_property_name, + g_type_name(gtype), + pspec->name); + } + } + } + } + + if (klass) { + for (l = 0; l < mif->n_obj_properties; l++) { + guint8 ridx = mif->obj_properties_reverse_idx[l]; + + if (ridx != 0xFFu) + g_assert_cmpint(mif->dbus_properties[ridx].obj_properties_idx, ==, l); + } + } + + g_assert(mif == nml_dbus_meta_iface_get(mif->dbus_iface_name)); + } + + meta_iface = nml_dbus_meta_iface_get(NM_DBUS_INTERFACE); + g_assert(meta_iface); + g_assert(meta_iface == &_nml_dbus_meta_iface_nm); + g_assert_cmpstr(meta_iface->dbus_iface_name, ==, NM_DBUS_INTERFACE); + + meta_property = nml_dbus_meta_property_get(meta_iface, "Version", &prop_idx); + g_assert(meta_property); + g_assert_cmpstr(meta_property->dbus_property_name, ==, "Version"); + g_assert(&meta_iface->dbus_properties[prop_idx] == meta_property); +} + +/*****************************************************************************/ + +static void +test_dbus_meta_types(void) +{ + struct list_data { + const char * dbus_iface_name; + GType gtype; + NMLDBusMetaInteracePrio interface_prio; + } list[] = { + { + NM_DBUS_INTERFACE, + NM_TYPE_CLIENT, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + }, + { + NM_DBUS_INTERFACE_ACCESS_POINT, + NM_TYPE_ACCESS_POINT, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + NM_TYPE_ACTIVE_CONNECTION, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_10, + }, /* otherwise, NM_TYPE_VPN_CONNECTION. */ + { + NM_DBUS_INTERFACE_DEVICE_6LOWPAN, + NM_TYPE_DEVICE_6LOWPAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_ADSL, + NM_TYPE_DEVICE_ADSL, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_BOND, + NM_TYPE_DEVICE_BOND, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_BRIDGE, + NM_TYPE_DEVICE_BRIDGE, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_BLUETOOTH, + NM_TYPE_DEVICE_BT, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_DUMMY, + NM_TYPE_DEVICE_DUMMY, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WIRED, + NM_TYPE_DEVICE_ETHERNET, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_20, + }, + { + NM_DBUS_INTERFACE_DEVICE_VETH, + NM_TYPE_DEVICE_VETH, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_GENERIC, + NM_TYPE_DEVICE_GENERIC, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_INFINIBAND, + NM_TYPE_DEVICE_INFINIBAND, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL, + NM_TYPE_DEVICE_IP_TUNNEL, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_MACSEC, + NM_TYPE_DEVICE_MACSEC, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_MACVLAN, + NM_TYPE_DEVICE_MACVLAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_MODEM, + NM_TYPE_DEVICE_MODEM, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_OLPC_MESH, + NM_TYPE_DEVICE_OLPC_MESH, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE, + NM_TYPE_DEVICE_OVS_INTERFACE, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_OVS_PORT, + NM_TYPE_DEVICE_OVS_PORT, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE, + NM_TYPE_DEVICE_OVS_BRIDGE, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + NM_TYPE_DEVICE_WIFI_P2P, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_PPP, + NM_TYPE_DEVICE_PPP, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_TEAM, + NM_TYPE_DEVICE_TEAM, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_TUN, + NM_TYPE_DEVICE_TUN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_VLAN, + NM_TYPE_DEVICE_VLAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WPAN, + NM_TYPE_DEVICE_WPAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_VXLAN, + NM_TYPE_DEVICE_VXLAN, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + NM_TYPE_DEVICE_WIFI, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DEVICE_WIREGUARD, + NM_TYPE_DEVICE_WIREGUARD, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DHCP4_CONFIG, + NM_TYPE_DHCP4_CONFIG, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_DHCP6_CONFIG, + NM_TYPE_DHCP6_CONFIG, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_IP4_CONFIG, + NM_TYPE_IP4_CONFIG, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_IP6_CONFIG, + NM_TYPE_IP6_CONFIG, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_WIFI_P2P_PEER, + NM_TYPE_WIFI_P2P_PEER, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + NM_TYPE_REMOTE_CONNECTION, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_SETTINGS, + NM_TYPE_CLIENT, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + }, + { + NM_DBUS_INTERFACE_DNS_MANAGER, + NM_TYPE_CLIENT, + NML_DBUS_META_INTERFACE_PRIO_NMCLIENT, + }, + { + NM_DBUS_INTERFACE_VPN_CONNECTION, + NM_TYPE_VPN_CONNECTION, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + { + NM_DBUS_INTERFACE_CHECKPOINT, + NM_TYPE_CHECKPOINT, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, + }, + }; + guint i; + + /* These iface<->gtype associations are copied from "nm-client.c"'s obj_nm_for_gdbus_object(). + * This is redundant to the meta-data, still check that the meta data matches. */ + for (i = 0; i < G_N_ELEMENTS(list); i++) { + const struct list_data *d = &list[i]; + const NMLDBusMetaIface *meta_iface; + + meta_iface = nml_dbus_meta_iface_get(d->dbus_iface_name); + g_assert(meta_iface); + g_assert_cmpint(meta_iface->interface_prio, ==, d->interface_prio); + g_assert(meta_iface->get_type_fcn() == d->gtype); + } +} + +/*****************************************************************************/ + +static void +test_nm_auth_permissions(void) +{ + int i, j; + + G_STATIC_ASSERT(G_N_ELEMENTS(nm_auth_permission_names_by_idx) == NM_CLIENT_PERMISSION_LAST); + G_STATIC_ASSERT(G_N_ELEMENTS(nm_auth_permission_sorted) == NM_CLIENT_PERMISSION_LAST); + + for (i = 0; i < NM_CLIENT_PERMISSION_LAST; i++) { + g_assert(nm_auth_permission_names_by_idx[i]); + g_assert(NM_STR_HAS_PREFIX(nm_auth_permission_names_by_idx[i], + "org.freedesktop.NetworkManager.")); + g_assert_cmpint(nm_auth_permission_sorted[i], >, 0); + g_assert_cmpint(nm_auth_permission_sorted[i], <=, NM_CLIENT_PERMISSION_LAST); + for (j = i + 1; j < NM_CLIENT_PERMISSION_LAST; j++) { + g_assert_cmpint(nm_auth_permission_sorted[i], !=, nm_auth_permission_sorted[j]); + g_assert_cmpstr(nm_auth_permission_names_by_idx[i], + !=, + nm_auth_permission_names_by_idx[j]); + } + } + for (i = 1; i < NM_CLIENT_PERMISSION_LAST; i++) { + NMClientPermission a = nm_auth_permission_sorted[i - 1]; + NMClientPermission b = nm_auth_permission_sorted[i]; + const char * s_a = nm_auth_permission_names_by_idx[a - 1]; + const char * s_b = nm_auth_permission_names_by_idx[b - 1]; + + g_assert_cmpstr(s_a, <, s_b); + g_assert(a != b); + g_assert(s_a != s_b); + } + for (i = 1; i <= NM_CLIENT_PERMISSION_LAST; i++) { + const char *s = nm_auth_permission_to_string(i); + + g_assert_cmpstr(s, ==, nm_auth_permission_names_by_idx[i - 1]); + g_assert(s == nm_auth_permission_names_by_idx[i - 1]); + g_assert_cmpint(nm_auth_permission_from_string(s), ==, i); + } + + for (i = 0; i < NM_CLIENT_PERMISSION_LAST; i++) + g_assert_cmpint(nm_auth_permission_from_string(nm_auth_permission_names_by_idx[i]), + ==, + i + 1); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init(&argc, &argv, TRUE); + + g_test_add_func("/libnm/general/fixup_product_string", test_fixup_product_string); + g_test_add_func("/libnm/general/fixup_vendor_string", test_fixup_vendor_string); + g_test_add_func("/libnm/general/nm_vpn_service_plugin_read_vpn_details", + test_nm_vpn_service_plugin_read_vpn_details); + g_test_add_func("/libnm/general/test_types", test_types); + g_test_add_func("/libnm/general/test_nml_dbus_meta", test_nml_dbus_meta); + g_test_add_func("/libnm/general/test_dbus_meta_types", test_dbus_meta_types); + g_test_add_func("/libnm/general/test_nm_auth_permissions", test_nm_auth_permissions); + + return g_test_run(); +} diff --git a/src/libnm-client-impl/tests/test-nm-client.c b/src/libnm-client-impl/tests/test-nm-client.c new file mode 100644 index 00000000..55b886a0 --- /dev/null +++ b/src/libnm-client-impl/tests/test-nm-client.c @@ -0,0 +1,1300 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include +#include + +#include "libnm-client-test/nm-test-libnm-utils.h" + +static struct { + GMainLoop *loop; +} gl = {}; + +/*****************************************************************************/ + +static gboolean +loop_quit(gpointer user_data) +{ + g_main_loop_quit((GMainLoop *) user_data); + return G_SOURCE_REMOVE; +} + +/*****************************************************************************/ + +static void +devices_notify_cb(NMClient *c, GParamSpec *pspec, gpointer user_data) +{ + gboolean * notified = user_data; + const GPtrArray *devices; + NMDevice * device; + + devices = nm_client_get_devices(c); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 1); + + device = g_ptr_array_index(devices, 0); + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + + *notified = TRUE; +} + +static void +test_device_added(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + const GPtrArray * devices; + NMDevice * device; + gboolean notified = FALSE; + GError * error = NULL; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + devices = nm_client_get_devices(client); + g_assert(devices->len == 0); + + g_signal_connect(client, "notify::devices", (GCallback) devices_notify_cb, ¬ified); + + /* Tell the test service to add a new device */ + nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + + /* coverity[loop_condition] */ + while (!notified) + g_main_context_iteration(NULL, TRUE); + + g_signal_handlers_disconnect_by_func(client, devices_notify_cb, ¬ified); + + devices = nm_client_get_devices(client); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 1); + + device = g_ptr_array_index(devices, 0); + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + + /* Try deleting the device via the ordinary NM interface, which should fail */ + nm_device_delete(device, NULL, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_SOFTWARE); + g_clear_error(&error); +} + +/*****************************************************************************/ + +typedef enum { + SIGNAL_FIRST = 0x01, + SIGNAL_SECOND = 0x02, + SIGNAL_MASK = 0x0F, + NOTIFY_FIRST = 0x10, + NOTIFY_SECOND = 0x20, + NOTIFY_MASK = 0xF0 +} DeviceSignaledAfterInitType; + +static void +device_sai_added_cb(NMClient *c, NMDevice *device, gpointer user_data) +{ + guint *result = user_data; + + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + + g_assert((*result & SIGNAL_MASK) == 0); + *result |= *result ? SIGNAL_SECOND : SIGNAL_FIRST; +} + +static void +devices_sai_notify_cb(NMClient *c, GParamSpec *pspec, gpointer user_data) +{ + guint * result = user_data; + const GPtrArray *devices; + NMDevice * device; + + g_assert_cmpstr(pspec->name, ==, "devices"); + + devices = nm_client_get_devices(c); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 1); + + device = g_ptr_array_index(devices, 0); + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + + g_assert((*result & NOTIFY_MASK) == 0); + *result |= *result ? NOTIFY_SECOND : NOTIFY_FIRST; +} + +static void +test_device_added_signal_after_init(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + const GPtrArray * devices; + NMDevice * device; + guint result = 0; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + devices = nm_client_get_devices(client); + g_assert(devices->len == 0); + + g_signal_connect(client, NM_CLIENT_DEVICE_ADDED, (GCallback) device_sai_added_cb, &result); + + g_signal_connect(client, + "notify::" NM_CLIENT_DEVICES, + (GCallback) devices_sai_notify_cb, + &result); + + /* Tell the test service to add a new device */ + nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + + /* Ensure the 'device-added' signal doesn't show up before + * the 'Devices' property change notification */ + /* coverity[loop_condition] */ + while (!(result & SIGNAL_MASK) && !(result & NOTIFY_MASK)) + g_main_context_iteration(NULL, TRUE); + + g_signal_handlers_disconnect_by_func(client, device_sai_added_cb, &result); + g_signal_handlers_disconnect_by_func(client, devices_sai_notify_cb, &result); + + g_assert((result & SIGNAL_MASK) == SIGNAL_SECOND); + g_assert((result & NOTIFY_MASK) == NOTIFY_FIRST); + + devices = nm_client_get_devices(client); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 1); + + device = g_ptr_array_index(devices, 0); + g_assert(device); + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); +} + +/*****************************************************************************/ + +static const char *expected_bssid = "66:55:44:33:22:11"; + +typedef struct { + GMainLoop *loop; + gboolean found; + char * ap_path; + gboolean signaled; + gboolean notified; + guint quit_id; + guint quit_count; +} WifiApInfo; + +static void +wifi_check_quit(WifiApInfo *info) +{ + info->quit_count--; + if (info->quit_count == 0) { + g_source_remove(info->quit_id); + info->quit_id = 0; + g_main_loop_quit(info->loop); + } +} + +static void +got_ap_path(WifiApInfo *info, const char *path) +{ + if (info->ap_path) + g_assert_cmpstr(info->ap_path, ==, path); + else + info->ap_path = g_strdup(path); +} + +static void +wifi_ap_added_cb(NMDeviceWifi *w, NMAccessPoint *ap, WifiApInfo *info) +{ + g_assert(ap); + g_assert_cmpstr(nm_access_point_get_bssid(ap), ==, expected_bssid); + got_ap_path(info, nm_object_get_path(NM_OBJECT(ap))); + + info->signaled = TRUE; + wifi_check_quit(info); +} + +static void +wifi_ap_add_notify_cb(NMDeviceWifi *w, GParamSpec *pspec, WifiApInfo *info) +{ + const GPtrArray *aps; + NMAccessPoint * ap; + + aps = nm_device_wifi_get_access_points(w); + g_assert(aps); + g_assert_cmpint(aps->len, ==, 1); + + ap = g_ptr_array_index(aps, 0); + g_assert(ap); + g_assert_cmpstr(nm_access_point_get_bssid(ap), ==, "66:55:44:33:22:11"); + got_ap_path(info, nm_object_get_path(NM_OBJECT(ap))); + + info->notified = TRUE; + wifi_check_quit(info); +} + +static void +wifi_ap_removed_cb(NMDeviceWifi *w, NMAccessPoint *ap, WifiApInfo *info) +{ + g_assert(ap); + g_assert_cmpstr(info->ap_path, ==, nm_object_get_path(NM_OBJECT(ap))); + + info->signaled = TRUE; + wifi_check_quit(info); +} + +static void +wifi_ap_remove_notify_cb(NMDeviceWifi *w, GParamSpec *pspec, WifiApInfo *info) +{ + const GPtrArray *aps; + + aps = nm_device_wifi_get_access_points(w); + g_assert(aps->len == 0); + + info->notified = TRUE; + wifi_check_quit(info); +} + +static void +test_wifi_ap_added_removed(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + NMDeviceWifi * wifi; + WifiApInfo info = {gl.loop, FALSE, FALSE, 0, 0}; + GVariant * ret; + GError * error = NULL; + gs_free char * expected_path = NULL; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + /*************************************/ + /* Add the wifi device */ + wifi = (NMDeviceWifi *) nmtstc_service_add_device(sinfo, client, "AddWifiDevice", "wlan0"); + g_assert(NM_IS_DEVICE_WIFI(wifi)); + + /*************************************/ + /* Add the wifi AP */ + info.signaled = FALSE; + info.notified = FALSE; + info.quit_id = 0; + + ret = g_dbus_proxy_call_sync(sinfo->proxy, + "AddWifiAp", + g_variant_new("(sss)", "wlan0", "test-ap", expected_bssid), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error(error); + g_assert(ret); + g_assert_cmpstr(g_variant_get_type_string(ret), ==, "(o)"); + g_variant_get(ret, "(o)", &expected_path); + g_variant_unref(ret); + + g_signal_connect(wifi, "access-point-added", (GCallback) wifi_ap_added_cb, &info); + info.quit_count = 1; + + g_signal_connect(wifi, "notify::access-points", (GCallback) wifi_ap_add_notify_cb, &info); + info.quit_count++; + + /* Wait for libnm to find the AP */ + info.quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + + g_assert(info.signaled); + g_assert(info.notified); + g_assert(info.ap_path); + g_assert_cmpstr(info.ap_path, ==, expected_path); + g_signal_handlers_disconnect_by_func(wifi, wifi_ap_added_cb, &info); + g_signal_handlers_disconnect_by_func(wifi, wifi_ap_add_notify_cb, &info); + + /*************************************/ + /* Remove the wifi device */ + info.signaled = FALSE; + info.notified = FALSE; + info.quit_id = 0; + + ret = g_dbus_proxy_call_sync(sinfo->proxy, + "RemoveWifiAp", + g_variant_new("(so)", "wlan0", expected_path), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error(error); + nm_clear_pointer(&ret, g_variant_unref); + + g_signal_connect(wifi, "access-point-removed", (GCallback) wifi_ap_removed_cb, &info); + info.quit_count = 1; + + g_signal_connect(wifi, "notify::access-points", (GCallback) wifi_ap_remove_notify_cb, &info); + info.quit_count++; + + /* Wait for libnm to find the AP */ + info.quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + + g_assert(info.signaled); + g_assert(info.notified); + g_signal_handlers_disconnect_by_func(wifi, wifi_ap_removed_cb, &info); + g_signal_handlers_disconnect_by_func(wifi, wifi_ap_remove_notify_cb, &info); + + g_free(info.ap_path); +} + +/*****************************************************************************/ + +typedef struct { + GMainLoop *loop; + gboolean signaled; + gboolean notified; + guint quit_count; + guint quit_id; +} DaInfo; + +static void +da_check_quit(DaInfo *info) +{ + info->quit_count--; + if (info->quit_count == 0) { + g_source_remove(info->quit_id); + info->quit_id = 0; + g_main_loop_quit(info->loop); + } +} + +static void +da_device_removed_cb(NMClient *c, NMDevice *device, DaInfo *info) +{ + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0"); + info->signaled = TRUE; + da_check_quit(info); +} + +static void +da_devices_notify_cb(NMClient *c, GParamSpec *pspec, DaInfo *info) +{ + const GPtrArray *devices; + NMDevice * device; + guint i; + const char * iface; + + devices = nm_client_get_devices(c); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 2); + + for (i = 0; i < devices->len; i++) { + device = g_ptr_array_index(devices, i); + iface = nm_device_get_iface(device); + + g_assert(!strcmp(iface, "wlan0") || !strcmp(iface, "eth1")); + } + + info->notified = TRUE; + da_check_quit(info); +} + +static void +test_devices_array(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + DaInfo info = {gl.loop}; + NMDevice * wlan0, *eth0, *eth1, *device; + const GPtrArray * devices; + GError * error = NULL; + GVariant * ret; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + /*************************************/ + /* Add some devices */ + wlan0 = nmtstc_service_add_device(sinfo, client, "AddWifiDevice", "wlan0"); + eth0 = nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + eth1 = nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth1"); + + /* Ensure the devices now exist */ + devices = nm_client_get_devices(client); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 3); + + device = nm_client_get_device_by_iface(client, "wlan0"); + g_assert(NM_IS_DEVICE_WIFI(device)); + g_assert(device == wlan0); + + device = nm_client_get_device_by_iface(client, "eth0"); + g_assert(NM_IS_DEVICE_ETHERNET(device)); + g_assert(device == eth0); + + device = nm_client_get_device_by_iface(client, "eth1"); + g_assert(NM_IS_DEVICE_ETHERNET(device)); + g_assert(device == eth1); + + /********************************/ + /* Now remove the device in the middle */ + ret = g_dbus_proxy_call_sync(sinfo->proxy, + "RemoveDevice", + g_variant_new("(o)", nm_object_get_path(NM_OBJECT(eth0))), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error(error); + g_assert(ret); + g_variant_unref(ret); + + g_signal_connect(client, "device-removed", (GCallback) da_device_removed_cb, &info); + + g_signal_connect(client, "notify::devices", (GCallback) da_devices_notify_cb, &info); + info.quit_count = 2; + + /* Wait for libnm to notice the changes */ + info.quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + + g_assert_cmpint(info.quit_count, ==, 0); + g_signal_handlers_disconnect_by_func(client, da_device_removed_cb, &info); + g_signal_handlers_disconnect_by_func(client, da_devices_notify_cb, &info); + + /* Ensure only two are left */ + devices = nm_client_get_devices(client); + g_assert(devices); + g_assert_cmpint(devices->len, ==, 2); + + device = nm_client_get_device_by_iface(client, "wlan0"); + g_assert(NM_IS_DEVICE_WIFI(device)); + g_assert(device == wlan0); + + device = nm_client_get_device_by_iface(client, "eth1"); + g_assert(NM_IS_DEVICE_ETHERNET(device)); + g_assert(device == eth1); +} + +static void +nm_running_changed(GObject *client, GParamSpec *pspec, gpointer user_data) +{ + int *running_changed = user_data; + + (*running_changed)++; + g_main_loop_quit(gl.loop); +} + +static void +test_client_nm_running(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client1 = NULL; + gs_unref_object NMClient *client2 = NULL; + guint quit_id; + int running_changed = 0; + GError * error = NULL; + + client1 = nmtstc_client_new(TRUE); + + g_assert(!nm_client_get_nm_running(client1)); + g_assert_cmpstr(nm_client_get_version(client1), ==, NULL); + + g_assert(!nm_client_networking_get_enabled(client1)); + /* This will have no effect, but it shouldn't cause any warnings either. */ + nm_client_networking_set_enabled(client1, TRUE, NULL); + g_assert(!nm_client_networking_get_enabled(client1)); + + /* OTOH, this should result in an error */ + nm_client_set_logging(client1, "DEFAULT", "INFO", &error); + g_assert_error(error, NM_CLIENT_ERROR, NM_CLIENT_ERROR_MANAGER_NOT_RUNNING); + g_clear_error(&error); + + /* Now start the test service. */ + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client2 = nmtstc_client_new(FALSE); + + /* client2 should know that NM is running, but the previously-created + * client1 hasn't gotten the news yet. + */ + g_assert(!nm_client_get_nm_running(client1)); + g_assert(nm_client_get_nm_running(client2)); + + g_signal_connect(client1, + "notify::" NM_CLIENT_NM_RUNNING, + G_CALLBACK(nm_running_changed), + &running_changed); + quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + g_assert_cmpint(running_changed, ==, 1); + g_assert(nm_client_get_nm_running(client1)); + g_source_remove(quit_id); + + /* And kill it */ + nm_clear_pointer(&sinfo, nmtstc_service_cleanup); + + g_assert(nm_client_get_nm_running(client1)); + + quit_id = g_timeout_add_seconds(5, loop_quit, gl.loop); + g_main_loop_run(gl.loop); + g_assert_cmpint(running_changed, ==, 2); + g_assert(!nm_client_get_nm_running(client1)); + g_source_remove(quit_id); +} + +typedef struct { + GMainLoop * loop; + NMActiveConnection *ac; + + int remaining; + + NMDevice *device; + gulong ac_signal_id; +} TestACInfo; + +static void +assert_ac_and_device(NMClient *client) +{ + const GPtrArray * devices, *acs, *ac_devices; + NMDevice * device, *ac_device; + NMActiveConnection *ac, *device_ac; + + acs = nm_client_get_active_connections(client); + g_assert(acs != NULL); + g_assert_cmpint(acs->len, ==, 1); + devices = nm_client_get_devices(client); + g_assert(devices != NULL); + g_assert_cmpint(devices->len, >=, 1); + + ac = acs->pdata[0]; + ac_devices = nm_active_connection_get_devices(ac); + g_assert(ac_devices != NULL); + g_assert_cmpint(ac_devices->len, ==, 1); + ac_device = ac_devices->pdata[0]; + g_assert(ac_device != NULL); + + device = devices->pdata[0]; + if (device != ac_device && devices->len > 1) + device = devices->pdata[1]; + + g_assert_cmpstr(nm_object_get_path(NM_OBJECT(device)), + ==, + nm_object_get_path(NM_OBJECT(ac_device))); + g_assert(device == ac_device); + + device_ac = nm_device_get_active_connection(device); + if (!device_ac) { + /* the stub NetworkManager service starts activating in an idle handler (delayed). That means, the + * device may not yet refer to the active connection at this point. */ + } else { + g_assert_cmpstr(nm_object_get_path(NM_OBJECT(ac)), + ==, + nm_object_get_path(NM_OBJECT(device_ac))); + g_assert(ac == device_ac); + } +} + +static void +add_and_activate_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMClient * client = NM_CLIENT(object); + TestACInfo *info = user_data; + GError * error = NULL; + + info->ac = nm_client_add_and_activate_connection_finish(client, result, &error); + g_assert_no_error(error); + g_assert(info->ac != NULL); + + assert_ac_and_device(client); + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +static void +client_acs_changed_cb(GObject *client, GParamSpec *pspec, gpointer user_data) +{ + TestACInfo * info = user_data; + const GPtrArray *acs; + + acs = nm_client_get_active_connections(NM_CLIENT(client)); + g_assert(acs != NULL); + g_assert_cmpint(acs->len, ==, 1); + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +static void +device_ac_changed_cb(GObject *device, GParamSpec *pspec, gpointer user_data) +{ + TestACInfo *info = user_data; + + g_assert(nm_device_get_active_connection(NM_DEVICE(device)) != NULL); + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +static void +test_active_connections(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + NMDevice * device; + NMConnection * conn; + TestACInfo info = {gl.loop, NULL, 0}; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + /* Tell the test service to add a new device */ + device = nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + + conn = nmtst_create_minimal_connection("test-ac", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + nm_client_add_and_activate_connection_async(client, + conn, + device, + NULL, + NULL, + add_and_activate_cb, + &info); + g_object_unref(conn); + + g_signal_connect(client, + "notify::" NM_CLIENT_ACTIVE_CONNECTIONS, + G_CALLBACK(client_acs_changed_cb), + &info); + g_signal_connect(device, + "notify::" NM_DEVICE_ACTIVE_CONNECTION, + G_CALLBACK(device_ac_changed_cb), + &info); + + /* Two signals plus activate_cb */ + info.remaining = 3; + g_main_loop_run(gl.loop); + g_signal_handlers_disconnect_by_func(client, client_acs_changed_cb, &info); + g_signal_handlers_disconnect_by_func(device, device_ac_changed_cb, &info); + + g_assert(info.ac != NULL); + + g_object_unref(info.ac); + g_clear_object(&client); + + /* Ensure that we can correctly resolve the recursive property link between the + * AC and the Device in a newly-created client. + */ + client = nmtstc_client_new(TRUE); + assert_ac_and_device(client); + g_clear_object(&client); + + client = nmtstc_client_new(TRUE); + assert_ac_and_device(client); + g_clear_object(&client); +} + +static void +client_devices_changed_cb(GObject *client, GParamSpec *pspec, gpointer user_data) +{ + TestACInfo * info = user_data; + const GPtrArray *devices; + NMDevice * device; + + devices = nm_client_get_devices(NM_CLIENT(client)); + g_assert(devices != NULL); + if (devices->len < 2) + return; + g_assert_cmpint(devices->len, ==, 2); + + if (NM_IS_DEVICE_VLAN(devices->pdata[0])) + device = devices->pdata[0]; + else if (NM_IS_DEVICE_VLAN(devices->pdata[1])) + device = devices->pdata[1]; + else + g_assert_not_reached(); + + g_assert_cmpstr(nm_device_get_iface(device), ==, "eth0.1"); + + if (!nm_device_get_active_connection(device)) { + g_assert(info->ac_signal_id == 0); + info->remaining++; + info->device = device; + g_object_add_weak_pointer(G_OBJECT(device), (gpointer *) &info->device); + info->ac_signal_id = g_signal_connect(device, + "notify::" NM_DEVICE_ACTIVE_CONNECTION, + G_CALLBACK(device_ac_changed_cb), + info); + } + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +typedef struct { + GMainLoop * loop; + NMRemoteConnection *remote; +} TestConnectionInfo; + +static void +add_connection_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + TestConnectionInfo *info = user_data; + GError * error = NULL; + + info->remote = nm_client_add_connection_finish(NM_CLIENT(object), result, &error); + g_assert_no_error(error); + g_main_loop_quit(info->loop); +} + +static void +activate_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMClient * client = NM_CLIENT(object); + TestACInfo *info = user_data; + GError * error = NULL; + + info->ac = nm_client_activate_connection_finish(client, result, &error); + g_assert_no_error(error); + g_assert(info->ac != NULL); + + assert_ac_and_device(client); + + info->remaining--; + if (!info->remaining) + g_main_loop_quit(info->loop); +} + +static void +test_activate_virtual(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + NMConnection * conn; + NMSettingConnection * s_con; + NMSettingVlan * s_vlan; + TestACInfo info = {gl.loop, NULL, 0}; + TestConnectionInfo conn_info = {gl.loop, NULL}; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + nmtstc_service_add_device(sinfo, client, "AddWiredDevice", "eth0"); + + conn = nmtst_create_minimal_connection("test-ac", NULL, NM_SETTING_VLAN_SETTING_NAME, &s_con); + g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0.1", NULL); + s_vlan = nm_connection_get_setting_vlan(conn); + g_object_set(s_vlan, NM_SETTING_VLAN_ID, 1, NM_SETTING_VLAN_PARENT, "eth0", NULL); + + nm_client_add_connection_async(client, conn, TRUE, NULL, add_connection_cb, &conn_info); + g_main_loop_run(gl.loop); + g_object_unref(conn); + conn = NM_CONNECTION(conn_info.remote); + + nm_client_activate_connection_async(client, conn, NULL, NULL, NULL, activate_cb, &info); + g_object_unref(conn); + + g_signal_connect(client, + "notify::" NM_CLIENT_ACTIVE_CONNECTIONS, + G_CALLBACK(client_acs_changed_cb), + &info); + g_signal_connect(client, + "notify::" NM_CLIENT_DEVICES, + G_CALLBACK(client_devices_changed_cb), + &info); + + /* We're expecting a client::devices change, client::activate callback, + * and a device::active-connection change. + * The client::devices callback can hook a client::active-connections + * change and bump this if the property is not yet loaded. + */ + info.remaining = 3; + + g_main_loop_run(gl.loop); + g_signal_handlers_disconnect_by_func(client, client_acs_changed_cb, &info); + g_signal_handlers_disconnect_by_func(client, client_devices_changed_cb, &info); + + g_assert(info.ac != NULL); + g_clear_object(&info.ac); + + if (info.device) { + g_object_remove_weak_pointer(G_OBJECT(info.device), (gpointer *) &info.device); + nm_clear_g_signal_handler(info.device, &info.ac_signal_id); + } +} + +static void +test_device_connection_compatibility(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + gs_unref_object NMConnection *conn = NULL; + NMDevice * device1; + NMDevice * device2; + NMSettingWired * s_wired; + GError * error = NULL; + const char * subchannels[] = {"0.0.8000", "0.0.8001", "0.0.8002", NULL}; + const char * subchannels_2[] = {"0.0.8000", "0.0.8001", NULL}; + const char * subchannels_x[] = {"0.0.8000", "0.0.8001", "0.0.800X", NULL}; + const char * hw_addr1 = "52:54:00:ab:db:23"; + const char * hw_addr2 = "52:54:00:ab:db:24"; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + client = nmtstc_client_new(TRUE); + + /* Create two devices */ + device1 = nmtstc_service_add_wired_device(sinfo, client, "eth0", hw_addr1, subchannels); + device2 = nmtstc_service_add_wired_device(sinfo, client, "eth1", hw_addr2, NULL); + + g_assert_cmpstr(nm_device_get_hw_address(device1), ==, hw_addr1); + g_assert_cmpstr(nm_device_get_hw_address(device2), ==, hw_addr2); + + conn = + nmtst_create_minimal_connection("wired-matches", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + s_wired = nm_connection_get_setting_wired(conn); + nm_setting_wired_add_mac_blacklist_item(s_wired, "00:11:22:33:44:55"); + + /* device1 and conn are compatible */ + g_object_set(s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, + hw_addr1, + NM_SETTING_WIRED_S390_SUBCHANNELS, + subchannels, + NULL); + nm_device_connection_compatible(device1, conn, &error); + g_assert_no_error(error); + + /* device2 and conn differ in subchannels */ + g_object_set(s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, subchannels_x, NULL); + nm_device_connection_compatible(device2, conn, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION); + g_clear_error(&error); + + /* device1 and conn differ in subchannels - 2 in connection, 3 in device */ + g_object_set(s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, subchannels_2, NULL); + nm_device_connection_compatible(device1, conn, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION); + g_clear_error(&error); + + g_object_set(s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, NULL, NULL); + + /* device2 and conn differ in MAC address */ + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, "aa:bb:cc:dd:ee:ee", NULL); + nm_device_connection_compatible(device2, conn, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION); + g_clear_error(&error); + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, NULL, NULL); + + /* device1 is blacklisted in conn */ + nm_setting_wired_add_mac_blacklist_item(s_wired, hw_addr1); + nm_device_connection_compatible(device1, conn, &error); + g_assert_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION); + g_clear_error(&error); +} + +/*****************************************************************************/ + +static gboolean +_test_connection_invalid_find_connections(gpointer element, gpointer needle, gpointer user_data) +{ + NMRemoteConnection *con = NM_REMOTE_CONNECTION(element); + const char * path = needle; + + g_assert(NM_IS_REMOTE_CONNECTION(con)); + g_assert(path && *path); + + return strcmp(path, nm_connection_get_path((NMConnection *) con)) == 0; +} + +#define ASSERT_IDX(i) \ + G_STMT_START \ + { \ + g_assert_cmpint(idx[i], >=, 0); \ + g_assert(path##i &&*path##i); \ + g_assert(NM_IS_REMOTE_CONNECTION(connections->pdata[idx[i]])); \ + g_assert_cmpstr(nm_connection_get_path(connections->pdata[idx[i]]), ==, path##i); \ + } \ + G_STMT_END + +static void +test_connection_invalid(void) +{ + NMTSTC_SERVICE_INFO_SETUP(my_sinfo) + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + gs_unref_object NMClient *client = NULL; + const GPtrArray * connections; + gs_free char * path0 = NULL; + gs_free char * path1 = NULL; + gs_free char * path2 = NULL; + gs_free char * path3 = NULL; + gs_free char * uuid2 = NULL; + gsize n_found; + gssize idx[4]; + gs_unref_variant GVariant *variant = NULL; + + g_assert(g_main_loop_get_context(gl.loop) + == (g_main_context_get_thread_default() ?: g_main_context_default())); + + /************************************************************************** + * Add three connections before starting libnm. One valid, two invalid. + *************************************************************************/ + + connection = nmtst_create_minimal_connection("test-connection-invalid-0", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + nmtst_connection_normalize(connection); + g_object_set(s_con, NM_SETTING_CONNECTION_UUID, nmtst_uuid_generate(), NULL); + nmtstc_service_add_connection(my_sinfo, connection, TRUE, &path0); + + nm_connection_remove_setting(connection, NM_TYPE_SETTING_WIRED); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-1", + NM_SETTING_CONNECTION_TYPE, + "invalid-type-1", + NM_SETTING_CONNECTION_UUID, + nmtst_uuid_generate(), + NULL); + nmtstc_service_add_connection(my_sinfo, connection, FALSE, &path1); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-2", + NM_SETTING_CONNECTION_TYPE, + "invalid-type-2", + NM_SETTING_CONNECTION_UUID, + nmtst_uuid_generate(), + NULL); + variant = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + NMTST_VARIANT_EDITOR( + variant, + NMTST_VARIANT_ADD_SETTING("invalid-type-2", + nmtst_variant_new_vardict("some-key1", + g_variant_new_string("some-value1"), + "some-key2", + g_variant_new_uint32(4722)))); + g_variant_ref_sink(variant); + nmtstc_service_add_connection_variant(my_sinfo, variant, FALSE, &path2); + + client = nmtstc_client_new(TRUE); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 3); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2}), + 3, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 3); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[2]], 0, 0); + + /************************************************************************** + * After having the client up and running, add another invalid connection + *************************************************************************/ + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-2", + NM_SETTING_CONNECTION_TYPE, + "invalid-type-2", + NM_SETTING_CONNECTION_UUID, + (uuid2 = g_strdup(nmtst_uuid_generate())), + NULL); + nmtstc_service_add_connection(my_sinfo, connection, FALSE, &path3); + + nmtst_main_loop_run(gl.loop, 1000); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[3]], 0, 0); + + /************************************************************************** + * Modify the invalid connection (still invalid) + *************************************************************************/ + + NMTST_VARIANT_EDITOR(variant, + NMTST_VARIANT_CHANGE_PROPERTY("invalid-type-2", "some-key2", "u", 4721)); + g_variant_ref_sink(variant); + nmtstc_service_update_connection_variant(my_sinfo, path2, variant, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[3]], 0, 0); + + /************************************************************************** + * Modify the invalid connection (becomes valid) + *************************************************************************/ + + NMTST_VARIANT_EDITOR(variant, NMTST_VARIANT_DROP_SETTING("invalid-type-2")); + NMTST_VARIANT_EDITOR(variant, + NMTST_VARIANT_CHANGE_PROPERTY(NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_TYPE, + "s", + NM_SETTING_WIRED_SETTING_NAME)); + g_variant_ref_sink(variant); + nmtstc_service_update_connection_variant(my_sinfo, path2, variant, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_verifies_after_normalization(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[3]], 0, 0); + + /************************************************************************** + * Modify the invalid connection (still invalid) + *************************************************************************/ + + g_object_set(s_con, NM_SETTING_CONNECTION_ID, "test-connection-invalid-2x", NULL); + nmtstc_service_update_connection(my_sinfo, path3, connection, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_verifies_after_normalization(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[3]], 0, 0); + g_assert_cmpstr("test-connection-invalid-2x", + ==, + nm_connection_get_id(connections->pdata[idx[3]])); + + /************************************************************************** + * Modify the invalid connection (now becomes valid) + *************************************************************************/ + + g_clear_object(&connection); + connection = nmtst_create_minimal_connection("test-connection-invalid-2", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + nmtst_connection_normalize(connection); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-2z", + NM_SETTING_CONNECTION_TYPE, + "802-3-ethernet", + NM_SETTING_CONNECTION_UUID, + uuid2, + NULL); + + nmtstc_service_update_connection(my_sinfo, path3, connection, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_unnormalizable(connections->pdata[idx[1]], 0, 0); + nmtst_assert_connection_verifies_after_normalization(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[3]]); + g_assert_cmpstr("test-connection-invalid-2z", + ==, + nm_connection_get_id(connections->pdata[idx[3]])); + + /************************************************************************** + * Modify the invalid connection and make it valid + *************************************************************************/ + + g_clear_object(&connection); + connection = nmtst_create_minimal_connection("test-connection-invalid-1", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + nmtst_connection_normalize(connection); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "test-connection-invalid-1x", + NM_SETTING_CONNECTION_TYPE, + "802-3-ethernet", + NM_SETTING_CONNECTION_UUID, + nm_connection_get_uuid(connections->pdata[idx[1]]), + NULL); + + nmtstc_service_update_connection(my_sinfo, path1, connection, FALSE); + + nmtst_main_loop_run(gl.loop, 100); + + connections = nm_client_get_connections(client); + g_assert(connections); + + g_assert_cmpint(connections->len, ==, 4); + n_found = nmtst_find_all_indexes(connections->pdata, + connections->len, + (gpointer *) ((const char *[]){path0, path1, path2, path3}), + 4, + _test_connection_invalid_find_connections, + NULL, + idx); + g_assert_cmpint(n_found, ==, 4); + ASSERT_IDX(0); + ASSERT_IDX(1); + ASSERT_IDX(2); + ASSERT_IDX(3); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[0]]); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[1]]); + nmtst_assert_connection_verifies_after_normalization(connections->pdata[idx[2]], 0, 0); + nmtst_assert_connection_verifies_without_normalization(connections->pdata[idx[3]]); + g_assert_cmpstr("test-connection-invalid-1x", + ==, + nm_connection_get_id(connections->pdata[idx[1]])); + g_assert_cmpstr("test-connection-invalid-2z", + ==, + nm_connection_get_id(connections->pdata[idx[3]])); + +#undef ASSERT_IDX +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + g_setenv("LIBNM_USE_SESSION_BUS", "1", TRUE); + + nmtst_init(&argc, &argv, TRUE); + + gl.loop = g_main_loop_new(NULL, FALSE); + + g_test_add_func("/libnm/device-added", test_device_added); + g_test_add_func("/libnm/device-added-signal-after-init", test_device_added_signal_after_init); + g_test_add_func("/libnm/wifi-ap-added-removed", test_wifi_ap_added_removed); + g_test_add_func("/libnm/devices-array", test_devices_array); + g_test_add_func("/libnm/client-nm-running", test_client_nm_running); + g_test_add_func("/libnm/active-connections", test_active_connections); + g_test_add_func("/libnm/activate-virtual", test_activate_virtual); + g_test_add_func("/libnm/device-connection-compatibility", test_device_connection_compatibility); + g_test_add_func("/libnm/connection/invalid", test_connection_invalid); + + return g_test_run(); +} + +/*****************************************************************************/ + +/* this header is intended to be copied to users of nm_vpn_editor_plugin_call(), + * to simplify invocation of generic functions. Include it here, to compile + * the code. */ +#include "contrib/nm-vpn-editor-plugin-call.h" diff --git a/src/libnm-client-impl/tests/test-remote-settings-client.c b/src/libnm-client-impl/tests/test-remote-settings-client.c new file mode 100644 index 00000000..e5415f54 --- /dev/null +++ b/src/libnm-client-impl/tests/test-remote-settings-client.c @@ -0,0 +1,498 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2011 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include +#include + +#include "libnm-glib-aux/nm-time-utils.h" + +#include "libnm-client-test/nm-test-libnm-utils.h" + +static struct { + NMTstcServiceInfo * sinfo; + NMClient * client; + GDBusConnection * bus; + NMRemoteConnection *remote; +} gl = {}; + +/*****************************************************************************/ + +static void +add_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + gboolean *done = user_data; + GError * error = NULL; + + gl.remote = nm_client_add_connection_finish(gl.client, result, &error); + g_assert_no_error(error); + + *done = TRUE; + g_object_add_weak_pointer(G_OBJECT(gl.remote), (void **) &gl.remote); + + /* nm_client_add_connection_finish() adds a ref to @remote, but we + * want the weak pointer to be cleared as soon as @client drops its own ref. + * So drop ours. + */ + g_object_unref(gl.remote); +} + +#define TEST_CON_ID "blahblahblah" + +static void +test_add_connection(void) +{ + NMConnection *connection; + gboolean done = FALSE; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + connection = + nmtst_create_minimal_connection(TEST_CON_ID, NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + + nm_client_add_connection_async(gl.client, connection, TRUE, NULL, add_cb, &done); + + nmtst_main_context_iterate_until_assert(NULL, 5000, done); + + g_assert(gl.remote != NULL); + + /* Make sure the connection is the same as what we added */ + g_assert( + nm_connection_compare(connection, NM_CONNECTION(gl.remote), NM_SETTING_COMPARE_FLAG_EXACT) + == TRUE); + g_object_unref(connection); +} + +/*****************************************************************************/ + +static void +set_visible_cb(GObject *proxy, GAsyncResult *result, gpointer user_data) +{ + GError * error = NULL; + GVariant *ret; + + ret = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), result, &error); + g_assert_no_error(error); + g_variant_unref(ret); +} + +static void +visible_changed_cb(GObject *object, GParamSpec *pspec, gboolean *done) +{ + if (!nm_remote_connection_get_visible(NM_REMOTE_CONNECTION(object))) + *done = TRUE; +} + +static void +connection_removed_cb(NMClient *s, NMRemoteConnection *connection, gboolean *done) +{ + if (connection == gl.remote) + *done = TRUE; +} + +static void +invis_has_settings_cb(NMSetting * setting, + const char * key, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + *((gboolean *) user_data) = TRUE; +} + +static void +test_make_invisible(void) +{ + const GPtrArray *conns; + int i; + GDBusProxy * proxy; + gboolean visible_changed = FALSE, connection_removed = FALSE; + gboolean has_settings = FALSE; + char * path; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + g_assert(gl.remote != NULL); + + /* Listen for the remove event when the connection becomes invisible */ + g_signal_connect(gl.remote, + "notify::" NM_REMOTE_CONNECTION_VISIBLE, + G_CALLBACK(visible_changed_cb), + &visible_changed); + g_signal_connect(gl.client, + "connection-removed", + G_CALLBACK(connection_removed_cb), + &connection_removed); + + path = g_strdup(nm_connection_get_path(NM_CONNECTION(gl.remote))); + proxy = g_dbus_proxy_new_sync(gl.bus, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + NM_DBUS_SERVICE, + path, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + NULL, + NULL); + g_assert(proxy != NULL); + + /* Bypass the NMClient object so we can test it independently */ + g_dbus_proxy_call(proxy, + "SetVisible", + g_variant_new("(b)", FALSE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + set_visible_cb, + NULL); + + /* Wait for the connection to be removed */ + nmtst_main_context_iterate_until_assert(NULL, 5000, visible_changed && connection_removed); + + g_signal_handlers_disconnect_by_func(gl.remote, + G_CALLBACK(visible_changed_cb), + &visible_changed); + g_signal_handlers_disconnect_by_func(gl.client, + G_CALLBACK(connection_removed_cb), + &connection_removed); + + /* Ensure NMClient no longer has the connection */ + conns = nm_client_get_connections(gl.client); + for (i = 0; i < conns->len; i++) { + NMConnection *candidate = NM_CONNECTION(conns->pdata[i]); + + g_assert((gpointer) gl.remote != (gpointer) candidate); + g_assert(strcmp(path, nm_connection_get_path(candidate)) != 0); + } + + /* And ensure the invisible connection no longer has any settings */ + g_assert(gl.remote); + nm_connection_for_each_setting_value(NM_CONNECTION(gl.remote), + invis_has_settings_cb, + &has_settings); + g_assert(has_settings == FALSE); + + g_free(path); + g_object_unref(proxy); +} + +/*****************************************************************************/ + +static void +vis_new_connection_cb(NMClient *foo, NMRemoteConnection *connection, NMRemoteConnection **new) +{ + *new = connection; +} + +static void +test_make_visible(void) +{ + const GPtrArray *conns; + int i; + GDBusProxy * proxy; + gboolean found = FALSE; + char * path; + NMRemoteConnection *new = NULL; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + g_assert(gl.remote != NULL); + + /* Wait for the new-connection signal when the connection is visible again */ + g_signal_connect(gl.client, + NM_CLIENT_CONNECTION_ADDED, + G_CALLBACK(vis_new_connection_cb), + &new); + + path = g_strdup(nm_connection_get_path(NM_CONNECTION(gl.remote))); + proxy = g_dbus_proxy_new_sync(gl.bus, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + NM_DBUS_SERVICE, + path, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + NULL, + NULL); + g_assert(proxy != NULL); + + /* Bypass the NMClient object so we can test it independently */ + g_dbus_proxy_call(proxy, + "SetVisible", + g_variant_new("(b)", TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + set_visible_cb, + NULL); + + /* Wait for the settings service to announce the connection again */ + nmtst_main_context_iterate_until_assert(NULL, 5000, new); + + /* Ensure the new connection is the same as the one we made visible again */ + g_assert(new == gl.remote); + + g_signal_handlers_disconnect_by_func(gl.client, G_CALLBACK(vis_new_connection_cb), &new); + + /* Ensure NMClient has the connection */ + conns = nm_client_get_connections(gl.client); + for (i = 0; i < conns->len; i++) { + NMConnection *candidate = NM_CONNECTION(conns->pdata[i]); + + if ((gpointer) gl.remote == (gpointer) candidate) { + g_assert_cmpstr(path, ==, nm_connection_get_path(candidate)); + g_assert_cmpstr(TEST_CON_ID, ==, nm_connection_get_id(candidate)); + found = TRUE; + break; + } + } + g_assert(found == TRUE); + + g_free(path); + g_object_unref(proxy); +} + +/*****************************************************************************/ + +static void +deleted_cb(GObject *proxy, GAsyncResult *result, gpointer user_data) +{ + GError * error = NULL; + GVariant *ret; + + ret = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), result, &error); + g_assert_no_error(error); + g_variant_unref(ret); +} + +static void +removed_cb(NMClient *s, NMRemoteConnection *connection, gboolean *done) +{ + if (connection == gl.remote) + *done = TRUE; +} + +static void +test_remove_connection(void) +{ + NMRemoteConnection *connection; + const GPtrArray * conns; + int i; + GDBusProxy * proxy; + gboolean done = FALSE; + char * path; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + /* Find a connection to delete */ + conns = nm_client_get_connections(gl.client); + g_assert_cmpint(conns->len, >, 0); + + connection = NM_REMOTE_CONNECTION(conns->pdata[0]); + g_assert(connection); + g_assert(gl.remote == connection); + path = g_strdup(nm_connection_get_path(NM_CONNECTION(connection))); + g_signal_connect(gl.client, "connection-removed", G_CALLBACK(removed_cb), &done); + + proxy = g_dbus_proxy_new_sync(gl.bus, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + NM_DBUS_SERVICE, + path, + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + NULL, + NULL); + g_assert(proxy != NULL); + + /* Bypass the NMClient object so we can test it independently */ + g_dbus_proxy_call(proxy, "Delete", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, deleted_cb, NULL); + + nmtst_main_context_iterate_until_assert(NULL, 5000, done && !gl.remote); + + /* Ensure NMClient no longer has the connection */ + conns = nm_client_get_connections(gl.client); + for (i = 0; i < conns->len; i++) { + NMConnection *candidate = NM_CONNECTION(conns->pdata[i]); + + g_assert((gpointer) connection != (gpointer) candidate); + g_assert_cmpstr(path, ==, nm_connection_get_path(candidate)); + } + + g_free(path); + g_object_unref(proxy); +} + +/*****************************************************************************/ + +#define TEST_ADD_REMOVE_ID "add-remove-test-connection" + +static void +add_remove_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + NMRemoteConnection *connection; + gboolean * done = user_data; + gs_free_error GError *error = NULL; + + connection = nm_client_add_connection_finish(gl.client, result, &error); + g_assert_error(error, NM_CLIENT_ERROR, NM_CLIENT_ERROR_OBJECT_CREATION_FAILED); + g_assert(connection == NULL); + + *done = TRUE; +} + +static void +test_add_remove_connection(void) +{ + gs_unref_variant GVariant *ret = NULL; + GError * error = NULL; + gs_unref_object NMConnection *connection = NULL; + gboolean done = FALSE; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + /* This will cause the test server to immediately delete the connection + * after creating it. + */ + ret = g_dbus_proxy_call_sync(gl.sinfo->proxy, + "AutoRemoveNextConnection", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + nmtst_assert_success(ret, error); + + connection = nmtst_create_minimal_connection(TEST_ADD_REMOVE_ID, + NULL, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + nm_client_add_connection_async(gl.client, connection, TRUE, NULL, add_remove_cb, &done); + + nmtst_main_context_iterate_until_assert(NULL, 5000, done); +} + +/*****************************************************************************/ + +static void +add_bad_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + gboolean * done = user_data; + gs_free_error GError *error = NULL; + + gl.remote = nm_client_add_connection_finish(gl.client, result, &error); + g_assert_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + + *done = TRUE; +} + +static void +test_add_bad_connection(void) +{ + gs_unref_object NMConnection *connection = NULL; + gboolean done = FALSE; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + /* The test daemon doesn't support bond connections */ + connection = nmtst_create_minimal_connection("bad connection test", + NULL, + NM_SETTING_BOND_SETTING_NAME, + NULL); + + nm_client_add_connection_async(gl.client, connection, TRUE, NULL, add_bad_cb, &done); + g_clear_object(&connection); + + nmtst_main_context_iterate_until_assert(NULL, 5000, done); + g_assert(gl.remote == NULL); +} + +/*****************************************************************************/ + +static void +save_hostname_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + gboolean * done = user_data; + gs_free_error GError *error = NULL; + + nm_client_save_hostname_finish(gl.client, result, &error); + g_assert_no_error(error); + + *done = TRUE; +} + +static void +test_save_hostname(void) +{ + gint64 until_ts; + gboolean done = FALSE; + GError * error = NULL; + + if (!nmtstc_service_available(gl.sinfo)) + return; + + /* test-networkmanager-service.py requires the hostname to contain a '.' */ + nm_client_save_hostname(gl.client, "foo", NULL, &error); + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_HOSTNAME); + g_clear_error(&error); + + nm_client_save_hostname_async(gl.client, "example.com", NULL, save_hostname_cb, &done); + + until_ts = nm_utils_get_monotonic_timestamp_msec() + 5000; + while (TRUE) { + g_main_context_iteration(NULL, FALSE); + if (done) + break; + if (nm_utils_get_monotonic_timestamp_msec() >= until_ts) + g_assert_not_reached(); + } + + g_assert(gl.remote == NULL); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + int ret; + GError *error = NULL; + + g_setenv("LIBNM_USE_SESSION_BUS", "1", TRUE); + + nmtst_init(&argc, &argv, TRUE); + + gl.bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); + nmtst_assert_success(gl.bus, error); + + gl.sinfo = nmtstc_service_init(); + + gl.client = nmtstc_client_new(TRUE); + + /* FIXME: these tests assume that they get run in order, but g_test_run() + * does not actually guarantee that! + */ + g_test_add_func("/client/add_connection", test_add_connection); + g_test_add_func("/client/make_invisible", test_make_invisible); + g_test_add_func("/client/make_visible", test_make_visible); + g_test_add_func("/client/remove_connection", test_remove_connection); + g_test_add_func("/client/add_remove_connection", test_add_remove_connection); + g_test_add_func("/client/add_bad_connection", test_add_bad_connection); + g_test_add_func("/client/save_hostname", test_save_hostname); + + ret = g_test_run(); + + nm_clear_pointer(&gl.sinfo, nmtstc_service_cleanup); + g_clear_object(&gl.client); + g_clear_object(&gl.bus); + + return ret; +} diff --git a/src/libnm-client-impl/tests/test-secret-agent.c b/src/libnm-client-impl/tests/test-secret-agent.c new file mode 100644 index 00000000..8f758f3d --- /dev/null +++ b/src/libnm-client-impl/tests/test-secret-agent.c @@ -0,0 +1,746 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2014 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include +#include + +#include "nm-secret-agent-old.h" + +#include "libnm-client-test/nm-test-libnm-utils.h" + +/*****************************************************************************/ + +enum { + SECRET_REQUESTED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef NMSecretAgentOld TestSecretAgent; +typedef NMSecretAgentOldClass TestSecretAgentClass; + +GType test_secret_agent_get_type(void); + +G_DEFINE_TYPE(TestSecretAgent, test_secret_agent, NM_TYPE_SECRET_AGENT_OLD) + +static void +test_secret_agent_get_secrets(NMSecretAgentOld * agent, + NMConnection * connection, + const char * connection_path, + const char * setting_name, + const char ** hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentOldGetSecretsFunc callback, + gpointer callback_data) +{ + NMSettingWirelessSecurity *s_wsec; + GVariant * secrets = NULL; + GVariantBuilder secrets_builder, setting_builder; + char * secret = NULL; + GError * error = NULL; + + g_assert_cmpstr(setting_name, ==, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "wpa-psk"); + g_assert_cmpstr(nm_setting_wireless_security_get_psk(s_wsec), ==, NULL); + + g_signal_emit(agent, + signals[SECRET_REQUESTED], + 0, + connection, + connection_path, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK, + &secret); + + if (!secret) { + error = g_error_new(NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, "No secrets"); + goto done; + } + + if (!strcmp(secret, "CANCEL")) { + error = g_error_new(NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_USER_CANCELED, + "User canceled"); + goto done; + } + + g_variant_builder_init(&setting_builder, NM_VARIANT_TYPE_SETTING); + g_variant_builder_add(&setting_builder, + "{sv}", + NM_SETTING_WIRELESS_SECURITY_PSK, + g_variant_new_string(secret)); + + g_variant_builder_init(&secrets_builder, NM_VARIANT_TYPE_CONNECTION); + g_variant_builder_add(&secrets_builder, "{sa{sv}}", setting_name, &setting_builder); + secrets = g_variant_ref_sink(g_variant_builder_end(&secrets_builder)); + +done: + callback(agent, connection, secrets, error, callback_data); + g_clear_error(&error); + nm_clear_pointer(&secrets, g_variant_unref); + g_free(secret); +} + +static void +test_secret_agent_cancel_get_secrets(NMSecretAgentOld *agent, + const char * connection_path, + const char * setting_name) +{ + g_assert_not_reached(); +} + +static void +test_secret_agent_save_secrets(NMSecretAgentOld * agent, + NMConnection * connection, + const char * connection_path, + NMSecretAgentOldSaveSecretsFunc callback, + gpointer callback_data) +{ + g_assert_not_reached(); +} + +static void +test_secret_agent_delete_secrets(NMSecretAgentOld * agent, + NMConnection * connection, + const char * connection_path, + NMSecretAgentOldDeleteSecretsFunc callback, + gpointer callback_data) +{ + g_assert_not_reached(); +} + +static void +test_secret_agent_init(TestSecretAgent *agent) +{} + +static NMSecretAgentOld * +test_secret_agent_new(gboolean auto_register) +{ + return nmtstc_context_object_new(test_secret_agent_get_type(), + TRUE, + NM_SECRET_AGENT_OLD_IDENTIFIER, + "test-secret-agent", + NM_SECRET_AGENT_OLD_AUTO_REGISTER, + auto_register, + NULL); +} + +static void +test_secret_agent_class_init(TestSecretAgentClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS(klass); + + agent_class->get_secrets = test_secret_agent_get_secrets; + agent_class->cancel_get_secrets = test_secret_agent_cancel_get_secrets; + agent_class->save_secrets = test_secret_agent_save_secrets; + agent_class->delete_secrets = test_secret_agent_delete_secrets; + + signals[SECRET_REQUESTED] = g_signal_new("secret-requested", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_STRING, + 4, + NM_TYPE_CONNECTION, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); +} + +/*****************************************************************************/ + +typedef struct { + NMTstcServiceInfo *sinfo; + NMClient * client; + + NMSecretAgentOld *agent; + NMDevice * device; + NMConnection * connection; + + GMainLoop *loop; + GSource * timeout_source; + + char *ifname; + char *con_id; + + int secrets_requested; +} TestSecretAgentData; + +static void +connection_added_cb(GObject *s, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + NMRemoteConnection * connection; + GError * error = NULL; + + connection = nm_client_add_connection_finish(sadata->client, result, &error); + + g_assert_no_error(error); + g_assert_cmpstr(nm_connection_get_id(NM_CONNECTION(connection)), ==, sadata->con_id); + + sadata->connection = NM_CONNECTION(connection); + g_main_loop_quit(sadata->loop); +} + +static void +register_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + GError * error = NULL; + + nm_secret_agent_old_register_finish(sadata->agent, result, &error); + g_assert_no_error(error); + g_assert(nm_secret_agent_old_get_registered(sadata->agent)); + + g_main_loop_quit(sadata->loop); +} + +#define TEST_CON_ID_PREFIX "test-secret-agent" + +static void +test_setup(TestSecretAgentData *sadata, gconstpointer test_data) +{ + static int static_counter = 0; + const int counter = static_counter++; + const char * create_agent = test_data; + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWireless * s_wireless; + GBytes * ssid; + NMSetting * s_wsec; + gs_free_error GError *error = NULL; + + sadata->sinfo = nmtstc_service_init(); + if (!sadata->sinfo) + return; + + g_assert(nm_g_main_context_is_thread_default(NULL)); + + sadata->client = nmtstc_client_new(TRUE); + + g_assert(nm_g_main_context_is_thread_default(NULL)); + g_assert(nm_g_main_context_is_thread_default(nm_client_get_main_context(sadata->client))); + + sadata->loop = g_main_loop_new(NULL, FALSE); + + sadata->timeout_source = g_timeout_source_new_seconds(5); + g_source_set_callback(sadata->timeout_source, nmtst_g_source_assert_not_called, NULL, NULL); + g_source_attach(sadata->timeout_source, NULL); + + sadata->ifname = g_strdup_printf("wlan%d", counter); + sadata->con_id = g_strdup_printf("%s-%d", TEST_CON_ID_PREFIX, counter); + + sadata->device = + nmtstc_service_add_device(sadata->sinfo, sadata->client, "AddWifiDevice", sadata->ifname); + + /* Create the connection */ + connection = nmtst_create_minimal_connection(sadata->con_id, + NULL, + NM_SETTING_WIRELESS_SETTING_NAME, + &s_con); + g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, sadata->ifname, NULL); + + s_wireless = nm_connection_get_setting_wireless(connection); + ssid = g_bytes_new("foo", 3); + g_object_set(s_wireless, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_bytes_unref(ssid); + + s_wsec = g_object_new(NM_TYPE_SETTING_WIRELESS_SECURITY, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", + NULL); + nm_connection_add_setting(connection, s_wsec); + + nm_client_add_connection_async(sadata->client, + connection, + TRUE, + NULL, + connection_added_cb, + sadata); + g_object_unref(connection); + + g_main_loop_run(sadata->loop); + g_assert(sadata->connection); + + if (nm_streq(create_agent, "1")) { + gboolean auto_register = nmtst_get_rand_bool(); + + sadata->agent = test_secret_agent_new(auto_register); + + if (auto_register) { + g_assert(nm_secret_agent_old_get_registered(sadata->agent)); + nm_secret_agent_old_register(sadata->agent, NULL, &error); + g_assert_no_error(error); + } else { + g_assert(!nm_secret_agent_old_get_registered(sadata->agent)); + nm_secret_agent_old_register_async(sadata->agent, NULL, register_cb, sadata); + g_main_loop_run(sadata->loop); + } + + g_assert(nm_secret_agent_old_get_registered(sadata->agent)); + } +} + +static void +test_cleanup(TestSecretAgentData *sadata, gconstpointer test_data) +{ + GVariant * ret; + GError * error = NULL; + NMTstContextBusyWatcherData watcher_data = {}; + + g_assert(nm_g_main_context_is_thread_default(NULL)); + + if (!sadata->sinfo) + return; + + g_assert(nm_g_main_context_is_thread_default(nm_client_get_main_context(sadata->client))); + + nmtst_context_busy_watcher_add(&watcher_data, + nm_client_get_context_busy_watcher(sadata->client)); + + if (sadata->agent) { + nmtst_context_busy_watcher_add(&watcher_data, + nm_secret_agent_old_get_context_busy_watcher(sadata->agent)); + + if (nm_secret_agent_old_get_registered(sadata->agent)) { + nm_secret_agent_old_unregister(sadata->agent, NULL, &error); + g_assert_no_error(error); + } + g_object_unref(sadata->agent); + } + + ret = + g_dbus_proxy_call_sync(sadata->sinfo->proxy, + "RemoveDevice", + g_variant_new("(s)", nm_object_get_path(NM_OBJECT(sadata->device))), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error(error); + g_variant_unref(ret); + + g_object_unref(sadata->connection); + g_object_unref(sadata->client); + + nmtstc_service_cleanup(sadata->sinfo); + + nm_clear_g_source_inst(&sadata->timeout_source); + + g_main_loop_unref(sadata->loop); + + g_free(sadata->ifname); + g_free(sadata->con_id); + + *sadata = (TestSecretAgentData){}; + + nmtst_context_busy_watcher_wait(&watcher_data); + + while (g_main_context_iteration(NULL, FALSE)) {} + + nmtst_main_context_assert_no_dispatch(NULL, nmtst_get_rand_uint32() % 500); +} + +/*****************************************************************************/ + +static void +connection_activated_none_cb(GObject *c, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + gs_free_error GError *error = NULL; + + nm_client_activate_connection_finish(sadata->client, result, &error); + g_assert_error(error, NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_NO_SECRETS); + + g_main_loop_quit(sadata->loop); +} + +static void +test_secret_agent_none(TestSecretAgentData *sadata, gconstpointer test_data) +{ + if (!nmtstc_service_available(sadata->sinfo)) + return; + + nm_client_activate_connection_async(sadata->client, + sadata->connection, + sadata->device, + NULL, + NULL, + connection_activated_none_cb, + sadata); + g_main_loop_run(sadata->loop); +} + +/*****************************************************************************/ + +static char * +secrets_requested_no_secrets_cb(TestSecretAgent *agent, + NMConnection * connection, + const char * connection_path, + const char * setting_name, + const char * secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr(connection_path, ==, nm_connection_get_path(sadata->connection)); + sadata->secrets_requested++; + + return NULL; +} + +static void +connection_activated_no_secrets_cb(GObject *c, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + gs_unref_object NMActiveConnection *ac = NULL; + gs_free_error GError *error = NULL; + + ac = nm_client_activate_connection_finish(sadata->client, result, &error); + g_assert_error(error, NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_NO_SECRETS); + g_main_loop_quit(sadata->loop); +} + +static void +test_secret_agent_no_secrets(TestSecretAgentData *sadata, gconstpointer test_data) +{ + if (!nmtstc_service_available(sadata->sinfo)) + return; + + g_signal_connect(sadata->agent, + "secret-requested", + G_CALLBACK(secrets_requested_no_secrets_cb), + sadata); + + nm_client_activate_connection_async(sadata->client, + sadata->connection, + sadata->device, + NULL, + NULL, + connection_activated_no_secrets_cb, + sadata); + g_main_loop_run(sadata->loop); + + g_assert_cmpint(sadata->secrets_requested, ==, 1); +} + +/*****************************************************************************/ + +static void +connection_activated_cancel_cb(GObject *c, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + gs_unref_object NMActiveConnection *ac = NULL; + gs_free_error GError *error = NULL; + + ac = nm_client_activate_connection_finish(sadata->client, result, &error); + g_assert_error(error, NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_USER_CANCELED); + g_main_loop_quit(sadata->loop); +} + +static char * +secrets_requested_cancel_cb(TestSecretAgent *agent, + NMConnection * connection, + const char * connection_path, + const char * setting_name, + const char * secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr(connection_path, ==, nm_connection_get_path(sadata->connection)); + sadata->secrets_requested++; + + return g_strdup("CANCEL"); +} + +static void +test_secret_agent_cancel(TestSecretAgentData *sadata, gconstpointer test_data) +{ + if (!nmtstc_service_available(sadata->sinfo)) + return; + + g_signal_connect(sadata->agent, + "secret-requested", + G_CALLBACK(secrets_requested_cancel_cb), + sadata); + + nm_client_activate_connection_async(sadata->client, + sadata->connection, + sadata->device, + NULL, + NULL, + connection_activated_cancel_cb, + sadata); + g_main_loop_run(sadata->loop); + + g_assert_cmpint(sadata->secrets_requested, ==, 1); +} + +/*****************************************************************************/ + +static void +connection_activated_good_cb(GObject *c, GAsyncResult *result, gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + NMActiveConnection * ac; + GError * error = NULL; + + ac = nm_client_activate_connection_finish(sadata->client, result, &error); + g_assert_no_error(error); + + g_object_unref(ac); + + g_main_loop_quit(sadata->loop); +} + +static char * +secrets_requested_good_cb(TestSecretAgent *agent, + NMConnection * connection, + const char * connection_path, + const char * setting_name, + const char * secret_name, + gpointer user_data) +{ + TestSecretAgentData *sadata = user_data; + + g_assert_cmpstr(connection_path, ==, nm_connection_get_path(sadata->connection)); + sadata->secrets_requested++; + + return g_strdup("password"); +} + +static void +test_secret_agent_good(TestSecretAgentData *sadata, gconstpointer test_data) +{ + if (!nmtstc_service_available(sadata->sinfo)) + return; + + g_signal_connect(sadata->agent, + "secret-requested", + G_CALLBACK(secrets_requested_good_cb), + sadata); + + nm_client_activate_connection_async(sadata->client, + sadata->connection, + sadata->device, + NULL, + NULL, + connection_activated_good_cb, + sadata); + g_main_loop_run(sadata->loop); + + g_assert_cmpint(sadata->secrets_requested, ==, 1); +} + +/*****************************************************************************/ + +static void +async_init_cb(GObject *object, GAsyncResult *result, gpointer user_data) +{ + GMainLoop * loop = user_data; + gs_free_error GError *error = NULL; + gs_unref_object GObject *agent = NULL; + + agent = g_async_initable_new_finish(G_ASYNC_INITABLE(object), result, &error); + nmtst_assert_success(NM_IS_SECRET_AGENT_OLD(agent), error); + g_assert(!nm_secret_agent_old_get_registered(NM_SECRET_AGENT_OLD(agent))); + + g_main_loop_quit(loop); +} + +static void +test_secret_agent_nm_not_running(void) +{ + gs_unref_object NMSecretAgentOld *agent = NULL; + nm_auto_unref_gmainloop GMainLoop *loop = NULL; + GError * error = NULL; + + agent = g_initable_new(test_secret_agent_get_type(), + NULL, + &error, + NM_SECRET_AGENT_OLD_IDENTIFIER, + "test-secret-agent", + NULL); + nmtst_assert_success(NM_IS_SECRET_AGENT_OLD(agent), error); + g_assert(!nm_secret_agent_old_get_registered(agent)); + g_clear_object(&agent); + + loop = g_main_loop_new(NULL, FALSE); + g_async_initable_new_async(test_secret_agent_get_type(), + G_PRIORITY_DEFAULT, + NULL, + async_init_cb, + loop, + NM_SECRET_AGENT_OLD_IDENTIFIER, + "test-secret-agent", + NULL); + g_main_loop_run(loop); +} + +/*****************************************************************************/ + +typedef struct { + int step; + int invoke_count; +} AutoRegisterData; + +static void +registered_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMSecretAgentOld *agent = NM_SECRET_AGENT_OLD(object); + AutoRegisterData *data = user_data; + + g_assert(data); + g_assert(NM_IS_SECRET_AGENT_OLD(agent)); + + data->invoke_count++; + g_assert_cmpint(data->invoke_count, ==, data->step); + + switch (data->step) { + case 1: + case 3: + g_assert(nm_secret_agent_old_get_registered(agent)); + break; + case 2: + case 4: + g_assert(!nm_secret_agent_old_get_registered(agent)); + break; + default: + g_assert_not_reached(); + } +} + +static void +test_secret_agent_auto_register(void) +{ + NMTstcServiceInfo *sinfo; + gs_unref_object NMSecretAgentOld *agent = NULL; + GError * error = NULL; + AutoRegisterData auto_register_data = { + .step = 0, + .invoke_count = 0, + }; + gulong signal_id; + NMTstContextBusyWatcherData watcher_data = {}; + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + agent = test_secret_agent_new(FALSE); + g_assert(!nm_secret_agent_old_get_registered(agent)); + + signal_id = g_signal_connect(agent, + "notify::" NM_SECRET_AGENT_OLD_REGISTERED, + G_CALLBACK(registered_changed), + &auto_register_data); + + if (nmtst_get_rand_bool()) { + g_object_set(agent, NM_SECRET_AGENT_OLD_AUTO_REGISTER, TRUE, NULL); + } else + nm_secret_agent_old_enable(agent, TRUE); + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nm_secret_agent_old_register(agent, NULL, &error); + g_assert_no_error(error); + g_assert(!nm_secret_agent_old_get_registered(agent)); + + auto_register_data.step = 1; + nmtst_main_context_iterate_until_assert(NULL, 1000, nm_secret_agent_old_get_registered(agent)); + + auto_register_data.step = 2; + nm_secret_agent_old_enable(agent, FALSE); + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nmtst_main_context_iterate_until(NULL, nmtst_get_rand_uint32() % 200, FALSE); + + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nmtstc_service_cleanup(sinfo); + + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nm_secret_agent_old_enable(agent, TRUE); + + g_assert(!nm_secret_agent_old_get_registered(agent)); + + nmtst_main_context_iterate_until(NULL, nmtst_get_rand_uint32() % 200, FALSE); + + g_assert(!nm_secret_agent_old_get_registered(agent)); + + sinfo = nmtstc_service_init(); + g_assert(nmtstc_service_available(sinfo)); + + auto_register_data.step = 3; + nmtst_main_context_iterate_until_assert(NULL, 1000, nm_secret_agent_old_get_registered(agent)); + + nmtstc_service_cleanup(sinfo); + + auto_register_data.step = 4; + nmtst_main_context_iterate_until_assert(NULL, 1000, !nm_secret_agent_old_get_registered(agent)); + + nm_clear_g_signal_handler(agent, &signal_id); + + nmtst_context_busy_watcher_add(&watcher_data, + nm_secret_agent_old_get_context_busy_watcher(agent)); + + g_clear_object(&agent); + + nmtst_context_busy_watcher_wait(&watcher_data); + + nmtst_main_context_assert_no_dispatch(NULL, nmtst_get_rand_uint32() % 500); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + g_setenv("LIBNM_USE_SESSION_BUS", "1", TRUE); + + nmtst_init(&argc, &argv, TRUE); + + g_test_add("/libnm/secret-agent/none", + TestSecretAgentData, + "0", + test_setup, + test_secret_agent_none, + test_cleanup); + g_test_add("/libnm/secret-agent/no-secrets", + TestSecretAgentData, + "1", + test_setup, + test_secret_agent_no_secrets, + test_cleanup); + g_test_add("/libnm/secret-agent/cancel", + TestSecretAgentData, + "1", + test_setup, + test_secret_agent_cancel, + test_cleanup); + g_test_add("/libnm/secret-agent/good", + TestSecretAgentData, + "1", + test_setup, + test_secret_agent_good, + test_cleanup); + g_test_add_func("/libnm/secret-agent/nm-not-running", test_secret_agent_nm_not_running); + g_test_add_func("/libnm/secret-agent/auto-register", test_secret_agent_auto_register); + + return g_test_run(); +} -- cgit 1.3.0-6-gf8a5