diff options
Diffstat (limited to 'libnm')
74 files changed, 25833 insertions, 0 deletions
diff --git a/libnm/nm-access-point.c b/libnm/nm-access-point.c new file mode 100644 index 0000000000..b462a6b169 --- /dev/null +++ b/libnm/nm-access-point.c @@ -0,0 +1,673 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2011 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#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-access-point.h" +#include "NetworkManager.h" +#include "nm-types-private.h" +#include "nm-object-private.h" + +G_DEFINE_TYPE (NMAccessPoint, nm_access_point, NM_TYPE_OBJECT) + +#define NM_ACCESS_POINT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ACCESS_POINT, NMAccessPointPrivate)) + +typedef struct { + DBusGProxy *proxy; + + NM80211ApFlags flags; + NM80211ApSecurityFlags wpa_flags; + NM80211ApSecurityFlags rsn_flags; + GByteArray *ssid; + guint32 frequency; + char *bssid; + NM80211Mode mode; + guint32 max_bitrate; + guint8 strength; +} NMAccessPointPrivate; + +enum { + PROP_0, + PROP_FLAGS, + PROP_WPA_FLAGS, + PROP_RSN_FLAGS, + PROP_SSID, + PROP_FREQUENCY, + PROP_HW_ADDRESS, + PROP_MODE, + PROP_MAX_BITRATE, + PROP_STRENGTH, + PROP_BSSID, + + LAST_PROP +}; + +/** + * nm_access_point_new: + * @connection: the #DBusGConnection + * @path: the DBusobject path of the access point + * + * Creates a new #NMAccessPoint. + * + * Returns: (transfer full): a new access point + **/ +GObject * +nm_access_point_new (DBusGConnection *connection, const char *path) +{ + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + return (GObject *) g_object_new (NM_TYPE_ACCESS_POINT, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + 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); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + 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); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + 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: the #GByteArray containing the SSID. This is the internal copy used by the + * access point, and must not be modified. + **/ +const GByteArray * +nm_access_point_get_ssid (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), NULL); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + return NM_ACCESS_POINT_GET_PRIVATE (ap)->ssid; +} + +/** + * nm_access_point_get_frequency: + * @ap: a #NMAccessPoint + * + * Gets the frequency of the access point. + * + * Returns: the frequency + **/ +guint32 +nm_access_point_get_frequency (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), 0); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + 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); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + return NM_ACCESS_POINT_GET_PRIVATE (ap)->bssid; +} + +/** + * nm_access_point_get_hw_address: + * @ap: a #NMAccessPoint + * + * Gets the hardware (MAC) address of the access point. + * + * Returns: the hardware address of the access point. This is the internal string used by the + * access point and must not be modified. + * + * Deprecated: 0.9: Use nm_access_point_get_bssid() instead. + **/ +const char * +nm_access_point_get_hw_address (NMAccessPoint *ap) +{ + return nm_access_point_get_bssid (ap); +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + 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); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + 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. + * + * Returns: the signal strength + **/ +guint8 +nm_access_point_get_strength (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), 0); + + _nm_object_ensure_inited (NM_OBJECT (ap)); + return NM_ACCESS_POINT_GET_PRIVATE (ap)->strength; +} + +/** + * 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_str; + const GByteArray *setting_ssid; + const GByteArray *ap_ssid; + const GByteArray *setting_bssid; + struct ether_addr *ap_bssid; + const char *setting_mode; + NM80211Mode ap_mode; + const char *setting_band; + guint32 ap_freq, setting_chan, ap_chan; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_WIRELESS_SETTING_NAME) != 0) + 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); + g_warn_if_fail (ap_ssid != NULL); + setting_ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!setting_ssid || !ap_ssid || (setting_ssid->len != ap_ssid->len)) + return FALSE; + if (memcmp (setting_ssid->data, ap_ssid->data, ap_ssid->len) != 0) + return FALSE; + + /* BSSID checks */ + ap_bssid_str = nm_access_point_get_bssid (ap); + g_warn_if_fail (ap_bssid_str); + setting_bssid = nm_setting_wireless_get_bssid (s_wifi); + if (setting_bssid && ap_bssid_str) { + g_assert (setting_bssid->len == ETH_ALEN); + ap_bssid = ether_aton (ap_bssid_str); + g_warn_if_fail (ap_bssid); + if (ap_bssid) { + if (memcmp (ap_bssid->ether_addr_octet, setting_bssid->data, ETH_ALEN) != 0) + return FALSE; + } + } + + /* Mode */ + ap_mode = nm_access_point_get_mode (ap); + g_warn_if_fail (ap_mode != NM_802_11_MODE_UNKNOWN); + 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): a list of + * #NMConnection objects to filter + * + * Filters a given list of connections for a given #NMAccessPoint object and + * return 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_remote_settings_list_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): a + * list of #NMConnection objects that could be activated with the given @ap. + * The elements of the list are owned by their creator and should not be freed + * by the caller, but the returned list itself is owned by the caller and should + * be freed with g_slist_free() when it is no longer required. + **/ +GSList * +nm_access_point_filter_connections (NMAccessPoint *ap, const GSList *connections) +{ + GSList *filtered = NULL; + const GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + if (nm_access_point_connection_valid (ap, candidate)) + filtered = g_slist_prepend (filtered, candidate); + } + + return g_slist_reverse (filtered); +} + +/************************************************************/ + +static void +nm_access_point_init (NMAccessPoint *ap) +{ +} + +static void +dispose (GObject *object) +{ + NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_access_point_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE (object); + + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + + 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); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_uint (value, nm_access_point_get_flags (ap)); + break; + case PROP_WPA_FLAGS: + g_value_set_uint (value, nm_access_point_get_wpa_flags (ap)); + break; + case PROP_RSN_FLAGS: + g_value_set_uint (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_uint (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; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +demarshal_ssid (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + if (!_nm_ssid_demarshal (value, (GByteArray **) field)) + return FALSE; + + _nm_object_queue_notify (object, NM_ACCESS_POINT_SSID); + return TRUE; +} + +static void +register_properties (NMAccessPoint *ap) +{ + NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE (ap); + const NMPropertiesInfo property_info[] = { + { NM_ACCESS_POINT_FLAGS, &priv->flags }, + { NM_ACCESS_POINT_WPA_FLAGS, &priv->wpa_flags }, + { NM_ACCESS_POINT_RSN_FLAGS, &priv->rsn_flags }, + { NM_ACCESS_POINT_SSID, &priv->ssid, demarshal_ssid }, + { NM_ACCESS_POINT_FREQUENCY, &priv->frequency }, + { NM_ACCESS_POINT_HW_ADDRESS, &priv->bssid }, + { NM_ACCESS_POINT_MODE, &priv->mode }, + { NM_ACCESS_POINT_MAX_BITRATE, &priv->max_bitrate }, + { NM_ACCESS_POINT_STRENGTH, &priv->strength }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (ap), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMAccessPointPrivate *priv; + + G_OBJECT_CLASS (nm_access_point_parent_class)->constructed (object); + + priv = NM_ACCESS_POINT_GET_PRIVATE (object); + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_ACCESS_POINT); + register_properties (NM_ACCESS_POINT (object)); +} + + +static void +nm_access_point_class_init (NMAccessPointClass *ap_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ap_class); + + g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMAccessPoint:flags: + * + * The flags of the access point. + **/ + g_object_class_install_property + (object_class, PROP_FLAGS, + g_param_spec_uint (NM_ACCESS_POINT_FLAGS, "", "", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMAccessPoint:wpa-flags: + * + * The WPA flags of the access point. + **/ + g_object_class_install_property + (object_class, PROP_WPA_FLAGS, + g_param_spec_uint (NM_ACCESS_POINT_WPA_FLAGS, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMAccessPoint:rsn-flags: + * + * The RSN flags of the access point. + **/ + g_object_class_install_property + (object_class, PROP_RSN_FLAGS, + g_param_spec_uint (NM_ACCESS_POINT_RSN_FLAGS, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMAccessPoint:ssid: + * + * The SSID of the access point. + **/ + g_object_class_install_property + (object_class, PROP_SSID, + g_param_spec_boxed (NM_ACCESS_POINT_SSID, "", "", + NM_TYPE_SSID, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMAccessPoint:frequency: + * + * The frequency of the access point. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_BSSID, + g_param_spec_string (NM_ACCESS_POINT_BSSID, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMAccessPoint:hw-address: + * + * The hardware address of the access point. + **/ + g_object_class_install_property + (object_class, 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). + **/ + g_object_class_install_property + (object_class, PROP_MODE, + g_param_spec_uint (NM_ACCESS_POINT_MODE, "", "", + NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMAccessPoint:max-bitrate: + * + * The maximum bit rate of the access point in kbit/s. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_STRENGTH, + g_param_spec_uchar (NM_ACCESS_POINT_STRENGTH, "", "", + 0, G_MAXUINT8, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-access-point.h b/libnm/nm-access-point.h new file mode 100644 index 0000000000..d3150f8eee --- /dev/null +++ b/libnm/nm-access-point.h @@ -0,0 +1,96 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2011 Red Hat, Inc. + */ + +#ifndef NM_ACCESS_POINT_H +#define NM_ACCESS_POINT_H + +#include <glib.h> +#include <glib-object.h> +#include <NetworkManager.h> +#include <nm-connection.h> +#include "nm-object.h" + +G_BEGIN_DECLS + +#define NM_TYPE_ACCESS_POINT (nm_access_point_get_type ()) +#define NM_ACCESS_POINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACCESS_POINT, NMAccessPoint)) +#define NM_ACCESS_POINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACCESS_POINT, NMAccessPointClass)) +#define NM_IS_ACCESS_POINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACCESS_POINT)) +#define NM_IS_ACCESS_POINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ACCESS_POINT)) +#define NM_ACCESS_POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACCESS_POINT, NMAccessPointClass)) + +#define NM_ACCESS_POINT_FLAGS "flags" +#define NM_ACCESS_POINT_WPA_FLAGS "wpa-flags" +#define NM_ACCESS_POINT_RSN_FLAGS "rsn-flags" +#define NM_ACCESS_POINT_SSID "ssid" +#define NM_ACCESS_POINT_BSSID "bssid" +#define NM_ACCESS_POINT_FREQUENCY "frequency" +#define NM_ACCESS_POINT_MODE "mode" +#define NM_ACCESS_POINT_MAX_BITRATE "max-bitrate" +#define NM_ACCESS_POINT_STRENGTH "strength" + +/* DEPRECATED */ +#define NM_ACCESS_POINT_HW_ADDRESS "hw-address" + + +typedef struct { + NMObject parent; +} NMAccessPoint; + +typedef struct { + NMObjectClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMAccessPointClass; + +GType nm_access_point_get_type (void); + +GObject *nm_access_point_new (DBusGConnection *connection, const char *path); + +NM80211ApFlags nm_access_point_get_flags (NMAccessPoint *ap); +NM80211ApSecurityFlags nm_access_point_get_wpa_flags (NMAccessPoint *ap); +NM80211ApSecurityFlags nm_access_point_get_rsn_flags (NMAccessPoint *ap); +const GByteArray * nm_access_point_get_ssid (NMAccessPoint *ap); +const char * nm_access_point_get_bssid (NMAccessPoint *ap); +guint32 nm_access_point_get_frequency (NMAccessPoint *ap); +NM80211Mode nm_access_point_get_mode (NMAccessPoint *ap); +guint32 nm_access_point_get_max_bitrate (NMAccessPoint *ap); +guint8 nm_access_point_get_strength (NMAccessPoint *ap); + +GSList * nm_access_point_filter_connections (NMAccessPoint *ap, + const GSList *connections); + +gboolean nm_access_point_connection_valid (NMAccessPoint *ap, + NMConnection *connection); + +/* DEPRECATED */ +NM_DEPRECATED_IN_0_9_10 +const char * nm_access_point_get_hw_address (NMAccessPoint *ap); + +G_END_DECLS + +#endif /* NM_ACCESS_POINT_H */ diff --git a/libnm/nm-active-connection.c b/libnm/nm-active-connection.c new file mode 100644 index 0000000000..358264fd25 --- /dev/null +++ b/libnm/nm-active-connection.c @@ -0,0 +1,851 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#include <string.h> + +#include "NetworkManager.h" +#include "nm-active-connection.h" +#include "nm-object-private.h" +#include "nm-types-private.h" +#include "nm-device.h" +#include "nm-device-private.h" +#include "nm-connection.h" +#include "nm-vpn-connection.h" +#include "nm-glib-compat.h" +#include "nm-dbus-helpers-private.h" + +static GType _nm_active_connection_type_for_path (DBusGConnection *connection, + const char *path); +static void _nm_active_connection_type_for_path_async (DBusGConnection *connection, + const char *path, + NMObjectTypeCallbackFunc callback, + gpointer user_data); + +G_DEFINE_TYPE_WITH_CODE (NMActiveConnection, nm_active_connection, NM_TYPE_OBJECT, + _nm_object_register_type_func (g_define_type_id, + _nm_active_connection_type_for_path, + _nm_active_connection_type_for_path_async); + ) + +#define NM_ACTIVE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *connection; + char *id; + char *uuid; + char *type; + char *specific_object; + GPtrArray *devices; + NMActiveConnectionState state; + gboolean is_default; + NMIP4Config *ip4_config; + NMDHCP4Config *dhcp4_config; + gboolean is_default6; + NMIP6Config *ip6_config; + NMDHCP6Config *dhcp6_config; + gboolean is_vpn; + char *master; +} NMActiveConnectionPrivate; + +enum { + PROP_0, + PROP_CONNECTION, + PROP_ID, + PROP_UUID, + PROP_TYPE, + PROP_SPECIFIC_OBJECT, + PROP_DEVICES, + PROP_STATE, + PROP_DEFAULT, + PROP_IP4_CONFIG, + PROP_DHCP4_CONFIG, + PROP_DEFAULT6, + PROP_IP6_CONFIG, + PROP_DHCP6_CONFIG, + PROP_VPN, + PROP_MASTER, + + LAST_PROP +}; + +/** + * nm_active_connection_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMActiveConnection. + * + * Returns: (transfer full): a new active connection + **/ +GObject * +nm_active_connection_new (DBusGConnection *connection, const char *path) +{ + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + return g_object_new (NM_TYPE_ACTIVE_CONNECTION, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); +} + +static GType +_nm_active_connection_type_for_path (DBusGConnection *connection, + const char *path) +{ + DBusGProxy *proxy; + GError *error = NULL; + GValue value = G_VALUE_INIT; + GType type; + + proxy = _nm_dbus_new_proxy_for_connection (connection, path, "org.freedesktop.DBus.Properties"); + if (!proxy) { + g_warning ("%s: couldn't create D-Bus object proxy.", __func__); + return G_TYPE_INVALID; + } + + /* Have to create an NMVPNConnection if it's a VPN connection, otherwise + * a plain NMActiveConnection. + */ + if (dbus_g_proxy_call (proxy, + "Get", &error, + G_TYPE_STRING, NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + G_TYPE_STRING, "Vpn", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, G_TYPE_INVALID)) { + if (g_value_get_boolean (&value)) + type = NM_TYPE_VPN_CONNECTION; + else + type = NM_TYPE_ACTIVE_CONNECTION; + } else { + g_warning ("Error in getting active connection 'Vpn' property: (%d) %s", + error->code, error->message); + g_error_free (error); + type = G_TYPE_INVALID; + } + + g_object_unref (proxy); + return type; +} + +typedef struct { + DBusGConnection *connection; + NMObjectTypeCallbackFunc callback; + gpointer user_data; +} NMActiveConnectionAsyncData; + +static void +async_got_type (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMActiveConnectionAsyncData *async_data = user_data; + GValue value = G_VALUE_INIT; + const char *path = dbus_g_proxy_get_path (proxy); + GError *error = NULL; + GType type; + + if (dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + if (g_value_get_boolean (&value)) + type = NM_TYPE_VPN_CONNECTION; + else + type = NM_TYPE_ACTIVE_CONNECTION; + } else { + g_warning ("%s: could not read properties for %s: %s", __func__, path, error->message); + type = G_TYPE_INVALID; + } + + async_data->callback (type, async_data->user_data); + + g_object_unref (proxy); + g_slice_free (NMActiveConnectionAsyncData, async_data); +} + +static void +_nm_active_connection_type_for_path_async (DBusGConnection *connection, + const char *path, + NMObjectTypeCallbackFunc callback, + gpointer user_data) +{ + NMActiveConnectionAsyncData *async_data; + DBusGProxy *proxy; + + async_data = g_slice_new (NMActiveConnectionAsyncData); + async_data->connection = connection; + async_data->callback = callback; + async_data->user_data = user_data; + + proxy = _nm_dbus_new_proxy_for_connection (connection, path, "org.freedesktop.DBus.Properties"); + dbus_g_proxy_begin_call (proxy, "Get", + async_got_type, async_data, NULL, + G_TYPE_STRING, NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + G_TYPE_STRING, "Vpn", + G_TYPE_INVALID); +} + +/** + * nm_active_connection_get_connection: + * @connection: a #NMActiveConnection + * + * Gets the #NMConnection's DBus object path. This is often used with + * nm_remote_settings_get_connection_by_path() to retrieve the + * #NMRemoteConnection object that describes the connection. + * + * Returns: the object path of the #NMConnection which this #NMActiveConnection + * is an active instance of. This is the internal string used by the + * connection, and must not be modified. + **/ +const char * +nm_active_connection_get_connection (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->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. + * + * Since: 0.9.10 + **/ +const char * +nm_active_connection_get_id (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return 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); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return 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. + * + * Since: 0.9.10 + **/ +const char * +nm_active_connection_get_connection_type (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->type; +} + +/** + * nm_active_connection_get_specific_object: + * @connection: a #NMActiveConnection + * + * Gets the "specific object" used at the activation. + * + * Returns: the specific object's DBus path. This is the internal string used by the + * connection, and must not be modified. + **/ +const char * +nm_active_connection_get_specific_object (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->specific_object; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return handle_ptr_array_return (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); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->state; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->is_default; +} + +/** + * nm_active_connection_get_ip4_config: + * @connection: an #NMActiveConnection + * + * Gets the current #NMIP4Config associated with the #NMActiveConnection. + * + * Returns: (transfer none): the #NMIP4Config, or %NULL if the + * connection is not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED + * state. + * + * Since: 0.9.10 + **/ +NMIP4Config * +nm_active_connection_get_ip4_config (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->ip4_config; +} + +/** + * nm_active_connection_get_dhcp4_config: + * @connection: an #NMActiveConnection + * + * Gets the current #NMDHCP4Config (if any) associated with the + * #NMActiveConnection. + * + * Returns: (transfer none): the #NMDHCP4Config, or %NULL if the + * connection does not use DHCP, or is not in the + * %NM_ACTIVE_CONNECTION_STATE_ACTIVATED state. + * + * Since: 0.9.10 + **/ +NMDHCP4Config * +nm_active_connection_get_dhcp4_config (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->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); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->is_default6; +} + +/** + * nm_active_connection_get_ip6_config: + * @connection: an #NMActiveConnection + * + * Gets the current #NMIP6Config associated with the #NMActiveConnection. + * + * Returns: (transfer none): the #NMIP6Config, or %NULL if the + * connection is not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED + * state. + * + * Since: 0.9.10 + **/ +NMIP6Config * +nm_active_connection_get_ip6_config (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->ip6_config; +} + +/** + * nm_active_connection_get_dhcp6_config: + * @connection: an #NMActiveConnection + * + * Gets the current #NMDHCP6Config (if any) associated with the + * #NMActiveConnection. + * + * Returns: (transfer none): the #NMDHCP6Config, or %NULL if the + * connection does not use DHCPv6, or is not in the + * %NM_ACTIVE_CONNECTION_STATE_ACTIVATED state. + * + * Since: 0.9.10 + **/ +NMDHCP6Config * +nm_active_connection_get_dhcp6_config (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->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 + * + * Since: 0.9.10 + **/ +gboolean +nm_active_connection_get_vpn (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->is_vpn; +} + +/** + * nm_active_connection_get_master: + * @connection: a #NMActiveConnection + * + * Gets the path to the master #NMDevice of the connection. + * + * Returns: the path of the master #NMDevice of the #NMActiveConnection. + * This is the internal string used by the connection, and must not be modified. + **/ +const char * +nm_active_connection_get_master (NMActiveConnection *connection) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL); + + _nm_object_ensure_inited (NM_OBJECT (connection)); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->master; +} + +static void +nm_active_connection_init (NMActiveConnection *ap) +{ +} + +static void +dispose (GObject *object) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object); + + if (priv->devices) { + g_ptr_array_set_free_func (priv->devices, g_object_unref); + g_ptr_array_free (priv->devices, TRUE); + priv->devices = NULL; + } + + g_clear_object (&priv->ip4_config); + g_clear_object (&priv->dhcp4_config); + g_clear_object (&priv->ip6_config); + g_clear_object (&priv->dhcp6_config); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_active_connection_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object); + + g_free (priv->connection); + g_free (priv->id); + g_free (priv->uuid); + g_free (priv->type); + g_free (priv->specific_object); + g_free (priv->master); + + 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); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_string (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: + g_value_set_boxed (value, nm_active_connection_get_specific_object (self)); + break; + case PROP_DEVICES: + g_value_set_boxed (value, nm_active_connection_get_devices (self)); + break; + case PROP_STATE: + g_value_set_uint (value, nm_active_connection_get_state (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_string (value, nm_active_connection_get_master (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +register_properties (NMActiveConnection *connection) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (connection); + const NMPropertiesInfo property_info[] = { + { NM_ACTIVE_CONNECTION_CONNECTION, &priv->connection }, + { NM_ACTIVE_CONNECTION_ID, &priv->id }, + { NM_ACTIVE_CONNECTION_UUID, &priv->uuid }, + { NM_ACTIVE_CONNECTION_TYPE, &priv->type }, + { NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, &priv->specific_object }, + { NM_ACTIVE_CONNECTION_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE }, + { NM_ACTIVE_CONNECTION_STATE, &priv->state }, + { NM_ACTIVE_CONNECTION_DEFAULT, &priv->is_default }, + { NM_ACTIVE_CONNECTION_IP4_CONFIG, &priv->ip4_config, NULL, NM_TYPE_IP4_CONFIG }, + { NM_ACTIVE_CONNECTION_DHCP4_CONFIG, &priv->dhcp4_config, NULL, NM_TYPE_DHCP4_CONFIG }, + { NM_ACTIVE_CONNECTION_DEFAULT6, &priv->is_default6 }, + { NM_ACTIVE_CONNECTION_IP6_CONFIG, &priv->ip6_config, NULL, NM_TYPE_IP6_CONFIG }, + { NM_ACTIVE_CONNECTION_DHCP6_CONFIG, &priv->dhcp6_config, NULL, NM_TYPE_DHCP6_CONFIG }, + { NM_ACTIVE_CONNECTION_VPN, &priv->is_vpn }, + { NM_ACTIVE_CONNECTION_MASTER, &priv->master }, + + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (connection), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_active_connection_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_ACTIVE_CONNECTION); + register_properties (NM_ACTIVE_CONNECTION (object)); +} + + +static void +nm_active_connection_class_init (NMActiveConnectionClass *ap_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ap_class); + + g_type_class_add_private (ap_class, sizeof (NMActiveConnectionPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMActiveConnection:connection: + * + * The connection's path of the active connection. + **/ + g_object_class_install_property + (object_class, PROP_CONNECTION, + g_param_spec_string (NM_ACTIVE_CONNECTION_CONNECTION, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:id: + * + * The active connection's ID + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, 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 + **/ + g_object_class_install_property + (object_class, 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 + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_TYPE, + g_param_spec_string (NM_ACTIVE_CONNECTION_TYPE, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:specific-object: + * + * The specific object's path of the active connection. + **/ + g_object_class_install_property + (object_class, PROP_SPECIFIC_OBJECT, + g_param_spec_string (NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:device: + * + * The devices (#NMDevice) of the active connection. + **/ + g_object_class_install_property + (object_class, PROP_DEVICES, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_DEVICES, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:state: + * + * The state of the active connection. + **/ + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE, "", "", + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:default: + * + * Whether the active connection is the default IPv4 one. + **/ + g_object_class_install_property + (object_class, PROP_DEFAULT, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:ip4-config: + * + * The #NMIP4Config of the connection. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_IP4_CONFIG, + g_param_spec_object (NM_ACTIVE_CONNECTION_IP4_CONFIG, "", "", + NM_TYPE_IP4_CONFIG, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:dhcp4-config: + * + * The #NMDHCP4Config of the connection. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_DHCP4_CONFIG, + g_param_spec_object (NM_ACTIVE_CONNECTION_DHCP4_CONFIG, "", "", + NM_TYPE_DHCP4_CONFIG, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:default6: + * + * Whether the active connection is the default IPv6 one. + **/ + g_object_class_install_property + (object_class, PROP_DEFAULT6, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:ip6-config: + * + * The #NMIP6Config of the connection. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_IP6_CONFIG, + g_param_spec_object (NM_ACTIVE_CONNECTION_IP6_CONFIG, "", "", + NM_TYPE_IP6_CONFIG, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:dhcp6-config: + * + * The #NMDHCP6Config of the connection. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_DHCP6_CONFIG, + g_param_spec_object (NM_ACTIVE_CONNECTION_DHCP6_CONFIG, "", "", + NM_TYPE_DHCP6_CONFIG, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:vpn: + * + * Whether the active connection is a VPN connection. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_VPN, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_VPN, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMActiveConnection:master: + * + * The path of the master device if one exists. + **/ + g_object_class_install_property + (object_class, PROP_MASTER, + g_param_spec_string (NM_ACTIVE_CONNECTION_MASTER, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-active-connection.h b/libnm/nm-active-connection.h new file mode 100644 index 0000000000..e29415649c --- /dev/null +++ b/libnm/nm-active-connection.h @@ -0,0 +1,105 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#ifndef NM_ACTIVE_CONNECTION_H +#define NM_ACTIVE_CONNECTION_H + +#include <glib.h> +#include <glib-object.h> +#include "nm-object.h" +#include <nm-connection.h> +#include <NetworkManager.h> +#include "nm-ip4-config.h" +#include "nm-dhcp4-config.h" +#include "nm-ip6-config.h" +#include "nm-dhcp6-config.h" + +G_BEGIN_DECLS + +#define NM_TYPE_ACTIVE_CONNECTION (nm_active_connection_get_type ()) +#define NM_ACTIVE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnection)) +#define NM_ACTIVE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionClass)) +#define NM_IS_ACTIVE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACTIVE_CONNECTION)) +#define NM_IS_ACTIVE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ACTIVE_CONNECTION)) +#define NM_ACTIVE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionClass)) + +#define NM_ACTIVE_CONNECTION_CONNECTION "connection" +#define NM_ACTIVE_CONNECTION_ID "id" +#define NM_ACTIVE_CONNECTION_UUID "uuid" +#define NM_ACTIVE_CONNECTION_TYPE "type" +#define NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT "specific-object" +#define NM_ACTIVE_CONNECTION_DEVICES "devices" +#define NM_ACTIVE_CONNECTION_STATE "state" +#define NM_ACTIVE_CONNECTION_DEFAULT "default" +#define NM_ACTIVE_CONNECTION_IP4_CONFIG "ip4-config" +#define NM_ACTIVE_CONNECTION_DHCP4_CONFIG "dhcp4-config" +#define NM_ACTIVE_CONNECTION_DEFAULT6 "default6" +#define NM_ACTIVE_CONNECTION_IP6_CONFIG "ip6-config" +#define NM_ACTIVE_CONNECTION_DHCP6_CONFIG "dhcp6-config" +#define NM_ACTIVE_CONNECTION_VPN "vpn" +#define NM_ACTIVE_CONNECTION_MASTER "master" + +typedef struct { + NMObject parent; +} NMActiveConnection; + +typedef struct { + NMObjectClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMActiveConnectionClass; + +GType nm_active_connection_get_type (void); + +GObject *nm_active_connection_new (DBusGConnection *connection, const char *path); + +const char * nm_active_connection_get_connection (NMActiveConnection *connection); +NM_AVAILABLE_IN_0_9_10 +const char * nm_active_connection_get_id (NMActiveConnection *connection); +const char * nm_active_connection_get_uuid (NMActiveConnection *connection); +NM_AVAILABLE_IN_0_9_10 +const char * nm_active_connection_get_connection_type (NMActiveConnection *connection); +const char * nm_active_connection_get_specific_object (NMActiveConnection *connection); +const GPtrArray *nm_active_connection_get_devices (NMActiveConnection *connection); +NMActiveConnectionState nm_active_connection_get_state (NMActiveConnection *connection); +const char * nm_active_connection_get_master (NMActiveConnection *connection); +gboolean nm_active_connection_get_default (NMActiveConnection *connection); +NM_AVAILABLE_IN_0_9_10 +NMIP4Config * nm_active_connection_get_ip4_config (NMActiveConnection *connection); +NM_AVAILABLE_IN_0_9_10 +NMDHCP4Config *nm_active_connection_get_dhcp4_config (NMActiveConnection *connection); +gboolean nm_active_connection_get_default6 (NMActiveConnection *connection); +NM_AVAILABLE_IN_0_9_10 +NMIP6Config * nm_active_connection_get_ip6_config (NMActiveConnection *connection); +NM_AVAILABLE_IN_0_9_10 +NMDHCP6Config *nm_active_connection_get_dhcp6_config (NMActiveConnection *connection); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_active_connection_get_vpn (NMActiveConnection *connection); + +G_END_DECLS + +#endif /* NM_ACTIVE_CONNECTION_H */ diff --git a/libnm/nm-client.c b/libnm/nm-client.c new file mode 100644 index 0000000000..711da7f4a0 --- /dev/null +++ b/libnm/nm-client.c @@ -0,0 +1,2442 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2013 Red Hat, Inc. + */ + +#include <dbus/dbus-glib.h> +#include <string.h> +#include <nm-utils.h> + +#include "nm-client.h" +#include "nm-device-ethernet.h" +#include "nm-device-wifi.h" +#include "nm-device-private.h" +#include "nm-types-private.h" +#include "nm-object-private.h" +#include "nm-active-connection.h" +#include "nm-vpn-connection.h" +#include "nm-object-cache.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" + +void _nm_device_wifi_set_wireless_enabled (NMDeviceWifi *device, gboolean enabled); + +static void nm_client_initable_iface_init (GInitableIface *iface); +static void nm_client_async_initable_iface_init (GAsyncInitableIface *iface); +static GInitableIface *nm_client_parent_initable_iface; +static GAsyncInitableIface *nm_client_parent_async_initable_iface; + +G_DEFINE_TYPE_WITH_CODE (NMClient, nm_client, NM_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(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CLIENT, NMClientPrivate)) + +typedef struct { + DBusGProxy *client_proxy; + DBusGProxy *bus_proxy; + gboolean manager_running; + char *version; + NMState state; + gboolean startup; + GPtrArray *devices; + GPtrArray *active_connections; + NMConnectivityState connectivity; + NMActiveConnection *primary_connection; + NMActiveConnection *activating_connection; + + DBusGProxyCall *perm_call; + GHashTable *permissions; + + /* Activations waiting for their NMActiveConnection + * to appear and then their callback to be called. + */ + GSList *pending_activations; + + gboolean networking_enabled; + gboolean wireless_enabled; + gboolean wireless_hw_enabled; + + gboolean wwan_enabled; + gboolean wwan_hw_enabled; + + gboolean wimax_enabled; + gboolean wimax_hw_enabled; +} NMClientPrivate; + +enum { + PROP_0, + PROP_VERSION, + PROP_STATE, + PROP_STARTUP, + PROP_MANAGER_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_PRIMARY_CONNECTION, + PROP_ACTIVATING_CONNECTION, + PROP_DEVICES, + + LAST_PROP +}; + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + PERMISSION_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void proxy_name_owner_changed (DBusGProxy *proxy, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data); + +/**********************************************************************/ + +/** + * nm_client_error_quark: + * + * Registers an error quark for #NMClient if necessary. + * + * Returns: the error quark used for #NMClient errors. + * + * Since: 0.9.10 + **/ +GQuark +nm_client_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-client-error-quark"); + return quark; +} + +/**********************************************************************/ + +static void +nm_client_init (NMClient *client) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); + + priv->state = NM_STATE_UNKNOWN; + + priv->permissions = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +poke_wireless_devices_with_rf_status (NMClient *client) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); + int i; + + for (i = 0; priv->devices && (i < priv->devices->len); i++) { + NMDevice *device = g_ptr_array_index (priv->devices, i); + + if (NM_IS_DEVICE_WIFI (device)) + _nm_device_wifi_set_wireless_enabled (NM_DEVICE_WIFI (device), priv->wireless_enabled); + } +} + +static void +wireless_enabled_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + poke_wireless_devices_with_rf_status (NM_CLIENT (object)); +} + +static void +register_properties (NMClient *client) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); + const NMPropertiesInfo property_info[] = { + { NM_CLIENT_VERSION, &priv->version }, + { NM_CLIENT_STATE, &priv->state }, + { NM_CLIENT_STARTUP, &priv->startup }, + { NM_CLIENT_NETWORKING_ENABLED, &priv->networking_enabled }, + { NM_CLIENT_WIRELESS_ENABLED, &priv->wireless_enabled }, + { NM_CLIENT_WIRELESS_HARDWARE_ENABLED, &priv->wireless_hw_enabled }, + { NM_CLIENT_WWAN_ENABLED, &priv->wwan_enabled }, + { NM_CLIENT_WWAN_HARDWARE_ENABLED, &priv->wwan_hw_enabled }, + { NM_CLIENT_WIMAX_ENABLED, &priv->wimax_enabled }, + { NM_CLIENT_WIMAX_HARDWARE_ENABLED, &priv->wimax_hw_enabled }, + { NM_CLIENT_ACTIVE_CONNECTIONS, &priv->active_connections, NULL, NM_TYPE_ACTIVE_CONNECTION }, + { NM_CLIENT_CONNECTIVITY, &priv->connectivity }, + { NM_CLIENT_PRIMARY_CONNECTION, &priv->primary_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, + { NM_CLIENT_ACTIVATING_CONNECTION, &priv->activating_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, + { NM_CLIENT_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE, "device" }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (client), + priv->client_proxy, + property_info); +} + +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX "org.freedesktop.NetworkManager.enable-disable-wimax" +#define NM_AUTH_PERMISSION_SLEEP_WAKE "org.freedesktop.NetworkManager.sleep-wake" +#define NM_AUTH_PERMISSION_NETWORK_CONTROL "org.freedesktop.NetworkManager.network-control" +#define NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED "org.freedesktop.NetworkManager.wifi.share.protected" +#define NM_AUTH_PERMISSION_WIFI_SHARE_OPEN "org.freedesktop.NetworkManager.wifi.share.open" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM "org.freedesktop.NetworkManager.settings.modify.system" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN "org.freedesktop.NetworkManager.settings.modify.own" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME "org.freedesktop.NetworkManager.settings.modify.hostname" + +static NMClientPermission +nm_permission_to_client (const char *nm) +{ + if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK)) + return NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK; + else if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI)) + return NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI; + else if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN)) + return NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN; + else if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX)) + return NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX; + else if (!strcmp (nm, NM_AUTH_PERMISSION_SLEEP_WAKE)) + return NM_CLIENT_PERMISSION_SLEEP_WAKE; + else if (!strcmp (nm, NM_AUTH_PERMISSION_NETWORK_CONTROL)) + return NM_CLIENT_PERMISSION_NETWORK_CONTROL; + else if (!strcmp (nm, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED)) + return NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED; + else if (!strcmp (nm, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN)) + return NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN; + else if (!strcmp (nm, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM)) + return NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM; + else if (!strcmp (nm, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN)) + return NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN; + else if (!strcmp (nm, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME)) + return NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME; + + return NM_CLIENT_PERMISSION_NONE; +} + +static NMClientPermissionResult +nm_permission_result_to_client (const char *nm) +{ + if (!strcmp (nm, "yes")) + return NM_CLIENT_PERMISSION_RESULT_YES; + else if (!strcmp (nm, "no")) + return NM_CLIENT_PERMISSION_RESULT_NO; + else if (!strcmp (nm, "auth")) + return NM_CLIENT_PERMISSION_RESULT_AUTH; + return NM_CLIENT_PERMISSION_RESULT_UNKNOWN; +} + +static void +update_permissions (NMClient *self, GHashTable *permissions) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + GHashTableIter iter; + gpointer key, value; + NMClientPermission perm; + NMClientPermissionResult perm_result; + GList *keys, *keys_iter; + + /* get list of old permissions for change notification */ + keys = g_hash_table_get_keys (priv->permissions); + g_hash_table_remove_all (priv->permissions); + + if (permissions) { + /* Process new permissions */ + g_hash_table_iter_init (&iter, permissions); + while (g_hash_table_iter_next (&iter, &key, &value)) { + perm = nm_permission_to_client ((const char *) key); + perm_result = nm_permission_result_to_client ((const char *) value); + if (perm) { + g_hash_table_insert (priv->permissions, + GUINT_TO_POINTER (perm), + GUINT_TO_POINTER (perm_result)); + + /* Remove this permission from the list of previous permissions + * we'll be sending NM_CLIENT_PERMISSION_RESULT_UNKNOWN for + * in the change signal since it is still a known permission. + */ + keys = g_list_remove (keys, GUINT_TO_POINTER (perm)); + } + } + } + + /* Signal changes in all updated permissions */ + g_hash_table_iter_init (&iter, priv->permissions); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_signal_emit (self, signals[PERMISSION_CHANGED], 0, + GPOINTER_TO_UINT (key), + GPOINTER_TO_UINT (value)); + } + + /* And signal changes in all permissions that used to be valid but for + * some reason weren't received in the last request (if any). + */ + for (keys_iter = keys; keys_iter; keys_iter = g_list_next (keys_iter)) { + g_signal_emit (self, signals[PERMISSION_CHANGED], 0, + GPOINTER_TO_UINT (keys_iter->data), + NM_CLIENT_PERMISSION_RESULT_UNKNOWN); + } + g_list_free (keys); +} + +static gboolean +get_permissions_sync (NMClient *self, GError **error) +{ + gboolean success; + GHashTable *permissions = NULL; + + success = dbus_g_proxy_call_with_timeout (NM_CLIENT_GET_PRIVATE (self)->client_proxy, + "GetPermissions", 3000, error, + G_TYPE_INVALID, + DBUS_TYPE_G_MAP_OF_STRING, &permissions, G_TYPE_INVALID); + update_permissions (self, success ? permissions : NULL); + if (permissions) + g_hash_table_destroy (permissions); + + return success; +} + +static void +get_permissions_reply (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + NMClient *self = NM_CLIENT (user_data); + GHashTable *permissions; + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_STRING, &permissions, + G_TYPE_INVALID); + NM_CLIENT_GET_PRIVATE (self)->perm_call = NULL; + update_permissions (NM_CLIENT (user_data), error ? NULL : permissions); + g_clear_error (&error); +} + +static void +client_recheck_permissions (DBusGProxy *proxy, gpointer user_data) +{ + NMClient *self = NM_CLIENT (user_data); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + if (!priv->perm_call) { + priv->perm_call = dbus_g_proxy_begin_call (NM_CLIENT_GET_PRIVATE (self)->client_proxy, "GetPermissions", + get_permissions_reply, self, NULL, + G_TYPE_INVALID); + } +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (client)); + + return handle_ptr_array_return (NM_CLIENT_GET_PRIVATE (client)->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) +{ + const GPtrArray *devices; + int i; + NMDevice *device = NULL; + + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + g_return_val_if_fail (object_path, NULL); + + devices = nm_client_get_devices (client); + if (!devices) + return NULL; + + for (i = 0; i < devices->len; i++) { + NMDevice *candidate = g_ptr_array_index (devices, i); + if (!strcmp (nm_object_get_path (NM_OBJECT (candidate)), object_path)) { + device = candidate; + break; + } + } + + return 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; + int i; + NMDevice *device = NULL; + + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + g_return_val_if_fail (iface, NULL); + + devices = nm_client_get_devices (client); + if (!devices) + return NULL; + + for (i = 0; i < devices->len; i++) { + NMDevice *candidate = g_ptr_array_index (devices, i); + if (!strcmp (nm_device_get_iface (candidate), iface)) { + device = candidate; + break; + } + } + + return device; +} + +typedef struct { + NMClient *client; + NMClientActivateFn act_fn; + NMClientAddActivateFn add_act_fn; + char *active_path; + char *new_connection_path; + guint idle_id; + gpointer user_data; +} ActivateInfo; + +static void +activate_info_free (ActivateInfo *info) +{ + if (info->idle_id) + g_source_remove (info->idle_id); + g_free (info->active_path); + g_free (info->new_connection_path); + memset (info, 0, sizeof (*info)); + g_slice_free (ActivateInfo, info); +} + +static void +activate_info_complete (ActivateInfo *info, + NMActiveConnection *active, + GError *error) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (info->client); + + if (info->act_fn) + info->act_fn (info->client, error ? NULL : active, error, info->user_data); + else if (info->add_act_fn) { + info->add_act_fn (info->client, + error ? NULL : active, + error ? NULL : info->new_connection_path, + error, + info->user_data); + } else if (error) + g_warning ("Device activation failed: (%d) %s", error->code, error->message); + + priv->pending_activations = g_slist_remove (priv->pending_activations, info); +} + +static void +recheck_pending_activations (NMClient *self, const char *failed_path, GError *error) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + GSList *iter, *next; + const GPtrArray *active_connections; + gboolean found_in_active = FALSE; + gboolean found_in_pending = FALSE; + ActivateInfo *ainfo = NULL; + int i; + + active_connections = nm_client_get_active_connections (self); + + /* For each pending activation, look for a active connection that has + * the pending activation's object path, and call pending connection's + * callback. + * If the connection to activate doesn't make it to active_connections, + * due to an error, we have to call the callback for failed_path. + */ + for (iter = priv->pending_activations; iter; iter = next) { + ActivateInfo *info = iter->data; + + next = g_slist_next (iter); + + if (!found_in_pending && failed_path && g_strcmp0 (failed_path, info->active_path) == 0) { + found_in_pending = TRUE; + ainfo = info; + } + + for (i = 0; active_connections && i < active_connections->len; i++) { + NMActiveConnection *active = g_ptr_array_index (active_connections, i); + const char *active_path = nm_object_get_path (NM_OBJECT (active)); + + if (!found_in_active && failed_path && g_strcmp0 (failed_path, active_path) == 0) + found_in_active = TRUE; + + if (g_strcmp0 (info->active_path, active_path) == 0) { + /* Call the pending activation's callback and it all up */ + activate_info_complete (info, active, NULL); + activate_info_free (info); + break; + } + } + } + + if (!found_in_active && found_in_pending) { + /* A newly activated connection failed due to some immediate error + * and disappeared from active connection list. Make sure the + * callback gets called. + */ + activate_info_complete (ainfo, NULL, error); + activate_info_free (ainfo); + } +} + +static void +activate_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + ActivateInfo *info = user_data; + char *path; + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_OBJECT_PATH, &path, + G_TYPE_INVALID); + if (error) { + activate_info_complete (info, NULL, error); + activate_info_free (info); + g_clear_error (&error); + } else { + info->active_path = path; + recheck_pending_activations (info->client, NULL, NULL); + } +} + +static gboolean +activate_nm_not_running (gpointer user_data) +{ + ActivateInfo *info = user_data; + GError *error; + + info->idle_id = 0; + + error = g_error_new_literal (NM_CLIENT_ERROR, + NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, + "NetworkManager is not running"); + activate_info_complete (info, NULL, error); + activate_info_free (info); + g_clear_error (&error); + return FALSE; +} + +/** + * nm_client_activate_connection: + * @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. + * @callback: (scope async) (allow-none): the function to call when the call is done + * @user_data: (closure): user data to pass to the callback function + * + * 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. + **/ +void +nm_client_activate_connection (NMClient *client, + NMConnection *connection, + NMDevice *device, + const char *specific_object, + NMClientActivateFn callback, + gpointer user_data) +{ + NMClientPrivate *priv; + ActivateInfo *info; + + g_return_if_fail (NM_IS_CLIENT (client)); + if (device) + g_return_if_fail (NM_IS_DEVICE (device)); + if (connection) + g_return_if_fail (NM_IS_CONNECTION (connection)); + + info = g_slice_new0 (ActivateInfo); + info->act_fn = callback; + info->user_data = user_data; + info->client = client; + + priv = NM_CLIENT_GET_PRIVATE (client); + priv->pending_activations = g_slist_prepend (priv->pending_activations, info); + + if (priv->manager_running == FALSE) { + info->idle_id = g_idle_add (activate_nm_not_running, info); + return; + } + + dbus_g_proxy_begin_call (priv->client_proxy, "ActivateConnection", + activate_cb, info, NULL, + DBUS_TYPE_G_OBJECT_PATH, connection ? nm_connection_get_path (connection) : "/", + DBUS_TYPE_G_OBJECT_PATH, device ? nm_object_get_path (NM_OBJECT (device)) : "/", + DBUS_TYPE_G_OBJECT_PATH, specific_object ? specific_object : "/", + G_TYPE_INVALID); +} + +static void +add_activate_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + ActivateInfo *info = user_data; + char *connection_path; + char *active_path; + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_OBJECT_PATH, &connection_path, + DBUS_TYPE_G_OBJECT_PATH, &active_path, + G_TYPE_INVALID); + if (error) { + activate_info_complete (info, NULL, error); + activate_info_free (info); + } else { + info->new_connection_path = connection_path; + info->active_path = active_path; + recheck_pending_activations (info->client, NULL, NULL); + } +} + +/** + * nm_client_add_and_activate_connection: + * @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. + * @callback: (scope async) (allow-none): the function to call when the call is done + * @user_data: (closure): user data to pass to the callback function + * + * 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 activated. + * Cannot be used for VPN connections at this time. + **/ +void +nm_client_add_and_activate_connection (NMClient *client, + NMConnection *partial, + NMDevice *device, + const char *specific_object, + NMClientAddActivateFn callback, + gpointer user_data) +{ + NMClientPrivate *priv; + ActivateInfo *info; + GHashTable *hash = NULL; + + g_return_if_fail (NM_IS_CLIENT (client)); + g_return_if_fail (NM_IS_DEVICE (device)); + + info = g_slice_new0 (ActivateInfo); + info->add_act_fn = callback; + info->user_data = user_data; + info->client = client; + + if (partial) + hash = nm_connection_to_hash (partial, NM_SETTING_HASH_FLAG_ALL); + if (!hash) + hash = g_hash_table_new (g_str_hash, g_str_equal); + + priv = NM_CLIENT_GET_PRIVATE (client); + priv->pending_activations = g_slist_prepend (priv->pending_activations, info); + + if (priv->manager_running) { + dbus_g_proxy_begin_call (priv->client_proxy, "AddAndActivateConnection", + add_activate_cb, info, NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash, + DBUS_TYPE_G_OBJECT_PATH, nm_object_get_path (NM_OBJECT (device)), + DBUS_TYPE_G_OBJECT_PATH, specific_object ? specific_object : "/", + G_TYPE_INVALID); + } else + info->idle_id = g_idle_add (activate_nm_not_running, info); + + g_hash_table_unref (hash); +} + +static void +active_connections_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + recheck_pending_activations (NM_CLIENT (object), NULL, NULL); +} + +static void +object_creation_failed_cb (GObject *object, GError *error, char *failed_path) +{ + if (error) + recheck_pending_activations (NM_CLIENT (object), failed_path, error); +} + +/** + * nm_client_deactivate_connection: + * @client: a #NMClient + * @active: the #NMActiveConnection to deactivate + * + * Deactivates an active #NMActiveConnection. + **/ +void +nm_client_deactivate_connection (NMClient *client, NMActiveConnection *active) +{ + NMClientPrivate *priv; + const char *path; + GError *error = NULL; + + g_return_if_fail (NM_IS_CLIENT (client)); + g_return_if_fail (NM_IS_ACTIVE_CONNECTION (active)); + + priv = NM_CLIENT_GET_PRIVATE (client); + if (!priv->manager_running) + return; + + path = nm_object_get_path (NM_OBJECT (active)); + if (!dbus_g_proxy_call (priv->client_proxy, "DeactivateConnection", &error, + DBUS_TYPE_G_OBJECT_PATH, path, + G_TYPE_INVALID, + G_TYPE_INVALID)) { + g_warning ("Could not deactivate connection '%s': %s", path, error->message); + g_error_free (error); + } +} + +/** + * 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) +{ + NMClientPrivate *priv; + + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + + _nm_object_ensure_inited (NM_OBJECT (client)); + + priv = NM_CLIENT_GET_PRIVATE (client); + if (!priv->manager_running) + return NULL; + + return handle_ptr_array_return (priv->active_connections); +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->wireless_enabled; +} + +/** + * nm_client_wireless_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable wireless + * + * Enables or disables wireless devices. + **/ +void +nm_client_wireless_set_enabled (NMClient *client, gboolean enabled) +{ + GValue value = G_VALUE_INIT; + + g_return_if_fail (NM_IS_CLIENT (client)); + + if (!NM_CLIENT_GET_PRIVATE (client)->manager_running) + return; + + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, enabled); + + _nm_object_set_property (NM_OBJECT (client), + NM_DBUS_INTERFACE, + "WirelessEnabled", + &value); +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->wireless_hw_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); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->wwan_enabled; +} + +/** + * nm_client_wwan_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable WWAN + * + * Enables or disables WWAN devices. + **/ +void +nm_client_wwan_set_enabled (NMClient *client, gboolean enabled) +{ + GValue value = G_VALUE_INIT; + + g_return_if_fail (NM_IS_CLIENT (client)); + + if (!NM_CLIENT_GET_PRIVATE (client)->manager_running) + return; + + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, enabled); + + _nm_object_set_property (NM_OBJECT (client), + NM_DBUS_INTERFACE, + "WwanEnabled", + &value); +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->wwan_hw_enabled; +} + +/** + * nm_client_wimax_get_enabled: + * @client: a #NMClient + * + * Determines whether WiMAX is enabled. + * + * Returns: %TRUE if WiMAX is enabled + **/ +gboolean +nm_client_wimax_get_enabled (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->wimax_enabled; +} + +/** + * nm_client_wimax_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to enable WiMAX + * + * Enables or disables WiMAX devices. + **/ +void +nm_client_wimax_set_enabled (NMClient *client, gboolean enabled) +{ + GValue value = G_VALUE_INIT; + + g_return_if_fail (NM_IS_CLIENT (client)); + + if (!NM_CLIENT_GET_PRIVATE (client)->manager_running) + return; + + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, enabled); + + _nm_object_set_property (NM_OBJECT (client), + NM_DBUS_INTERFACE, + "WimaxEnabled", + &value); +} + +/** + * nm_client_wimax_hardware_get_enabled: + * @client: a #NMClient + * + * Determines whether the WiMAX hardware is enabled. + * + * Returns: %TRUE if the WiMAX hardware is enabled + **/ +gboolean +nm_client_wimax_hardware_get_enabled (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->wimax_hw_enabled; +} + +/** + * nm_client_get_version: + * @client: a #NMClient + * + * Gets NetworkManager version. + * + * Returns: string with the version + **/ +const char * +nm_client_get_version (NMClient *client) +{ + NMClientPrivate *priv; + + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + + priv = NM_CLIENT_GET_PRIVATE (client); + + _nm_object_ensure_inited (NM_OBJECT (client)); + + return priv->manager_running ? priv->version : NULL; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (client)); + + return NM_CLIENT_GET_PRIVATE (client)->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 + * + * Since: 0.9.10 + **/ +gboolean +nm_client_get_startup (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), NM_STATE_UNKNOWN); + + _nm_object_ensure_inited (NM_OBJECT (client)); + + return NM_CLIENT_GET_PRIVATE (client)->startup; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->networking_enabled; +} + +/** + * nm_client_networking_set_enabled: + * @client: a #NMClient + * @enabled: %TRUE to set networking enabled, %FALSE to set networking disabled + * + * 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. + **/ +void +nm_client_networking_set_enabled (NMClient *client, gboolean enable) +{ + GError *err = NULL; + + g_return_if_fail (NM_IS_CLIENT (client)); + + if (!NM_CLIENT_GET_PRIVATE (client)->manager_running) + return; + + if (!dbus_g_proxy_call (NM_CLIENT_GET_PRIVATE (client)->client_proxy, "Enable", &err, + G_TYPE_BOOLEAN, enable, + G_TYPE_INVALID, + G_TYPE_INVALID)) { + g_warning ("Error enabling/disabling networking: %s", err->message); + g_error_free (err); + } +} + +/** + * nm_client_sleep: + * @client: a #NMClient + * @sleep_: %TRUE to put the daemon to sleep + * + * Deprecated; use nm_client_networking_set_enabled() instead. + **/ +void +nm_client_sleep (NMClient *client, gboolean sleep_) +{ + nm_client_networking_set_enabled (client, !sleep_); +} + +/** + * nm_client_get_manager_running: + * @client: a #NMClient + * + * Determines whether the daemon is running. + * + * Returns: %TRUE if the daemon is running + **/ +gboolean +nm_client_get_manager_running (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); + + return NM_CLIENT_GET_PRIVATE (client)->manager_running; +} + +/** + * 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) +{ + gpointer result; + + g_return_val_if_fail (NM_IS_CLIENT (client), NM_CLIENT_PERMISSION_RESULT_UNKNOWN); + + result = g_hash_table_lookup (NM_CLIENT_GET_PRIVATE (client)->permissions, + GUINT_TO_POINTER (permission)); + return GPOINTER_TO_UINT (result); +} + +/** + * 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 + * + * Since: 0.9.8 + **/ +gboolean +nm_client_get_logging (NMClient *client, char **level, char **domains, GError **error) +{ + NMClientPrivate *priv; + + 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); + + priv = NM_CLIENT_GET_PRIVATE (client); + if (!priv->manager_running) { + g_set_error_literal (error, + NM_CLIENT_ERROR, + NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, + "NetworkManager is not running"); + return FALSE; + } + + if (!level && !domains) + return TRUE; + + return dbus_g_proxy_call (priv->client_proxy, "GetLogging", error, + G_TYPE_INVALID, + G_TYPE_STRING, level, + G_TYPE_STRING, domains, + G_TYPE_INVALID); +} + +/** + * 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 + * + * Since: 0.9.8 + **/ +gboolean +nm_client_set_logging (NMClient *client, const char *level, const char *domains, GError **error) +{ + NMClientPrivate *priv; + + g_return_val_if_fail (NM_IS_CLIENT (client), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + priv = NM_CLIENT_GET_PRIVATE (client); + if (!priv->manager_running) { + g_set_error_literal (error, + NM_CLIENT_ERROR, + NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, + "NetworkManager is not running"); + return FALSE; + } + + if (!level && !domains) + return TRUE; + + return dbus_g_proxy_call (priv->client_proxy, "SetLogging", error, + G_TYPE_STRING, level ? level : "", + G_TYPE_STRING, domains ? domains : "", + G_TYPE_INVALID, + G_TYPE_INVALID); +} + +/** + * 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 + * + * Since: 0.9.8.6 + */ +NMActiveConnection * +nm_client_get_primary_connection (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->primary_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. + * + * Since: 0.9.8.6 + */ +NMActiveConnection * +nm_client_get_activating_connection (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + + _nm_object_ensure_inited (NM_OBJECT (client)); + return NM_CLIENT_GET_PRIVATE (client)->activating_connection; +} + +/****************************************************************/ + +static void +free_devices (NMClient *client, gboolean emit_signals) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); + GPtrArray *devices; + NMDevice *device; + int i; + + if (!priv->devices) + return; + + devices = priv->devices; + priv->devices = NULL; + for (i = 0; i < devices->len; i++) { + device = devices->pdata[i]; + if (emit_signals) + g_signal_emit (client, signals[DEVICE_REMOVED], 0, device); + g_object_unref (device); + } + g_ptr_array_free (devices, TRUE); +} + +static void +free_active_connections (NMClient *client, gboolean emit_signals) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); + GPtrArray *active_connections; + NMActiveConnection *active_connection; + int i; + + if (!priv->active_connections) + return; + + active_connections = priv->active_connections; + priv->active_connections = NULL; + for (i = 0; i < active_connections->len; i++) { + active_connection = active_connections->pdata[i]; + /* Break circular refs */ + g_object_run_dispose (G_OBJECT (active_connection)); + g_object_unref (active_connection); + } + g_ptr_array_free (active_connections, TRUE); + + if (emit_signals) + g_object_notify (G_OBJECT (client), NM_CLIENT_ACTIVE_CONNECTIONS); +} + +static void +updated_properties (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMClient *client = NM_CLIENT (user_data); + GError *error = NULL; + + if (!_nm_object_reload_properties_finish (NM_OBJECT (object), result, &error)) { + g_warning ("%s: error reading NMClient properties: %s", __func__, error->message); + g_error_free (error); + } + + _nm_object_queue_notify (NM_OBJECT (client), NM_CLIENT_MANAGER_RUNNING); +} + +static void +proxy_name_owner_changed (DBusGProxy *proxy, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + NMClient *client = NM_CLIENT (user_data); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); + gboolean old_good = (old_owner && strlen (old_owner)); + gboolean new_good = (new_owner && strlen (new_owner)); + gboolean new_running = FALSE; + + if (!name || strcmp (name, NM_DBUS_SERVICE)) + return; + + if (!old_good && new_good) + new_running = TRUE; + else if (old_good && !new_good) + new_running = FALSE; + + if (new_running == priv->manager_running) + return; + + priv->manager_running = new_running; + if (!priv->manager_running) { + priv->state = NM_STATE_UNKNOWN; + priv->startup = FALSE; + _nm_object_queue_notify (NM_OBJECT (client), NM_CLIENT_MANAGER_RUNNING); + _nm_object_suppress_property_updates (NM_OBJECT (client), TRUE); + poke_wireless_devices_with_rf_status (client); + free_devices (client, TRUE); + free_active_connections (client, TRUE); + update_permissions (client, NULL); + priv->wireless_enabled = FALSE; + priv->wireless_hw_enabled = FALSE; + priv->wwan_enabled = FALSE; + priv->wwan_hw_enabled = FALSE; + priv->wimax_enabled = FALSE; + priv->wimax_hw_enabled = FALSE; + g_free (priv->version); + priv->version = NULL; + + /* Clear object cache to ensure bad refcounting by clients doesn't + * keep objects in the cache. + */ + _nm_object_cache_clear (); + } else { + _nm_object_suppress_property_updates (NM_OBJECT (client), FALSE); + _nm_object_reload_properties_async (NM_OBJECT (client), updated_properties, client); + client_recheck_permissions (priv->client_proxy, client); + } +} + +/** + * 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 + * Since: 0.9.8.6 + */ +NMConnectivityState +nm_client_get_connectivity (NMClient *client) +{ + NMClientPrivate *priv; + + g_return_val_if_fail (NM_IS_CLIENT (client), NM_STATE_UNKNOWN); + priv = NM_CLIENT_GET_PRIVATE (client); + + _nm_object_ensure_inited (NM_OBJECT (client)); + + return priv->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 + * Since: 0.9.8.6 + */ +NMConnectivityState +nm_client_check_connectivity (NMClient *client, + GCancellable *cancellable, + GError **error) +{ + NMClientPrivate *priv; + NMConnectivityState connectivity; + + g_return_val_if_fail (NM_IS_CLIENT (client), NM_CONNECTIVITY_UNKNOWN); + priv = NM_CLIENT_GET_PRIVATE (client); + + if (!dbus_g_proxy_call (priv->client_proxy, "CheckConnectivity", error, + G_TYPE_INVALID, + G_TYPE_UINT, &connectivity, + G_TYPE_INVALID)) + connectivity = NM_CONNECTIVITY_UNKNOWN; + + return connectivity; +} + +typedef struct { + NMClient *client; + DBusGProxyCall *call; + GCancellable *cancellable; + guint cancelled_id; + NMConnectivityState connectivity; +} CheckConnectivityData; + +static void +check_connectivity_data_free (CheckConnectivityData *ccd) +{ + if (ccd->cancellable) { + if (ccd->cancelled_id) + g_signal_handler_disconnect (ccd->cancellable, ccd->cancelled_id); + g_object_unref (ccd->cancellable); + } + + g_slice_free (CheckConnectivityData, ccd); +} + +static void +check_connectivity_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + CheckConnectivityData *ccd = g_simple_async_result_get_op_res_gpointer (simple); + GError *error = NULL; + + if (ccd->cancellable) { + g_signal_handler_disconnect (ccd->cancellable, ccd->cancelled_id); + ccd->cancelled_id = 0; + } + + if (!dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_UINT, &ccd->connectivity, + G_TYPE_INVALID)) + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +check_connectivity_cancelled_cb (GCancellable *cancellable, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + CheckConnectivityData *ccd = g_simple_async_result_get_op_res_gpointer (simple); + + g_signal_handler_disconnect (cancellable, ccd->cancelled_id); + ccd->cancelled_id = 0; + + dbus_g_proxy_cancel_call (NM_CLIENT_GET_PRIVATE (ccd->client)->client_proxy, ccd->call); + g_simple_async_result_complete_in_idle (simple); +} + +/** + * 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. + * + * Since: 0.9.8.6 + */ +void +nm_client_check_connectivity_async (NMClient *client, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientPrivate *priv; + GSimpleAsyncResult *simple; + CheckConnectivityData *ccd; + + g_return_if_fail (NM_IS_CLIENT (client)); + priv = NM_CLIENT_GET_PRIVATE (client); + + ccd = g_slice_new (CheckConnectivityData); + ccd->client = client; + + simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, + nm_client_check_connectivity_async); + g_simple_async_result_set_op_res_gpointer (simple, ccd, (GDestroyNotify) check_connectivity_data_free); + + if (cancellable) { + ccd->cancellable = g_object_ref (cancellable); + ccd->cancelled_id = g_signal_connect (cancellable, "cancelled", + G_CALLBACK (check_connectivity_cancelled_cb), + simple); + g_simple_async_result_set_check_cancellable (simple, cancellable); + } + + ccd->call = dbus_g_proxy_begin_call (priv->client_proxy, "CheckConnectivity", + check_connectivity_cb, simple, NULL, + G_TYPE_INVALID); +} + +/** + * 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 + * Since: 0.9.8.6 + */ +NMConnectivityState +nm_client_check_connectivity_finish (NMClient *client, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + CheckConnectivityData *ccd; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (client), nm_client_check_connectivity_async), NM_CONNECTIVITY_UNKNOWN); + + simple = G_SIMPLE_ASYNC_RESULT (result); + ccd = g_simple_async_result_get_op_res_gpointer (simple); + + if (g_simple_async_result_propagate_error (simple, error)) + return NM_CONNECTIVITY_UNKNOWN; + + return ccd->connectivity; +} + +/****************************************************************/ + +/** + * nm_client_new: + * + * Creates a new #NMClient. + * + * Note that this will do blocking D-Bus calls to initialize the + * client. You can use nm_client_new_async() if you want to avoid + * that. + * + * NOTE: #NMClient provides information about devices and a mechanism to + * control them. To access and modify network configuration data, use the + * #NMRemoteSettings object. + * + * Returns: a new #NMClient or NULL on an error + **/ +NMClient * +nm_client_new (void) +{ + NMClient *client; + + client = g_object_new (NM_TYPE_CLIENT, NM_OBJECT_DBUS_PATH, NM_DBUS_PATH, NULL); + + /* NMObject's constructor() can fail on a D-Bus connection error. So we can + * get NULL here instead of a valid NMClient object. + */ + if (client) + _nm_object_ensure_inited (NM_OBJECT (client)); + + return client; +} + +static void +client_inited (GObject *source, GAsyncResult *result, gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + GError *error = NULL; + + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source), result, &error)) + g_simple_async_result_take_error (simple, error); + else + g_simple_async_result_set_op_res_gpointer (simple, source, g_object_unref); + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +/** + * 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 and begins asynchronously initializing it. + * @callback will be called when it is done; use + * nm_client_new_finish() to get the result. Note that on an error, + * the callback can be invoked with two first parameters as NULL. + * + * NOTE: #NMClient provides information about devices and a mechanism to + * control them. To access and modify network configuration data, use the + * #NMRemoteSettings object. + **/ +void +nm_client_new_async (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClient *client; + GSimpleAsyncResult *simple; + + client = g_object_new (NM_TYPE_CLIENT, NM_OBJECT_DBUS_PATH, NM_DBUS_PATH, NULL); + /* When client is NULL, do no continue with initialization and run callback + * directly with result == NULL indicating NMClient creation failure. + */ + if (!client) { + callback (NULL, NULL, user_data); + return; + } + + simple = g_simple_async_result_new (NULL, callback, user_data, nm_client_new_async); + g_async_initable_init_async (G_ASYNC_INITABLE (client), G_PRIORITY_DEFAULT, + cancellable, client_inited, simple); +} + +/** + * 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) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!result) { + g_set_error_literal (error, + NM_CLIENT_ERROR, + NM_CLIENT_ERROR_UNKNOWN, + "NMClient initialization failed (or you passed NULL 'result' by mistake)"); + return NULL; + } + + g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, nm_client_new_async), NULL); + + simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + else + return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); +} + +/* + * Validate D-Bus object path. + * The function is copied and adjusted version of + * g_variant_serialiser_is_object_path() from glib. + * FIXME: The function can be replaced by g_variant_is_object_path() + * when we start using GLib >= 2.24 + */ +static gboolean +_nm_client_is_object_path (const char *string) +{ + gsize i; + + if (!g_utf8_validate (string, -1, NULL)) + return FALSE; + + /* The path must begin with an ASCII '/' (integer 47) character */ + if (string[0] != '/') + return FALSE; + + for (i = 1; string[i]; i++) { + /* Each element must only contain the ASCII characters + * "[A-Z][a-z][0-9]_" + */ + if (g_ascii_isalnum (string[i]) || string[i] == '_') + ; + /* must consist of elements separated by slash characters. */ + else if (string[i] == '/') { + /* No element may be the empty string. */ + /* Multiple '/' characters cannot occur in sequence. */ + if (string[i - 1] == '/') + return FALSE; + } else + return FALSE; + } + + /* A trailing '/' character is not allowed unless the path is the + * root path (a single '/' character). + */ + if (i > 1 && string[i - 1] == '/') + return FALSE; + + return TRUE; +} + +/* + * constructor() shouldn't be overriden in most cases, rather constructed() + * method is preferred and more useful. + * But, this serves as a workaround for bindings (use) calling the constructor() + * directly instead of nm_client_new() function, and neither providing + * construction properties. So, we fill "dbus-path" here if it was not specified + * (was set to default value (NULL)). + * + * It allows this python code: + * from gi.repository import NMClient + * nmclient = NMClient.Client() + * print nmclient.get_active_connections() + * + * instead of proper + * nmclient = NMClient.Client().new() + * + * Note: + * A nice overview of GObject construction is here: + * http://blogs.gnome.org/desrt/2012/02/26/a-gentle-introduction-to-gobject-construction + * It is much better explanation than the official docs + * http://developer.gnome.org/gobject/unstable/chapter-gobject.html#gobject-instantiation + */ +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + guint i; + const char *dbus_path; + + for (i = 0; i < n_construct_params; i++) { + if (strcmp (construct_params[i].pspec->name, NM_OBJECT_DBUS_PATH) == 0) { + dbus_path = g_value_get_string (construct_params[i].value); + if (dbus_path == NULL) { + g_value_set_static_string (construct_params[i].value, NM_DBUS_PATH); + } else { + if (!_nm_client_is_object_path (dbus_path)) { + g_warning ("Passsed D-Bus object path '%s' is invalid; using default '%s' instead", + dbus_path, NM_DBUS_PATH); + g_value_set_static_string (construct_params[i].value, NM_DBUS_PATH); + } + } + break; + } + } + + object = G_OBJECT_CLASS (nm_client_parent_class)->constructor (type, + n_construct_params, + construct_params); + + return object; +} + +static void +constructed (GObject *object) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object); + GError *error = NULL; + + if (!nm_utils_init (&error)) { + g_warning ("Couldn't initilize nm-utils/crypto system: %d %s", + error->code, error->message); + g_clear_error (&error); + } + + G_OBJECT_CLASS (nm_client_parent_class)->constructed (object); + + priv->client_proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE); + + register_properties (NM_CLIENT (object)); + + /* Permissions */ + dbus_g_proxy_add_signal (priv->client_proxy, "CheckPermissions", G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->client_proxy, + "CheckPermissions", + G_CALLBACK (client_recheck_permissions), + object, + NULL); + + if (_nm_object_is_connection_private (NM_OBJECT (object))) + priv->manager_running = TRUE; + else { + priv->bus_proxy = dbus_g_proxy_new_for_name (nm_object_get_connection (NM_OBJECT (object)), + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + g_assert (priv->bus_proxy); + + dbus_g_proxy_add_signal (priv->bus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->bus_proxy, + "NameOwnerChanged", + G_CALLBACK (proxy_name_owner_changed), + object, NULL); + } + + g_signal_connect (object, "notify::" NM_CLIENT_WIRELESS_ENABLED, + G_CALLBACK (wireless_enabled_cb), NULL); + + g_signal_connect (object, "notify::" NM_CLIENT_ACTIVE_CONNECTIONS, + G_CALLBACK (active_connections_changed_cb), NULL); + + g_signal_connect (object, "object-creation-failed", + G_CALLBACK (object_creation_failed_cb), NULL); +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMClient *client = NM_CLIENT (initable); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); + + if (!nm_client_parent_initable_iface->init (initable, cancellable, error)) + return FALSE; + + if (!_nm_object_is_connection_private (NM_OBJECT (client))) { + if (!dbus_g_proxy_call (priv->bus_proxy, + "NameHasOwner", error, + G_TYPE_STRING, NM_DBUS_SERVICE, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &priv->manager_running, + G_TYPE_INVALID)) + return FALSE; + } + + if (priv->manager_running && !get_permissions_sync (client, error)) + return FALSE; + + return TRUE; +} + +typedef struct { + NMClient *client; + GSimpleAsyncResult *result; + gboolean properties_pending; + gboolean permissions_pending; +} NMClientInitData; + +static void +init_async_complete (NMClientInitData *init_data) +{ + if (init_data->properties_pending || init_data->permissions_pending) + return; + + g_simple_async_result_complete (init_data->result); + g_object_unref (init_data->result); + g_slice_free (NMClientInitData, init_data); +} + +static void +init_async_got_permissions (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMClientInitData *init_data = user_data; + GHashTable *permissions; + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_STRING, &permissions, + G_TYPE_INVALID); + update_permissions (init_data->client, error ? NULL : permissions); + g_clear_error (&error); + + init_data->permissions_pending = FALSE; + init_async_complete (init_data); +} + +static void +init_async_got_properties (GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMClientInitData *init_data = user_data; + GError *error = NULL; + + if (!nm_client_parent_async_initable_iface->init_finish (G_ASYNC_INITABLE (source), result, &error)) + g_simple_async_result_take_error (init_data->result, error); + + init_data->properties_pending = FALSE; + init_async_complete (init_data); +} + +static void +finish_init (NMClientInitData *init_data) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (init_data->client); + + nm_client_parent_async_initable_iface->init_async (G_ASYNC_INITABLE (init_data->client), + G_PRIORITY_DEFAULT, NULL, /* FIXME cancellable */ + init_async_got_properties, init_data); + init_data->properties_pending = TRUE; + + dbus_g_proxy_begin_call (priv->client_proxy, "GetPermissions", + init_async_got_permissions, init_data, NULL, + G_TYPE_INVALID); + init_data->permissions_pending = TRUE; +} + +static void +init_async_got_manager_running (DBusGProxy *proxy, DBusGProxyCall *call, + gpointer user_data) +{ + NMClientInitData *init_data = user_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (init_data->client); + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_BOOLEAN, &priv->manager_running, + G_TYPE_INVALID)) { + g_simple_async_result_take_error (init_data->result, error); + init_async_complete (init_data); + return; + } + + if (!priv->manager_running) { + init_async_complete (init_data); + return; + } + + finish_init (init_data); +} + +static void +init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientInitData *init_data; + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (initable); + + init_data = g_slice_new0 (NMClientInitData); + init_data->client = NM_CLIENT (initable); + init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback, + user_data, init_async); + g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE); + + if (_nm_object_is_connection_private (NM_OBJECT (init_data->client))) + finish_init (init_data); + else { + /* Check if NM is running */ + dbus_g_proxy_begin_call (priv->bus_proxy, "NameHasOwner", + init_async_got_manager_running, + init_data, NULL, + G_TYPE_STRING, NM_DBUS_SERVICE, + G_TYPE_INVALID); + } +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return TRUE; +} + +static void +dispose (GObject *object) +{ + NMClient *client = NM_CLIENT (object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object); + + if (priv->perm_call) { + dbus_g_proxy_cancel_call (priv->client_proxy, priv->perm_call); + priv->perm_call = NULL; + } + + g_clear_object (&priv->client_proxy); + g_clear_object (&priv->bus_proxy); + + free_devices (client, FALSE); + free_active_connections (client, FALSE); + g_clear_object (&priv->primary_connection); + g_clear_object (&priv->activating_connection); + + g_slist_free_full (priv->pending_activations, (GDestroyNotify) activate_info_free); + priv->pending_activations = NULL; + + g_hash_table_destroy (priv->permissions); + priv->permissions = NULL; + + G_OBJECT_CLASS (nm_client_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object); + + g_free (priv->version); + + G_OBJECT_CLASS (nm_client_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object); + gboolean b; + + switch (prop_id) { + case PROP_NETWORKING_ENABLED: + b = g_value_get_boolean (value); + if (priv->networking_enabled != b) { + nm_client_networking_set_enabled (NM_CLIENT (object), b); + /* 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->wireless_enabled != b) { + nm_client_wireless_set_enabled (NM_CLIENT (object), 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->wwan_enabled != b) { + nm_client_wwan_set_enabled (NM_CLIENT (object), b); + /* Let the property value flip when we get the change signal from NM */ + } + break; + case PROP_WIMAX_ENABLED: + b = g_value_get_boolean (value); + if (priv->wimax_enabled != b) { + nm_client_wimax_set_enabled (NM_CLIENT (object), b); + /* Let the property value flip when we get the change signal from NM */ + } + 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) +{ + NMClient *self = NM_CLIENT (object); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_VERSION: + g_value_set_string (value, nm_client_get_version (self)); + break; + case PROP_STATE: + g_value_set_uint (value, nm_client_get_state (self)); + break; + case PROP_STARTUP: + g_value_set_boolean (value, nm_client_get_startup (self)); + break; + case PROP_MANAGER_RUNNING: + g_value_set_boolean (value, priv->manager_running); + 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, priv->wireless_enabled); + break; + case PROP_WIRELESS_HARDWARE_ENABLED: + g_value_set_boolean (value, priv->wireless_hw_enabled); + break; + case PROP_WWAN_ENABLED: + g_value_set_boolean (value, priv->wwan_enabled); + break; + case PROP_WWAN_HARDWARE_ENABLED: + g_value_set_boolean (value, priv->wwan_hw_enabled); + break; + case PROP_WIMAX_ENABLED: + g_value_set_boolean (value, priv->wimax_enabled); + break; + case PROP_WIMAX_HARDWARE_ENABLED: + g_value_set_boolean (value, priv->wimax_hw_enabled); + break; + case PROP_ACTIVE_CONNECTIONS: + g_value_set_boxed (value, nm_client_get_active_connections (self)); + break; + case PROP_CONNECTIVITY: + g_value_set_uint (value, priv->connectivity); + break; + case PROP_PRIMARY_CONNECTION: + g_value_set_object (value, priv->primary_connection); + break; + case PROP_ACTIVATING_CONNECTION: + g_value_set_object (value, priv->activating_connection); + break; + case PROP_DEVICES: + g_value_set_boxed (value, nm_client_get_devices (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_client_class_init (NMClientClass *client_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (client_class); + + g_type_class_add_private (client_class, sizeof (NMClientPrivate)); + + /* virtual methods */ + object_class->constructor = constructor; + object_class->constructed = constructed; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMClient:version: + * + * The NetworkManager version. + **/ + g_object_class_install_property + (object_class, PROP_VERSION, + g_param_spec_string (NM_CLIENT_VERSION, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:state: + * + * The current daemon state. + **/ + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_CLIENT_STATE, "", "", + NM_STATE_UNKNOWN, NM_STATE_CONNECTED_GLOBAL, NM_STATE_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:startup: + * + * Whether the daemon is still starting up. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_STARTUP, + g_param_spec_boolean (NM_CLIENT_STARTUP, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:manager-running: + * + * Whether the daemon is running. + **/ + g_object_class_install_property + (object_class, PROP_MANAGER_RUNNING, + g_param_spec_boolean (NM_CLIENT_MANAGER_RUNNING, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:networking-enabled: + * + * Whether networking is enabled. + **/ + g_object_class_install_property + (object_class, PROP_NETWORKING_ENABLED, + g_param_spec_boolean (NM_CLIENT_NETWORKING_ENABLED, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:wireless-enabled: + * + * Whether wireless is enabled. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_WIRELESS_HARDWARE_ENABLED, + g_param_spec_boolean (NM_CLIENT_WIRELESS_HARDWARE_ENABLED, "", "", + TRUE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:wwan-enabled: + * + * Whether WWAN functionality is enabled. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_WIMAX_HARDWARE_ENABLED, + g_param_spec_boolean (NM_CLIENT_WIMAX_HARDWARE_ENABLED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:active-connections: + * + * The active connections. + * Type: GLib.PtrArray + **/ + g_object_class_install_property + (object_class, PROP_ACTIVE_CONNECTIONS, + g_param_spec_boxed (NM_CLIENT_ACTIVE_CONNECTIONS, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:connectivity: + * + * The network connectivity state. + * + * Since: 0.9.8.6 + */ + g_object_class_install_property + (object_class, PROP_CONNECTIVITY, + g_param_spec_uint (NM_CLIENT_CONNECTIVITY, "", "", + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, + 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. + * + * Since: 0.9.8.6 + **/ + g_object_class_install_property + (object_class, 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. + * + * Since: 0.9.8.6 + **/ + g_object_class_install_property + (object_class, PROP_ACTIVATING_CONNECTION, + g_param_spec_object (NM_CLIENT_ACTIVATING_CONNECTION, "", "", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMClient:devices: + * + * List of known network devices. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_DEVICES, + g_param_spec_boxed (NM_CLIENT_DEVICES, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* signals */ + + /** + * NMClient::device-added: + * @client: the client that received the signal + * @device: (type NMDevice): the new device + * + * Notifies that a #NMDevice is added. + **/ + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMClientClass, device_added), + 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. + **/ + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMClientClass, device_removed), + 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 ("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); +} + +static void +nm_client_initable_iface_init (GInitableIface *iface) +{ + nm_client_parent_initable_iface = g_type_interface_peek_parent (iface); + + iface->init = init_sync; +} + +static void +nm_client_async_initable_iface_init (GAsyncInitableIface *iface) +{ + nm_client_parent_async_initable_iface = g_type_interface_peek_parent (iface); + + iface->init_async = init_async; + iface->init_finish = init_finish; +} diff --git a/libnm/nm-client.h b/libnm/nm-client.h new file mode 100644 index 0000000000..ffe513cb03 --- /dev/null +++ b/libnm/nm-client.h @@ -0,0 +1,255 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#ifndef NM_CLIENT_H +#define NM_CLIENT_H + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> +#include <dbus/dbus-glib.h> +#include <NetworkManager.h> +#include "nm-object.h" +#include "nm-device.h" +#include "nm-active-connection.h" +#include "nm-vpn-connection.h" + +G_BEGIN_DECLS + +#define NM_TYPE_CLIENT (nm_client_get_type ()) +#define NM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CLIENT, NMClient)) +#define NM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CLIENT, NMClientClass)) +#define NM_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CLIENT)) +#define NM_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CLIENT)) +#define NM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CLIENT, NMClientClass)) + +#define NM_CLIENT_VERSION "version" +#define NM_CLIENT_STATE "state" +#define NM_CLIENT_STARTUP "startup" +#define NM_CLIENT_MANAGER_RUNNING "manager-running" +#define NM_CLIENT_NETWORKING_ENABLED "networking-enabled" +#define NM_CLIENT_WIRELESS_ENABLED "wireless-enabled" +#define NM_CLIENT_WIRELESS_HARDWARE_ENABLED "wireless-hardware-enabled" +#define NM_CLIENT_WWAN_ENABLED "wwan-enabled" +#define NM_CLIENT_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled" +#define NM_CLIENT_WIMAX_ENABLED "wimax-enabled" +#define NM_CLIENT_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled" +#define NM_CLIENT_ACTIVE_CONNECTIONS "active-connections" +#define NM_CLIENT_CONNECTIVITY "connectivity" +#define NM_CLIENT_PRIMARY_CONNECTION "primary-connection" +#define NM_CLIENT_ACTIVATING_CONNECTION "activating-connection" +#define NM_CLIENT_DEVICES "devices" + +/** + * NMClientPermission: + * @NM_CLIENT_PERMISSION_NONE: unknown or no permission + * @NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK: controls whether networking + * can be globally enabled or disabled + * @NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI: controls whether Wi-Fi can be + * globally enabled or disabled + * @NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN: controls whether WWAN (3G) can be + * globally enabled or disabled + * @NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX: controls whether WiMAX can be + * globally enabled or disabled + * @NM_CLIENT_PERMISSION_SLEEP_WAKE: controls whether the client can ask + * NetworkManager to sleep and wake + * @NM_CLIENT_PERMISSION_NETWORK_CONTROL: controls whether networking connections + * can be started, stopped, and changed + * @NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED: controls whether a password + * protected Wi-Fi hotspot can be created + * @NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN: controls whether an open Wi-Fi hotspot + * can be created + * @NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM: controls whether connections + * that are available to all users can be modified + * @NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN: controls whether connections + * owned by the current user can be modified + * @NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME: controls whether the + * persistent hostname can be changed + * @NM_CLIENT_PERMISSION_LAST: a reserved boundary value + * + * #NMClientPermission values indicate various permissions that NetworkManager + * clients can obtain to perform certain tasks on behalf of the current user. + **/ +typedef enum { + NM_CLIENT_PERMISSION_NONE = 0, + NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK = 1, + NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI = 2, + NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN = 3, + NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX = 4, + NM_CLIENT_PERMISSION_SLEEP_WAKE = 5, + NM_CLIENT_PERMISSION_NETWORK_CONTROL = 6, + NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED = 7, + NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN = 8, + NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM = 9, + NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN = 10, + NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME = 11, + + NM_CLIENT_PERMISSION_LAST = NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME +} NMClientPermission; + +/** + * NMClientPermissionResult: + * @NM_CLIENT_PERMISSION_RESULT_UNKNOWN: unknown or no authorization + * @NM_CLIENT_PERMISSION_RESULT_YES: the permission is available + * @NM_CLIENT_PERMISSION_RESULT_AUTH: authorization is necessary before the + * permission is available + * @NM_CLIENT_PERMISSION_RESULT_NO: permission to perform the operation is + * denied by system policy + * + * #NMClientPermissionResult values indicate what authorizations and permissions + * the user requires to obtain a given #NMClientPermission + **/ +typedef enum { + NM_CLIENT_PERMISSION_RESULT_UNKNOWN = 0, + NM_CLIENT_PERMISSION_RESULT_YES, + NM_CLIENT_PERMISSION_RESULT_AUTH, + NM_CLIENT_PERMISSION_RESULT_NO +} NMClientPermissionResult; + +/** + * NMClientError: + * @NM_CLIENT_ERROR_UNKNOWN: unknown or unclassified error + * @NM_CLIENT_ERROR_MANAGER_NOT_RUNNING: an operation that requires NetworkManager + * failed because NetworkManager is not running + * + * Describes errors that may result from operations involving a #NMClient. + **/ +typedef enum { + NM_CLIENT_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, /*< nick=ManagerNotRunning >*/ +} NMClientError; + +#define NM_CLIENT_ERROR nm_client_error_quark () +NM_AVAILABLE_IN_0_9_10 +GQuark nm_client_error_quark (void); + +typedef struct { + NMObject parent; +} NMClient; + +typedef struct { + NMObjectClass parent; + + /* Signals */ + void (*device_added) (NMClient *client, NMDevice *device); + void (*device_removed) (NMClient *client, NMDevice *device); + void (*permission_changed) (NMClient *client, + NMClientPermission permission, + NMClientPermissionResult result); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMClientClass; + +GType nm_client_get_type (void); + +NMClient *nm_client_new (void); + +void nm_client_new_async (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +NMClient *nm_client_new_finish (GAsyncResult *result, + GError **error); + +const GPtrArray *nm_client_get_devices (NMClient *client); +NMDevice *nm_client_get_device_by_path (NMClient *client, const char *object_path); +NMDevice *nm_client_get_device_by_iface (NMClient *client, const char *iface); + +typedef void (*NMClientActivateFn) (NMClient *client, + NMActiveConnection *active_connection, + GError *error, + gpointer user_data); + +void nm_client_activate_connection (NMClient *client, + NMConnection *connection, + NMDevice *device, + const char *specific_object, + NMClientActivateFn callback, + gpointer user_data); + +typedef void (*NMClientAddActivateFn) (NMClient *client, + NMActiveConnection *connection, + const char *new_connection_path, + GError *error, + gpointer user_data); + +void nm_client_add_and_activate_connection (NMClient *client, + NMConnection *partial, + NMDevice *device, + const char *specific_object, + NMClientAddActivateFn callback, + gpointer user_data); + +void nm_client_deactivate_connection (NMClient *client, NMActiveConnection *active); + +gboolean nm_client_networking_get_enabled (NMClient *client); +void nm_client_networking_set_enabled (NMClient *client, gboolean enabled); + +gboolean nm_client_wireless_get_enabled (NMClient *client); +void nm_client_wireless_set_enabled (NMClient *client, gboolean enabled); +gboolean nm_client_wireless_hardware_get_enabled (NMClient *client); + +gboolean nm_client_wwan_get_enabled (NMClient *client); +void nm_client_wwan_set_enabled (NMClient *client, gboolean enabled); +gboolean nm_client_wwan_hardware_get_enabled (NMClient *client); + +gboolean nm_client_wimax_get_enabled (NMClient *client); +void nm_client_wimax_set_enabled (NMClient *client, gboolean enabled); +gboolean nm_client_wimax_hardware_get_enabled (NMClient *client); + +const char *nm_client_get_version (NMClient *client); +NMState nm_client_get_state (NMClient *client); +NM_AVAILABLE_IN_0_9_10 +gboolean nm_client_get_startup (NMClient *client); +gboolean nm_client_get_manager_running (NMClient *client); +const GPtrArray *nm_client_get_active_connections (NMClient *client); +void nm_client_sleep (NMClient *client, gboolean sleep_); + +NMClientPermissionResult nm_client_get_permission_result (NMClient *client, + NMClientPermission permission); + +gboolean nm_client_get_logging (NMClient *client, char **level, char **domains, GError **error); +gboolean nm_client_set_logging (NMClient *client, const char *level, const char *domains, GError **error); + +NMConnectivityState nm_client_get_connectivity (NMClient *client); + +NMConnectivityState nm_client_check_connectivity (NMClient *client, + GCancellable *cancellable, + GError **error); +void nm_client_check_connectivity_async (NMClient *client, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +NMConnectivityState nm_client_check_connectivity_finish (NMClient *client, + GAsyncResult *result, + GError **error); + +NMActiveConnection *nm_client_get_primary_connection (NMClient *client); +NMActiveConnection *nm_client_get_activating_connection (NMClient *client); + +G_END_DECLS + +#endif /* NM_CLIENT_H */ diff --git a/libnm/nm-dbus-helpers-private.h b/libnm/nm-dbus-helpers-private.h new file mode 100644 index 0000000000..8a350e783d --- /dev/null +++ b/libnm/nm-dbus-helpers-private.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NM_DBUS_HELPERS_PRIVATE_H +#define NM_DBUS_HELPERS_PRIVATE_H + +#include <gio/gio.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib-lowlevel.h> + +DBusGConnection *_nm_dbus_new_connection (GError **error); + +gboolean _nm_dbus_is_connection_private (DBusGConnection *connection); + +DBusGProxy * _nm_dbus_new_proxy_for_connection (DBusGConnection *connection, + const char *path, + const char *interface); + +#endif /* NM_DBUS_HELPERS_PRIVATE_H */ diff --git a/libnm/nm-dbus-helpers.c b/libnm/nm-dbus-helpers.c new file mode 100644 index 0000000000..25004310d9 --- /dev/null +++ b/libnm/nm-dbus-helpers.c @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + */ + +#include <string.h> +#include <config.h> +#include <gio/gio.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib-lowlevel.h> +#include "nm-dbus-helpers-private.h" +#include "NetworkManager.h" + +static dbus_int32_t priv_slot = -1; + +static gboolean +_ensure_dbus_data_slot (void) +{ + static gsize init_value = 0; + gboolean success = TRUE; + + if (g_once_init_enter (&init_value)) { + success = dbus_connection_allocate_data_slot (&priv_slot); + g_once_init_leave (&init_value, 1); + } + return success; +} + +DBusGConnection * +_nm_dbus_new_connection (GError **error) +{ + DBusGConnection *connection = NULL; + + if (!_ensure_dbus_data_slot ()) { + g_set_error (error, DBUS_GERROR, DBUS_GERROR_FAILED, "failed to allocated data slot"); + return NULL; + } + +#if HAVE_DBUS_GLIB_100 + /* If running as root try the private bus first */ + if (0 == geteuid ()) { + connection = dbus_g_connection_open ("unix:path=" NMRUNDIR "/private", error); + if (connection) { + DBusConnection *dbus_connection = dbus_g_connection_get_connection (connection); + + /* Mark this connection as private */ + dbus_connection_set_data (dbus_connection, priv_slot, GUINT_TO_POINTER (TRUE), NULL); + dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE); + return connection; + } + /* Fall back to a bus if for some reason private socket isn't available */ + g_clear_error (error); + } +#endif + + if (connection == NULL) + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, error); + + return connection; +} + +gboolean +_nm_dbus_is_connection_private (DBusGConnection *connection) +{ + if (!_ensure_dbus_data_slot ()) + return FALSE; + return !!dbus_connection_get_data (dbus_g_connection_get_connection (connection), priv_slot); +} + +DBusGProxy * +_nm_dbus_new_proxy_for_connection (DBusGConnection *connection, + const char *path, + const char *interface) +{ + /* Private connections can't use dbus_g_proxy_new_for_name() or + * dbus_g_proxy_new_for_name_owner() because peer-to-peer connections don't + * have either a bus daemon or name owners, both of which those functions + * require. + */ + if (_nm_dbus_is_connection_private (connection)) + return dbus_g_proxy_new_for_peer (connection, path, interface); + + return dbus_g_proxy_new_for_name (connection, NM_DBUS_SERVICE, path, interface); +} diff --git a/libnm/nm-device-adsl.c b/libnm/nm-device-adsl.c new file mode 100644 index 0000000000..eb54957413 --- /dev/null +++ b/libnm/nm-device-adsl.c @@ -0,0 +1,242 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * author: Pantelis Koukousoulas <pktoss@gmail.com> + * Copyright 2009 - 2011 Red Hat, Inc. + */ + +#include "nm-device-adsl.h" +#include "nm-device-private.h" +#include "nm-object-private.h" + +#include "nm-setting-adsl.h" + +#include <string.h> + +G_DEFINE_TYPE (NMDeviceAdsl, nm_device_adsl, NM_TYPE_DEVICE) + +#define NM_DEVICE_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_ADSL, NMDeviceAdslPrivate)) + +typedef struct { + DBusGProxy *proxy; + + gboolean carrier; + + gboolean disposed; +} NMDeviceAdslPrivate; + +enum { + PROP_0, + PROP_CARRIER, + LAST_PROP +}; + +/** + * nm_device_adsl_error_quark: + * + * Registers an error quark for #NMDeviceAdsl if necessary. + * + * Returns: the error quark used for #NMDeviceAdsl errors. + **/ +GQuark +nm_device_adsl_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-adsl-error-quark"); + return quark; +} + +/** + * nm_device_adsl_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceAdsl. + * + * Returns: (transfer full): a new device + **/ +GObject * +nm_device_adsl_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_ADSL, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_ADSL_GET_PRIVATE (device)->carrier; +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingAdsl *s_adsl; + const char *ctype; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_ADSL_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_ADSL_ERROR, NM_DEVICE_ADSL_ERROR_NOT_ADSL_CONNECTION, + "The connection was not an ADSL connection."); + return FALSE; + } + + s_adsl = nm_connection_get_setting_adsl (connection); + if (!s_adsl) { + g_set_error (error, NM_DEVICE_ADSL_ERROR, NM_DEVICE_ADSL_ERROR_INVALID_ADSL_CONNECTION, + "The connection was not a valid ADSL connection."); + return FALSE; + } + + return NM_DEVICE_CLASS (nm_device_adsl_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_ADSL; +} + +/******************************************************************/ + +static void +nm_device_adsl_init (NMDeviceAdsl *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_ADSL); +} + +static void +register_properties (NMDeviceAdsl *device) +{ + NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_ADSL_CARRIER, &priv->carrier }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_adsl_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_ADSL); + register_properties (NM_DEVICE_ADSL (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (object); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object); + return; + } + + priv->disposed = TRUE; + + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + G_OBJECT_CLASS (nm_device_adsl_parent_class)->finalize (object); +} + +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; + } +} + +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); + + g_type_class_add_private (object_class, sizeof (NMDeviceAdslPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /* properties */ + /** + * NMDeviceAdsl:carrier: + * + * Whether the device has carrier. + **/ + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_ADSL_CARRIER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-device-adsl.h b/libnm/nm-device-adsl.h new file mode 100644 index 0000000000..35d2731a2c --- /dev/null +++ b/libnm/nm-device-adsl.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 Pantelis Koukousoulas <pktoss@gmail.com> + */ + +#ifndef NM_DEVICE_ADSL_H +#define NM_DEVICE_ADSL_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_ADSL (nm_device_adsl_get_type ()) +#define NM_DEVICE_ADSL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ADSL, NMDeviceAdsl)) +#define NM_DEVICE_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_ADSL, NMDeviceAdslClass)) +#define NM_IS_DEVICE_ADSL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ADSL)) +#define NM_IS_DEVICE_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_ADSL)) +#define NM_DEVICE_ADSL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ADSL, NMDeviceAdslClass)) + +/** + * NMDeviceAdslError: + * @NM_DEVICE_ADSL_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_ADSL_ERROR_NOT_ADSL_CONNECTION: the connection was not of ADSL type + * @NM_DEVICE_ADSL_ERROR_INVALID_ADSL_CONNECTION: the ADSL connection was invalid + */ +typedef enum { + NM_DEVICE_ADSL_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_ADSL_ERROR_NOT_ADSL_CONNECTION, /*< nick=NotAdslConnection >*/ + NM_DEVICE_ADSL_ERROR_INVALID_ADSL_CONNECTION, /*< nick=InvalidAdslConnection >*/ +} NMDeviceAdslError; + +#define NM_DEVICE_ADSL_ERROR nm_device_adsl_error_quark () +GQuark nm_device_adsl_error_quark (void); + +#define NM_DEVICE_ADSL_CARRIER "carrier" + +typedef struct { + NMDevice parent; +} NMDeviceAdsl; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceAdslClass; + +GType nm_device_adsl_get_type (void); + +GObject *nm_device_adsl_new (DBusGConnection *connection, const char *path); +gboolean nm_device_adsl_get_carrier (NMDeviceAdsl *device); + +G_END_DECLS + +#endif /* NM_DEVICE_ADSL_H */ diff --git a/libnm/nm-device-bond.c b/libnm/nm-device-bond.c new file mode 100644 index 0000000000..662b2f4a38 --- /dev/null +++ b/libnm/nm-device-bond.c @@ -0,0 +1,347 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-bond.h> +#include <nm-utils.h> + +#include "nm-device-bond.h" +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-types.h" + +G_DEFINE_TYPE (NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE) + +#define NM_DEVICE_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BOND, NMDeviceBondPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + gboolean carrier; + GPtrArray *slaves; +} NMDeviceBondPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_CARRIER, + PROP_SLAVES, + + LAST_PROP +}; + +/** + * nm_device_bond_error_quark: + * + * Registers an error quark for #NMDeviceBond if necessary. + * + * Returns: the error quark used for #NMDeviceBond errors. + **/ +GQuark +nm_device_bond_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-bond-error-quark"); + return quark; +} + +/** + * nm_device_bond_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceBond. + * + * Returns: (transfer full): a new device + **/ +GObject * +nm_device_bond_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_BOND, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_bond_get_hw_address: + * @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. + **/ +const char * +nm_device_bond_get_hw_address (NMDeviceBond *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_BOND (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_BOND_GET_PRIVATE (device)->hw_address; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_BOND_GET_PRIVATE (device)->carrier; +} + +/** + * nm_device_bond_get_slaves: + * @device: a #NMDeviceBond + * + * Gets the devices currently slaved 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: 0.9.6.4 + **/ +const GPtrArray * +nm_device_bond_get_slaves (NMDeviceBond *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_BOND (device), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return handle_ptr_array_return (NM_DEVICE_BOND_GET_PRIVATE (device)->slaves); +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingBond *s_bond; + const char *ctype, *dev_iface_name, *bond_iface_name; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_BOND_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_BOND_ERROR, NM_DEVICE_BOND_ERROR_NOT_BOND_CONNECTION, + "The connection was not a bond connection."); + return FALSE; + } + + s_bond = nm_connection_get_setting_bond (connection); + if (!s_bond) { + g_set_error (error, NM_DEVICE_BOND_ERROR, NM_DEVICE_BOND_ERROR_INVALID_BOND_CONNECTION, + "The connection was not a valid bond connection."); + return FALSE; + } + + dev_iface_name = nm_device_get_iface (device); + bond_iface_name = nm_setting_bond_get_interface_name (s_bond); + if (g_strcmp0 (dev_iface_name, bond_iface_name) != 0) { + g_set_error (error, NM_DEVICE_BOND_ERROR, NM_DEVICE_BOND_ERROR_INTERFACE_MISMATCH, + "The interfaces of the device and the connection didn't match."); + return FALSE; + } + + /* FIXME: check slaves? */ + + return NM_DEVICE_CLASS (nm_device_bond_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_BOND; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_bond_get_hw_address (NM_DEVICE_BOND (device)); +} + +/***********************************************************/ + +static void +nm_device_bond_init (NMDeviceBond *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_BOND); +} + +static void +register_properties (NMDeviceBond *device) +{ + NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_BOND_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_BOND_CARRIER, &priv->carrier }, + { NM_DEVICE_BOND_SLAVES, &priv->slaves, NULL, NM_TYPE_DEVICE }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_bond_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_BOND); + register_properties (NM_DEVICE_BOND (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + if (priv->slaves) { + g_ptr_array_set_free_func (priv->slaves, g_object_unref); + g_ptr_array_free (priv->slaves, TRUE); + priv->slaves = NULL; + } + + G_OBJECT_CLASS (nm_device_bond_parent_class)->dispose (object); +} + +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); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_bond_get_hw_address (device)); + break; + case PROP_CARRIER: + g_value_set_boolean (value, nm_device_bond_get_carrier (device)); + break; + case PROP_SLAVES: + g_value_set_boxed (value, nm_device_bond_get_slaves (device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_bond_class_init (NMDeviceBondClass *eth_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (eth_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class); + + g_type_class_add_private (eth_class, sizeof (NMDeviceBondPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + + /* properties */ + + /** + * NMDeviceBond:hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_BOND_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceBond:carrier: + * + * Whether the device has carrier. + **/ + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_BOND_CARRIER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceBond:slaves: + * + * The devices (#NMDevice) slaved to the bond device. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_SLAVES, + g_param_spec_boxed (NM_DEVICE_BOND_SLAVES, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-device-bond.h b/libnm/nm-device-bond.h new file mode 100644 index 0000000000..cd1a60271f --- /dev/null +++ b/libnm/nm-device-bond.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_BOND_H +#define NM_DEVICE_BOND_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_BOND (nm_device_bond_get_type ()) +#define NM_DEVICE_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BOND, NMDeviceBond)) +#define NM_DEVICE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_BOND, NMDeviceBondClass)) +#define NM_IS_DEVICE_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BOND)) +#define NM_IS_DEVICE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BOND)) +#define NM_DEVICE_BOND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BOND, NMDeviceBondClass)) + +/** + * NMDeviceBondError: + * @NM_DEVICE_BOND_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_BOND_ERROR_NOT_BOND_CONNECTION: the connection was not of bond type + * @NM_DEVICE_BOND_ERROR_INVALID_BOND_CONNECTION: the bond connection was invalid + * @NM_DEVICE_BOND_ERROR_INTERFACE_MISMATCH: the interfaces of the connection and the device mismatched + */ +typedef enum { + NM_DEVICE_BOND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_BOND_ERROR_NOT_BOND_CONNECTION, /*< nick=NotBondConnection >*/ + NM_DEVICE_BOND_ERROR_INVALID_BOND_CONNECTION, /*< nick=InvalidBondConnection >*/ + NM_DEVICE_BOND_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/ +} NMDeviceBondError; + +#define NM_DEVICE_BOND_ERROR nm_device_bond_error_quark () +GQuark nm_device_bond_error_quark (void); + +#define NM_DEVICE_BOND_HW_ADDRESS "hw-address" +#define NM_DEVICE_BOND_CARRIER "carrier" +#define NM_DEVICE_BOND_SLAVES "slaves" + +typedef struct { + NMDevice parent; +} NMDeviceBond; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceBondClass; + +GType nm_device_bond_get_type (void); + +GObject *nm_device_bond_new (DBusGConnection *connection, const char *path); + +const char *nm_device_bond_get_hw_address (NMDeviceBond *device); +gboolean nm_device_bond_get_carrier (NMDeviceBond *device); +const GPtrArray *nm_device_bond_get_slaves (NMDeviceBond *device); + +G_END_DECLS + +#endif /* NM_DEVICE_BOND_H */ diff --git a/libnm/nm-device-bridge.c b/libnm/nm-device-bridge.c new file mode 100644 index 0000000000..bf19737555 --- /dev/null +++ b/libnm/nm-device-bridge.c @@ -0,0 +1,359 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-bridge.h> +#include <nm-utils.h> + +#include "nm-device-bridge.h" +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-types.h" + +G_DEFINE_TYPE (NMDeviceBridge, nm_device_bridge, NM_TYPE_DEVICE) + +#define NM_DEVICE_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgePrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + gboolean carrier; + GPtrArray *slaves; +} NMDeviceBridgePrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_CARRIER, + PROP_SLAVES, + + LAST_PROP +}; + +/** + * nm_device_bridge_error_quark: + * + * Registers an error quark for #NMDeviceBridge if necessary. + * + * Returns: the error quark used for #NMDeviceBridge errors. + * + * Since: 0.9.8 + **/ +GQuark +nm_device_bridge_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-bridge-error-quark"); + return quark; +} + +/** + * nm_device_bridge_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceBridge. + * + * Returns: (transfer full): a new device + * + * Since: 0.9.8 + **/ +GObject * +nm_device_bridge_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_BRIDGE, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_bridge_get_hw_address: + * @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. + * + * Since: 0.9.8 + **/ +const char * +nm_device_bridge_get_hw_address (NMDeviceBridge *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_BRIDGE (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_BRIDGE_GET_PRIVATE (device)->hw_address; +} + +/** + * nm_device_bridge_get_carrier: + * @device: a #NMDeviceBridge + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + * + * Since: 0.9.8 + **/ +gboolean +nm_device_bridge_get_carrier (NMDeviceBridge *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_BRIDGE (device), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_BRIDGE_GET_PRIVATE (device)->carrier; +} + +/** + * nm_device_bridge_get_slaves: + * @device: a #NMDeviceBridge + * + * Gets the devices currently slaved 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: 0.9.8 + **/ +const GPtrArray * +nm_device_bridge_get_slaves (NMDeviceBridge *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_BRIDGE (device), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return handle_ptr_array_return (NM_DEVICE_BRIDGE_GET_PRIVATE (device)->slaves); +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingBridge *s_bridge; + const char *ctype, *dev_iface_name, *bridge_iface_name; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_BRIDGE_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_BRIDGE_ERROR, NM_DEVICE_BRIDGE_ERROR_NOT_BRIDGE_CONNECTION, + "The connection was not a bridge connection."); + return FALSE; + } + + s_bridge = nm_connection_get_setting_bridge (connection); + if (!s_bridge) { + g_set_error (error, NM_DEVICE_BRIDGE_ERROR, NM_DEVICE_BRIDGE_ERROR_INVALID_BRIDGE_CONNECTION, + "The connection was not a valid bridge connection."); + return FALSE; + } + + dev_iface_name = nm_device_get_iface (device); + bridge_iface_name = nm_setting_bridge_get_interface_name (s_bridge); + if (g_strcmp0 (dev_iface_name, bridge_iface_name) != 0) { + g_set_error (error, NM_DEVICE_BRIDGE_ERROR, NM_DEVICE_BRIDGE_ERROR_INTERFACE_MISMATCH, + "The interfaces of the device and the connection didn't match."); + return FALSE; + } + + /* FIXME: check ports? */ + + return NM_DEVICE_CLASS (nm_device_bridge_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_BRIDGE; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_bridge_get_hw_address (NM_DEVICE_BRIDGE (device)); +} + +/***********************************************************/ + +static void +nm_device_bridge_init (NMDeviceBridge *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_BRIDGE); +} + +static void +register_properties (NMDeviceBridge *device) +{ + NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_BRIDGE_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_BRIDGE_CARRIER, &priv->carrier }, + { NM_DEVICE_BRIDGE_SLAVES, &priv->slaves, NULL, NM_TYPE_DEVICE }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_bridge_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_BRIDGE); + register_properties (NM_DEVICE_BRIDGE (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + if (priv->slaves) { + g_ptr_array_set_free_func (priv->slaves, g_object_unref); + g_ptr_array_free (priv->slaves, TRUE); + priv->slaves = NULL; + } + + G_OBJECT_CLASS (nm_device_bridge_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (object); + + g_free (priv->hw_address); + + G_OBJECT_CLASS (nm_device_bridge_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDeviceBridge *device = NM_DEVICE_BRIDGE (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_bridge_get_hw_address (device)); + break; + case PROP_CARRIER: + g_value_set_boolean (value, nm_device_bridge_get_carrier (device)); + break; + case PROP_SLAVES: + g_value_set_boxed (value, nm_device_bridge_get_slaves (device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_bridge_class_init (NMDeviceBridgeClass *bridge_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bridge_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (bridge_class); + + g_type_class_add_private (bridge_class, sizeof (NMDeviceBridgePrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + + /* properties */ + + /** + * NMDeviceBridge:hw-address: + * + * The hardware (MAC) address of the device. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_BRIDGE_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceBridge:carrier: + * + * Whether the device has carrier. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_BRIDGE_CARRIER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceBridge:slaves: + * + * The devices (#NMDevice) slaved to the bridge device. + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_SLAVES, + g_param_spec_boxed (NM_DEVICE_BRIDGE_SLAVES, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-device-bridge.h b/libnm/nm-device-bridge.h new file mode 100644 index 0000000000..2eaf475d8c --- /dev/null +++ b/libnm/nm-device-bridge.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_BRIDGE_H +#define NM_DEVICE_BRIDGE_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_BRIDGE (nm_device_bridge_get_type ()) +#define NM_DEVICE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridge)) +#define NM_DEVICE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgeClass)) +#define NM_IS_DEVICE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BRIDGE)) +#define NM_IS_DEVICE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BRIDGE)) +#define NM_DEVICE_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgeClass)) + +/** + * NMDeviceBridgeError: + * @NM_DEVICE_BRIDGE_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_BRIDGE_ERROR_NOT_BRIDGE_CONNECTION: the connection was not of bridge type + * @NM_DEVICE_BRIDGE_ERROR_INVALID_BRIDGE_CONNECTION: the bridge connection was invalid + * @NM_DEVICE_BRIDGE_ERROR_INTERFACE_MISMATCH: the interfaces of the connection and the device mismatched + * + * Since: 0.9.8 + */ +typedef enum { + NM_DEVICE_BRIDGE_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_BRIDGE_ERROR_NOT_BRIDGE_CONNECTION, /*< nick=NotBridgeConnection >*/ + NM_DEVICE_BRIDGE_ERROR_INVALID_BRIDGE_CONNECTION, /*< nick=InvalidBridgeConnection >*/ + NM_DEVICE_BRIDGE_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/ +} NMDeviceBridgeError; + +#define NM_DEVICE_BRIDGE_ERROR nm_device_bridge_error_quark () +GQuark nm_device_bridge_error_quark (void); + +#define NM_DEVICE_BRIDGE_HW_ADDRESS "hw-address" +#define NM_DEVICE_BRIDGE_CARRIER "carrier" +#define NM_DEVICE_BRIDGE_SLAVES "slaves" + +typedef struct { + NMDevice parent; +} NMDeviceBridge; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceBridgeClass; + +GType nm_device_bridge_get_type (void); + +GObject * nm_device_bridge_new (DBusGConnection *connection, const char *path); + +const char *nm_device_bridge_get_hw_address (NMDeviceBridge *device); +gboolean nm_device_bridge_get_carrier (NMDeviceBridge *device); +const GPtrArray *nm_device_bridge_get_slaves (NMDeviceBridge *device); + +G_END_DECLS + +#endif /* NM_DEVICE_BRIDGE_H */ diff --git a/libnm/nm-device-bt.c b/libnm/nm-device-bt.c new file mode 100644 index 0000000000..00616a95a8 --- /dev/null +++ b/libnm/nm-device-bt.c @@ -0,0 +1,373 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-bluetooth.h> + +#include "nm-device-bt.h" +#include "nm-device-private.h" +#include "nm-object-private.h" + +G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE) + +#define NM_DEVICE_BT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BT, NMDeviceBtPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + char *name; + guint32 bt_capabilities; +} NMDeviceBtPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_NAME, + PROP_BT_CAPABILITIES, + + LAST_PROP +}; + +/** + * nm_device_bt_error_quark: + * + * Registers an error quark for #NMDeviceBt if necessary. + * + * Returns: the error quark used for #NMDeviceBt errors. + **/ +GQuark +nm_device_bt_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-bt-error-quark"); + return quark; +} + +/** + * nm_device_bt_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceBt. + * + * Returns: (transfer full): a new device + **/ +GObject * +nm_device_bt_new (DBusGConnection *connection, const char *path) +{ + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + return g_object_new (NM_TYPE_DEVICE_BT, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); +} + +/** + * nm_device_bt_get_hw_address: + * @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. + **/ +const char * +nm_device_bt_get_hw_address (NMDeviceBt *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_BT (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_BT_GET_PRIVATE (device)->hw_address; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + 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) +{ + NMSettingConnection *s_con; + NMSettingBluetooth *s_bt; + const char *ctype; + const GByteArray *mac; + const char *hw_str; + struct ether_addr *hw_mac; + NMBluetoothCapabilities dev_caps; + NMBluetoothCapabilities bt_type; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_BLUETOOTH_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_NOT_BT_CONNECTION, + "The connection was not a Bluetooth connection."); + return FALSE; + } + + s_bt = nm_connection_get_setting_bluetooth (connection); + if (!s_bt) { + g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_INVALID_BT_CONNECTION, + "The connection was not a valid Bluetooth connection."); + return FALSE; + } + + /* Check BT address */ + hw_str = nm_device_bt_get_hw_address (NM_DEVICE_BT (device)); + if (hw_str) { + hw_mac = ether_aton (hw_str); + if (!hw_mac) { + g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_INVALID_DEVICE_MAC, + "Invalid device MAC address."); + return FALSE; + } + mac = nm_setting_bluetooth_get_bdaddr (s_bt); + if (mac && hw_mac && memcmp (mac->data, hw_mac->ether_addr_octet, ETH_ALEN)) { + g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_MAC_MISMATCH, + "The MACs 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 (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_MISSING_DEVICE_CAPS, + "The device missed BT capabilities required by the connection."); + return FALSE; + } + + return NM_DEVICE_CLASS (nm_device_bt_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_BLUETOOTH; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_bt_get_hw_address (NM_DEVICE_BT (device)); +} + +/************************************************************/ + +static void +nm_device_bt_init (NMDeviceBt *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_BT); +} + +static void +register_properties (NMDeviceBt *device) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_BT_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_BT_NAME, &priv->name }, + { NM_DEVICE_BT_CAPABILITIES, &priv->bt_capabilities }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_BLUETOOTH); + register_properties (NM_DEVICE_BT (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_bt_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); + + g_free (priv->hw_address); + 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); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_bt_get_hw_address (device)); + break; + case PROP_NAME: + g_value_set_string (value, nm_device_bt_get_name (device)); + break; + case PROP_BT_CAPABILITIES: + g_value_set_uint (value, nm_device_bt_get_capabilities (device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +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); + + g_type_class_add_private (bt_class, sizeof (NMDeviceBtPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + + /* properties */ + + /** + * NMDeviceBt:hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_BT_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceBt:name: + * + * The name of the bluetooth device. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_BT_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_BT_CAPABILITIES, "", "", + NM_BT_CAPABILITY_NONE, G_MAXUINT32, NM_BT_CAPABILITY_NONE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + +} diff --git a/libnm/nm-device-bt.h b/libnm/nm-device-bt.h new file mode 100644 index 0000000000..51dc86e39e --- /dev/null +++ b/libnm/nm-device-bt.h @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2012 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#ifndef NM_DEVICE_BT_H +#define NM_DEVICE_BT_H + +#include "NetworkManager.h" +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_BT (nm_device_bt_get_type ()) +#define NM_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt)) +#define NM_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_BT, NMDeviceBtClass)) +#define NM_IS_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BT)) +#define NM_IS_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BT)) +#define NM_DEVICE_BT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BT, NMDeviceBtClass)) + +/** + * NMDeviceBtError: + * @NM_DEVICE_BT_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_BT_ERROR_NOT_BT_CONNECTION: the connection was not of bluetooth type + * @NM_DEVICE_BT_ERROR_INVALID_BT_CONNECTION: the bluetooth connection was invalid + * @NM_DEVICE_BT_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid + * @NM_DEVICE_BT_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched + * @NM_DEVICE_BT_ERROR_MISSING_DEVICE_CAPS: the device missed required capabilities + */ +typedef enum { + NM_DEVICE_BT_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_BT_ERROR_NOT_BT_CONNECTION, /*< nick=NotBtConnection >*/ + NM_DEVICE_BT_ERROR_INVALID_BT_CONNECTION, /*< nick=InvalidBtConnection >*/ + NM_DEVICE_BT_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/ + NM_DEVICE_BT_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/ + NM_DEVICE_BT_ERROR_MISSING_DEVICE_CAPS, /*< nick=MissingDeviceCaps >*/ +} NMDeviceBtError; + +#define NM_DEVICE_BT_ERROR nm_device_bt_error_quark () +GQuark nm_device_bt_error_quark (void); + +#define NM_DEVICE_BT_HW_ADDRESS "hw-address" +#define NM_DEVICE_BT_NAME "name" +#define NM_DEVICE_BT_CAPABILITIES "bt-capabilities" + +typedef struct { + NMDevice parent; +} NMDeviceBt; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceBtClass; + +GType nm_device_bt_get_type (void); + +GObject *nm_device_bt_new (DBusGConnection *connection, const char *path); + +const char *nm_device_bt_get_hw_address (NMDeviceBt *device); + +const char *nm_device_bt_get_name (NMDeviceBt *device); + +NMBluetoothCapabilities nm_device_bt_get_capabilities (NMDeviceBt *device); + +G_END_DECLS + +#endif /* NM_DEVICE_BT_H */ diff --git a/libnm/nm-device-ethernet.c b/libnm/nm-device-ethernet.c new file mode 100644 index 0000000000..690041a515 --- /dev/null +++ b/libnm/nm-device-ethernet.c @@ -0,0 +1,392 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-wired.h> +#include <nm-setting-pppoe.h> + +#include "nm-device-ethernet.h" +#include "nm-device-private.h" +#include "nm-object-private.h" + +G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE) + +#define NM_DEVICE_ETHERNET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + char *perm_hw_address; + guint32 speed; + gboolean carrier; +} NMDeviceEthernetPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_PERM_HW_ADDRESS, + PROP_SPEED, + PROP_CARRIER, + + LAST_PROP +}; + +/** + * nm_device_ethernet_error_quark: + * + * Registers an error quark for #NMDeviceEthernet if necessary. + * + * Returns: the error quark used for #NMDeviceEthernet errors. + **/ +GQuark +nm_device_ethernet_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-ethernet-error-quark"); + return quark; +} + +/** + * nm_device_ethernet_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceEthernet. + * + * Returns: (transfer full): a new device + **/ +GObject * +nm_device_ethernet_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_ETHERNET, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_ethernet_get_hw_address: + * @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. + **/ +const char * +nm_device_ethernet_get_hw_address (NMDeviceEthernet *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_ETHERNET (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_ETHERNET_GET_PRIVATE (device)->hw_address; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_ETHERNET_GET_PRIVATE (device)->perm_hw_address; +} + +/** + * nm_device_ethernet_get_speed: + * @device: a #NMDeviceEthernet + * + * Gets the speed of the #NMDeviceEthernet. + * + * Returns: the speed of the device + **/ +guint32 +nm_device_ethernet_get_speed (NMDeviceEthernet *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_ETHERNET (device), 0); + + _nm_object_ensure_inited (NM_OBJECT (device)); + 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_ETHERNET_GET_PRIVATE (device)->carrier; +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingWired *s_wired; + const char *ctype; + gboolean is_pppoe = FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (!strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) + is_pppoe = TRUE; + else if (strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_NOT_ETHERNET_CONNECTION, + "The connection was not a wired or PPPoE connection."); + return FALSE; + } + + s_wired = nm_connection_get_setting_wired (connection); + /* Wired setting optional for PPPoE */ + if (!is_pppoe && !s_wired) { + g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_INVALID_ETHERNET_CONNECTION, + "The connection was not a valid Ethernet connection."); + return FALSE; + } + + if (s_wired) { + const GByteArray *mac; + const char *perm_str; + struct ether_addr *perm_mac; + + /* FIXME: filter using s390 subchannels when they are exported over the bus */ + + /* Check MAC address */ + perm_str = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device)); + if (perm_str) { + perm_mac = ether_aton (perm_str); + if (!perm_mac) { + g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_INVALID_DEVICE_MAC, + "Invalid device MAC address."); + return FALSE; + } + mac = nm_setting_wired_get_mac_address (s_wired); + if (mac && perm_mac && memcmp (mac->data, perm_mac->ether_addr_octet, ETH_ALEN)) { + g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_MAC_MISMATCH, + "The MACs of the device and the connection didn't match."); + return FALSE; + } + } + } + + return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_WIRED; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_ethernet_get_hw_address (NM_DEVICE_ETHERNET (device)); +} + +/***********************************************************/ + +static void +nm_device_ethernet_init (NMDeviceEthernet *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_ETHERNET); +} + +static void +register_properties (NMDeviceEthernet *device) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_ETHERNET_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, &priv->perm_hw_address }, + { NM_DEVICE_ETHERNET_SPEED, &priv->speed }, + { NM_DEVICE_ETHERNET_CARRIER, &priv->carrier }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_WIRED); + register_properties (NM_DEVICE_ETHERNET (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (object); + + g_free (priv->hw_address); + g_free (priv->perm_hw_address); + + 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); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_ethernet_get_hw_address (device)); + break; + 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; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_ethernet_class_init (NMDeviceEthernetClass *eth_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (eth_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class); + + g_type_class_add_private (eth_class, sizeof (NMDeviceEthernetPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + + /* properties */ + + /** + * NMDeviceEthernet:hw-address: + * + * The active hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_ETHERNET_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceEthernet:perm-hw-address: + * + * The permanent hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_ETHERNET_CARRIER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + +} diff --git a/libnm/nm-device-ethernet.h b/libnm/nm-device-ethernet.h new file mode 100644 index 0000000000..be289cdfcc --- /dev/null +++ b/libnm/nm-device-ethernet.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_ETHERNET_H +#define NM_DEVICE_ETHERNET_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_ETHERNET (nm_device_ethernet_get_type ()) +#define NM_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet)) +#define NM_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) +#define NM_IS_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ETHERNET)) +#define NM_IS_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_ETHERNET)) +#define NM_DEVICE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) + +/** + * NMDeviceEthernetError: + * @NM_DEVICE_ETHERNET_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_ETHERNET_ERROR_NOT_ETHERNET_CONNECTION: the connection was not of Ethernet or PPPoE type + * @NM_DEVICE_ETHERNET_ERROR_INVALID_ETHERNET_CONNECTION: the Ethernet connection was invalid + * @NM_DEVICE_ETHERNET_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid + * @NM_DEVICE_ETHERNET_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched + */ +typedef enum { + NM_DEVICE_ETHERNET_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_ETHERNET_ERROR_NOT_ETHERNET_CONNECTION, /*< nick=NotEthernetConnection >*/ + NM_DEVICE_ETHERNET_ERROR_INVALID_ETHERNET_CONNECTION, /*< nick=InvalidEthernetConnection >*/ + NM_DEVICE_ETHERNET_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/ + NM_DEVICE_ETHERNET_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/ +} NMDeviceEthernetError; + +#define NM_DEVICE_ETHERNET_ERROR nm_device_ethernet_error_quark () +GQuark nm_device_ethernet_error_quark (void); + +#define NM_DEVICE_ETHERNET_HW_ADDRESS "hw-address" +#define NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS "perm-hw-address" +#define NM_DEVICE_ETHERNET_SPEED "speed" +#define NM_DEVICE_ETHERNET_CARRIER "carrier" + +typedef struct { + NMDevice parent; +} NMDeviceEthernet; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceEthernetClass; + +GType nm_device_ethernet_get_type (void); + +GObject *nm_device_ethernet_new (DBusGConnection *connection, const char *path); + +const char * nm_device_ethernet_get_hw_address (NMDeviceEthernet *device); +const char * nm_device_ethernet_get_permanent_hw_address (NMDeviceEthernet *device); +guint32 nm_device_ethernet_get_speed (NMDeviceEthernet *device); +gboolean nm_device_ethernet_get_carrier (NMDeviceEthernet *device); + +G_END_DECLS + +#endif /* NM_DEVICE_ETHERNET_H */ diff --git a/libnm/nm-device-generic.c b/libnm/nm-device-generic.c new file mode 100644 index 0000000000..875ea8eb39 --- /dev/null +++ b/libnm/nm-device-generic.c @@ -0,0 +1,284 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + */ + +#include <config.h> + +#include <string.h> + +#include "nm-device-generic.h" +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-setting-generic.h" + +G_DEFINE_TYPE (NMDeviceGeneric, nm_device_generic, NM_TYPE_DEVICE) + +#define NM_DEVICE_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_GENERIC, NMDeviceGenericPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + char *type_description; +} NMDeviceGenericPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_TYPE_DESCRIPTION, + + LAST_PROP +}; + +/** + * nm_device_generic_error_quark: + * + * Registers an error quark for #NMDeviceGeneric if necessary. + * + * Returns: the error quark used for #NMDeviceGeneric errors. + * + * Since: 0.9.10 + **/ +GQuark +nm_device_generic_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-generic-error-quark"); + return quark; +} + +/** + * nm_device_generic_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceGeneric. + * + * Returns: (transfer full): a new device + * + * Since: 0.9.10 + **/ +GObject * +nm_device_generic_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_GENERIC, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_generic_get_hw_address: + * @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. + * + * Since: 0.9.10 + **/ +const char * +nm_device_generic_get_hw_address (NMDeviceGeneric *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_GENERIC (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GENERIC_GET_PRIVATE (device)->hw_address; +} + +/***********************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (device); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return priv->type_description; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_generic_get_hw_address (NM_DEVICE_GENERIC (device)); +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + const char *ctype, *iface_name; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_GENERIC_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_GENERIC_ERROR, NM_DEVICE_GENERIC_ERROR_NOT_GENERIC_CONNECTION, + "The connection was not a generic connection."); + return FALSE; + } + + iface_name = nm_setting_connection_get_interface_name (s_con); + if (!iface_name) { + g_set_error (error, NM_DEVICE_GENERIC_ERROR, NM_DEVICE_GENERIC_ERROR_MISSING_INTERFACE_NAME, + "The connection did not specify an interface name."); + return FALSE; + } + + return NM_DEVICE_CLASS (nm_device_generic_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_GENERIC; +} + +/***********************************************************/ + +static void +nm_device_generic_init (NMDeviceGeneric *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_GENERIC); +} + +static void +register_properties (NMDeviceGeneric *device) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_GENERIC_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_GENERIC_TYPE_DESCRIPTION, &priv->type_description }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_generic_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_GENERIC); + register_properties (NM_DEVICE_GENERIC (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_generic_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (object); + + g_free (priv->hw_address); + 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) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, priv->hw_address); + break; + case PROP_TYPE_DESCRIPTION: + g_value_set_string (value, priv->type_description); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_generic_class_init (NMDeviceGenericClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMDeviceGenericPrivate)); + + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + + device_class->get_type_description = get_type_description; + device_class->get_hw_address = get_hw_address; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + + /** + * NMDeviceGeneric:hw-address: + * + * The hardware address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_GENERIC_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceGeneric:type-description: + * + * A description of the specific type of device this is, or %NULL + * if not known. + **/ + g_object_class_install_property + (object_class, PROP_TYPE_DESCRIPTION, + g_param_spec_string (NM_DEVICE_GENERIC_TYPE_DESCRIPTION, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-device-generic.h b/libnm/nm-device-generic.h new file mode 100644 index 0000000000..5bff1e3f55 --- /dev/null +++ b/libnm/nm-device-generic.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_GENERIC_H +#define NM_DEVICE_GENERIC_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_GENERIC (nm_device_generic_get_type ()) +#define NM_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_GENERIC, NMDeviceGeneric)) +#define NM_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_GENERIC, NMDeviceGenericClass)) +#define NM_IS_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_GENERIC)) +#define NM_IS_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_GENERIC)) +#define NM_DEVICE_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_GENERIC, NMDeviceGenericClass)) + +/** + * NMDeviceGenericError: + * @NM_DEVICE_GENERIC_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_GENERIC_ERROR_NOT_GENERIC_CONNECTION: the connection was not of generic type + * @NM_DEVICE_GENERIC_ERROR_MISSING_INTERFACE_NAME: the connection did not specify the interface name + */ +typedef enum { + NM_DEVICE_GENERIC_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_GENERIC_ERROR_NOT_GENERIC_CONNECTION, /*< nick=NotGenericConnection >*/ + NM_DEVICE_GENERIC_ERROR_MISSING_INTERFACE_NAME, /*< nick=MissingInterfaceName >*/ +} NMDeviceGenericError; + +#define NM_DEVICE_GENERIC_ERROR nm_device_generic_error_quark () +GQuark nm_device_generic_error_quark (void); + +#define NM_DEVICE_GENERIC_HW_ADDRESS "hw-address" +#define NM_DEVICE_GENERIC_TYPE_DESCRIPTION "type-description" + +typedef struct { + NMDevice parent; +} NMDeviceGeneric; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceGenericClass; + +NM_AVAILABLE_IN_0_9_10 +GType nm_device_generic_get_type (void); + +NM_AVAILABLE_IN_0_9_10 +GObject *nm_device_generic_new (DBusGConnection *connection, const char *path); + +const char *nm_device_generic_get_hw_address (NMDeviceGeneric *device); + +G_END_DECLS + +#endif /* NM_DEVICE_GENERIC_H */ diff --git a/libnm/nm-device-infiniband.c b/libnm/nm-device-infiniband.c new file mode 100644 index 0000000000..d740ed006e --- /dev/null +++ b/libnm/nm-device-infiniband.c @@ -0,0 +1,311 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2012 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <linux/if_infiniband.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-infiniband.h> +#include <nm-utils.h> + +#include "nm-device-infiniband.h" +#include "nm-device-private.h" +#include "nm-object-private.h" + +G_DEFINE_TYPE (NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE) + +#define NM_DEVICE_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + gboolean carrier; +} NMDeviceInfinibandPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_CARRIER, + + LAST_PROP +}; + +/** + * nm_device_infiniband_error_quark: + * + * Registers an error quark for #NMDeviceInfiniband if necessary. + * + * Returns: the error quark used for #NMDeviceInfiniband errors. + **/ +GQuark +nm_device_infiniband_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-infiniband-error-quark"); + return quark; +} + +/** + * nm_device_infiniband_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceInfiniband. + * + * Returns: (transfer full): a new device + **/ +GObject * +nm_device_infiniband_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_INFINIBAND, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_infiniband_get_hw_address: + * @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. + **/ +const char * +nm_device_infiniband_get_hw_address (NMDeviceInfiniband *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_INFINIBAND_GET_PRIVATE (device)->hw_address; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_INFINIBAND_GET_PRIVATE (device)->carrier; +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingInfiniband *s_infiniband; + const char *ctype, *hwaddr_str; + const GByteArray *mac; + guint8 *hwaddr, hwaddr_buf[INFINIBAND_ALEN]; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_INFINIBAND_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_INFINIBAND_ERROR, NM_DEVICE_INFINIBAND_ERROR_NOT_INFINIBAND_CONNECTION, + "The connection was not a InfiniBand connection."); + return FALSE; + } + + s_infiniband = nm_connection_get_setting_infiniband (connection); + if (!s_infiniband) { + g_set_error (error, NM_DEVICE_INFINIBAND_ERROR, NM_DEVICE_INFINIBAND_ERROR_INVALID_INFINIBAND_CONNECTION, + "The connection was not a valid InfiniBand connection."); + return FALSE; + } + + hwaddr_str = nm_device_infiniband_get_hw_address (NM_DEVICE_INFINIBAND (device)); + if (hwaddr_str) { + hwaddr = nm_utils_hwaddr_aton (hwaddr_str, ARPHRD_INFINIBAND, hwaddr_buf); + if (!hwaddr) { + g_set_error (error, NM_DEVICE_INFINIBAND_ERROR, NM_DEVICE_INFINIBAND_ERROR_INVALID_DEVICE_MAC, + "Invalid device MAC address."); + return FALSE; + } + mac = nm_setting_infiniband_get_mac_address (s_infiniband); + + /* We only match against the last 8 bytes */ + if (mac && hwaddr && memcmp (mac->data + INFINIBAND_ALEN - 8, hwaddr + INFINIBAND_ALEN - 8, 8)) { + g_set_error (error, NM_DEVICE_INFINIBAND_ERROR, NM_DEVICE_INFINIBAND_ERROR_MAC_MISMATCH, + "The MACs of the device and the connection didn't match."); + return FALSE; + } + } + + return NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_INFINIBAND; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_infiniband_get_hw_address (NM_DEVICE_INFINIBAND (device)); +} + +/***********************************************************/ + +static void +nm_device_infiniband_init (NMDeviceInfiniband *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_INFINIBAND); +} + +static void +register_properties (NMDeviceInfiniband *device) +{ + NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_INFINIBAND_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_INFINIBAND_CARRIER, &priv->carrier }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_infiniband_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_INFINIBAND); + register_properties (NM_DEVICE_INFINIBAND (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_infiniband_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (object); + + g_free (priv->hw_address); + + G_OBJECT_CLASS (nm_device_infiniband_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDeviceInfiniband *device = NM_DEVICE_INFINIBAND (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_infiniband_get_hw_address (device)); + break; + 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; + } +} + +static void +nm_device_infiniband_class_init (NMDeviceInfinibandClass *eth_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (eth_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class); + + g_type_class_add_private (eth_class, sizeof (NMDeviceInfinibandPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + + /* properties */ + + /** + * NMDeviceInfiniband:hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_INFINIBAND_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceInfiniband:carrier: + * + * Whether the device has carrier. + **/ + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_INFINIBAND_CARRIER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + +} diff --git a/libnm/nm-device-infiniband.h b/libnm/nm-device-infiniband.h new file mode 100644 index 0000000000..ba587d93b9 --- /dev/null +++ b/libnm/nm-device-infiniband.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2012 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_INFINIBAND_H +#define NM_DEVICE_INFINIBAND_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_INFINIBAND (nm_device_infiniband_get_type ()) +#define NM_DEVICE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfiniband)) +#define NM_DEVICE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandClass)) +#define NM_IS_DEVICE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_INFINIBAND)) +#define NM_IS_DEVICE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_INFINIBAND)) +#define NM_DEVICE_INFINIBAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandClass)) + +/** + * NMDeviceInfinibandError: + * @NM_DEVICE_INFINIBAND_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_INFINIBAND_ERROR_NOT_INFINIBAND_CONNECTION: the connection was not of InfiniBand type + * @NM_DEVICE_INFINIBAND_ERROR_INVALID_INFINIBAND_CONNECTION: the InfiniBand connection was invalid + * @NM_DEVICE_INFINIBAND_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid + * @NM_DEVICE_INFINIBAND_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched + */ +typedef enum { + NM_DEVICE_INFINIBAND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_INFINIBAND_ERROR_NOT_INFINIBAND_CONNECTION, /*< nick=NotInfinibandConnection >*/ + NM_DEVICE_INFINIBAND_ERROR_INVALID_INFINIBAND_CONNECTION, /*< nick=InvalidInfinibandConnection >*/ + NM_DEVICE_INFINIBAND_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/ + NM_DEVICE_INFINIBAND_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/ +} NMDeviceInfinibandError; + +#define NM_DEVICE_INFINIBAND_ERROR nm_device_infiniband_error_quark () +GQuark nm_device_infiniband_error_quark (void); + +#define NM_DEVICE_INFINIBAND_HW_ADDRESS "hw-address" +#define NM_DEVICE_INFINIBAND_CARRIER "carrier" + +typedef struct { + NMDevice parent; +} NMDeviceInfiniband; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceInfinibandClass; + +GType nm_device_infiniband_get_type (void); + +GObject *nm_device_infiniband_new (DBusGConnection *connection, const char *path); + +const char * nm_device_infiniband_get_hw_address (NMDeviceInfiniband *device); +gboolean nm_device_infiniband_get_carrier (NMDeviceInfiniband *device); + +G_END_DECLS + +#endif /* NM_DEVICE_INFINIBAND_H */ diff --git a/libnm/nm-device-modem.c b/libnm/nm-device-modem.c new file mode 100644 index 0000000000..7d286190f3 --- /dev/null +++ b/libnm/nm-device-modem.c @@ -0,0 +1,291 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2012 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#include <config.h> +#include <string.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-gsm.h> +#include <nm-setting-cdma.h> + +#include "nm-device-modem.h" +#include "nm-device-private.h" +#include "nm-object-private.h" + +G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE) + +#define NM_DEVICE_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MODEM, NMDeviceModemPrivate)) + +typedef struct { + DBusGProxy *proxy; + + NMDeviceModemCapabilities caps; + NMDeviceModemCapabilities current_caps; +} NMDeviceModemPrivate; + +enum { + PROP_0, + PROP_MODEM_CAPS, + PROP_CURRENT_CAPS, + LAST_PROP +}; + +/** + * nm_device_modem_error_quark: + * + * Registers an error quark for #NMDeviceModem if necessary. + * + * Returns: the error quark used for #NMDeviceModem errors. + **/ +GQuark +nm_device_modem_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-modem-error-quark"); + return quark; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (self)); + return NM_DEVICE_MODEM_GET_PRIVATE (self)->caps; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (self)); + return NM_DEVICE_MODEM_GET_PRIVATE (self)->current_caps; +} + +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) +{ + NMSettingConnection *s_con; + NMSettingGsm *s_gsm; + NMSettingCdma *s_cdma; + const char *ctype; + NMDeviceModemCapabilities current_caps; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if ( strcmp (ctype, NM_SETTING_GSM_SETTING_NAME) != 0 + && strcmp (ctype, NM_SETTING_CDMA_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_MODEM_ERROR, NM_DEVICE_MODEM_ERROR_NOT_MODEM_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 (error, NM_DEVICE_MODEM_ERROR, NM_DEVICE_MODEM_ERROR_INVALID_MODEM_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 (error, NM_DEVICE_MODEM_ERROR, NM_DEVICE_MODEM_ERROR_MISSING_DEVICE_CAPS, + "The device missed capabilities required by the GSM/CDMA connection."); + return FALSE; + } + + return NM_DEVICE_CLASS (nm_device_modem_parent_class)->connection_compatible (device, connection, error); +} + +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) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_MODEM); +} + +static void +register_properties (NMDeviceModem *device) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_MODEM_MODEM_CAPABILITIES, &priv->caps }, + { NM_DEVICE_MODEM_CURRENT_CAPABILITIES, &priv->current_caps }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_modem_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_MODEM); + register_properties (NM_DEVICE_MODEM (object)); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_MODEM_CAPS: + g_value_set_uint (value, nm_device_modem_get_modem_capabilities (self)); + break; + case PROP_CURRENT_CAPS: + g_value_set_uint (value, nm_device_modem_get_current_capabilities (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object); +} + +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); + + g_type_class_add_private (modem_class, sizeof (NMDeviceModemPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + + 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. + **/ + g_object_class_install_property + (object_class, PROP_MODEM_CAPS, + g_param_spec_uint (NM_DEVICE_MODEM_MODEM_CAPABILITIES, "", "", + 0, G_MAXUINT32, 0, + 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. + **/ + g_object_class_install_property + (object_class, PROP_CURRENT_CAPS, + g_param_spec_uint (NM_DEVICE_MODEM_CURRENT_CAPABILITIES, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-device-modem.h b/libnm/nm-device-modem.h new file mode 100644 index 0000000000..226cd04695 --- /dev/null +++ b/libnm/nm-device-modem.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2012 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#ifndef NM_DEVICE_MODEM_H +#define NM_DEVICE_MODEM_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_MODEM (nm_device_modem_get_type ()) +#define NM_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModem)) +#define NM_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass)) +#define NM_IS_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_MODEM)) +#define NM_IS_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_MODEM)) +#define NM_DEVICE_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass)) + +/** + * NMDeviceModemError: + * @NM_DEVICE_MODEM_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_MODEM_ERROR_NOT_MODEM_CONNECTION: the connection was not of modem type + * @NM_DEVICE_MODEM_ERROR_INVALID_MODEM_CONNECTION: the modem connection was invalid + * @NM_DEVICE_MODEM_ERROR_MISSING_DEVICE_CAPS: the device missed required capabilities + */ +typedef enum { + NM_DEVICE_MODEM_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_MODEM_ERROR_NOT_MODEM_CONNECTION, /*< nick=NotModemConnection >*/ + NM_DEVICE_MODEM_ERROR_INVALID_MODEM_CONNECTION, /*< nick=InvalidModemConnection >*/ + NM_DEVICE_MODEM_ERROR_MISSING_DEVICE_CAPS, /*< nick=MissingDeviceCaps >*/ +} NMDeviceModemError; + +#define NM_DEVICE_MODEM_ERROR nm_device_modem_error_quark () +GQuark nm_device_modem_error_quark (void); + +#define NM_DEVICE_MODEM_MODEM_CAPABILITIES "modem-capabilities" +#define NM_DEVICE_MODEM_CURRENT_CAPABILITIES "current-capabilities" + +typedef struct { + NMDevice parent; +} NMDeviceModem; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceModemClass; + +GType nm_device_modem_get_type (void); + +NMDeviceModemCapabilities nm_device_modem_get_modem_capabilities (NMDeviceModem *self); +NMDeviceModemCapabilities nm_device_modem_get_current_capabilities (NMDeviceModem *self); + +G_END_DECLS + +#endif /* NM_DEVICE_MODEM_H */ diff --git a/libnm/nm-device-olpc-mesh.c b/libnm/nm-device-olpc-mesh.c new file mode 100644 index 0000000000..666ddc2636 --- /dev/null +++ b/libnm/nm-device-olpc-mesh.c @@ -0,0 +1,326 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-olpc-mesh.h> + +#include "nm-device-olpc-mesh.h" +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-device-wifi.h" + +G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) + +#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + NMDeviceWifi *companion; + guint32 active_channel; +} NMDeviceOlpcMeshPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_COMPANION, + PROP_ACTIVE_CHANNEL, + + LAST_PROP +}; + +/** + * nm_device_olpc_mesh_error_quark: + * + * Registers an error quark for #NMDeviceOlpcMesh if necessary. + * + * Returns: the error quark used for #NMDeviceOlpcMesh errors. + **/ +GQuark +nm_device_olpc_mesh_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-olpc-mesh-error-quark"); + return quark; +} + +/** + * nm_device_olpc_mesh_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceOlpcMesh. + * + * Returns: (transfer full): a new OlpcMesh device + **/ +GObject * +nm_device_olpc_mesh_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_OLPC_MESH, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_olpc_mesh_get_hw_address: + * @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. + **/ +const char * +nm_device_olpc_mesh_get_hw_address (NMDeviceOlpcMesh *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_OLPC_MESH (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_OLPC_MESH_GET_PRIVATE (device)->hw_address; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_OLPC_MESH_GET_PRIVATE (device)->active_channel; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_olpc_mesh_get_hw_address (NM_DEVICE_OLPC_MESH (device)); +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingOlpcMesh *s_olpc_mesh; + const char *ctype; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_OLPC_MESH_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_OLPC_MESH_ERROR, NM_DEVICE_OLPC_MESH_ERROR_NOT_OLPC_MESH_CONNECTION, + "The connection was not a Olpc Mesh connection."); + return FALSE; + } + + s_olpc_mesh = nm_connection_get_setting_olpc_mesh (connection); + if (!s_olpc_mesh) { + g_set_error (error, NM_DEVICE_OLPC_MESH_ERROR, NM_DEVICE_OLPC_MESH_ERROR_INVALID_OLPC_MESH_CONNECTION, + "The connection was not a valid Olpc Mesh connection."); + return FALSE; + } + + return NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_OLPC_MESH; +} + +/**************************************************************/ + +static void +nm_device_olpc_mesh_init (NMDeviceOlpcMesh *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_OLPC_MESH); +} + +static void +register_properties (NMDeviceOlpcMesh *device) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_OLPC_MESH_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_OLPC_MESH_COMPANION, &priv->companion, NULL, NM_TYPE_DEVICE_WIFI }, + { NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, &priv->active_channel }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_OLPC_MESH); + register_properties (NM_DEVICE_OLPC_MESH (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (object); + + g_clear_object (&priv->companion); + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (object); + + g_free (priv->hw_address); + + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_olpc_mesh_get_hw_address (device)); + break; + 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; + } +} + +static void +nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *olpc_mesh_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (olpc_mesh_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (olpc_mesh_class); + + g_type_class_add_private (olpc_mesh_class, sizeof (NMDeviceOlpcMeshPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + + /* properties */ + + /** + * NMDeviceOlpcMesh:hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_OLPC_MESH_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceOlpcMesh:companion: + * + * The companion device. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_ACTIVE_CHANNEL, + g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + +} diff --git a/libnm/nm-device-olpc-mesh.h b/libnm/nm-device-olpc-mesh.h new file mode 100644 index 0000000000..fcef83bcd1 --- /dev/null +++ b/libnm/nm-device-olpc-mesh.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_OLPC_MESH_H +#define NM_DEVICE_OLPC_MESH_H + +#include "nm-device.h" +#include "nm-device-wifi.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_OLPC_MESH (nm_device_olpc_mesh_get_type ()) +#define NM_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh)) +#define NM_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) +#define NM_IS_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OLPC_MESH)) +#define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OLPC_MESH)) +#define NM_DEVICE_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) + +/** + * NMDeviceOlpcMeshError: + * @NM_DEVICE_OLPC_MESH_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_OLPC_MESH_ERROR_NOT_OLPC_MESH_CONNECTION: the connection was not of Olpc Mesh type + * @NM_DEVICE_OLPC_MESH_ERROR_INVALID_OLPC_MESH_CONNECTION: the Olpc Mesh connection was invalid + */ +typedef enum { + NM_DEVICE_OLPC_MESH_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_OLPC_MESH_ERROR_NOT_OLPC_MESH_CONNECTION, /*< nick=NotOlpcMeshConnection >*/ + NM_DEVICE_OLPC_MESH_ERROR_INVALID_OLPC_MESH_CONNECTION, /*< nick=InvalidOlpcMeshConnection >*/ +} NMDeviceOlpcMeshError; + +#define NM_DEVICE_OLPC_MESH_ERROR nm_device_olpc_mesh_error_quark () +GQuark nm_device_olpc_mesh_error_quark (void); + +#define NM_DEVICE_OLPC_MESH_HW_ADDRESS "hw-address" +#define NM_DEVICE_OLPC_MESH_COMPANION "companion" +#define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel" + +typedef struct { + NMDevice parent; +} NMDeviceOlpcMesh; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceOlpcMeshClass; + +GType nm_device_olpc_mesh_get_type (void); + +GObject *nm_device_olpc_mesh_new (DBusGConnection *connection, const char *path); + +const char *nm_device_olpc_mesh_get_hw_address (NMDeviceOlpcMesh *device); +NMDeviceWifi *nm_device_olpc_mesh_get_companion (NMDeviceOlpcMesh *device); +guint32 nm_device_olpc_mesh_get_active_channel (NMDeviceOlpcMesh *device); + +G_END_DECLS + +#endif /* NM_DEVICE_OLPC_MESH_H */ diff --git a/libnm/nm-device-private.h b/libnm/nm-device-private.h new file mode 100644 index 0000000000..82d676f92e --- /dev/null +++ b/libnm/nm-device-private.h @@ -0,0 +1,26 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_PRIVATE_H +#define NM_DEVICE_PRIVATE_H + +void _nm_device_set_device_type (NMDevice *device, NMDeviceType dtype); + +#endif /* NM_DEVICE_PRIVATE_H */ diff --git a/libnm/nm-device-team.c b/libnm/nm-device-team.c new file mode 100644 index 0000000000..c9ac2d8b85 --- /dev/null +++ b/libnm/nm-device-team.c @@ -0,0 +1,353 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Jiri Pirko <jiri@resnulli.us> + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-team.h> +#include <nm-utils.h> + +#include "nm-device-team.h" +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-types.h" + +G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE) + +#define NM_DEVICE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TEAM, NMDeviceTeamPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + gboolean carrier; + GPtrArray *slaves; +} NMDeviceTeamPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_CARRIER, + PROP_SLAVES, + + LAST_PROP +}; + +/** + * nm_device_team_error_quark: + * + * Registers an error quark for #NMDeviceTeam if necessary. + * + * Returns: the error quark used for #NMDeviceTeam errors. + * + * Since: 0.9.10 + **/ +GQuark +nm_device_team_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-team-error-quark"); + return quark; +} + +/** + * nm_device_team_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceTeam. + * + * Returns: (transfer full): a new device + * + * Since: 0.9.10 + **/ +GObject * +nm_device_team_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_TEAM, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_team_get_hw_address: + * @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. + * + * Since: 0.9.10 + **/ +const char * +nm_device_team_get_hw_address (NMDeviceTeam *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_TEAM (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_TEAM_GET_PRIVATE (device)->hw_address; +} + +/** + * nm_device_team_get_carrier: + * @device: a #NMDeviceTeam + * + * Whether the device has carrier. + * + * Returns: %TRUE if the device has carrier + * + * Since: 0.9.10 + **/ +gboolean +nm_device_team_get_carrier (NMDeviceTeam *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_TEAM (device), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (device)); + 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. + * + * Since: 0.9.10 + **/ +const GPtrArray * +nm_device_team_get_slaves (NMDeviceTeam *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_TEAM (device), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return handle_ptr_array_return (NM_DEVICE_TEAM_GET_PRIVATE (device)->slaves); +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_team_get_hw_address (NM_DEVICE_TEAM (device)); +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingTeam *s_team; + const char *ctype, *dev_iface_name, *team_iface_name; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_TEAM_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_TEAM_ERROR, NM_DEVICE_TEAM_ERROR_NOT_TEAM_CONNECTION, + "The connection was not a team connection."); + return FALSE; + } + + s_team = nm_connection_get_setting_team (connection); + if (!s_team) { + g_set_error (error, NM_DEVICE_TEAM_ERROR, NM_DEVICE_TEAM_ERROR_INVALID_TEAM_CONNECTION, + "The connection was not a valid team connection."); + return FALSE; + } + + dev_iface_name = nm_device_get_iface (device); + team_iface_name = nm_setting_team_get_interface_name (s_team); + if (g_strcmp0 (dev_iface_name, team_iface_name) != 0) { + g_set_error (error, NM_DEVICE_TEAM_ERROR, NM_DEVICE_TEAM_ERROR_INTERFACE_MISMATCH, + "The interfaces of the device and the connection didn't match."); + return FALSE; + } + + /* FIXME: check slaves? */ + + return NM_DEVICE_CLASS (nm_device_team_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_TEAM; +} + +/***********************************************************/ + +static void +nm_device_team_init (NMDeviceTeam *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_TEAM); +} + +static void +register_properties (NMDeviceTeam *device) +{ + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_TEAM_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_TEAM_CARRIER, &priv->carrier }, + { NM_DEVICE_TEAM_SLAVES, &priv->slaves, NULL, NM_TYPE_DEVICE }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_team_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_TEAM); + register_properties (NM_DEVICE_TEAM (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + if (priv->slaves) { + g_ptr_array_set_free_func (priv->slaves, g_object_unref); + g_ptr_array_free (priv->slaves, TRUE); + priv->slaves = NULL; + } + + G_OBJECT_CLASS (nm_device_team_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object); + + g_free (priv->hw_address); + + 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); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_team_get_hw_address (device)); + break; + case PROP_CARRIER: + g_value_set_boolean (value, nm_device_team_get_carrier (device)); + break; + case PROP_SLAVES: + g_value_set_boxed (value, nm_device_team_get_slaves (device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_team_class_init (NMDeviceTeamClass *eth_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (eth_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class); + + g_type_class_add_private (eth_class, sizeof (NMDeviceTeamPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + + /* properties */ + + /** + * NMDeviceTeam:hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_TEAM_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceTeam:carrier: + * + * Whether the device has carrier. + **/ + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_TEAM_CARRIER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceTeam:slaves: + * + * The devices (#NMDevice) enslaved to the team device. + **/ + g_object_class_install_property + (object_class, PROP_SLAVES, + g_param_spec_boxed (NM_DEVICE_TEAM_SLAVES, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-device-team.h b/libnm/nm-device-team.h new file mode 100644 index 0000000000..46e910bad1 --- /dev/null +++ b/libnm/nm-device-team.h @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2013 Jiri Pirko <jiri@resnulli.us> + */ + +#ifndef NM_DEVICE_TEAM_H +#define NM_DEVICE_TEAM_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_TEAM (nm_device_team_get_type ()) +#define NM_DEVICE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeam)) +#define NM_DEVICE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass)) +#define NM_IS_DEVICE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_TEAM)) +#define NM_IS_DEVICE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_TEAM)) +#define NM_DEVICE_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass)) + +/** + * NMDeviceTeamError: + * @NM_DEVICE_TEAM_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_TEAM_ERROR_NOT_TEAM_CONNECTION: the connection was not of team type + * @NM_DEVICE_TEAM_ERROR_INVALID_TEAM_CONNECTION: the team connection was invalid + * @NM_DEVICE_TEAM_ERROR_INTERFACE_MISMATCH: the interfaces of the connection and the device mismatched + */ +typedef enum { + NM_DEVICE_TEAM_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_TEAM_ERROR_NOT_TEAM_CONNECTION, /*< nick=NotTeamConnection >*/ + NM_DEVICE_TEAM_ERROR_INVALID_TEAM_CONNECTION, /*< nick=InvalidTeamConnection >*/ + NM_DEVICE_TEAM_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/ +} NMDeviceTeamError; + +#define NM_DEVICE_TEAM_ERROR nm_device_team_error_quark () +NM_AVAILABLE_IN_0_9_10 +GQuark nm_device_team_error_quark (void); + +#define NM_DEVICE_TEAM_HW_ADDRESS "hw-address" +#define NM_DEVICE_TEAM_CARRIER "carrier" +#define NM_DEVICE_TEAM_SLAVES "slaves" + +typedef struct { + NMDevice parent; +} NMDeviceTeam; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceTeamClass; + +NM_AVAILABLE_IN_0_9_10 +GType nm_device_team_get_type (void); + +NM_AVAILABLE_IN_0_9_10 +GObject *nm_device_team_new (DBusGConnection *connection, const char *path); + +const char *nm_device_team_get_hw_address (NMDeviceTeam *device); +gboolean nm_device_team_get_carrier (NMDeviceTeam *device); +const GPtrArray *nm_device_team_get_slaves (NMDeviceTeam *device); + +G_END_DECLS + +#endif /* NM_DEVICE_TEAM_H */ diff --git a/libnm/nm-device-vlan.c b/libnm/nm-device-vlan.c new file mode 100644 index 0000000000..8df3025ebb --- /dev/null +++ b/libnm/nm-device-vlan.c @@ -0,0 +1,353 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-vlan.h> +#include <nm-utils.h> + +#include "nm-device-vlan.h" +#include "nm-device-private.h" +#include "nm-object-private.h" + +G_DEFINE_TYPE (NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE) + +#define NM_DEVICE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VLAN, NMDeviceVlanPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + gboolean carrier; + guint vlan_id; +} NMDeviceVlanPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_CARRIER, + PROP_VLAN_ID, + + LAST_PROP +}; + +/** + * nm_device_vlan_error_quark: + * + * Registers an error quark for #NMDeviceVlan if necessary. + * + * Returns: the error quark used for #NMDeviceVlan errors. + **/ +GQuark +nm_device_vlan_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-vlan-error-quark"); + return quark; +} + +/** + * nm_device_vlan_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceVlan. + * + * Returns: (transfer full): a new device + **/ +GObject * +nm_device_vlan_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_VLAN, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_vlan_get_hw_address: + * @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. + **/ +const char * +nm_device_vlan_get_hw_address (NMDeviceVlan *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_VLAN (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_VLAN_GET_PRIVATE (device)->hw_address; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_VLAN_GET_PRIVATE (device)->carrier; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_VLAN_GET_PRIVATE (device)->vlan_id; +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingVlan *s_vlan; + NMSettingWired *s_wired; + const char *ctype, *dev_iface_name, *vlan_iface_name; + const GByteArray *mac_address; + char *mac_address_str; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_VLAN_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_NOT_VLAN_CONNECTION, + "The connection was not a VLAN connection."); + return FALSE; + } + + s_vlan = nm_connection_get_setting_vlan (connection); + if (!s_vlan) { + g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_INVALID_VLAN_CONNECTION, + "The connection was not a valid VLAN connection."); + return FALSE; + } + + if (nm_setting_vlan_get_id (s_vlan) != nm_device_vlan_get_vlan_id (NM_DEVICE_VLAN (device))) { + g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_ID_MISMATCH, + "The VLAN identifiers of the device and the connection didn't match."); + return FALSE; + } + + dev_iface_name = nm_device_get_iface (device); + vlan_iface_name = nm_setting_vlan_get_interface_name (s_vlan); + if (vlan_iface_name && g_strcmp0 (dev_iface_name, vlan_iface_name) != 0) { + g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_INTERFACE_MISMATCH, + "The interfaces of the device and the connection didn't match."); + return FALSE; + } + + s_wired = nm_connection_get_setting_wired (connection); + if (s_wired) + mac_address = nm_setting_wired_get_mac_address (s_wired); + else + mac_address = NULL; + if (mac_address) { + mac_address_str = nm_utils_hwaddr_ntoa_len (mac_address->data, mac_address->len); + if (!g_strcmp0 (mac_address_str, NM_DEVICE_VLAN_GET_PRIVATE (device)->hw_address)) { + g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_MAC_MISMATCH, + "The hardware address of the device and the connection didn't match."); + } + g_free (mac_address_str); + } + + return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_VLAN; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_vlan_get_hw_address (NM_DEVICE_VLAN (device)); +} + +/***********************************************************/ + +static void +nm_device_vlan_init (NMDeviceVlan *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_VLAN); +} + +static void +register_properties (NMDeviceVlan *device) +{ + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_VLAN_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_VLAN_CARRIER, &priv->carrier }, + { NM_DEVICE_VLAN_VLAN_ID, &priv->vlan_id }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_VLAN); + register_properties (NM_DEVICE_VLAN (object)); +} + +static void +dispose (GObject *object) +{ + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object); + + g_free (priv->hw_address); + + G_OBJECT_CLASS (nm_device_vlan_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDeviceVlan *device = NM_DEVICE_VLAN (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_vlan_get_hw_address (device)); + break; + case PROP_CARRIER: + g_value_set_boolean (value, nm_device_vlan_get_carrier (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; + } +} + +static void +nm_device_vlan_class_init (NMDeviceVlanClass *eth_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (eth_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class); + + g_type_class_add_private (eth_class, sizeof (NMDeviceVlanPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + + /* properties */ + + /** + * NMDeviceVlan:hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_VLAN_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceVlan:carrier: + * + * Whether the device has carrier. + **/ + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_VLAN_CARRIER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceVlan:vlan-id: + * + * The device's VLAN ID. + **/ + g_object_class_install_property + (object_class, PROP_VLAN_ID, + g_param_spec_uint (NM_DEVICE_VLAN_VLAN_ID, "", "", + 0, 4095, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-device-vlan.h b/libnm/nm-device-vlan.h new file mode 100644 index 0000000000..511d02215b --- /dev/null +++ b/libnm/nm-device-vlan.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2012 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_VLAN_H +#define NM_DEVICE_VLAN_H + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_VLAN (nm_device_vlan_get_type ()) +#define NM_DEVICE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_VLAN, NMDeviceVlan)) +#define NM_DEVICE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_VLAN, NMDeviceVlanClass)) +#define NM_IS_DEVICE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_VLAN)) +#define NM_IS_DEVICE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_VLAN)) +#define NM_DEVICE_VLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_VLAN, NMDeviceVlanClass)) + +/** + * NMDeviceVlanError: + * @NM_DEVICE_VLAN_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_VLAN_ERROR_NOT_VLAN_CONNECTION: the connection was not of VLAN type + * @NM_DEVICE_VLAN_ERROR_INVALID_VLAN_CONNECTION: the VLAN connection was invalid + * @NM_DEVICE_VLAN_ERROR_ID_MISMATCH: the VLAN identifiers of the connection and the device mismatched + * @NM_DEVICE_VLAN_ERROR_INTERFACE_MISMATCH: the interfaces of the connection and the device mismatched + * @NM_DEVICE_VLAN_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched + */ +typedef enum { + NM_DEVICE_VLAN_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_VLAN_ERROR_NOT_VLAN_CONNECTION, /*< nick=NotVlanConnection >*/ + NM_DEVICE_VLAN_ERROR_INVALID_VLAN_CONNECTION, /*< nick=InvalidVlanConnection >*/ + NM_DEVICE_VLAN_ERROR_ID_MISMATCH, /*< nick=IdMismatch >*/ + NM_DEVICE_VLAN_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/ + NM_DEVICE_VLAN_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/ +} NMDeviceVlanError; + +#define NM_DEVICE_VLAN_ERROR nm_device_vlan_error_quark () +GQuark nm_device_vlan_error_quark (void); + +#define NM_DEVICE_VLAN_HW_ADDRESS "hw-address" +#define NM_DEVICE_VLAN_CARRIER "carrier" +#define NM_DEVICE_VLAN_VLAN_ID "vlan-id" + +typedef struct { + NMDevice parent; +} NMDeviceVlan; + +typedef struct { + NMDeviceClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceVlanClass; + +GType nm_device_vlan_get_type (void); + +GObject *nm_device_vlan_new (DBusGConnection *connection, const char *path); + +const char * nm_device_vlan_get_hw_address (NMDeviceVlan *device); +gboolean nm_device_vlan_get_carrier (NMDeviceVlan *device); +guint nm_device_vlan_get_vlan_id (NMDeviceVlan *device); + +G_END_DECLS + +#endif /* NM_DEVICE_VLAN_H */ diff --git a/libnm/nm-device-wifi.c b/libnm/nm-device-wifi.c new file mode 100644 index 0000000000..a4c108fb08 --- /dev/null +++ b/libnm/nm-device-wifi.c @@ -0,0 +1,838 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-wireless.h> +#include <nm-setting-wireless-security.h> + +#include "nm-device-wifi.h" +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-object-cache.h" +#include "nm-dbus-glib-types.h" +#include "nm-types-private.h" + +G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate)) + +void _nm_device_wifi_set_wireless_enabled (NMDeviceWifi *device, gboolean enabled); + +typedef struct { + NMDeviceWifi *device; + NMDeviceWifiRequestScanFn callback; + gpointer user_data; +} RequestScanInfo; + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + char *perm_hw_address; + NM80211Mode mode; + guint32 rate; + NMAccessPoint *active_ap; + NMDeviceWifiCapabilities wireless_caps; + GPtrArray *aps; + + DBusGProxyCall *scan_call; + RequestScanInfo *scan_info; +} NMDeviceWifiPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_PERM_HW_ADDRESS, + PROP_MODE, + PROP_BITRATE, + PROP_ACTIVE_ACCESS_POINT, + PROP_WIRELESS_CAPABILITIES, + PROP_ACCESS_POINTS, + + LAST_PROP +}; + +enum { + ACCESS_POINT_ADDED, + ACCESS_POINT_REMOVED, + + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +/** + * nm_device_wifi_error_quark: + * + * Registers an error quark for #NMDeviceWifi if necessary. + * + * Returns: the error quark used for #NMDeviceWifi errors. + **/ +GQuark +nm_device_wifi_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-wifi-error-quark"); + return quark; +} + +/** + * nm_device_wifi_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDeviceWifi. + * + * Returns: (transfer full): a new Wi-Fi device + **/ +GObject * +nm_device_wifi_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_WIFI, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * nm_device_wifi_get_hw_address: + * @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. + **/ +const char * +nm_device_wifi_get_hw_address (NMDeviceWifi *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_WIFI_GET_PRIVATE (device)->hw_address; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + 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; + } + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_WIFI_GET_PRIVATE (device)->rate; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_WIFI_GET_PRIVATE (device)->wireless_caps; +} + +/** + * 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) +{ + NMDeviceState state; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL); + + state = nm_device_get_state (NM_DEVICE (device)); + switch (state) { + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + 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 NULL; + break; + } + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_WIFI_GET_PRIVATE (device)->active_ap; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return handle_ptr_array_return (NM_DEVICE_WIFI_GET_PRIVATE (device)->aps); +} + +/** + * 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; +} + +static void +request_scan_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + RequestScanInfo *info = user_data; + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (info->device); + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + + if (info->callback) + info->callback (info->device, error, info->user_data); + + g_clear_error (&error); + g_slice_free (RequestScanInfo, info); + + priv->scan_call = NULL; + priv->scan_info = NULL; +} + +/** + * nm_device_wifi_request_scan_simple: + * @device: a #NMDeviceWifi + * @callback: (scope async) (allow-none): the function to call when the call is done + * @user_data: (closure): user data to pass to the callback function + * + * Request NM to scan for access points on the #NMDeviceWifi. This function only + * instructs NM to perform scanning. Use nm_device_wifi_get_access_points() + * to get available access points. + * + * Since: 0.9.8 + **/ +void +nm_device_wifi_request_scan_simple (NMDeviceWifi *device, + NMDeviceWifiRequestScanFn callback, + gpointer user_data) +{ + RequestScanInfo *info; + GHashTable *options; + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); + + g_return_if_fail (NM_IS_DEVICE_WIFI (device)); + + /* If a scan is in progress, just return */ + if (priv->scan_call) + return; + + options = g_hash_table_new (g_str_hash, g_str_equal); + + info = g_slice_new0 (RequestScanInfo); + info->device = device; + info->callback = callback; + info->user_data = user_data; + + priv->scan_info = info; + priv->scan_call = dbus_g_proxy_begin_call (NM_DEVICE_WIFI_GET_PRIVATE (device)->proxy, "RequestScan", + request_scan_cb, info, NULL, + DBUS_TYPE_G_MAP_OF_VARIANT, options, + G_TYPE_INVALID); + + g_hash_table_unref (options); +} + +static void +clean_up_aps (NMDeviceWifi *self, gboolean notify) +{ + NMDeviceWifiPrivate *priv; + + g_return_if_fail (NM_IS_DEVICE_WIFI (self)); + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->active_ap) { + g_object_unref (priv->active_ap); + priv->active_ap = NULL; + } + + if (priv->aps) { + while (priv->aps->len) { + NMAccessPoint *ap = NM_ACCESS_POINT (g_ptr_array_index (priv->aps, 0)); + + if (notify) + g_signal_emit (self, signals[ACCESS_POINT_REMOVED], 0, ap); + g_ptr_array_remove (priv->aps, ap); + g_object_unref (ap); + } + g_ptr_array_free (priv->aps, TRUE); + priv->aps = NULL; + } +} + +/** + * _nm_device_wifi_set_wireless_enabled: + * @device: a #NMDeviceWifi + * @enabled: %TRUE to enable the device + * + * Enables or disables the wireless device. + **/ +void +_nm_device_wifi_set_wireless_enabled (NMDeviceWifi *device, + gboolean enabled) +{ + g_return_if_fail (NM_IS_DEVICE_WIFI (device)); + + if (!enabled) + clean_up_aps (device, TRUE); +} + +#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) +{ + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + const char *ctype; + const GByteArray *mac; + const char *hw_str; + struct ether_addr *hw_mac; + NMDeviceWifiCapabilities wifi_caps; + const char *key_mgmt; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_WIRELESS_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_NOT_WIFI_CONNECTION, + "The connection was not a Wi-Fi connection."); + return FALSE; + } + + s_wifi = nm_connection_get_setting_wireless (connection); + if (!s_wifi) { + g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_INVALID_WIFI_CONNECTION, + "The connection was not a valid Wi-Fi connection."); + return FALSE; + } + + /* Check MAC address */ + hw_str = nm_device_wifi_get_permanent_hw_address (NM_DEVICE_WIFI (device)); + if (hw_str) { + hw_mac = ether_aton (hw_str); + if (!hw_mac) { + g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_INVALID_DEVICE_MAC, + "Invalid device MAC address."); + return FALSE; + } + mac = nm_setting_wireless_get_mac_address (s_wifi); + if (mac && hw_mac && memcmp (mac->data, hw_mac->ether_addr_octet, ETH_ALEN)) { + g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_MAC_MISMATCH, + "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 */ + wifi_caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (device)); + + 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 ( !g_strcmp0 (key_mgmt, "wpa-none") + || !g_strcmp0 (key_mgmt, "wpa-psk") + || !g_strcmp0 (key_mgmt, "wpa-eap")) { + + /* Is device only WEP capable? */ + if (!(wifi_caps & WPA_CAPS)) { + g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_WPA_CAPS, + "The device missed 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 (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_RSN_CAPS, + "The device missed WPA2/RSN capabilities required by the connection."); + return FALSE; + } + } + } + + return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_WIRELESS; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_wifi_get_hw_address (NM_DEVICE_WIFI (device)); +} + +/**************************************************************/ + +static void +nm_device_wifi_init (NMDeviceWifi *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_WIFI); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_wifi_get_hw_address (self)); + break; + 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_uint (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_uint (value, nm_device_wifi_get_capabilities (self)); + break; + case PROP_ACCESS_POINTS: + g_value_set_boxed (value, nm_device_wifi_get_access_points (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +state_changed_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + switch (nm_device_get_state (device)) { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + case NM_DEVICE_STATE_FAILED: + /* Just clear active AP; don't clear the AP list unless wireless is disabled completely */ + if (priv->active_ap) { + g_object_unref (priv->active_ap); + priv->active_ap = NULL; + } + _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT); + priv->rate = 0; + _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_WIFI_BITRATE); + break; + default: + break; + } +} + +static void +register_properties (NMDeviceWifi *device) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_WIFI_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, &priv->perm_hw_address }, + { NM_DEVICE_WIFI_MODE, &priv->mode }, + { NM_DEVICE_WIFI_BITRATE, &priv->rate }, + { NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, &priv->active_ap, NULL, NM_TYPE_ACCESS_POINT }, + { NM_DEVICE_WIFI_CAPABILITIES, &priv->wireless_caps }, + { NM_DEVICE_WIFI_ACCESS_POINTS, &priv->aps, NULL, NM_TYPE_ACCESS_POINT, "access-point" }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +static void +access_point_removed (NMDeviceWifi *self, NMAccessPoint *ap) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (ap == priv->active_ap) { + g_object_unref (priv->active_ap); + priv->active_ap = NULL; + _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT); + + priv->rate = 0; + _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIFI_BITRATE); + } +} + +static void +constructed (GObject *object) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_wifi_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_WIRELESS); + register_properties (NM_DEVICE_WIFI (object)); + + g_signal_connect (NM_DEVICE (object), + "notify::" NM_DEVICE_STATE, + G_CALLBACK (state_changed_cb), + NULL); +} + +static void +dispose (GObject *object) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object); + GError *error = NULL; + + if (priv->scan_call) { + g_set_error_literal (&error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_UNKNOWN, + "Wi-Fi device was destroyed"); + if (priv->scan_info) { + if (priv->scan_info->callback) + priv->scan_info->callback (NULL, error, priv->scan_info->user_data); + g_slice_free (RequestScanInfo, priv->scan_info); + priv->scan_info = NULL; + } + g_clear_error (&error); + + dbus_g_proxy_cancel_call (priv->proxy, priv->scan_call); + priv->scan_call = NULL; + } + + clean_up_aps (NM_DEVICE_WIFI (object), FALSE); + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object); + + g_free (priv->hw_address); + g_free (priv->perm_hw_address); + + G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object); +} + +static void +nm_device_wifi_class_init (NMDeviceWifiClass *wifi_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (wifi_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (wifi_class); + + g_type_class_add_private (wifi_class, sizeof (NMDeviceWifiPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + wifi_class->access_point_removed = access_point_removed; + + /* properties */ + + /** + * NMDeviceWifi:hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_WIFI_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceWifi:perm-hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_MODE, + g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "", + NM_802_11_MODE_UNKNOWN, NM_802_11_MODE_AP, NM_802_11_MODE_INFRA, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceWifi:bitrate: + * + * The bit rate of the device in kbit/s. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_WIRELESS_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceWifi:access-points: + * + * List of all Wi-Fi access points the device can see. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_ACCESS_POINTS, + g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* signals */ + + /** + * 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, + G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added), + 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, + G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); +} diff --git a/libnm/nm-device-wifi.h b/libnm/nm-device-wifi.h new file mode 100644 index 0000000000..2bb432a7b0 --- /dev/null +++ b/libnm/nm-device-wifi.h @@ -0,0 +1,115 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_WIFI_H +#define NM_DEVICE_WIFI_H + +#include "nm-device.h" +#include "nm-access-point.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_WIFI (nm_device_wifi_get_type ()) +#define NM_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi)) +#define NM_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass)) +#define NM_IS_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI)) +#define NM_IS_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIFI)) +#define NM_DEVICE_WIFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass)) + +/** + * NMDeviceWifiError: + * @NM_DEVICE_WIFI_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_WIFI_ERROR_NOT_WIFI_CONNECTION: the connection was not of Wi-Fi type + * @NM_DEVICE_WIFI_ERROR_INVALID_WIFI_CONNECTION: the Wi-Fi connection was invalid + * @NM_DEVICE_WIFI_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid + * @NM_DEVICE_WIFI_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched + * @NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_WPA_CAPS: the device missed WPA capabilities + * required by the connection + * @NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_RSN_CAPS: the device missed RSN capabilities + * required by the connection + */ +typedef enum { + NM_DEVICE_WIFI_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_WIFI_ERROR_NOT_WIFI_CONNECTION, /*< nick=NotWifiConnection >*/ + NM_DEVICE_WIFI_ERROR_INVALID_WIFI_CONNECTION, /*< nick=InvalidWifiConnection >*/ + NM_DEVICE_WIFI_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/ + NM_DEVICE_WIFI_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/ + NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_WPA_CAPS, /*< nick=MissingDeviceWpaCaps >*/ + NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_RSN_CAPS, /*< nick=MissingDeviceRsnCaps >*/ +} NMDeviceWifiError; + +#define NM_DEVICE_WIFI_ERROR nm_device_wifi_error_quark () +GQuark nm_device_wifi_error_quark (void); + +#define NM_DEVICE_WIFI_HW_ADDRESS "hw-address" +#define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address" +#define NM_DEVICE_WIFI_MODE "mode" +#define NM_DEVICE_WIFI_BITRATE "bitrate" +#define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point" +#define NM_DEVICE_WIFI_CAPABILITIES "wireless-capabilities" +#define NM_DEVICE_WIFI_ACCESS_POINTS "access-points" + +typedef struct { + NMDevice parent; +} NMDeviceWifi; + +typedef struct { + NMDeviceClass parent; + + /* Signals */ + void (*access_point_added) (NMDeviceWifi *device, NMAccessPoint *ap); + void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDeviceWifiClass; + +GType nm_device_wifi_get_type (void); + +GObject *nm_device_wifi_new (DBusGConnection *connection, const char *path); + +const char * nm_device_wifi_get_hw_address (NMDeviceWifi *device); +const char * nm_device_wifi_get_permanent_hw_address (NMDeviceWifi *device); +NM80211Mode nm_device_wifi_get_mode (NMDeviceWifi *device); +guint32 nm_device_wifi_get_bitrate (NMDeviceWifi *device); +NMDeviceWifiCapabilities nm_device_wifi_get_capabilities (NMDeviceWifi *device); +NMAccessPoint * nm_device_wifi_get_active_access_point (NMDeviceWifi *device); + +NMAccessPoint * nm_device_wifi_get_access_point_by_path (NMDeviceWifi *device, + const char *path); + +const GPtrArray * nm_device_wifi_get_access_points (NMDeviceWifi *device); + +typedef void (*NMDeviceWifiRequestScanFn) (NMDeviceWifi *device, + GError *error, + gpointer user_data); +void nm_device_wifi_request_scan_simple (NMDeviceWifi *device, + NMDeviceWifiRequestScanFn callback, + gpointer user_data); + +G_END_DECLS + +#endif /* NM_DEVICE_WIFI_H */ diff --git a/libnm/nm-device-wimax.c b/libnm/nm-device-wimax.c new file mode 100644 index 0000000000..8fac2a5ac7 --- /dev/null +++ b/libnm/nm-device-wimax.c @@ -0,0 +1,755 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2012 Red Hat, Inc. + * Copyright 2009 Novell, Inc. + */ + +#include <config.h> +#include <string.h> +#include <netinet/ether.h> + +#include "nm-glib-compat.h" + +#include <nm-setting-connection.h> +#include <nm-setting-wimax.h> + +#include "nm-device-wimax.h" +#include "nm-object-private.h" +#include "nm-object-cache.h" +#include "nm-dbus-glib-types.h" +#include "nm-types-private.h" +#include "nm-device-private.h" + +G_DEFINE_TYPE (NMDeviceWimax, nm_device_wimax, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIMAX, NMDeviceWimaxPrivate)) + +void _nm_device_wimax_set_wireless_enabled (NMDeviceWimax *wimax, gboolean enabled); + +typedef struct { + DBusGProxy *proxy; + + char *hw_address; + NMWimaxNsp *active_nsp; + GPtrArray *nsps; + + guint center_freq; + gint rssi; + gint cinr; + gint tx_power; + char *bsid; +} NMDeviceWimaxPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_ACTIVE_NSP, + PROP_CENTER_FREQ, + PROP_RSSI, + PROP_CINR, + PROP_TX_POWER, + PROP_BSID, + PROP_NSPS, + + LAST_PROP +}; + +enum { + NSP_ADDED, + NSP_REMOVED, + + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +/** + * nm_device_wimax_error_quark: + * + * Registers an error quark for #NMDeviceWimax if necessary. + * + * Returns: the error quark used for #NMDeviceWimax errors. + **/ +GQuark +nm_device_wimax_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-wimax-error-quark"); + return quark; +} + +/** + * nm_device_wimax_new: + * @connection: the #DBusGConnection + * @path: the D-Bus object path of the WiMAX device + * + * Creates a new #NMDeviceWimax. + * + * Returns: (transfer full): a new WiMAX device + **/ +GObject * +nm_device_wimax_new (DBusGConnection *connection, const char *path) +{ + GObject *device; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (NM_TYPE_DEVICE_WIMAX, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return device; +} + +/** + * 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. + **/ +const char * +nm_device_wimax_get_hw_address (NMDeviceWimax *wimax) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (wimax), NULL); + + _nm_object_ensure_inited (NM_OBJECT (wimax)); + return NM_DEVICE_WIMAX_GET_PRIVATE (wimax)->hw_address; +} + +/** + * 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 + **/ +NMWimaxNsp * +nm_device_wimax_get_active_nsp (NMDeviceWimax *wimax) +{ + NMDeviceState state; + + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (wimax), NULL); + + state = nm_device_get_state (NM_DEVICE (wimax)); + switch (state) { + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + 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 NULL; + break; + } + + _nm_object_ensure_inited (NM_OBJECT (wimax)); + return NM_DEVICE_WIMAX_GET_PRIVATE (wimax)->active_nsp; +} + +/** + * 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. + **/ +const GPtrArray * +nm_device_wimax_get_nsps (NMDeviceWimax *wimax) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (wimax), NULL); + + _nm_object_ensure_inited (NM_OBJECT (wimax)); + return handle_ptr_array_return (NM_DEVICE_WIMAX_GET_PRIVATE (wimax)->nsps); +} + +/** + * 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. + **/ +NMWimaxNsp * +nm_device_wimax_get_nsp_by_path (NMDeviceWimax *wimax, + const char *path) +{ + const GPtrArray *nsps; + int i; + NMWimaxNsp *nsp = NULL; + + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (wimax), NULL); + g_return_val_if_fail (path != NULL, NULL); + + nsps = nm_device_wimax_get_nsps (wimax); + if (!nsps) + return NULL; + + for (i = 0; i < nsps->len; i++) { + NMWimaxNsp *candidate = g_ptr_array_index (nsps, i); + if (!strcmp (nm_object_get_path (NM_OBJECT (candidate)), path)) { + nsp = candidate; + break; + } + } + + return nsp; +} + +static void +clean_up_nsps (NMDeviceWimax *self, gboolean notify) +{ + NMDeviceWimaxPrivate *priv; + + g_return_if_fail (NM_IS_DEVICE_WIMAX (self)); + + priv = NM_DEVICE_WIMAX_GET_PRIVATE (self); + + if (priv->active_nsp) { + g_object_unref (priv->active_nsp); + priv->active_nsp = NULL; + } + + if (priv->nsps) { + while (priv->nsps->len) { + NMWimaxNsp *nsp = NM_WIMAX_NSP (g_ptr_array_index (priv->nsps, 0)); + + if (notify) + g_signal_emit (self, signals[NSP_REMOVED], 0, nsp); + g_ptr_array_remove (priv->nsps, nsp); + g_object_unref (nsp); + } + g_ptr_array_free (priv->nsps, TRUE); + priv->nsps = 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 + **/ +guint +nm_device_wimax_get_center_frequency (NMDeviceWimax *self) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), 0); + + _nm_object_ensure_inited (NM_OBJECT (self)); + return NM_DEVICE_WIMAX_GET_PRIVATE (self)->center_freq; +} + +/** + * 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 + **/ +gint +nm_device_wimax_get_rssi (NMDeviceWimax *self) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), 0); + + _nm_object_ensure_inited (NM_OBJECT (self)); + return NM_DEVICE_WIMAX_GET_PRIVATE (self)->rssi; +} + +/** + * 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 + **/ +gint +nm_device_wimax_get_cinr (NMDeviceWimax *self) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), 0); + + _nm_object_ensure_inited (NM_OBJECT (self)); + return NM_DEVICE_WIMAX_GET_PRIVATE (self)->cinr; +} + +/** + * 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 + **/ +gint +nm_device_wimax_get_tx_power (NMDeviceWimax *self) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), 0); + + _nm_object_ensure_inited (NM_OBJECT (self)); + return NM_DEVICE_WIMAX_GET_PRIVATE (self)->tx_power; +} + +/** + * 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 + **/ +const char * +nm_device_wimax_get_bsid (NMDeviceWimax *self) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), NULL); + + _nm_object_ensure_inited (NM_OBJECT (self)); + return NM_DEVICE_WIMAX_GET_PRIVATE (self)->bsid; +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + NMSettingWimax *s_wimax; + const char *ctype; + const GByteArray *mac; + const char *hw_str; + struct ether_addr *hw_mac; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_WIMAX_SETTING_NAME) != 0) { + g_set_error (error, NM_DEVICE_WIMAX_ERROR, NM_DEVICE_WIMAX_ERROR_NOT_WIMAX_CONNECTION, + "The connection was not a Wimax connection."); + return FALSE; + } + + s_wimax = nm_connection_get_setting_wimax (connection); + if (!s_wimax) { + g_set_error (error, NM_DEVICE_WIMAX_ERROR, NM_DEVICE_WIMAX_ERROR_INVALID_WIMAX_CONNECTION, + "The connection was not a valid Wimax connection."); + return FALSE; + } + + /* Check MAC address */ + hw_str = nm_device_wimax_get_hw_address (NM_DEVICE_WIMAX (device)); + if (hw_str) { + hw_mac = ether_aton (hw_str); + if (!hw_mac) { + g_set_error (error, NM_DEVICE_WIMAX_ERROR, NM_DEVICE_WIMAX_ERROR_INVALID_DEVICE_MAC, + "Invalid device MAC address."); + return FALSE; + } + mac = nm_setting_wimax_get_mac_address (s_wimax); + if (mac && hw_mac && memcmp (mac->data, hw_mac->ether_addr_octet, ETH_ALEN)) { + g_set_error (error, NM_DEVICE_WIMAX_ERROR, NM_DEVICE_WIMAX_ERROR_MAC_MISMATCH, + "The MACs of the device and the connection didn't match."); + return FALSE; + } + } + + return NM_DEVICE_CLASS (nm_device_wimax_parent_class)->connection_compatible (device, connection, error); +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_WIMAX; +} + +static const char * +get_hw_address (NMDevice *device) +{ + return nm_device_wimax_get_hw_address (NM_DEVICE_WIMAX (device)); +} + +/**************************************************************/ + +static void +nm_device_wimax_init (NMDeviceWimax *device) +{ + _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_WIMAX); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDeviceWimax *self = NM_DEVICE_WIMAX (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, nm_device_wimax_get_hw_address (self)); + break; + case PROP_ACTIVE_NSP: + g_value_set_object (value, nm_device_wimax_get_active_nsp (self)); + break; + case PROP_CENTER_FREQ: + g_value_set_uint (value, nm_device_wimax_get_center_frequency (self)); + break; + case PROP_RSSI: + g_value_set_int (value, nm_device_wimax_get_rssi (self)); + break; + case PROP_CINR: + g_value_set_int (value, nm_device_wimax_get_cinr (self)); + break; + case PROP_TX_POWER: + g_value_set_int (value, nm_device_wimax_get_tx_power (self)); + break; + case PROP_BSID: + g_value_set_string (value, nm_device_wimax_get_bsid (self)); + break; + case PROP_NSPS: + g_value_set_boxed (value, nm_device_wimax_get_nsps (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clear_link_status (NMDeviceWimax *self) +{ + NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self); + + if (priv->center_freq) { + priv->center_freq = 0; + _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_CENTER_FREQUENCY); + } + + if (priv->rssi) { + priv->rssi = 0; + _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_RSSI); + } + + if (priv->cinr) { + priv->cinr = 0; + _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_CINR); + } + + if (priv->tx_power) { + priv->tx_power = 0; + _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_TX_POWER); + } + + if (priv->bsid) { + g_free (priv->bsid); + priv->bsid = NULL; + _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_BSID); + } +} + +static void +state_changed_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceWimax *self = NM_DEVICE_WIMAX (device); + NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self); + NMDeviceState state; + + state = nm_device_get_state (device); + switch (state) { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + case NM_DEVICE_STATE_FAILED: + if (priv->active_nsp) { + g_object_unref (priv->active_nsp); + priv->active_nsp = NULL; + } + _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_WIMAX_ACTIVE_NSP); + clear_link_status (self); + break; + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_IP_CONFIG: + clear_link_status (self); + break; + default: + break; + } +} + +static void +register_properties (NMDeviceWimax *wimax) +{ + NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (wimax); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_WIMAX_HW_ADDRESS, &priv->hw_address }, + { NM_DEVICE_WIMAX_ACTIVE_NSP, &priv->active_nsp, NULL, NM_TYPE_WIMAX_NSP }, + { NM_DEVICE_WIMAX_CENTER_FREQUENCY, &priv->center_freq }, + { NM_DEVICE_WIMAX_RSSI, &priv->rssi }, + { NM_DEVICE_WIMAX_CINR, &priv->cinr }, + { NM_DEVICE_WIMAX_TX_POWER, &priv->tx_power }, + { NM_DEVICE_WIMAX_BSID, &priv->bsid }, + { NM_DEVICE_WIMAX_NSPS, &priv->nsps, NULL, NM_TYPE_WIMAX_NSP, "nsp" }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (wimax), + priv->proxy, + property_info); +} + +static void +nsp_removed (NMDeviceWimax *self, NMWimaxNsp *nsp) +{ + NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self); + + if (nsp == priv->active_nsp) { + g_object_unref (priv->active_nsp); + priv->active_nsp = NULL; + _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_ACTIVE_NSP); + } +} + +static void +constructed (GObject *object) +{ + NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_device_wimax_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_WIMAX); + register_properties (NM_DEVICE_WIMAX (object)); + + g_signal_connect (object, + "notify::" NM_DEVICE_STATE, + G_CALLBACK (state_changed_cb), + NULL); +} + +static void +dispose (GObject *object) +{ + NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (object); + + if (priv->hw_address) { + g_free (priv->hw_address); + priv->hw_address = NULL; + } + + if (priv->bsid) { + g_free (priv->bsid); + priv->bsid = NULL; + } + + clean_up_nsps (NM_DEVICE_WIMAX (object), FALSE); + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_device_wimax_parent_class)->dispose (object); +} + +static void +nm_device_wimax_class_init (NMDeviceWimaxClass *wimax_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (wimax_class); + NMDeviceClass *device_class = NM_DEVICE_CLASS (wimax_class); + + g_type_class_add_private (wimax_class, sizeof (NMDeviceWimaxPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; + device_class->get_hw_address = get_hw_address; + wimax_class->nsp_removed = nsp_removed; + + /* properties */ + + /** + * NMDeviceWimax:hw-address: + * + * The hardware (MAC) address of the device. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_BSID, + g_param_spec_string (NM_DEVICE_WIMAX_BSID, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDeviceWimax:nsps: + * + * List of all WiMAX Network Service Providers the device can see. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_NSPS, + g_param_spec_boxed (NM_DEVICE_WIMAX_NSPS, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* signals */ + + /** + * 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. + **/ + signals[NSP_ADDED] = + g_signal_new ("nsp-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceWimaxClass, nsp_added), + 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. + **/ + signals[NSP_REMOVED] = + g_signal_new ("nsp-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceWimaxClass, nsp_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); +} diff --git a/libnm/nm-device-wimax.h b/libnm/nm-device-wimax.h new file mode 100644 index 0000000000..1b889d95fe --- /dev/null +++ b/libnm/nm-device-wimax.h @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 - 2012 Red Hat, Inc. + * Copyright 2009 Novell, Inc. + */ + +#ifndef NM_DEVICE_WIMAX_H +#define NM_DEVICE_WIMAX_H + +#include "nm-device.h" +#include "nm-wimax-nsp.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_WIMAX (nm_device_wimax_get_type ()) +#define NM_DEVICE_WIMAX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIMAX, NMDeviceWimax)) +#define NM_DEVICE_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_WIMAX, NMDeviceWimaxClass)) +#define NM_IS_DEVICE_WIMAX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIMAX)) +#define NM_IS_DEVICE_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIMAX)) +#define NM_DEVICE_WIMAX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIMAX, NMDeviceWimaxClass)) + +/** + * NMDeviceWimaxError: + * @NM_DEVICE_WIMAX_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_WIMAX_ERROR_NOT_WIMAX_CONNECTION: the connection was not of WiMax type + * @NM_DEVICE_WIMAX_ERROR_INVALID_WIMAX_CONNECTION: the WiMax connection was invalid + * @NM_DEVICE_WIMAX_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid + * @NM_DEVICE_WIMAX_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched + */ +typedef enum { + NM_DEVICE_WIMAX_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_WIMAX_ERROR_NOT_WIMAX_CONNECTION, /*< nick=NotWimaxConnection >*/ + NM_DEVICE_WIMAX_ERROR_INVALID_WIMAX_CONNECTION, /*< nick=InvalidWimaxConnection >*/ + NM_DEVICE_WIMAX_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/ + NM_DEVICE_WIMAX_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/ +} NMDeviceWimaxError; + +#define NM_DEVICE_WIMAX_ERROR nm_device_wimax_error_quark () +GQuark nm_device_wimax_error_quark (void); + +#define NM_DEVICE_WIMAX_HW_ADDRESS "hw-address" +#define NM_DEVICE_WIMAX_ACTIVE_NSP "active-nsp" +#define NM_DEVICE_WIMAX_CENTER_FREQUENCY "center-frequency" +#define NM_DEVICE_WIMAX_RSSI "rssi" +#define NM_DEVICE_WIMAX_CINR "cinr" +#define NM_DEVICE_WIMAX_TX_POWER "tx-power" +#define NM_DEVICE_WIMAX_BSID "bsid" +#define NM_DEVICE_WIMAX_NSPS "nsps" + +typedef struct { + NMDevice parent; +} NMDeviceWimax; + +typedef struct { + NMDeviceClass parent; + + /* Signals */ + void (*nsp_added) (NMDeviceWimax *self, NMWimaxNsp *nsp); + void (*nsp_removed) (NMDeviceWimax *self, NMWimaxNsp *nsp); +} NMDeviceWimaxClass; + +GType nm_device_wimax_get_type (void); + +GObject *nm_device_wimax_new (DBusGConnection *connection, + const char *path); + +const char *nm_device_wimax_get_hw_address (NMDeviceWimax *wimax); +NMWimaxNsp *nm_device_wimax_get_active_nsp (NMDeviceWimax *wimax); +NMWimaxNsp *nm_device_wimax_get_nsp_by_path (NMDeviceWimax *wimax, + const char *path); + +const GPtrArray *nm_device_wimax_get_nsps (NMDeviceWimax *wimax); + +guint nm_device_wimax_get_center_frequency (NMDeviceWimax *self); +gint nm_device_wimax_get_rssi (NMDeviceWimax *self); +gint nm_device_wimax_get_cinr (NMDeviceWimax *self); +gint nm_device_wimax_get_tx_power (NMDeviceWimax *self); +const char * nm_device_wimax_get_bsid (NMDeviceWimax *self); + +G_END_DECLS + +#endif /* NM_DEVICE_WIMAX_H */ diff --git a/libnm/nm-device.c b/libnm/nm-device.c new file mode 100644 index 0000000000..64b5dd1861 --- /dev/null +++ b/libnm/nm-device.c @@ -0,0 +1,2310 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#include <string.h> + +#include <glib/gi18n.h> +#include <gudev/gudev.h> + +#include "NetworkManager.h" +#include "nm-device-ethernet.h" +#include "nm-device-adsl.h" +#include "nm-device-wifi.h" +#include "nm-device-modem.h" +#include "nm-device-bt.h" +#include "nm-device-olpc-mesh.h" +#include "nm-device-wimax.h" +#include "nm-device-infiniband.h" +#include "nm-device-bond.h" +#include "nm-device-team.h" +#include "nm-device-bridge.h" +#include "nm-device-vlan.h" +#include "nm-device-generic.h" +#include "nm-device.h" +#include "nm-device-private.h" +#include "nm-object-private.h" +#include "nm-object-cache.h" +#include "nm-remote-connection.h" +#include "nm-types.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" +#include "nm-utils.h" +#include "nm-dbus-helpers-private.h" + +static GType _nm_device_type_for_path (DBusGConnection *connection, + const char *path); +static void _nm_device_type_for_path_async (DBusGConnection *connection, + const char *path, + NMObjectTypeCallbackFunc callback, + gpointer user_data); +gboolean connection_compatible (NMDevice *device, NMConnection *connection, GError **error); + +G_DEFINE_TYPE_WITH_CODE (NMDevice, nm_device, NM_TYPE_OBJECT, + _nm_object_register_type_func (g_define_type_id, _nm_device_type_for_path, + _nm_device_type_for_path_async); + ) + +#define DBUS_G_TYPE_UINT_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)) + +#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *iface; + char *ip_iface; + NMDeviceType device_type; + char *udi; + char *driver; + char *driver_version; + char *firmware_version; + char *type_description; + NMDeviceCapabilities capabilities; + gboolean managed; + gboolean firmware_missing; + gboolean autoconnect; + NMIP4Config *ip4_config; + NMDHCP4Config *dhcp4_config; + NMIP6Config *ip6_config; + NMDHCP6Config *dhcp6_config; + NMDeviceState state; + NMDeviceState last_seen_state; + NMDeviceStateReason reason; + + NMActiveConnection *active_connection; + GPtrArray *available_connections; + + GUdevClient *client; + char *product, *short_product; + char *vendor, *short_vendor; + char *description, *bus_name; + + char *physical_port_id; + guint32 mtu; +} NMDevicePrivate; + +enum { + PROP_0, + PROP_INTERFACE, + PROP_UDI, + PROP_DRIVER, + PROP_DRIVER_VERSION, + PROP_FIRMWARE_VERSION, + PROP_CAPABILITIES, + PROP_MANAGED, + PROP_AUTOCONNECT, + PROP_FIRMWARE_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, + + LAST_PROP +}; + +enum { + STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/** + * nm_device_error_quark: + * + * Registers an error quark for #NMDevice if necessary. + * + * Returns: the error quark used for #NMDevice errors. + * + * Since: 0.9.10 + **/ +GQuark +nm_device_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-device-error-quark"); + return quark; +} + +static void +nm_device_init (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + priv->state = NM_DEVICE_STATE_UNKNOWN; + priv->reason = NM_DEVICE_STATE_REASON_NONE; +} + +static gboolean +demarshal_state_reason (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); + + if (!G_VALUE_HOLDS (value, DBUS_G_TYPE_UINT_STRUCT)) + return FALSE; + + dbus_g_type_struct_get (value, + 0, &priv->state, + 1, &priv->reason, + G_MAXUINT); + + _nm_object_queue_notify (object, NM_DEVICE_STATE_REASON); + return TRUE; +} + +static void +register_properties (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + const NMPropertiesInfo property_info[] = { + { NM_DEVICE_UDI, &priv->udi }, + { NM_DEVICE_INTERFACE, &priv->iface }, + { NM_DEVICE_IP_INTERFACE, &priv->ip_iface }, + { NM_DEVICE_DRIVER, &priv->driver }, + { NM_DEVICE_DRIVER_VERSION, &priv->driver_version }, + { NM_DEVICE_FIRMWARE_VERSION, &priv->firmware_version }, + { NM_DEVICE_CAPABILITIES, &priv->capabilities }, + { NM_DEVICE_MANAGED, &priv->managed }, + { NM_DEVICE_AUTOCONNECT, &priv->autoconnect }, + { NM_DEVICE_FIRMWARE_MISSING, &priv->firmware_missing }, + { NM_DEVICE_IP4_CONFIG, &priv->ip4_config, NULL, NM_TYPE_IP4_CONFIG }, + { NM_DEVICE_DHCP4_CONFIG, &priv->dhcp4_config, NULL, NM_TYPE_DHCP4_CONFIG }, + { NM_DEVICE_IP6_CONFIG, &priv->ip6_config, NULL, NM_TYPE_IP6_CONFIG }, + { NM_DEVICE_DHCP6_CONFIG, &priv->dhcp6_config, NULL, NM_TYPE_DHCP6_CONFIG }, + { NM_DEVICE_STATE, &priv->state }, + { NM_DEVICE_STATE_REASON, &priv->state, demarshal_state_reason }, + { NM_DEVICE_ACTIVE_CONNECTION, &priv->active_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, + { NM_DEVICE_AVAILABLE_CONNECTIONS, &priv->available_connections, NULL, NM_TYPE_REMOTE_CONNECTION }, + { NM_DEVICE_PHYSICAL_PORT_ID, &priv->physical_port_id }, + { NM_DEVICE_MTU, &priv->mtu }, + + /* Properties that exist in D-Bus but that we don't track */ + { "ip4-address", NULL }, + { "device-type", NULL }, + + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (device), + priv->proxy, + property_info); +} + +typedef struct { + NMDeviceState old_state; + NMDeviceState new_state; + NMDeviceStateReason reason; +} StateChangeData; + +static void +device_state_change_reloaded (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + StateChangeData *data = user_data; + NMDeviceState old_state = data->old_state; + NMDeviceState new_state = data->new_state; + NMDeviceStateReason reason = data->reason; + + g_slice_free (StateChangeData, data); + + _nm_object_reload_properties_finish (NM_OBJECT (object), result, NULL); + + /* If the device changes state several times in rapid succession, then we'll + * queue several reload_properties() calls, and there's no guarantee that + * they'll finish in the right order. In that case, only emit the signal + * for the last one. + */ + if (priv->last_seen_state != new_state) + return; + + /* Ensure that nm_device_get_state() will return the right value even if + * we haven't processed the corresponding PropertiesChanged yet. + */ + priv->state = new_state; + + g_signal_emit (self, signals[STATE_CHANGED], 0, + new_state, old_state, reason); +} + +static void +device_state_changed (DBusGProxy *proxy, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (old_state != new_state) { + StateChangeData *data; + + /* Our object-valued properties (eg, ip4_config) will still + * have their old values at this point, because NMObject is + * in the process of asynchronously reading the new values. + * Wait for that to finish before emitting the signal. + */ + priv->last_seen_state = new_state; + + data = g_slice_new (StateChangeData); + data->old_state = old_state; + data->new_state = new_state; + data->reason = reason; + _nm_object_reload_properties_async (NM_OBJECT (user_data), + device_state_change_reloaded, + data); + } +} + +static GType +_nm_device_gtype_from_dtype (NMDeviceType dtype) +{ + switch (dtype) { + case NM_DEVICE_TYPE_ETHERNET: + return NM_TYPE_DEVICE_ETHERNET; + case NM_DEVICE_TYPE_WIFI: + return NM_TYPE_DEVICE_WIFI; + case NM_DEVICE_TYPE_MODEM: + return NM_TYPE_DEVICE_MODEM; + case NM_DEVICE_TYPE_BT: + return NM_TYPE_DEVICE_BT; + case NM_DEVICE_TYPE_ADSL: + return NM_TYPE_DEVICE_ADSL; + case NM_DEVICE_TYPE_OLPC_MESH: + return NM_TYPE_DEVICE_OLPC_MESH; + case NM_DEVICE_TYPE_WIMAX: + return NM_TYPE_DEVICE_WIMAX; + case NM_DEVICE_TYPE_INFINIBAND: + return NM_TYPE_DEVICE_INFINIBAND; + case NM_DEVICE_TYPE_BOND: + return NM_TYPE_DEVICE_BOND; + case NM_DEVICE_TYPE_TEAM: + return NM_TYPE_DEVICE_TEAM; + case NM_DEVICE_TYPE_BRIDGE: + return NM_TYPE_DEVICE_BRIDGE; + case NM_DEVICE_TYPE_VLAN: + return NM_TYPE_DEVICE_VLAN; + case NM_DEVICE_TYPE_GENERIC: + return NM_TYPE_DEVICE_GENERIC; + default: + g_warning ("Unknown device type %d", dtype); + return G_TYPE_INVALID; + } +} + +static void +constructed (GObject *object) +{ + NMDevicePrivate *priv; + + G_OBJECT_CLASS (nm_device_parent_class)->constructed (object); + + priv = NM_DEVICE_GET_PRIVATE (object); + /* Catch failure of subclasses to call _nm_device_set_device_type() */ + g_warn_if_fail (priv->device_type != NM_DEVICE_TYPE_UNKNOWN); + /* Catch a subclass setting the wrong type */ + g_warn_if_fail (G_OBJECT_TYPE (object) == _nm_device_gtype_from_dtype (priv->device_type)); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE); + + register_properties (NM_DEVICE (object)); + + dbus_g_object_register_marshaller (g_cclosure_marshal_generic, + G_TYPE_NONE, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, + G_TYPE_INVALID); + + dbus_g_proxy_add_signal (priv->proxy, + "StateChanged", + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->proxy, "StateChanged", + G_CALLBACK (device_state_changed), + NM_DEVICE (object), + NULL); +} + +static void +dispose (GObject *object) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + g_clear_object (&priv->ip4_config); + g_clear_object (&priv->dhcp4_config); + g_clear_object (&priv->ip6_config); + g_clear_object (&priv->dhcp6_config); + g_clear_object (&priv->client); + g_clear_object (&priv->active_connection); + + if (priv->available_connections) { + int i; + + for (i = 0; i < priv->available_connections->len; i++) + g_object_unref (priv->available_connections->pdata[i]); + g_ptr_array_free (priv->available_connections, TRUE); + priv->available_connections = NULL; + } + + G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); + + g_free (priv->iface); + g_free (priv->ip_iface); + g_free (priv->udi); + g_free (priv->driver); + g_free (priv->driver_version); + g_free (priv->firmware_version); + g_free (priv->product); + g_free (priv->short_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_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); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_DEVICE_TYPE: + g_value_set_uint (value, nm_device_get_device_type (device)); + break; + case PROP_UDI: + g_value_set_string (value, nm_device_get_udi (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_uint (value, nm_device_get_capabilities (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_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_uint (value, nm_device_get_state (device)); + break; + case PROP_STATE_REASON: + g_value_set_boxed (value, + dbus_g_type_specialized_construct (DBUS_G_TYPE_UINT_STRUCT)); + dbus_g_type_struct_set (value, + 0, priv->state, + 1, priv->reason, + G_MAXUINT); + break; + case PROP_ACTIVE_CONNECTION: + g_value_set_object (value, nm_device_get_active_connection (device)); + break; + case PROP_AVAILABLE_CONNECTIONS: + g_value_set_boxed (value, 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; + 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_DEVICE_TYPE: + /* Construct only */ + priv->device_type = g_value_get_uint (value); + break; + 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; + } +} + +static void +nm_device_class_init (NMDeviceClass *device_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (device_class); + + g_type_class_add_private (device_class, sizeof (NMDevicePrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + device_class->connection_compatible = connection_compatible; + + /* properties */ + + /** + * NMDevice:interface: + * + * The interface of the device. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_DEVICE_TYPE, + g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, "", "", + NM_DEVICE_TYPE_UNKNOWN, G_MAXUINT32, 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. + **/ + g_object_class_install_property + (object_class, PROP_UDI, + g_param_spec_string (NM_DEVICE_UDI, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:driver: + * + * The driver of the device. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_CAPABILITIES, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:managed: + * + * Whether the device is managed by NetworkManager. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_AUTOCONNECT, + g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:firmware-missing: + * + * When %TRUE indicates the device is likely missing firmware required + * for its operation. + **/ + g_object_class_install_property + (object_class, PROP_FIRMWARE_MISSING, + g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:ip4-config: + * + * The #NMIP4Config of the device. + **/ + g_object_class_install_property + (object_class, PROP_IP4_CONFIG, + g_param_spec_object (NM_DEVICE_IP4_CONFIG, "", "", + NM_TYPE_IP4_CONFIG, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:dhcp4-config: + * + * The #NMDHCP4Config of the device. + **/ + g_object_class_install_property + (object_class, PROP_DHCP4_CONFIG, + g_param_spec_object (NM_DEVICE_DHCP4_CONFIG, "", "", + NM_TYPE_DHCP4_CONFIG, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:ip6-config: + * + * The #NMIP6Config of the device. + **/ + g_object_class_install_property + (object_class, PROP_IP6_CONFIG, + g_param_spec_object (NM_DEVICE_IP6_CONFIG, "", "", + NM_TYPE_IP6_CONFIG, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:dhcp6-config: + * + * The #NMDHCP6Config of the device. + **/ + g_object_class_install_property + (object_class, PROP_DHCP6_CONFIG, + g_param_spec_object (NM_DEVICE_DHCP6_CONFIG, "", "", + NM_TYPE_DHCP6_CONFIG, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:state: + * + * The state of the device. + **/ + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_DEVICE_STATE, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:state-reason: + * + * The state and reason of the device. + **/ + g_object_class_install_property + (object_class, PROP_STATE_REASON, + g_param_spec_boxed (NM_DEVICE_STATE_REASON, "", "", + DBUS_G_TYPE_UINT_STRUCT, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:active-connection: + * + * The #NMActiveConnection object that "owns" this device during activation. + **/ + g_object_class_install_property + (object_class, 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: + * + * The available connections (#NMRemoteConnection) of the device + * + * Since: 0.9.8 + **/ + g_object_class_install_property + (object_class, PROP_AVAILABLE_CONNECTIONS, + g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMDevice:vendor: + * + * The vendor string of the device. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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().) + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, 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. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_MTU, + g_param_spec_uint (NM_DEVICE_MTU, "", "", + 0, G_MAXUINT32, 1500, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* signals */ + + /** + * 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, + G_STRUCT_OFFSET (NMDeviceClass, state_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 3, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); +} + +/** + * _nm_device_set_device_type: + * @device: the device + * @dtype: the NM device type + * + * Sets the NM device type if it wasn't set during construction. INTERNAL + * ONLY METHOD. + **/ +void +_nm_device_set_device_type (NMDevice *device, NMDeviceType dtype) +{ + NMDevicePrivate *priv; + + g_return_if_fail (device != NULL); + g_return_if_fail (dtype != NM_DEVICE_TYPE_UNKNOWN); + + priv = NM_DEVICE_GET_PRIVATE (device); + if (priv->device_type == NM_DEVICE_TYPE_UNKNOWN) + priv->device_type = dtype; + else + g_warn_if_fail (dtype == priv->device_type); +} + +static GType +_nm_device_type_for_path (DBusGConnection *connection, + const char *path) +{ + DBusGProxy *proxy; + GError *err = NULL; + GValue value = G_VALUE_INIT; + NMDeviceType nm_dtype; + + proxy = _nm_dbus_new_proxy_for_connection (connection, path, "org.freedesktop.DBus.Properties"); + if (!proxy) { + g_warning ("%s: couldn't create D-Bus object proxy.", __func__); + return G_TYPE_INVALID; + } + + if (!dbus_g_proxy_call (proxy, + "Get", &err, + G_TYPE_STRING, NM_DBUS_INTERFACE_DEVICE, + G_TYPE_STRING, "DeviceType", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, G_TYPE_INVALID)) { + g_warning ("Error in get_property: %s\n", err->message); + g_error_free (err); + g_object_unref (proxy); + return G_TYPE_INVALID; + } + g_object_unref (proxy); + + nm_dtype = g_value_get_uint (&value); + return _nm_device_gtype_from_dtype (nm_dtype); +} + +/** + * nm_device_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the device + * + * Creates a new #NMDevice. + * + * Returns: (transfer full): a new device + **/ +GObject * +nm_device_new (DBusGConnection *connection, const char *path) +{ + GType dtype; + NMDevice *device = NULL; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + dtype = _nm_device_type_for_path (connection, path); + if (dtype == G_TYPE_INVALID) + return NULL; + + device = (NMDevice *) g_object_new (dtype, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + _nm_object_ensure_inited (NM_OBJECT (device)); + return G_OBJECT (device); +} + +typedef struct { + DBusGConnection *connection; + NMObjectTypeCallbackFunc callback; + gpointer user_data; +} NMDeviceAsyncData; + +static void +async_got_type (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMDeviceAsyncData *async_data = user_data; + GValue value = G_VALUE_INIT; + const char *path = dbus_g_proxy_get_path (proxy); + GError *error = NULL; + GType type; + + if (dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + NMDeviceType dtype; + + dtype = g_value_get_uint (&value); + type = _nm_device_gtype_from_dtype (dtype); + } else { + g_warning ("%s: could not read properties for %s: %s", __func__, path, error->message); + g_error_free (error); + type = G_TYPE_INVALID; + } + + async_data->callback (type, async_data->user_data); + g_object_unref (proxy); + g_slice_free (NMDeviceAsyncData, async_data); +} + +static void +_nm_device_type_for_path_async (DBusGConnection *connection, + const char *path, + NMObjectTypeCallbackFunc callback, + gpointer user_data) +{ + NMDeviceAsyncData *async_data; + DBusGProxy *proxy; + + async_data = g_slice_new (NMDeviceAsyncData); + async_data->connection = connection; + async_data->callback = callback; + async_data->user_data = user_data; + + proxy = _nm_dbus_new_proxy_for_connection (connection, path, "org.freedesktop.DBus.Properties"); + dbus_g_proxy_begin_call (proxy, "Get", + async_got_type, async_data, NULL, + G_TYPE_STRING, NM_DBUS_INTERFACE_DEVICE, + G_TYPE_STRING, "DeviceType", + G_TYPE_INVALID); +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->iface; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->ip_iface; +} + +/** + * 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 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->udi; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return 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. + * + * Since: 0.9.10 + **/ +const char * +nm_device_get_type_description (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + const char *desc, *typename; + + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + if (priv->type_description) + return 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; + priv->type_description = g_ascii_strdown (typename, -1); + + return priv->type_description; +} + +/** + * 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. + * + * Since: 0.9.10 + **/ +const char * +nm_device_get_hw_address (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + if (NM_DEVICE_GET_CLASS (device)->get_hw_address) + return NM_DEVICE_GET_CLASS (device)->get_hw_address (device); + + return NULL; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + 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. + **/ +void +nm_device_set_autoconnect (NMDevice *device, gboolean autoconnect) +{ + GValue value = G_VALUE_INIT; + + g_return_if_fail (NM_IS_DEVICE (device)); + + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, autoconnect); + + + NM_DEVICE_GET_PRIVATE (device)->autoconnect = autoconnect; + + _nm_object_set_property (NM_OBJECT (device), + NM_DBUS_INTERFACE_DEVICE, + "Autoconnect", + &value); +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->firmware_missing; +} + +/** + * nm_device_get_ip4_config: + * @device: a #NMDevice + * + * Gets the current #NMIP4Config associated with the #NMDevice. + * + * Note that as of NetworkManager 0.9.10, you can alternatively use + * nm_active_connection_get_ip4_config(), which also works with VPN + * connections. + * + * Returns: (transfer none): the #NMIP4Config or %NULL if the device is not activated. + **/ +NMIP4Config * +nm_device_get_ip4_config (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->ip4_config; +} + +/** + * nm_device_get_dhcp4_config: + * @device: a #NMDevice + * + * Gets the current #NMDHCP4Config associated with the #NMDevice. + * + * Note that as of NetworkManager 0.9.10, you can alternatively use + * nm_active_connection_get_dhcp4_config(), which also works with VPN + * connections. + * + * Returns: (transfer none): the #NMDHCP4Config or %NULL if the device is not activated or not + * using DHCP. + **/ +NMDHCP4Config * +nm_device_get_dhcp4_config (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->dhcp4_config; +} + +/** + * nm_device_get_ip6_config: + * @device: a #NMDevice + * + * Gets the current #NMIP6Config associated with the #NMDevice. + * + * Note that as of NetworkManager 0.9.10, you can alternatively use + * nm_active_connection_get_ip6_config(), which also works with VPN + * connections. + * + * Returns: (transfer none): the #NMIP6Config or %NULL if the device is not activated. + **/ +NMIP6Config * +nm_device_get_ip6_config (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->ip6_config; +} + +/** + * nm_device_get_dhcp6_config: + * @device: a #NMDevice + * + * Gets the current #NMDHCP6Config associated with the #NMDevice. + * + * Note that as of NetworkManager 0.9.10, you can alternatively use + * nm_active_connection_get_dhcp6_config(), which also works with VPN + * connections. + * + * Returns: (transfer none): the #NMDHCP6Config or %NULL if the device is not activated or not + * using DHCP. + **/ +NMDHCP6Config * +nm_device_get_dhcp6_config (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->dhcp6_config; +} + +/** + * 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); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->state; +} + +/** + * nm_device_get_state_reason: + * @device: a #NMDevice + * @reason: (out) (allow-none): location to store reason (#NMDeviceStateReason), or %NULL + * + * Gets the current #NMDevice state (return value) and the reason for entering + * the state (@reason argument). + * + * Returns: the current device state + **/ +NMDeviceState +nm_device_get_state_reason (NMDevice *device, NMDeviceStateReason *reason) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN); + + _nm_object_ensure_inited (NM_OBJECT (device)); + if (reason) + *reason = NM_DEVICE_GET_PRIVATE (device)->reason; + return NM_DEVICE_GET_PRIVATE (device)->state; +} + +/** + * 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) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->active_connection; +} + +/** + * 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. + * + * Since: 0.9.8 + **/ +const GPtrArray * +nm_device_get_available_connections (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return handle_ptr_array_return (NM_DEVICE_GET_PRIVATE (device)->available_connections); +} + +static char * +get_decoded_property (GUdevDevice *device, const char *property) +{ + const char *orig, *p; + char *unescaped, *n; + guint len; + + p = orig = g_udev_device_get_property (device, property); + if (!orig) + return NULL; + + len = strlen (orig); + n = unescaped = g_malloc0 (len + 1); + while (*p) { + if ((len >= 4) && (*p == '\\') && (*(p+1) == 'x')) { + *n++ = (char) nm_utils_hex2byte (p + 2); + p += 4; + len -= 4; + } else { + *n++ = *p++; + len--; + } + } + + return unescaped; +} + +static gboolean +ensure_udev_client (NMDevice *device) +{ + static const char *const subsys[3] = { "net", "tty", NULL }; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + if (!priv->client) + priv->client = g_udev_client_new (subsys); + + return priv->client != 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); + GUdevDevice *udev_device = NULL, *tmpdev, *olddev; + const char *ifname; + guint32 count = 0; + char *enc_value = NULL, *db_value = NULL; + + if (!ensure_udev_client (device)) + return NULL; + + ifname = nm_device_get_iface (device); + if (!ifname) + return NULL; + + udev_device = g_udev_client_query_by_subsystem_and_name (priv->client, "net", ifname); + if (!udev_device) + udev_device = g_udev_client_query_by_subsystem_and_name (priv->client, "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. + */ + + /* Ref the device again because we have to unref it each iteration, + * as g_udev_device_get_parent() returns a ref-ed object. + */ + tmpdev = g_object_ref (udev_device); + while ((count++ < 3) && tmpdev && !enc_value) { + if (!enc_value) + enc_value = get_decoded_property (tmpdev, enc_prop); + if (!db_value) + db_value = g_strdup (g_udev_device_get_property (tmpdev, db_prop)); + + olddev = tmpdev; + tmpdev = g_udev_device_get_parent (tmpdev); + g_object_unref (olddev); + } + + /* Unref the last device if we found what we needed before running out + * of parents. + */ + if (tmpdev) + g_object_unref (tmpdev); + + /* Balance the initial g_udev_client_query_by_subsystem_and_name() */ + g_object_unref (udev_device); + + /* Prefer the encoded value which comes directly from the device + * over the hwdata database value. + */ + if (enc_value) { + g_free (db_value); + return enc_value; + } + + return db_value; +} + +/** + * 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. + **/ +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 (device, "ID_MODEL_ENC", "ID_MODEL_FROM_DATABASE"); + if (!priv->product) { + /* Sometimes ID_PRODUCT_FROM_DATABASE is used? */ + priv->product = _get_udev_property (device, "ID_MODEL_ENC", "ID_PRODUCT_FROM_DATABASE"); + } + _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_PRODUCT); + } + 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. + **/ +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 (device, "ID_VENDOR_ENC", "ID_VENDOR_FROM_DATABASE"); + _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_VENDOR); + } + return priv->vendor; +} + +static const char * const ignored_words[] = { + "Semiconductor", + "Components", + "Corporation", + "Communications", + "Company", + "Corp.", + "Corp", + "Co.", + "Inc.", + "Inc", + "Incorporated", + "Ltd.", + "Limited.", + "Intel?", + "chipset", + "adapter", + "[hex]", + "NDIS", + "Module", + NULL +}; + +static const char * const ignored_phrases[] = { + "Multiprotocol MAC/baseband processor", + "Wireless LAN Controller", + "Wireless LAN Adapter", + "Wireless Adapter", + "Network Connection", + "Wireless Cardbus Adapter", + "Wireless CardBus Adapter", + "54 Mbps Wireless PC Card", + "Wireless PC Card", + "Wireless PC", + "PC Card with XJACK(r) Antenna", + "Wireless cardbus", + "Wireless LAN PC Card", + "Technology Group Ltd.", + "Communication S.p.A.", + "Business Mobile Networks BV", + "Mobile Broadband Minicard Composite Device", + "Mobile Communications AB", + "(PC-Suite Mode)", + NULL +}; + +static char * +fixup_desc_string (const char *desc) +{ + char *p, *temp; + char **words, **item; + GString *str; + int i; + + if (!desc) + return NULL; + + p = temp = g_strdup (desc); + while (*p) { + if (*p == '_' || *p == ',') + *p = ' '; + p++; + } + + /* Attempt to shorten ID by ignoring certain phrases */ + for (i = 0; ignored_phrases[i]; i++) { + p = strstr (temp, ignored_phrases[i]); + if (p) { + guint32 ignored_len = strlen (ignored_phrases[i]); + + memmove (p, p + ignored_len, strlen (p + ignored_len) + 1); /* +1 for the \0 */ + } + } + + /* Attempt to shorten ID by ignoring certain individual words */ + words = g_strsplit (temp, " ", 0); + str = g_string_new_len (NULL, strlen (temp)); + g_free (temp); + + for (item = words; *item; item++) { + gboolean ignore = FALSE; + + if (**item == '\0') + continue; + + for (i = 0; ignored_words[i]; i++) { + if (!strcmp (*item, ignored_words[i])) { + ignore = TRUE; + break; + } + } + + if (!ignore) { + if (str->len) + g_string_append_c (str, ' '); + g_string_append (str, *item); + } + } + g_strfreev (words); + + temp = str->str; + g_string_free (str, FALSE); + + return temp; +} + +static void +get_description (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + const char *dev_product; + const char *dev_vendor; + char *pdown; + char *vdown; + GString *str; + + dev_product = nm_device_get_product (device); + priv->short_product = fixup_desc_string (dev_product); + + dev_vendor = nm_device_get_vendor (device); + priv->short_vendor = fixup_desc_string (dev_vendor); + + if (!dev_product || !dev_vendor) { + priv->description = g_strdup (nm_device_get_iface (device)); + return; + } + + str = g_string_new_len (NULL, strlen (priv->short_vendor) + strlen (priv->short_product) + 1); + + /* Another quick hack; if all of the fixed up vendor string + * is found in product, ignore the vendor. + */ + pdown = g_ascii_strdown (priv->short_product, -1); + vdown = g_ascii_strdown (priv->short_vendor, -1); + if (!strstr (pdown, vdown)) { + g_string_append (str, priv->short_vendor); + g_string_append_c (str, ' '); + } + g_free (pdown); + g_free (vdown); + + g_string_append (str, priv->short_product); + + priv->description = g_string_free (str, FALSE); +} + +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) + get_description (device); + + return priv->short_vendor; +} + +/** + * nm_device_get_description: + * @device: an #NMDevice + * + * Gets a description of @device, incorporating the results of + * nm_device_get_short_vendor() and nm_device_get_short_product(). + * + * Returns: a description of @device. If either the vendor or the + * product name is unknown, this returns the interface name. + * + * Since: 0.9.10 + */ +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) + get_description (device); + + return priv->description; +} + +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_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"); + default: + 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); + GUdevDevice *udevice; + const char *ifname, *bus; + + if (priv->bus_name) + goto out; + + if (!ensure_udev_client (device)) + return NULL; + + ifname = nm_device_get_iface (device); + if (!ifname) + return NULL; + + udevice = g_udev_client_query_by_subsystem_and_name (priv->client, "net", ifname); + if (!udevice) + udevice = g_udev_client_query_by_subsystem_and_name (priv->client, "tty", ifname); + if (!udevice) + return NULL; + + bus = g_udev_device_get_property (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 (""); + } + +out: + if (*priv->bus_name) + return priv->bus_name; + else + return NULL; +} + +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 + * + * Since: 0.9.10 + */ +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; + + /* 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. + * + * Since: 0.9.10 + **/ +const char * +nm_device_get_physical_port_id (NMDevice *device) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + priv = NM_DEVICE_GET_PRIVATE (device); + + _nm_object_ensure_inited (NM_OBJECT (device)); + if (priv->physical_port_id && *priv->physical_port_id) + return priv->physical_port_id; + else + return NULL; +} + +/** + * nm_device_get_mtu: + * @device: a #NMDevice + * + * Gets the MTU of the #NMDevice. + * + * Returns: the MTU of the device. + * + * Since: 0.9.10 + **/ +guint32 +nm_device_get_mtu (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), 0); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->mtu; +} + +/** + * 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. + * + * Since: 1.0 + **/ +gboolean +nm_device_is_software (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & NM_DEVICE_CAP_IS_SOFTWARE); +} + +typedef struct { + NMDevice *device; + NMDeviceCallbackFn fn; + gpointer user_data; + const char *method; +} DeviceCallbackInfo; + +static void +device_operation_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + DeviceCallbackInfo *info = user_data; + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_INVALID); + if (info->fn) + info->fn (info->device, error, info->user_data); + else if (error) { + g_warning ("%s: device %s %s failed: (%d) %s", + __func__, + nm_object_get_path (NM_OBJECT (info->device)), + info->method, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + } + g_clear_error (&error); + + g_object_unref (info->device); + g_slice_free (DeviceCallbackInfo, info); +} + +/** + * nm_device_disconnect: + * @device: a #NMDevice + * @callback: (scope async) (allow-none): callback to be called when disconnect + * operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Disconnects 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 (NMDevice *device, + NMDeviceCallbackFn callback, + gpointer user_data) +{ + DeviceCallbackInfo *info; + + g_return_if_fail (NM_IS_DEVICE (device)); + + info = g_slice_new (DeviceCallbackInfo); + info->fn = callback; + info->user_data = user_data; + info->method = "Disconnect"; + info->device = g_object_ref (device); + + dbus_g_proxy_begin_call (NM_DEVICE_GET_PRIVATE (device)->proxy, "Disconnect", + device_operation_cb, info, NULL, + G_TYPE_INVALID); +} + +/** + * nm_device_delete: + * @device: a #NMDevice + * @callback: (scope async) (allow-none): callback to be called when delete + * operation completes + * @user_data: (closure): caller-specific data passed to @callback + * + * Deletes the software device. Hardware devices can't be deleted. + * + * Since: 1.0 + **/ +void +nm_device_delete (NMDevice *device, + NMDeviceCallbackFn callback, + gpointer user_data) +{ + DeviceCallbackInfo *info; + + g_return_if_fail (NM_IS_DEVICE (device)); + + info = g_slice_new (DeviceCallbackInfo); + info->fn = callback; + info->user_data = user_data; + info->method = "Delete"; + info->device = g_object_ref (device); + + dbus_g_proxy_begin_call (NM_DEVICE_GET_PRIVATE (device)->proxy, "Delete", + device_operation_cb, info, NULL, + G_TYPE_INVALID); +} + +/** + * 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); +} + +gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + const char *config_iface, *device_iface; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + config_iface = nm_setting_connection_get_interface_name (s_con); + 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_INTERFACE_MISMATCH, + "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): a list of #NMConnection objects to filter + * + * Filters a given list of connections for a given #NMDevice object and return + * connections which may be activated with the device. For example if @device + * is a Wi-Fi device that supports only WEP encryption, the returned list will + * contain any Wi-Fi connections in @connections that allow connection to + * unencrypted or WEP-enabled SSIDs. The returned list 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_remote_settings_list_connections(). + * + * Returns: (transfer container) (element-type NMConnection): a + * list of #NMConnection objects that could be activated with the given @device. + * The elements of the list are owned by their creator and should not be freed + * by the caller, but the returned list itself is owned by the caller and should + * be freed with g_slist_free() when it is no longer required. + **/ +GSList * +nm_device_filter_connections (NMDevice *device, const GSList *connections) +{ + GSList *filtered = NULL; + const GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + /* Connection applies to this device */ + if (nm_device_connection_valid (device, candidate)) + filtered = g_slist_prepend (filtered, candidate); + } + + return g_slist_reverse (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 + * + * Since: 0.9.10 + */ +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); +} diff --git a/libnm/nm-device.h b/libnm/nm-device.h new file mode 100644 index 0000000000..707fddf82a --- /dev/null +++ b/libnm/nm-device.h @@ -0,0 +1,186 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2013 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_H +#define NM_DEVICE_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "nm-object.h" +#include "NetworkManager.h" +#include "nm-ip4-config.h" +#include "nm-dhcp4-config.h" +#include "nm-ip6-config.h" +#include "nm-dhcp6-config.h" +#include "nm-connection.h" +#include "nm-active-connection.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE (nm_device_get_type ()) +#define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice)) +#define NM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE, NMDeviceClass)) +#define NM_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE)) +#define NM_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE)) +#define NM_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE, NMDeviceClass)) + +/** + * NMDeviceError: + * @NM_DEVICE_ERROR_UNKNOWN: unknown or unclassified error + * @NM_DEVICE_ERROR_INTERFACE_MISMATCH: the interface names of the connection and the + * device mismatched + */ +typedef enum { + NM_DEVICE_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_DEVICE_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/ +} NMDeviceError; + +#define NM_DEVICE_ERROR nm_device_error_quark () +NM_AVAILABLE_IN_0_9_10 +GQuark nm_device_error_quark (void); + +#define NM_DEVICE_DEVICE_TYPE "device-type" +#define NM_DEVICE_UDI "udi" +#define NM_DEVICE_INTERFACE "interface" +#define NM_DEVICE_IP_INTERFACE "ip-interface" +#define NM_DEVICE_DRIVER "driver" +#define NM_DEVICE_DRIVER_VERSION "driver-version" +#define NM_DEVICE_FIRMWARE_VERSION "firmware-version" +#define NM_DEVICE_CAPABILITIES "capabilities" +#define NM_DEVICE_MANAGED "managed" +#define NM_DEVICE_AUTOCONNECT "autoconnect" +#define NM_DEVICE_FIRMWARE_MISSING "firmware-missing" +#define NM_DEVICE_IP4_CONFIG "ip4-config" +#define NM_DEVICE_DHCP4_CONFIG "dhcp4-config" +#define NM_DEVICE_IP6_CONFIG "ip6-config" +#define NM_DEVICE_DHCP6_CONFIG "dhcp6-config" +#define NM_DEVICE_STATE "state" +#define NM_DEVICE_STATE_REASON "state-reason" +#define NM_DEVICE_ACTIVE_CONNECTION "active-connection" +#define NM_DEVICE_AVAILABLE_CONNECTIONS "available-connections" +#define NM_DEVICE_VENDOR "vendor" +#define NM_DEVICE_PRODUCT "product" +#define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id" +#define NM_DEVICE_MTU "mtu" + +typedef struct { + NMObject parent; +} NMDevice; + +typedef struct { + NMObjectClass parent; + + /* Signals */ + void (*state_changed) (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason); + + gboolean (*connection_compatible) (NMDevice *device, + NMConnection *connection, + GError **error); + + const char * (*get_type_description) (NMDevice *device); + const char * (*get_hw_address) (NMDevice *device); + + GType (*get_setting_type) (NMDevice *device); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); +} NMDeviceClass; + +GType nm_device_get_type (void); + +GObject * nm_device_new (DBusGConnection *connection, const char *path); + +const char * nm_device_get_iface (NMDevice *device); +const char * nm_device_get_ip_iface (NMDevice *device); +NMDeviceType nm_device_get_device_type (NMDevice *device); +const char * nm_device_get_udi (NMDevice *device); +const char * nm_device_get_driver (NMDevice *device); +const char * nm_device_get_driver_version (NMDevice *device); +const char * nm_device_get_firmware_version (NMDevice *device); +NM_AVAILABLE_IN_0_9_10 +const char * nm_device_get_type_description (NMDevice *device); +NM_AVAILABLE_IN_0_9_10 +const char * nm_device_get_hw_address (NMDevice *device); +NMDeviceCapabilities nm_device_get_capabilities (NMDevice *device); +gboolean nm_device_get_managed (NMDevice *device); +gboolean nm_device_get_autoconnect (NMDevice *device); +void nm_device_set_autoconnect (NMDevice *device, gboolean autoconnect); +gboolean nm_device_get_firmware_missing (NMDevice *device); +NMIP4Config * nm_device_get_ip4_config (NMDevice *device); +NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *device); +NMIP6Config * nm_device_get_ip6_config (NMDevice *device); +NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *device); +NMDeviceState nm_device_get_state (NMDevice *device); +NMDeviceState nm_device_get_state_reason (NMDevice *device, NMDeviceStateReason *reason); +NMActiveConnection * nm_device_get_active_connection(NMDevice *device); +const GPtrArray * nm_device_get_available_connections(NMDevice *device); +NM_AVAILABLE_IN_0_9_10 +const char * nm_device_get_physical_port_id (NMDevice *device); +NM_AVAILABLE_IN_0_9_10 +guint32 nm_device_get_mtu (NMDevice *device); +NM_AVAILABLE_IN_1_0 +gboolean nm_device_is_software (NMDevice *device); + +const char * nm_device_get_product (NMDevice *device); +const char * nm_device_get_vendor (NMDevice *device); +NM_AVAILABLE_IN_0_9_10 +const char * nm_device_get_description (NMDevice *device); +NM_AVAILABLE_IN_0_9_10 +char ** nm_device_disambiguate_names (NMDevice **devices, + int num_devices); + +typedef void (*NMDeviceCallbackFn) (NMDevice *device, GError *error, gpointer user_data); + +void nm_device_disconnect (NMDevice *device, + NMDeviceCallbackFn callback, + gpointer user_data); + +NM_AVAILABLE_IN_1_0 +void nm_device_delete (NMDevice *device, + NMDeviceCallbackFn callback, + gpointer user_data); + +GSList * nm_device_filter_connections (NMDevice *device, + const GSList *connections); + +gboolean nm_device_connection_valid (NMDevice *device, + NMConnection *connection); + +gboolean nm_device_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error); + +NM_AVAILABLE_IN_0_9_10 +GType nm_device_get_setting_type (NMDevice *device); + +/* Deprecated */ +NM_DEPRECATED_IN_1_0 +typedef void (*NMDeviceDeactivateFn) (NMDevice *device, GError *error, gpointer user_data); + +G_END_DECLS + +#endif /* NM_DEVICE_H */ diff --git a/libnm/nm-dhcp4-config.c b/libnm/nm-dhcp4-config.c new file mode 100644 index 0000000000..4fccee9034 --- /dev/null +++ b/libnm/nm-dhcp4-config.c @@ -0,0 +1,215 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2011 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#include <string.h> + +#include "nm-dhcp4-config.h" +#include "NetworkManager.h" +#include "nm-types-private.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +G_DEFINE_TYPE (NMDHCP4Config, nm_dhcp4_config, NM_TYPE_OBJECT) + +#define NM_DHCP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigPrivate)) + +typedef struct { + DBusGProxy *proxy; + + GHashTable *options; +} NMDHCP4ConfigPrivate; + +enum { + PROP_0, + PROP_OPTIONS, + + LAST_PROP +}; + +static void +nm_dhcp4_config_init (NMDHCP4Config *config) +{ +} + +static gboolean +demarshal_dhcp4_options (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object); + GHashTable *new_options; + GHashTableIter iter; + const char *key; + GValue *opt; + + g_hash_table_remove_all (priv->options); + + new_options = g_value_get_boxed (value); + if (new_options) { + g_hash_table_iter_init (&iter, new_options); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &opt)) + g_hash_table_insert (priv->options, g_strdup (key), g_value_dup_string (opt)); + } + + _nm_object_queue_notify (object, NM_DHCP4_CONFIG_OPTIONS); + return TRUE; +} + +static void +register_properties (NMDHCP4Config *config) +{ + NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (config); + const NMPropertiesInfo property_info[] = { + { NM_DHCP4_CONFIG_OPTIONS, &priv->options, demarshal_dhcp4_options }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (config), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_dhcp4_config_parent_class)->constructed (object); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DHCP4_CONFIG); + register_properties (NM_DHCP4_CONFIG (object)); +} + +static void +finalize (GObject *object) +{ + NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object); + + if (priv->options) + g_hash_table_destroy (priv->options); + + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_dhcp4_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDHCP4Config *self = NM_DHCP4_CONFIG (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_OPTIONS: + g_value_set_boxed (value, nm_dhcp4_config_get_options (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_dhcp4_config_class_init (NMDHCP4ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMDHCP4ConfigPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMDHCP4Config:options: + * + * The #GHashTable containing options of the configuration. + * + * Type: GLib.HashTable(utf8,GObject.Value) + **/ + g_object_class_install_property + (object_class, PROP_OPTIONS, + g_param_spec_boxed (NM_DHCP4_CONFIG_OPTIONS, "", "", + G_TYPE_HASH_TABLE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +/** + * nm_dhcp4_config_new: + * @connection: the #DBusGConnection + * @object_path: the DBus object path of the device + * + * Creates a new #NMDHCP4Config. + * + * Returns: (transfer full): a new configuration + **/ +GObject * +nm_dhcp4_config_new (DBusGConnection *connection, const char *object_path) +{ + return (GObject *) g_object_new (NM_TYPE_DHCP4_CONFIG, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, object_path, + NULL); +} + +/** + * nm_dhcp4_config_get_options: + * @config: a #NMDHCP4Config + * + * Gets all the options contained in the configuration. + * + * Returns: (transfer none) (element-type utf8 GObject.Value): the #GHashTable containing strings for keys and values. + * This is the internal copy used by the configuration, and must not be modified. + **/ +GHashTable * +nm_dhcp4_config_get_options (NMDHCP4Config *config) +{ + g_return_val_if_fail (NM_IS_DHCP4_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_DHCP4_CONFIG_GET_PRIVATE (config)->options; +} + +/** + * nm_dhcp4_config_get_one_option: + * @config: a #NMDHCP4Config + * @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_dhcp4_config_get_one_option (NMDHCP4Config *config, const char *option) +{ + g_return_val_if_fail (NM_IS_DHCP4_CONFIG (config), NULL); + + return g_hash_table_lookup (nm_dhcp4_config_get_options (config), option); +} diff --git a/libnm/nm-dhcp4-config.h b/libnm/nm-dhcp4-config.h new file mode 100644 index 0000000000..c64e9a9c8d --- /dev/null +++ b/libnm/nm-dhcp4-config.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#ifndef NM_DHCP4_CONFIG_H +#define NM_DHCP4_CONFIG_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "nm-object.h" + +G_BEGIN_DECLS + +#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)) + +typedef struct { + NMObject parent; +} NMDHCP4Config; + +typedef struct { + NMObjectClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDHCP4ConfigClass; + +#define NM_DHCP4_CONFIG_OPTIONS "options" + +GType nm_dhcp4_config_get_type (void); + +GObject *nm_dhcp4_config_new (DBusGConnection *connection, const char *object_path); + +GHashTable * nm_dhcp4_config_get_options (NMDHCP4Config *config); + +const char * nm_dhcp4_config_get_one_option (NMDHCP4Config *config, const char *option); + +G_END_DECLS + +#endif /* NM_DHCP4_CONFIG_H */ diff --git a/libnm/nm-dhcp6-config.c b/libnm/nm-dhcp6-config.c new file mode 100644 index 0000000000..32cc3b4755 --- /dev/null +++ b/libnm/nm-dhcp6-config.c @@ -0,0 +1,215 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2011 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#include <string.h> + +#include "nm-dhcp6-config.h" +#include "NetworkManager.h" +#include "nm-types-private.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +G_DEFINE_TYPE (NMDHCP6Config, nm_dhcp6_config, NM_TYPE_OBJECT) + +#define NM_DHCP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigPrivate)) + +typedef struct { + DBusGProxy *proxy; + + GHashTable *options; +} NMDHCP6ConfigPrivate; + +enum { + PROP_0, + PROP_OPTIONS, + + LAST_PROP +}; + +static void +nm_dhcp6_config_init (NMDHCP6Config *config) +{ +} + +static gboolean +demarshal_dhcp6_options (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + GHashTable *new_options; + GHashTableIter iter; + const char *key; + GValue *opt; + + g_hash_table_remove_all (priv->options); + + new_options = g_value_get_boxed (value); + if (new_options) { + g_hash_table_iter_init (&iter, new_options); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &opt)) + g_hash_table_insert (priv->options, g_strdup (key), g_value_dup_string (opt)); + } + + _nm_object_queue_notify (object, NM_DHCP6_CONFIG_OPTIONS); + return TRUE; +} + +static void +register_properties (NMDHCP6Config *config) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (config); + const NMPropertiesInfo property_info[] = { + { NM_DHCP6_CONFIG_OPTIONS, &priv->options, demarshal_dhcp6_options }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (config), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->constructed (object); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DHCP6_CONFIG); + register_properties (NM_DHCP6_CONFIG (object)); +} + +static void +finalize (GObject *object) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + + if (priv->options) + g_hash_table_destroy (priv->options); + + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDHCP6Config *self = NM_DHCP6_CONFIG (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_OPTIONS: + g_value_set_boxed (value, nm_dhcp6_config_get_options (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_dhcp6_config_class_init (NMDHCP6ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMDHCP6ConfigPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMDHCP6Config:options: + * + * The #GHashTable containing options of the configuration. + * + * Type: GLib.HashTable(utf8,GObject.Value) + **/ + g_object_class_install_property + (object_class, PROP_OPTIONS, + g_param_spec_boxed (NM_DHCP6_CONFIG_OPTIONS, "", "", + G_TYPE_HASH_TABLE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +/** + * nm_dhcp6_config_new: + * @connection: the #DBusGConnection + * @object_path: the DBus object path of the device + * + * Creates a new #NMDHCP6Config. + * + * Returns: (transfer full): a new configuration + **/ +GObject * +nm_dhcp6_config_new (DBusGConnection *connection, const char *object_path) +{ + return (GObject *) g_object_new (NM_TYPE_DHCP6_CONFIG, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, object_path, + NULL); +} + +/** + * nm_dhcp6_config_get_options: + * @config: a #NMDHCP6Config + * + * Gets all the options contained in the configuration. + * + * Returns: (transfer none) (element-type utf8 GObject.Value): the #GHashTable containing strings for keys and values. + * This is the internal copy used by the configuration, and must not be modified. + **/ +GHashTable * +nm_dhcp6_config_get_options (NMDHCP6Config *config) +{ + g_return_val_if_fail (NM_IS_DHCP6_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_DHCP6_CONFIG_GET_PRIVATE (config)->options; +} + +/** + * nm_dhcp6_config_get_one_option: + * @config: a #NMDHCP6Config + * @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_dhcp6_config_get_one_option (NMDHCP6Config *config, const char *option) +{ + g_return_val_if_fail (NM_IS_DHCP6_CONFIG (config), NULL); + + return g_hash_table_lookup (nm_dhcp6_config_get_options (config), option); +} diff --git a/libnm/nm-dhcp6-config.h b/libnm/nm-dhcp6-config.h new file mode 100644 index 0000000000..939b3fdbe4 --- /dev/null +++ b/libnm/nm-dhcp6-config.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2010 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#ifndef NM_DHCP6_CONFIG_H +#define NM_DHCP6_CONFIG_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "nm-object.h" + +G_BEGIN_DECLS + +#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)) + +typedef struct { + NMObject parent; +} NMDHCP6Config; + +typedef struct { + NMObjectClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDHCP6ConfigClass; + +#define NM_DHCP6_CONFIG_OPTIONS "options" + +GType nm_dhcp6_config_get_type (void); + +GObject *nm_dhcp6_config_new (DBusGConnection *connection, const char *object_path); + +GHashTable * nm_dhcp6_config_get_options (NMDHCP6Config *config); + +const char * nm_dhcp6_config_get_one_option (NMDHCP6Config *config, const char *option); + +G_END_DECLS + +#endif /* NM_DHCP6_CONFIG_H */ diff --git a/libnm/nm-ip4-config.c b/libnm/nm-ip4-config.c new file mode 100644 index 0000000000..3ac75254cf --- /dev/null +++ b/libnm/nm-ip4-config.c @@ -0,0 +1,467 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2011 Novell, Inc. + * Copyright 2008 Red Hat, Inc. + */ + +#include <string.h> + +#include <nm-setting-ip4-config.h> +#include "nm-ip4-config.h" +#include "NetworkManager.h" +#include "nm-types-private.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_OBJECT) + +#define NM_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP4_CONFIG, NMIP4ConfigPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *gateway; + GSList *addresses; + GSList *routes; + GArray *nameservers; + GPtrArray *domains; + GPtrArray *searches; + GArray *wins; +} NMIP4ConfigPrivate; + +enum { + PROP_0, + PROP_GATEWAY, + PROP_ADDRESSES, + PROP_ROUTES, + PROP_NAMESERVERS, + PROP_DOMAINS, + PROP_SEARCHES, + PROP_WINS_SERVERS, + + LAST_PROP +}; + +static void +nm_ip4_config_init (NMIP4Config *config) +{ +} + +static gboolean +demarshal_ip4_address_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object); + + g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); + priv->addresses = NULL; + + priv->addresses = nm_utils_ip4_addresses_from_gvalue (value); + _nm_object_queue_notify (object, NM_IP4_CONFIG_ADDRESSES); + + return TRUE; +} + +static gboolean +demarshal_ip4_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + if (!_nm_uint_array_demarshal (value, (GArray **) field)) + return FALSE; + + if (!strcmp (pspec->name, NM_IP4_CONFIG_NAMESERVERS)) + _nm_object_queue_notify (object, NM_IP4_CONFIG_NAMESERVERS); + else if (!strcmp (pspec->name, NM_IP4_CONFIG_WINS_SERVERS)) + _nm_object_queue_notify (object, NM_IP4_CONFIG_WINS_SERVERS); + + return TRUE; +} + +static gboolean +demarshal_string_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + if (!_nm_string_array_demarshal (value, (GPtrArray **) field)) + return FALSE; + + _nm_object_queue_notify (object, pspec->name); + return TRUE; +} + +static gboolean +demarshal_ip4_routes_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object); + + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); + priv->routes = NULL; + + priv->routes = nm_utils_ip4_routes_from_gvalue (value); + _nm_object_queue_notify (object, NM_IP4_CONFIG_ROUTES); + + return TRUE; +} + +static void +register_properties (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMPropertiesInfo property_info[] = { + { NM_IP4_CONFIG_GATEWAY, &priv->gateway, }, + { NM_IP4_CONFIG_ADDRESSES, &priv->addresses, demarshal_ip4_address_array }, + { NM_IP4_CONFIG_ROUTES, &priv->routes, demarshal_ip4_routes_array }, + { NM_IP4_CONFIG_NAMESERVERS, &priv->nameservers, demarshal_ip4_array }, + { NM_IP4_CONFIG_DOMAINS, &priv->domains, demarshal_string_array }, + { NM_IP4_CONFIG_SEARCHES, &priv->searches, demarshal_string_array }, + { NM_IP4_CONFIG_WINS_SERVERS, &priv->wins, demarshal_ip4_array }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (config), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_ip4_config_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_IP4_CONFIG); + register_properties (NM_IP4_CONFIG (object)); +} + +static void +finalize (GObject *object) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object); + + g_free (priv->gateway); + + g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); + + if (priv->nameservers) + g_array_free (priv->nameservers, TRUE); + + if (priv->wins) + g_array_free (priv->wins, TRUE); + + if (priv->domains) { + g_ptr_array_set_free_func (priv->domains, g_free); + g_ptr_array_free (priv->domains, TRUE); + } + + if (priv->searches) { + g_ptr_array_set_free_func (priv->searches, g_free); + g_ptr_array_free (priv->searches, TRUE); + } + + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMIP4Config *self = NM_IP4_CONFIG (object); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_GATEWAY: + g_value_set_string (value, nm_ip4_config_get_gateway (self)); + break; + case PROP_ADDRESSES: + nm_utils_ip4_addresses_to_gvalue (priv->addresses, value); + break; + case PROP_ROUTES: + nm_utils_ip4_routes_to_gvalue (priv->routes, value); + break; + case PROP_NAMESERVERS: + g_value_set_boxed (value, nm_ip4_config_get_nameservers (self)); + break; + case PROP_DOMAINS: + g_value_set_boxed (value, nm_ip4_config_get_domains (self)); + break; + case PROP_SEARCHES: + g_value_set_boxed (value, nm_ip4_config_get_searches (self)); + break; + case PROP_WINS_SERVERS: + g_value_set_boxed (value, nm_ip4_config_get_wins_servers (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_ip4_config_class_init (NMIP4ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMIP4ConfigPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMIP4Config:gateway: + * + * The IP4 gateway address of the configuration as string. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_GATEWAY, + g_param_spec_string (NM_IP4_CONFIG_GATEWAY, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP4Config:addresses: + * + * The #GPtrArray containing #NMIP4Address<!-- -->es of the configuration. + **/ + g_object_class_install_property + (object_class, PROP_ADDRESSES, + g_param_spec_pointer (NM_IP4_CONFIG_ADDRESSES, "", "", + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP4Config:routes: + * + * The #GPtrArray containing #NMSettingIP4Routes of the configuration. + **/ + g_object_class_install_property + (object_class, PROP_ROUTES, + g_param_spec_pointer (NM_IP4_CONFIG_ROUTES, "", "", + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP4Config:nameservers: + * + * The #GArray containing name servers (#guint32s) of the configuration. + **/ + g_object_class_install_property + (object_class, PROP_NAMESERVERS, + g_param_spec_boxed (NM_IP4_CONFIG_NAMESERVERS, "", "", + NM_TYPE_UINT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP4Config:domains: + * + * The #GPtrArray containing domain strings of the configuration. + **/ + g_object_class_install_property + (object_class, PROP_DOMAINS, + g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS, "", "", + NM_TYPE_STRING_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP4Config:searches: + * + * The #GPtrArray containing dns search strings of the configuration. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_SEARCHES, + g_param_spec_boxed (NM_IP4_CONFIG_SEARCHES, "", "", + NM_TYPE_STRING_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP4Config:wins-servers: + * + * The #GArray containing WINS servers (#guint32s) of the configuration. + **/ + g_object_class_install_property + (object_class, PROP_WINS_SERVERS, + g_param_spec_boxed (NM_IP4_CONFIG_WINS_SERVERS, "", "", + NM_TYPE_UINT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +/** + * nm_ip4_config_new: + * @connection: the #DBusGConnection + * @object_path: the DBus object path of the device + * + * Creates a new #NMIP4Config. + * + * Returns: (transfer full): a new IP4 configuration + **/ +GObject * +nm_ip4_config_new (DBusGConnection *connection, const char *object_path) +{ + return (GObject *) g_object_new (NM_TYPE_IP4_CONFIG, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, object_path, + NULL); +} + +/** + * nm_ip4_config_get_gateway: + * @config: a #NMIP4Config + * + * Gets the IP4 gateway address. + * + * Returns: the IP4 address of the gateway. + * + * Since: 0.9.10 + **/ +const char * +nm_ip4_config_get_gateway (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP4_CONFIG_GET_PRIVATE (config)->gateway; +} + +/** + * nm_ip4_config_get_addresses: + * @config: a #NMIP4Config + * + * Gets the IP4 addresses (containing the address, prefix, and gateway). + * + * Returns: (element-type NMIP4Address): the #GSList containing #NMIP4Address<!-- -->es. + * This is the internal copy used by the configuration and must not be modified. + **/ +const GSList * +nm_ip4_config_get_addresses (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP4_CONFIG_GET_PRIVATE (config)->addresses; +} + +/** + * nm_ip4_config_get_nameservers: + * @config: a #NMIP4Config + * + * Gets the domain name servers (DNS). + * + * Returns: (element-type guint32): the #GArray containing #guint32s. + * This is the internal copy used by the configuration and must not be + * modified. + **/ +const GArray * +nm_ip4_config_get_nameservers (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP4_CONFIG_GET_PRIVATE (config)->nameservers; +} + +/** + * nm_ip4_config_get_domains: + * @config: a #NMIP4Config + * + * Gets the domain names. + * + * Returns: (element-type utf8): the #GPtrArray containing domains as strings. This is the + * internal copy used by the configuration, and must not be modified. + **/ +const GPtrArray * +nm_ip4_config_get_domains (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return handle_ptr_array_return (NM_IP4_CONFIG_GET_PRIVATE (config)->domains); +} + +/** + * nm_ip4_config_get_searches: + * @config: a #NMIP4Config + * + * Gets the dns searches. + * + * Returns: (element-type utf8): the #GPtrArray containing dns searches as strings. This is the + * internal copy used by the configuration, and must not be modified. + * + * Since: 0.9.10 + **/ +const GPtrArray * +nm_ip4_config_get_searches (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return handle_ptr_array_return (NM_IP4_CONFIG_GET_PRIVATE (config)->searches); +} + +/** + * nm_ip4_config_get_wins_servers: + * @config: a #NMIP4Config + * + * Gets the Windows Internet Name Service servers (WINS). + * + * Returns: (element-type guint32): the #GArray containing #guint32s. + * This is the internal copy used by the configuration and must not be + * modified. + **/ +const GArray * +nm_ip4_config_get_wins_servers (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP4_CONFIG_GET_PRIVATE (config)->wins; +} + +/** + * nm_ip4_config_get_routes: + * @config: a #NMIP4Config + * + * Gets the routes. + * + * Returns: (element-type NMIP4Route): the #GSList containing + * #NMIP4Routes. This is the internal copy used by the configuration, + * and must not be modified. + **/ +const GSList * +nm_ip4_config_get_routes (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP4_CONFIG_GET_PRIVATE (config)->routes; +} diff --git a/libnm/nm-ip4-config.h b/libnm/nm-ip4-config.h new file mode 100644 index 0000000000..58b6b3714d --- /dev/null +++ b/libnm/nm-ip4-config.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2008 Red Hat, Inc. + */ + +#ifndef NM_IP4_CONFIG_H +#define NM_IP4_CONFIG_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "nm-object.h" + +G_BEGIN_DECLS + +#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)) + +typedef struct { + NMObject parent; +} NMIP4Config; + +typedef struct { + NMObjectClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMIP4ConfigClass; + +#define NM_IP4_CONFIG_GATEWAY "gateway" +#define NM_IP4_CONFIG_ADDRESSES "addresses" +#define NM_IP4_CONFIG_ROUTES "routes" +#define NM_IP4_CONFIG_NAMESERVERS "nameservers" +#define NM_IP4_CONFIG_DOMAINS "domains" +#define NM_IP4_CONFIG_SEARCHES "searches" +#define NM_IP4_CONFIG_WINS_SERVERS "wins-servers" + +GType nm_ip4_config_get_type (void); + +GObject *nm_ip4_config_new (DBusGConnection *connection, const char *object_path); + +NM_AVAILABLE_IN_0_9_10 +const char * nm_ip4_config_get_gateway (NMIP4Config *config); +const GSList * nm_ip4_config_get_addresses (NMIP4Config *config); +const GSList * nm_ip4_config_get_routes (NMIP4Config *config); +const GArray * nm_ip4_config_get_nameservers (NMIP4Config *config); +const GPtrArray *nm_ip4_config_get_domains (NMIP4Config *config); +NM_AVAILABLE_IN_0_9_10 +const GPtrArray *nm_ip4_config_get_searches (NMIP4Config *config); +const GArray * nm_ip4_config_get_wins_servers (NMIP4Config *config); + +G_END_DECLS + +#endif /* NM_IP4_CONFIG_H */ diff --git a/libnm/nm-ip6-config.c b/libnm/nm-ip6-config.c new file mode 100644 index 0000000000..21156090bd --- /dev/null +++ b/libnm/nm-ip6-config.c @@ -0,0 +1,493 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2008 - 2014 Red Hat, Inc. + */ + +#include <string.h> + +#include <nm-setting-ip6-config.h> +#include "nm-ip6-config.h" +#include "NetworkManager.h" +#include "nm-types-private.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_OBJECT) + +#define NM_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *gateway; + GSList *addresses; + GSList *routes; + GSList *nameservers; + GPtrArray *domains; + GPtrArray *searches; +} NMIP6ConfigPrivate; + +enum { + PROP_0, + PROP_GATEWAY, + PROP_ADDRESSES, + PROP_ROUTES, + PROP_NAMESERVERS, + PROP_DOMAINS, + PROP_SEARCHES, + + LAST_PROP +}; + +/** + * nm_ip6_config_new: + * @connection: the #DBusGConnection + * @object_path: the DBus object path of the device + * + * Creates a new #NMIP6Config. + * + * Returns: (transfer full): a new IP6 configuration + **/ +GObject * +nm_ip6_config_new (DBusGConnection *connection, const char *object_path) +{ + return (GObject *) g_object_new (NM_TYPE_IP6_CONFIG, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, object_path, + NULL); +} + +static gboolean +demarshal_ip6_address_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object); + + g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip6_address_unref); + priv->addresses = NULL; + + priv->addresses = nm_utils_ip6_addresses_from_gvalue (value); + _nm_object_queue_notify (object, NM_IP6_CONFIG_ADDRESSES); + + return TRUE; +} + +static gboolean +demarshal_ip6_nameserver_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + if (!_nm_ip6_address_array_demarshal (value, (GSList **) field)) + return FALSE; + + if (pspec && !strcmp (pspec->name, NM_IP6_CONFIG_NAMESERVERS)) + _nm_object_queue_notify (object, NM_IP6_CONFIG_NAMESERVERS); + + return TRUE; +} + +static gboolean +demarshal_domains (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + if (!_nm_string_array_demarshal (value, (GPtrArray **) field)) + return FALSE; + + _nm_object_queue_notify (object, NM_IP6_CONFIG_DOMAINS); + return TRUE; +} + +static gboolean +demarshal_searches (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + if (!_nm_string_array_demarshal (value, (GPtrArray **) field)) + return FALSE; + + _nm_object_queue_notify (object, NM_IP6_CONFIG_SEARCHES); + return TRUE; +} + +static gboolean +demarshal_ip6_routes_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object); + + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref); + priv->routes = NULL; + + priv->routes = nm_utils_ip6_routes_from_gvalue (value); + _nm_object_queue_notify (object, NM_IP6_CONFIG_ROUTES); + + return TRUE; +} + +static void +register_properties (NMIP6Config *config) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMPropertiesInfo property_info[] = { + { NM_IP6_CONFIG_GATEWAY, &priv->gateway, }, + { NM_IP6_CONFIG_ADDRESSES, &priv->addresses, demarshal_ip6_address_array }, + { NM_IP6_CONFIG_ROUTES, &priv->routes, demarshal_ip6_routes_array }, + { NM_IP6_CONFIG_NAMESERVERS, &priv->nameservers, demarshal_ip6_nameserver_array }, + { NM_IP6_CONFIG_DOMAINS, &priv->domains, demarshal_domains }, + { NM_IP6_CONFIG_SEARCHES, &priv->searches, demarshal_searches }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (config), + priv->proxy, + property_info); +} + +/** + * nm_ip6_config_get_gateway: + * @config: a #NMIP6Config + * + * Gets the IP6 gateway. + * + * Returns: the IPv6 gateway of the configuration. + * + * Since: 0.9.10 + **/ +const char * +nm_ip6_config_get_gateway (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP6_CONFIG_GET_PRIVATE (config)->gateway; +} + +/** + * nm_ip6_config_get_addresses: + * @config: a #NMIP6Config + * + * Gets the IP6 addresses (containing the address, prefix, and gateway). + * + * Returns: (element-type NMIP6Address): the #GSList containing + * #NMIP6Address<!-- -->es. This is the internal copy used by the configuration + * and must not be modified. + **/ +const GSList * +nm_ip6_config_get_addresses (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP6_CONFIG_GET_PRIVATE (config)->addresses; +} + +/** + * nm_ip6_config_get_num_nameservers: + * @config: a #NMIP6Config + * + * Gets the number of the domain name servers in the configuration. + * + * Returns: the number of domain name servers + * + * Since: 0.9.10 + **/ +guint32 +nm_ip6_config_get_num_nameservers (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return g_slist_length (NM_IP6_CONFIG_GET_PRIVATE (config)->nameservers); +} + +/** + * nm_ip6_config_get_nameserver: + * @config: a #NMIP6Config + * @idx: index of the nameserver to return + * + * Gets the domain name server at index @idx in the configuration. + * + * Returns: (array fixed-size=16) (element-type guint8) (transfer none): + * the IPv6 address of domain name server at index @iidx + * + * Since: 0.9.10 + **/ +const struct in6_addr * +nm_ip6_config_get_nameserver (NMIP6Config *config, guint32 idx) +{ + NMIP6ConfigPrivate *priv; + GSList *item; + guint32 i = 0; + + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + + for (item = priv->nameservers; item && i < idx; i++) + item = item->next; + + g_return_val_if_fail (item, NULL); + return item ? (const struct in6_addr *) item->data : NULL; +} + +/* FIXME: like in libnm_util, in6_addr is not introspectable, so skipping here */ +/** + * nm_ip6_config_get_nameservers: (skip) + * @config: a #NMIP6Config + * + * Gets the domain name servers (DNS). + * + * Returns: a #GSList containing elements of type 'struct in6_addr' which + * contain the addresses of nameservers of the configuration. This is the + * internal copy used by the configuration and must not be modified. + **/ +const GSList * +nm_ip6_config_get_nameservers (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP6_CONFIG_GET_PRIVATE (config)->nameservers; +} + +/** + * nm_ip6_config_get_domains: + * @config: a #NMIP6Config + * + * Gets the domain names. + * + * Returns: (element-type utf8): the #GPtrArray containing domains as strings. + * This is the internal copy used by the configuration, and must not be modified. + **/ +const GPtrArray * +nm_ip6_config_get_domains (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return handle_ptr_array_return (NM_IP6_CONFIG_GET_PRIVATE (config)->domains); +} + +/** + * nm_ip6_config_get_searches: + * @config: a #NMIP6Config + * + * Gets the dns searches. + * + * Returns: (element-type utf8): the #GPtrArray containing dns searches as strings. + * This is the internal copy used by the configuration, and must not be modified. + * + * Since: 0.9.10 + **/ +const GPtrArray * +nm_ip6_config_get_searches (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return handle_ptr_array_return (NM_IP6_CONFIG_GET_PRIVATE (config)->searches); +} + +/** + * nm_ip6_config_get_routes: + * @config: a #NMIP6Config + * + * Gets the routes. + * + * Returns: (element-type NMIP6Route): the #GSList containing + * #NMIP6Routes. This is the internal copy used by the configuration, + * and must not be modified. + **/ +const GSList * +nm_ip6_config_get_routes (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + _nm_object_ensure_inited (NM_OBJECT (config)); + return NM_IP6_CONFIG_GET_PRIVATE (config)->routes; +} + +static void +constructed (GObject *object) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_ip6_config_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_IP6_CONFIG); + register_properties (NM_IP6_CONFIG (object)); +} + +static void +finalize (GObject *object) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object); + + g_free (priv->gateway); + + g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip6_address_unref); + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref); + g_slist_free_full (priv->nameservers, g_free); + + if (priv->domains) { + g_ptr_array_set_free_func (priv->domains, g_free); + g_ptr_array_free (priv->domains, TRUE); + } + + if (priv->searches) { + g_ptr_array_set_free_func (priv->searches, g_free); + g_ptr_array_free (priv->searches, TRUE); + } + + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMIP6Config *self = NM_IP6_CONFIG (object); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_GATEWAY: + g_value_set_string (value, nm_ip6_config_get_gateway (self)); + break; + case PROP_ADDRESSES: + nm_utils_ip6_addresses_to_gvalue (priv->addresses, value); + break; + case PROP_ROUTES: + nm_utils_ip6_routes_to_gvalue (priv->routes, value); + break; + case PROP_NAMESERVERS: + g_value_set_boxed (value, nm_ip6_config_get_nameservers (self)); + break; + case PROP_DOMAINS: + g_value_set_boxed (value, nm_ip6_config_get_domains (self)); + break; + case PROP_SEARCHES: + g_value_set_boxed (value, nm_ip6_config_get_searches (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_ip6_config_init (NMIP6Config *config) +{ +} + +static void +nm_ip6_config_class_init (NMIP6ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMIP6ConfigPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMIP6Config:gateway: + * + * The IPv6 gateway as string + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_GATEWAY, + g_param_spec_string (NM_IP6_CONFIG_GATEWAY, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP6Config:addresses: + * + * The #GPtrArray containing the IPv6 addresses; use + * nm_utils_ip6_addresses_from_gvalue() to return a #GSList of + * #NMSettingIP6Address objects that is more usable than the raw data. + **/ + g_object_class_install_property + (object_class, PROP_ADDRESSES, + g_param_spec_boxed (NM_IP6_CONFIG_ADDRESSES, "", "", + NM_TYPE_IP6_ADDRESS_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP6Config:routes: + * + * The #GPtrArray containing the IPv6 routes; use + * nm_utils_ip6_routes_from_gvalue() to return a #GSList of + * #NMSettingIP6Address objects that is more usable than the raw data. + **/ + g_object_class_install_property + (object_class, PROP_ROUTES, + g_param_spec_boxed (NM_IP6_CONFIG_ROUTES, "", "", + NM_TYPE_IP6_ROUTE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP6Config:nameservers: + * + * The #GPtrArray containing elements of type 'struct ip6_addr' which + * contain the addresses of nameservers of the configuration. + **/ + g_object_class_install_property + (object_class, PROP_NAMESERVERS, + g_param_spec_boxed (NM_IP6_CONFIG_NAMESERVERS, "", "", + NM_TYPE_IP6_ADDRESS_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP6Config:domains: + * + * The #GPtrArray containing domain strings of the configuration. + **/ + g_object_class_install_property + (object_class, PROP_DOMAINS, + g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, "", "", + NM_TYPE_STRING_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMIP6Config:searches: + * + * The #GPtrArray containing dns search strings of the configuration. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_SEARCHES, + g_param_spec_boxed (NM_IP6_CONFIG_SEARCHES, "", "", + NM_TYPE_STRING_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + +} diff --git a/libnm/nm-ip6-config.h b/libnm/nm-ip6-config.h new file mode 100644 index 0000000000..7a64805b68 --- /dev/null +++ b/libnm/nm-ip6-config.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2008 - 2014 Red Hat, Inc. + */ + +#ifndef NM_IP6_CONFIG_H +#define NM_IP6_CONFIG_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "nm-object.h" + +G_BEGIN_DECLS + +#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)) + +typedef struct { + NMObject parent; +} NMIP6Config; + +typedef struct { + NMObjectClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMIP6ConfigClass; + +#define NM_IP6_CONFIG_GATEWAY "gateway" +#define NM_IP6_CONFIG_ADDRESSES "addresses" +#define NM_IP6_CONFIG_ROUTES "routes" +#define NM_IP6_CONFIG_NAMESERVERS "nameservers" +#define NM_IP6_CONFIG_DOMAINS "domains" +#define NM_IP6_CONFIG_SEARCHES "searches" + +GType nm_ip6_config_get_type (void); + +GObject *nm_ip6_config_new (DBusGConnection *connection, const char *object_path); + +NM_AVAILABLE_IN_0_9_10 +const char * nm_ip6_config_get_gateway (NMIP6Config *config); +const GSList * nm_ip6_config_get_addresses (NMIP6Config *config); +const GSList * nm_ip6_config_get_routes (NMIP6Config *config); +NM_AVAILABLE_IN_0_9_10 +guint32 nm_ip6_config_get_num_nameservers (NMIP6Config *config); +NM_AVAILABLE_IN_0_9_10 +const struct in6_addr *nm_ip6_config_get_nameserver (NMIP6Config *config, guint32 idx); +const GSList * nm_ip6_config_get_nameservers (NMIP6Config *config); +const GPtrArray * nm_ip6_config_get_domains (NMIP6Config *config); +NM_AVAILABLE_IN_0_9_10 +const GPtrArray * nm_ip6_config_get_searches (NMIP6Config *config); + +G_END_DECLS + +#endif /* NM_IP6_CONFIG_H */ diff --git a/libnm/nm-object-cache.c b/libnm/nm-object-cache.c new file mode 100644 index 0000000000..fe388803e7 --- /dev/null +++ b/libnm/nm-object-cache.c @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 Red Hat, Inc. + */ + +#include <string.h> +#include <glib.h> +#include "nm-object-cache.h" +#include "nm-object.h" + +static GHashTable *cache = NULL; + +static void +_init_cache (void) +{ + if (G_UNLIKELY (cache == NULL)) + cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); +} + +static void +_nm_object_cache_remove_by_path (char *path) +{ + _init_cache (); + g_hash_table_remove (cache, path); + g_free (path); +} + +void +_nm_object_cache_add (NMObject *object) +{ + char *path; + + _init_cache (); + path = g_strdup (nm_object_get_path (object)); + g_hash_table_insert (cache, path, object); + g_object_set_data_full (G_OBJECT (object), "nm-object-cache-tag", + g_strdup (path), (GDestroyNotify) _nm_object_cache_remove_by_path); +} + +NMObject * +_nm_object_cache_get (const char *path) +{ + NMObject *object; + + _init_cache (); + object = g_hash_table_lookup (cache, path); + return object ? g_object_ref (object) : NULL; +} + +void +_nm_object_cache_clear (void) +{ + GHashTableIter iter; + GObject *obj; + const char *path; + char *foo; + + if (!cache) + return; + + g_hash_table_iter_init (&iter, cache); + while (g_hash_table_iter_next (&iter, (gpointer) &path, (gpointer) &obj)) { + /* Remove the callback so that if the object isn't yet released + * by a client, when it does finally get unrefed, it won't trigger + * the cache removal for a new object with the same path as the + * one being released. + */ + foo = g_object_steal_data (obj, "nm-object-cache-tag"); + g_free (foo); + + g_hash_table_iter_remove (&iter); + } +} diff --git a/libnm/nm-object-cache.h b/libnm/nm-object-cache.h new file mode 100644 index 0000000000..7aca3b4fba --- /dev/null +++ b/libnm/nm-object-cache.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 Red Hat, Inc. + */ + +#ifndef NM_OBJECT_CACHE_H +#define NM_OBJECT_CACHE_H + +#include <glib.h> +#include <glib-object.h> +#include "nm-object.h" + +G_BEGIN_DECLS + +/* Returns referenced object from the cache */ +NMObject *_nm_object_cache_get (const char *path); +void _nm_object_cache_add (NMObject *object); +void _nm_object_cache_clear (void); + +G_END_DECLS + +#endif /* NM_OBJECT_CACHE_H */ diff --git a/libnm/nm-object-private.h b/libnm/nm-object-private.h new file mode 100644 index 0000000000..75e63b2e7e --- /dev/null +++ b/libnm/nm-object-private.h @@ -0,0 +1,92 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2011 Red Hat, Inc. + */ + +#ifndef NM_OBJECT_PRIVATE_H +#define NM_OBJECT_PRIVATE_H + +#include <gio/gio.h> +#include "nm-object.h" + +void _nm_object_ensure_inited (NMObject *object); + +typedef gboolean (*PropertyMarshalFunc) (NMObject *, GParamSpec *, GValue *, gpointer); + +typedef GObject * (*NMObjectCreatorFunc) (DBusGConnection *, const char *); + +typedef struct { + const char *name; + gpointer field; + PropertyMarshalFunc func; + GType object_type; + const char *signal_prefix; +} NMPropertiesInfo; + +DBusGProxy *_nm_object_new_proxy (NMObject *self, + const char *path, + const char *interface); + +gboolean _nm_object_is_connection_private (NMObject *self); + +void _nm_object_register_properties (NMObject *object, + DBusGProxy *proxy, + const NMPropertiesInfo *info); + +gboolean _nm_object_reload_properties (NMObject *object, GError **error); + +void _nm_object_reload_properties_async (NMObject *object, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean _nm_object_reload_properties_finish (NMObject *object, + GAsyncResult *result, + GError **error); + +void _nm_object_queue_notify (NMObject *object, const char *property); + +void _nm_object_suppress_property_updates (NMObject *object, gboolean suppress); + +/* DBus property accessors */ + +void _nm_object_reload_property (NMObject *object, + const char *interface, + const char *prop_name); + +void _nm_object_set_property (NMObject *object, + const char *interface, + const char *prop_name, + GValue *value); + +static inline const GPtrArray * +handle_ptr_array_return (GPtrArray *array) +{ + /* zero-length is special-case; return NULL */ + if (!array || !array->len) + return NULL; + return array; +} + +/* object demarshalling support */ +typedef GType (*NMObjectTypeFunc) (DBusGConnection *, const char *); +typedef void (*NMObjectTypeCallbackFunc) (GType, gpointer); +typedef void (*NMObjectTypeAsyncFunc) (DBusGConnection *, const char *, NMObjectTypeCallbackFunc, gpointer); + +void _nm_object_register_type_func (GType base_type, NMObjectTypeFunc type_func, + NMObjectTypeAsyncFunc type_async_func); + +#endif /* NM_OBJECT_PRIVATE_H */ diff --git a/libnm/nm-object.c b/libnm/nm-object.c new file mode 100644 index 0000000000..3550677df6 --- /dev/null +++ b/libnm/nm-object.c @@ -0,0 +1,1445 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#include <string.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <stdio.h> +#include <nm-utils.h> +#include "NetworkManager.h" +#include "nm-object.h" +#include "nm-object-cache.h" +#include "nm-object-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" +#include "nm-types.h" +#include "nm-dbus-helpers-private.h" + +static gboolean debug = FALSE; +#define dbgmsg(f,...) if (G_UNLIKELY (debug)) { g_message (f, ## __VA_ARGS__ ); } + +static void nm_object_initable_iface_init (GInitableIface *iface); +static void nm_object_async_initable_iface_init (GAsyncInitableIface *iface); + +static GHashTable *type_funcs, *type_async_funcs; + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMObject, nm_object, G_TYPE_OBJECT, + type_funcs = g_hash_table_new (NULL, NULL); + type_async_funcs = g_hash_table_new (NULL, NULL); + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_object_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_object_async_initable_iface_init); + ) + +#define NM_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OBJECT, NMObjectPrivate)) + +typedef struct { + PropertyMarshalFunc func; + GType object_type; + gpointer field; + const char *signal_prefix; +} PropertyInfo; + +static void reload_complete (NMObject *object); + +typedef struct { + DBusGConnection *connection; + DBusGProxy *bus_proxy; + gboolean nm_running; + + char *path; + DBusGProxy *properties_proxy; + GSList *property_interfaces; + GSList *property_tables; + NMObject *parent; + gboolean suppress_property_updates; + + GSList *notify_props; + guint32 notify_id; + gboolean inited; + + GSList *reload_results; + guint reload_remaining; + GError *reload_error; +} NMObjectPrivate; + +enum { + PROP_0, + PROP_DBUS_CONNECTION, + PROP_DBUS_PATH, + + LAST_PROP +}; + +enum { + OBJECT_CREATION_FAILED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/** + * nm_object_error_quark: + * + * Registers an error quark for #NMObject if necessary. + * + * Returns: the error quark used for #NMObject errors. + **/ +GQuark +nm_object_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-object-error-quark"); + return quark; +} + +static void +proxy_name_owner_changed (DBusGProxy *proxy, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + NMObject *self = NM_OBJECT (user_data); + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + + if (g_strcmp0 (name, NM_DBUS_SERVICE) == 0) { + gboolean old_good = (old_owner && old_owner[0]); + gboolean new_good = (new_owner && new_owner[0]); + + if (!old_good && new_good) + priv->nm_running = TRUE; + else if (old_good && !new_good) + priv->nm_running = FALSE; + } +} + +static void +nm_object_init (NMObject *object) +{ +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMObjectPrivate *priv; + + object = G_OBJECT_CLASS (nm_object_parent_class)->constructor (type, + n_construct_params, + construct_params); + + priv = NM_OBJECT_GET_PRIVATE (object); + + if (priv->connection == NULL || priv->path == NULL) { + g_warn_if_reached (); + g_object_unref (object); + return NULL; + } + + return object; +} + +static void +constructed (GObject *object) +{ + NMObject *self = NM_OBJECT (object); + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + if (G_OBJECT_CLASS (nm_object_parent_class)->constructed) + G_OBJECT_CLASS (nm_object_parent_class)->constructed (object); + + priv->properties_proxy = _nm_object_new_proxy (self, NULL, "org.freedesktop.DBus.Properties"); + + if (_nm_object_is_connection_private (self)) + priv->nm_running = TRUE; + else { + priv->bus_proxy = dbus_g_proxy_new_for_name (priv->connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + g_assert (priv->bus_proxy); + + dbus_g_proxy_add_signal (priv->bus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->bus_proxy, + "NameOwnerChanged", + G_CALLBACK (proxy_name_owner_changed), + object, NULL); + } +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (initable); + + if (priv->bus_proxy) { + if (!dbus_g_proxy_call (priv->bus_proxy, + "NameHasOwner", error, + G_TYPE_STRING, NM_DBUS_SERVICE, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &priv->nm_running, + G_TYPE_INVALID)) + return FALSE; + } + + priv->inited = TRUE; + return _nm_object_reload_properties (NM_OBJECT (initable), error); +} + +/* Takes ownership of @error */ +static void +init_async_complete (GSimpleAsyncResult *simple, GError *error) +{ + if (error) + g_simple_async_result_take_error (simple, error); + else + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +init_async_got_properties (GObject *object, GAsyncResult *result, gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + GError *error = NULL; + + NM_OBJECT_GET_PRIVATE (object)->inited = TRUE; + if (!_nm_object_reload_properties_finish (NM_OBJECT (object), result, &error)) + g_assert (error); + init_async_complete (simple, error); +} + +static void +init_async_got_manager_running (DBusGProxy *proxy, DBusGProxyCall *call, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + NMObject *self; + NMObjectPrivate *priv; + GError *error = NULL; + + self = NM_OBJECT (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + priv = NM_OBJECT_GET_PRIVATE (self); + + if (!dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_BOOLEAN, &priv->nm_running, + G_TYPE_INVALID)) { + init_async_complete (simple, error); + } else if (!priv->nm_running) { + priv->inited = TRUE; + init_async_complete (simple, NULL); + } else + _nm_object_reload_properties_async (self, init_async_got_properties, simple); + + /* g_async_result_get_source_object() adds a ref */ + g_object_unref (self); +} + +static void +init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (initable); + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, init_async); + + if (_nm_object_is_connection_private (NM_OBJECT (initable))) + _nm_object_reload_properties_async (NM_OBJECT (initable), init_async_got_properties, simple); + else { + /* Check if NM is running */ + dbus_g_proxy_begin_call (priv->bus_proxy, "NameHasOwner", + init_async_got_manager_running, + simple, NULL, + G_TYPE_STRING, NM_DBUS_SERVICE, + G_TYPE_INVALID); + } +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return TRUE; +} + +static void +dispose (GObject *object) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + if (priv->notify_id) { + g_source_remove (priv->notify_id); + priv->notify_id = 0; + } + + g_slist_free_full (priv->notify_props, g_free); + priv->notify_props = NULL; + + g_slist_free_full (priv->property_interfaces, g_free); + priv->property_interfaces = NULL; + + g_clear_object (&priv->properties_proxy); + g_clear_object (&priv->bus_proxy); + + if (priv->connection) { + dbus_g_connection_unref (priv->connection); + priv->connection = NULL; + } + + G_OBJECT_CLASS (nm_object_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + g_slist_free_full (priv->property_tables, (GDestroyNotify) g_hash_table_destroy); + g_free (priv->path); + + G_OBJECT_CLASS (nm_object_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DBUS_CONNECTION: + /* Construct only */ + priv->connection = g_value_dup_boxed (value); + if (!priv->connection) + priv->connection = _nm_dbus_new_connection (NULL); + break; + case PROP_DBUS_PATH: + /* Construct only */ + priv->path = g_value_dup_string (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) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DBUS_CONNECTION: + g_value_set_boxed (value, priv->connection); + break; + case PROP_DBUS_PATH: + g_value_set_string (value, priv->path); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_object_class_init (NMObjectClass *nm_object_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (nm_object_class); + + g_type_class_add_private (nm_object_class, sizeof (NMObjectPrivate)); + + /* virtual methods */ + object_class->constructor = constructor; + object_class->constructed = constructed; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* Properties */ + + /** + * NMObject:connection: + * + * The #DBusGConnection of the object. + **/ + g_object_class_install_property + (object_class, PROP_DBUS_CONNECTION, + g_param_spec_boxed (NM_OBJECT_DBUS_CONNECTION, "", "", + DBUS_TYPE_G_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NMObject:path: + * + * The DBus object path. + **/ + g_object_class_install_property + (object_class, PROP_DBUS_PATH, + g_param_spec_string (NM_OBJECT_DBUS_PATH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /* signals */ + + /** + * NMObject::object-creation-failed: + * @master_object: the object that received the signal + * @error: the error that occured while creating object + * @failed_path: object path of the failed object + * + * Indicates that an error occured while creating an #NMObject object + * during property handling of @master_object. + * + * Note: Be aware that the signal is private for libnm-glib's internal + * use. + **/ + signals[OBJECT_CREATION_FAILED] = + g_signal_new ("object-creation-failed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMObjectClass, object_creation_failed), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); +} + +static void +nm_object_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_object_async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} + +/** + * nm_object_get_connection: + * @object: a #NMObject + * + * Gets the #NMObject's DBusGConnection. + * + * Returns: (transfer none): the connection + **/ +DBusGConnection * +nm_object_get_connection (NMObject *object) +{ + g_return_val_if_fail (NM_IS_OBJECT (object), NULL); + + return NM_OBJECT_GET_PRIVATE (object)->connection; +} + +/** + * 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 + * device, and must not be modified. + **/ +const char * +nm_object_get_path (NMObject *object) +{ + g_return_val_if_fail (NM_IS_OBJECT (object), NULL); + + return NM_OBJECT_GET_PRIVATE (object)->path; +} + +static gboolean +deferred_notify_cb (gpointer data) +{ + NMObject *object = NM_OBJECT (data); + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GSList *props, *iter; + + priv->notify_id = 0; + + /* Clear priv->notify_props early so that an NMObject subclass that + * listens to property changes can queue up other property changes + * during the g_object_notify() call separately from the property + * list we're iterating. + */ + props = g_slist_reverse (priv->notify_props); + priv->notify_props = NULL; + + g_object_ref (object); + for (iter = props; iter; iter = g_slist_next (iter)) { + g_object_notify (G_OBJECT (object), (const char *) iter->data); + g_free (iter->data); + } + g_object_unref (object); + + g_slist_free (props); + return FALSE; +} + +void +_nm_object_queue_notify (NMObject *object, const char *property) +{ + NMObjectPrivate *priv; + gboolean found = FALSE; + GSList *iter; + + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (property != NULL); + + priv = NM_OBJECT_GET_PRIVATE (object); + if (!priv->notify_id) + priv->notify_id = g_idle_add_full (G_PRIORITY_LOW, deferred_notify_cb, object, NULL); + + for (iter = priv->notify_props; iter; iter = g_slist_next (iter)) { + if (!strcmp ((char *) iter->data, property)) { + found = TRUE; + break; + } + } + + if (!found) + priv->notify_props = g_slist_prepend (priv->notify_props, g_strdup (property)); +} + +void +_nm_object_register_type_func (GType base_type, NMObjectTypeFunc type_func, + NMObjectTypeAsyncFunc type_async_func) +{ + g_hash_table_insert (type_funcs, + GSIZE_TO_POINTER (base_type), + type_func); + g_hash_table_insert (type_async_funcs, + GSIZE_TO_POINTER (base_type), + type_async_func); +} + +static GObject * +_nm_object_create (GType type, DBusGConnection *connection, const char *path) +{ + NMObjectTypeFunc type_func; + GObject *object; + GError *error = NULL; + + type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type)); + if (type_func) + type = type_func (connection, path); + + if (type == G_TYPE_INVALID) { + dbgmsg ("Could not create object for %s: unknown object type", path); + return NULL; + } + + object = g_object_new (type, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); + if (NM_IS_OBJECT (object)) + _nm_object_cache_add (NM_OBJECT (object)); + if (!g_initable_init (G_INITABLE (object), NULL, &error)) { + dbgmsg ("Could not create object for %s: %s", path, error->message); + g_error_free (error); + g_clear_object (&object); + } + + return object; +} + +typedef void (*NMObjectCreateCallbackFunc) (GObject *, const char *, gpointer); +typedef struct { + DBusGConnection *connection; + char *path; + NMObjectCreateCallbackFunc callback; + gpointer user_data; +} NMObjectTypeAsyncData; + +static void +create_async_complete (GObject *object, NMObjectTypeAsyncData *async_data) +{ + async_data->callback (object, async_data->path, async_data->user_data); + + g_free (async_data->path); + g_slice_free (NMObjectTypeAsyncData, async_data); +} + +static const char * +nm_object_or_connection_get_path (gpointer instance) +{ + if (NM_IS_OBJECT (instance)) + return nm_object_get_path (instance); + else if (NM_IS_CONNECTION (instance)) + return nm_connection_get_path (instance); + + g_assert_not_reached (); +} + +static void +async_inited (GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMObjectTypeAsyncData *async_data = user_data; + GObject *object = G_OBJECT (source); + GError *error = NULL; + + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (object), result, &error)) { + dbgmsg ("Could not create object for %s: %s", + nm_object_or_connection_get_path (object), + error->message); + g_error_free (error); + g_clear_object (&object); + } + + create_async_complete (object, async_data); +} + +static void +async_got_type (GType type, gpointer user_data) +{ + NMObjectTypeAsyncData *async_data = user_data; + GObject *object; + + /* Ensure we don't have the object already; we may get multiple type + * requests for the same object if there are multiple properties on + * other objects that refer to the object at this path. One of those + * other requests may have already completed. + */ + object = (GObject *) _nm_object_cache_get (async_data->path); + if (object) { + create_async_complete (object, async_data); + return; + } + + if (type == G_TYPE_INVALID) { + /* Don't know how to create this object */ + create_async_complete (NULL, async_data); + return; + } + + object = g_object_new (type, + NM_OBJECT_DBUS_CONNECTION, async_data->connection, + NM_OBJECT_DBUS_PATH, async_data->path, + NULL); + g_warn_if_fail (object != NULL); + if (NM_IS_OBJECT (object)) + _nm_object_cache_add (NM_OBJECT (object)); + g_async_initable_init_async (G_ASYNC_INITABLE (object), G_PRIORITY_DEFAULT, + NULL, async_inited, async_data); +} + +static void +_nm_object_create_async (GType type, DBusGConnection *connection, const char *path, + NMObjectCreateCallbackFunc callback, gpointer user_data) +{ + NMObjectTypeAsyncFunc type_async_func; + NMObjectTypeFunc type_func; + NMObjectTypeAsyncData *async_data; + + async_data = g_slice_new (NMObjectTypeAsyncData); + async_data->connection = connection; + async_data->path = g_strdup (path); + async_data->callback = callback; + async_data->user_data = user_data; + + type_async_func = g_hash_table_lookup (type_async_funcs, GSIZE_TO_POINTER (type)); + if (type_async_func) { + type_async_func (connection, path, async_got_type, async_data); + return; + } + + type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type)); + if (type_func) + type = type_func (connection, path); + + async_got_type (type, async_data); +} + +/* Stolen from dbus-glib */ +static char* +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); +} + +/* Adds object to array if it's not already there */ +static void +add_to_object_array_unique (GPtrArray *array, GObject *obj) +{ + guint i; + + g_return_if_fail (array != NULL); + + if (obj != NULL) { + for (i = 0; i < array->len; i++) { + if (g_ptr_array_index (array, i) == obj) { + g_object_unref (obj); + return; + } + } + g_ptr_array_add (array, obj); + } +} + +typedef struct { + NMObject *self; + PropertyInfo *pi; + + GObject **objects; + int length, remaining; + + gboolean array; + const char *property_name; +} ObjectCreatedData; + +/* Places items from 'needles' that are not in 'haystack' into 'diff' */ +static void +array_diff (GPtrArray *needles, GPtrArray *haystack, GPtrArray *diff) +{ + guint i, j; + GObject *obj; + + g_assert (needles); + g_assert (haystack); + g_assert (diff); + + for (i = 0; i < needles->len; i++) { + obj = g_ptr_array_index (needles, i); + + for (j = 0; j < haystack->len; j++) { + if (g_ptr_array_index (haystack, j) == obj) + break; + } + + if (j == haystack->len) + g_ptr_array_add (diff, obj); + } +} + +static void +emit_added_removed_signal (NMObject *self, + const char *signal_prefix, + NMObject *changed, + gboolean added) +{ + char buf[50]; + int ret; + + ret = g_snprintf (buf, sizeof (buf), "%s-%s", signal_prefix, added ? "added" : "removed"); + g_assert (ret < sizeof (buf)); + g_signal_emit_by_name (self, buf, changed); +} + +static void +object_property_complete (ObjectCreatedData *odata) +{ + NMObject *self = odata->self; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + PropertyInfo *pi = odata->pi; + gboolean different = TRUE; + + if (odata->array) { + GPtrArray *old = *((GPtrArray **) pi->field); + GPtrArray *new; + int i; + + /* Build up new array */ + new = g_ptr_array_sized_new (odata->length); + for (i = 0; i < odata->length; i++) + add_to_object_array_unique (new, odata->objects[i]); + + if (pi->signal_prefix) { + GPtrArray *added = g_ptr_array_sized_new (3); + GPtrArray *removed = g_ptr_array_sized_new (3); + + if (old) { + /* Find objects in 'old' that do not exist in 'new' */ + array_diff (old, new, removed); + + /* Find objects in 'new' that do not exist in old */ + array_diff (new, old, added); + } else { + for (i = 0; i < new->len; i++) + g_ptr_array_add (added, g_ptr_array_index (new, i)); + } + + *((GPtrArray **) pi->field) = new; + + /* Emit added & removed */ + for (i = 0; i < removed->len; i++) { + emit_added_removed_signal (self, + pi->signal_prefix, + g_ptr_array_index (removed, i), + FALSE); + } + + for (i = 0; i < added->len; i++) { + emit_added_removed_signal (self, + pi->signal_prefix, + g_ptr_array_index (added, i), + TRUE); + } + + different = removed->len || added->len; + g_ptr_array_free (added, TRUE); + g_ptr_array_free (removed, TRUE); + } else { + /* No added/removed signals to send, just replace the property with + * the new values. + */ + *((GPtrArray **) pi->field) = new; + different = TRUE; + } + + /* Free old array last since it will release references, thus freeing + * any objects in the 'removed' array. + */ + if (old) + g_boxed_free (NM_TYPE_OBJECT_ARRAY, old); + } else { + GObject **obj_p = pi->field; + + different = (*obj_p != odata->objects[0]); + if (*obj_p) + g_object_unref (*obj_p); + *obj_p = odata->objects[0]; + } + + if (different && odata->property_name) + _nm_object_queue_notify (self, odata->property_name); + + if (priv->reload_results && --priv->reload_remaining == 0) + reload_complete (self); + + g_object_unref (self); + g_free (odata->objects); + g_slice_free (ObjectCreatedData, odata); +} + +static void +object_created (GObject *obj, const char *path, gpointer user_data) +{ + ObjectCreatedData *odata = user_data; + + /* We assume that on error, the creator_func printed something */ + + if (obj == NULL && g_strcmp0 (path, "/") != 0 ) { + GError *error; + error = g_error_new (NM_OBJECT_ERROR, + NM_OBJECT_ERROR_OBJECT_CREATION_FAILURE, + "Creating object for path '%s' failed in libnm-glib.", + path); + /* Emit a signal about the error. */ + g_signal_emit (odata->self, signals[OBJECT_CREATION_FAILED], 0, error, path); + g_error_free (error); + } + + odata->objects[--odata->remaining] = obj; + if (!odata->remaining) + object_property_complete (odata); +} + +static gboolean +handle_object_property (NMObject *self, const char *property_name, GValue *value, + PropertyInfo *pi, gboolean synchronously) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + GObject *obj; + const char *path; + ObjectCreatedData *odata; + + odata = g_slice_new (ObjectCreatedData); + odata->self = g_object_ref (self); + odata->pi = pi; + odata->objects = g_new (GObject *, 1); + odata->length = odata->remaining = 1; + odata->array = FALSE; + odata->property_name = property_name; + + if (priv->reload_results) + priv->reload_remaining++; + + path = g_value_get_boxed (value); + + if (!strcmp (path, "/")) { + object_created (NULL, path, odata); + return TRUE; + } + + obj = G_OBJECT (_nm_object_cache_get (path)); + if (obj) { + object_created (obj, path, odata); + return TRUE; + } else if (synchronously) { + obj = _nm_object_create (pi->object_type, priv->connection, path); + object_created (obj, path, odata); + return obj != NULL; + } else { + _nm_object_create_async (pi->object_type, priv->connection, path, + object_created, odata); + /* Assume success */ + return TRUE; + } +} + +static gboolean +handle_object_array_property (NMObject *self, const char *property_name, GValue *value, + PropertyInfo *pi, gboolean synchronously) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + GObject *obj; + GPtrArray *paths; + GPtrArray **array = pi->field; + const char *path; + ObjectCreatedData *odata; + int i; + + paths = g_value_get_boxed (value); + + odata = g_slice_new (ObjectCreatedData); + odata->self = g_object_ref (self); + odata->pi = pi; + odata->objects = g_new0 (GObject *, paths->len); + odata->length = odata->remaining = paths->len; + odata->array = TRUE; + odata->property_name = property_name; + + if (priv->reload_results) + priv->reload_remaining++; + + if (paths->len == 0) { + object_property_complete (odata); + return TRUE; + } + + for (i = 0; i < paths->len; i++) { + path = paths->pdata[i]; + if (!strcmp (path, "/")) { + /* FIXME: can't happen? */ + continue; + } + + obj = G_OBJECT (_nm_object_cache_get (path)); + if (obj) { + object_created (obj, path, odata); + } else if (synchronously) { + obj = _nm_object_create (pi->object_type, priv->connection, path); + object_created (obj, path, odata); + } else { + _nm_object_create_async (pi->object_type, priv->connection, path, + object_created, odata); + } + } + + if (!synchronously) { + /* Assume success */ + return TRUE; + } + + return *array && ((*array)->len == paths->len); +} + +static void +handle_property_changed (NMObject *self, const char *dbus_name, GValue *value, gboolean synchronously) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + char *prop_name; + PropertyInfo *pi; + GParamSpec *pspec; + gboolean success = FALSE, found = FALSE; + GSList *iter; + + prop_name = wincaps_to_dash (dbus_name); + + /* Iterate through the object and its parents to find the property */ + for (iter = priv->property_tables; iter; iter = g_slist_next (iter)) { + pi = g_hash_table_lookup ((GHashTable *) iter->data, prop_name); + if (pi) { + if (!pi->field) { + /* We know about this property but aren't tracking changes on it. */ + goto out; + } + + found = TRUE; + break; + } + } + + if (!found) { + dbgmsg ("Property '%s' unhandled.", prop_name); + goto out; + } + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (self)), prop_name); + if (!pspec) { + dbgmsg ("%s: property '%s' changed but wasn't defined by object type %s.", + __func__, + prop_name, + G_OBJECT_TYPE_NAME (self)); + goto out; + } + + if (G_UNLIKELY (debug)) { + char *s; + s = g_strdup_value_contents (value); + dbgmsg ("PC: (%p) %s::%s => '%s' (%s%s%s)", + self, G_OBJECT_TYPE_NAME (self), + prop_name, + s, + G_VALUE_TYPE_NAME (value), + pi->object_type ? " / " : "", + pi->object_type ? g_type_name (pi->object_type) : ""); + g_free (s); + } + + if (pi->object_type) { + if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) + success = handle_object_property (self, pspec->name, value, pi, synchronously); + else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH)) + success = handle_object_array_property (self, pspec->name, value, pi, synchronously); + else { + g_warn_if_reached (); + goto out; + } + } else + success = (*(pi->func)) (self, pspec, value, pi->field); + + if (!success) { + dbgmsg ("%s: failed to update property '%s' of object type %s.", + __func__, + prop_name, + G_OBJECT_TYPE_NAME (self)); + } + +out: + g_free (prop_name); +} + +static void +process_properties_changed (NMObject *self, GHashTable *properties, gboolean synchronously) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + GHashTableIter iter; + gpointer name, value; + + if (priv->suppress_property_updates) + return; + + g_hash_table_iter_init (&iter, properties); + while (g_hash_table_iter_next (&iter, &name, &value)) { + if (value) + handle_property_changed (self, name, value, synchronously); + else { + dbgmsg ("%s:%d %s(): object %s property '%s' value is unexpectedly NULL", + __FILE__, __LINE__, __func__, G_OBJECT_TYPE_NAME (self), (const char *) name); + } + } +} + +static void +properties_changed_proxy (DBusGProxy *proxy, + GHashTable *properties, + gpointer user_data) +{ + process_properties_changed (NM_OBJECT (user_data), properties, FALSE); +} + +#define HANDLE_TYPE(ucase, lcase, getter) \ + } else if (pspec->value_type == G_TYPE_##ucase) { \ + if (G_VALUE_HOLDS_##ucase (value)) { \ + g##lcase *param = (g##lcase *) field; \ + *param = g_value_get_##getter (value); \ + } else { \ + success = FALSE; \ + goto done; \ + } + +static gboolean +demarshal_generic (NMObject *object, + GParamSpec *pspec, + GValue *value, + gpointer field) +{ + gboolean success = TRUE; + + if (pspec->value_type == G_TYPE_STRING) { + if (G_VALUE_HOLDS_STRING (value)) { + char **param = (char **) field; + g_free (*param); + *param = g_value_dup_string (value); + } else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) { + char **param = (char **) field; + g_free (*param); + *param = g_strdup (g_value_get_boxed (value)); + /* Handle "NULL" object paths */ + if (g_strcmp0 (*param, "/") == 0) { + g_free (*param); + *param = NULL; + } + } else { + success = FALSE; + goto done; + } + HANDLE_TYPE(BOOLEAN, boolean, boolean) + HANDLE_TYPE(CHAR, char, schar) + HANDLE_TYPE(UCHAR, uchar, uchar) + HANDLE_TYPE(DOUBLE, double, double) + HANDLE_TYPE(INT, int, int) + HANDLE_TYPE(UINT, uint, uint) + HANDLE_TYPE(INT64, int, int) + HANDLE_TYPE(UINT64, uint, uint) + HANDLE_TYPE(LONG, long, long) + HANDLE_TYPE(ULONG, ulong, ulong) + } else { + dbgmsg ("%s: %s/%s unhandled type %s.", + __func__, + G_OBJECT_TYPE_NAME (object), + pspec->name, + g_type_name (pspec->value_type)); + success = FALSE; + } + +done: + if (success) { + _nm_object_queue_notify (object, pspec->name); + } else { + dbgmsg ("%s: %s/%s (type %s) couldn't be set with type %s.", + __func__, G_OBJECT_TYPE_NAME (object), pspec->name, + g_type_name (pspec->value_type), G_VALUE_TYPE_NAME (value)); + } + return success; +} + +void +_nm_object_register_properties (NMObject *object, + DBusGProxy *proxy, + const NMPropertiesInfo *info) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + static gsize dval = 0; + const char *debugstr; + NMPropertiesInfo *tmp; + GHashTable *instance; + + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (proxy != NULL); + g_return_if_fail (info != NULL); + + if (g_once_init_enter (&dval)) { + debugstr = getenv ("LIBNM_GLIB_DEBUG"); + if (debugstr && strstr (debugstr, "properties-changed")) + debug = TRUE; + g_once_init_leave (&dval, 1); + } + + priv->property_interfaces = g_slist_prepend (priv->property_interfaces, + g_strdup (dbus_g_proxy_get_interface (proxy))); + + dbus_g_proxy_add_signal (proxy, "PropertiesChanged", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (proxy, + "PropertiesChanged", + G_CALLBACK (properties_changed_proxy), + object, + NULL); + + instance = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->property_tables = g_slist_prepend (priv->property_tables, instance); + + for (tmp = (NMPropertiesInfo *) info; tmp->name; tmp++) { + PropertyInfo *pi; + + if (!tmp->name || (tmp->func && !tmp->field)) { + g_warning ("%s: missing field in NMPropertiesInfo", __func__); + continue; + } + + pi = g_malloc0 (sizeof (PropertyInfo)); + pi->func = tmp->func ? tmp->func : demarshal_generic; + pi->object_type = tmp->object_type; + pi->field = tmp->field; + pi->signal_prefix = tmp->signal_prefix; + g_hash_table_insert (instance, g_strdup (tmp->name), pi); + } +} + +gboolean +_nm_object_reload_properties (NMObject *object, GError **error) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GHashTable *props = NULL; + GSList *p; + + if (!priv->property_interfaces || !priv->nm_running) + return TRUE; + + for (p = priv->property_interfaces; p; p = p->next) { + if (!dbus_g_proxy_call (priv->properties_proxy, "GetAll", error, + G_TYPE_STRING, p->data, + G_TYPE_INVALID, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) + return FALSE; + + process_properties_changed (object, props, TRUE); + g_hash_table_destroy (props); + } + + return TRUE; +} + +void +_nm_object_suppress_property_updates (NMObject *object, gboolean suppress) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + priv->suppress_property_updates = suppress; +} + + +void +_nm_object_ensure_inited (NMObject *object) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GError *error = NULL; + + if (!priv->inited) { + if (!g_initable_init (G_INITABLE (object), NULL, &error)) { + dbgmsg ("Could not initialize %s %s: %s", + G_OBJECT_TYPE_NAME (object), + priv->path, + error->message); + g_error_free (error); + + /* Only warn once */ + priv->inited = TRUE; + } + } +} + +void +_nm_object_reload_property (NMObject *object, + const char *interface, + const char *prop_name) +{ + GValue value = G_VALUE_INIT; + GError *err = NULL; + + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (interface != NULL); + g_return_if_fail (prop_name != NULL); + + if (!NM_OBJECT_GET_PRIVATE (object)->nm_running) + return; + + if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy, + "Get", 15000, &err, + G_TYPE_STRING, interface, + G_TYPE_STRING, prop_name, + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + dbgmsg ("%s: Error getting '%s' for %s: (%d) %s\n", + __func__, + prop_name, + nm_object_get_path (object), + err->code, + err->message); + g_clear_error (&err); + return; + } + + handle_property_changed (object, prop_name, &value, TRUE); + g_value_unset (&value); +} + +void +_nm_object_set_property (NMObject *object, + const char *interface, + const char *prop_name, + GValue *value) +{ + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (interface != NULL); + g_return_if_fail (prop_name != NULL); + g_return_if_fail (G_IS_VALUE (value)); + + if (!NM_OBJECT_GET_PRIVATE (object)->nm_running) + return; + + if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy, + "Set", 2000, NULL, + G_TYPE_STRING, interface, + G_TYPE_STRING, prop_name, + G_TYPE_VALUE, value, + G_TYPE_INVALID)) { + + /* Ignore errors. dbus_g_proxy_call_with_timeout() is called instead of + * dbus_g_proxy_call_no_reply() to give NM chance to authenticate the caller. + */ + } +} + +static void +reload_complete (NMObject *object) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GSimpleAsyncResult *simple; + GSList *results, *iter; + GError *error; + + results = priv->reload_results; + priv->reload_results = NULL; + error = priv->reload_error; + priv->reload_error = NULL; + + for (iter = results; iter; iter = iter->next) { + simple = iter->data; + + if (error) + g_simple_async_result_set_from_error (simple, error); + else + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + + g_simple_async_result_complete (simple); + g_object_unref (simple); + } + g_slist_free (results); + g_clear_error (&error); +} + +static void +reload_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, + gpointer user_data) +{ + NMObject *object = user_data; + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GHashTable *props = NULL; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) { + process_properties_changed (object, props, FALSE); + g_hash_table_destroy (props); + } else { + if (priv->reload_error) + g_error_free (error); + else + priv->reload_error = error; + } + + if (--priv->reload_remaining == 0) + reload_complete (object); +} + +void +_nm_object_reload_properties_async (NMObject *object, GAsyncReadyCallback callback, gpointer user_data) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + GSimpleAsyncResult *simple; + GSList *p; + + simple = g_simple_async_result_new (G_OBJECT (object), callback, + user_data, _nm_object_reload_properties_async); + + if (!priv->property_interfaces) { + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + + priv->reload_results = g_slist_prepend (priv->reload_results, simple); + + /* If there was already a reload happening, we don't need to + * re-read the properties again, we just need to wait for the + * existing reload to finish. + */ + if (priv->reload_results->next) + return; + + for (p = priv->property_interfaces; p; p = p->next) { + priv->reload_remaining++; + dbus_g_proxy_begin_call (priv->properties_proxy, "GetAll", + reload_got_properties, object, NULL, + G_TYPE_STRING, p->data, + G_TYPE_INVALID); + } +} + +gboolean +_nm_object_reload_properties_finish (NMObject *object, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (NM_IS_OBJECT (object), FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (object), _nm_object_reload_properties_async), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + return g_simple_async_result_get_op_res_gboolean (simple); +} + +DBusGProxy * +_nm_object_new_proxy (NMObject *self, const char *path, const char *interface) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self); + + return _nm_dbus_new_proxy_for_connection (priv->connection, path ? path : priv->path, interface); +} + +gboolean +_nm_object_is_connection_private (NMObject *self) +{ + return _nm_dbus_is_connection_private (NM_OBJECT_GET_PRIVATE (self)->connection); +} diff --git a/libnm/nm-object.h b/libnm/nm-object.h new file mode 100644 index 0000000000..07348f2491 --- /dev/null +++ b/libnm/nm-object.h @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#ifndef NM_OBJECT_H +#define NM_OBJECT_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +#include <nm-version.h> + +G_BEGIN_DECLS + +#define NM_TYPE_OBJECT (nm_object_get_type ()) +#define NM_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OBJECT, NMObject)) +#define NM_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OBJECT, NMObjectClass)) +#define NM_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OBJECT)) +#define NM_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OBJECT)) +#define NM_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OBJECT, NMObjectClass)) + +/** + * NMObjectError: + * @NM_OBJECT_ERROR_UNKNOWN: unknown or unclassified error + * @NM_OBJECT_ERROR_OBJECT_CREATION_FAILURE: an error ocured while creating an #NMObject + * + * Describes errors that may result from operations involving a #NMObject. + * + **/ +typedef enum { + NM_OBJECT_ERROR_UNKNOWN = 0, + NM_OBJECT_ERROR_OBJECT_CREATION_FAILURE, +} NMObjectError; + +#define NM_OBJECT_ERROR nm_object_error_quark () +GQuark nm_object_error_quark (void); + +#define NM_OBJECT_DBUS_CONNECTION "dbus-connection" +#define NM_OBJECT_DBUS_PATH "dbus-path" + +typedef struct { + GObject parent; +} NMObject; + +typedef struct { + GObjectClass parent; + + /* Signals */ + /* The "object-creation-failed" signal is PRIVATE for libnm-glib and + * is not meant for any external usage. It indicates that an error + * occured during creation of an object. + */ + void (*object_creation_failed) (NMObject *master_object, + GError *error, + char *failed_path); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMObjectClass; + +GType nm_object_get_type (void); + +DBusGConnection *nm_object_get_connection (NMObject *object); +const char *nm_object_get_path (NMObject *object); + +G_END_DECLS + +#endif /* NM_OBJECT_H */ diff --git a/libnm/nm-remote-connection-private.h b/libnm/nm-remote-connection-private.h new file mode 100644 index 0000000000..e3f35428cd --- /dev/null +++ b/libnm/nm-remote-connection-private.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2009 Red Hat, Inc. + */ + +#ifndef __NM_REMOTE_CONNECTION_PRIVATE_H__ +#define __NM_REMOTE_CONNECTION_PRIVATE_H__ + +#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/libnm/nm-remote-connection.c b/libnm/nm-remote-connection.c new file mode 100644 index 0000000000..e3050b0169 --- /dev/null +++ b/libnm/nm-remote-connection.c @@ -0,0 +1,963 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2011 Red Hat, Inc. + */ + +#include <string.h> +#include <gio/gio.h> +#include <glib/gi18n.h> + +#include <NetworkManager.h> +#include <nm-utils.h> +#include <nm-setting-connection.h> +#include "nm-remote-connection.h" +#include "nm-remote-connection-private.h" +#include "nm-object-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" +#include "nm-dbus-helpers-private.h" + +#define NM_REMOTE_CONNECTION_BUS "bus" +#define NM_REMOTE_CONNECTION_DBUS_CONNECTION "dbus-connection" +#define NM_REMOTE_CONNECTION_DBUS_PATH "dbus-path" + +static void nm_remote_connection_initable_iface_init (GInitableIface *iface); +static void nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (NMRemoteConnection, nm_remote_connection, NM_TYPE_CONNECTION, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_remote_connection_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_remote_connection_async_initable_iface_init); + ) + +enum { + PROP_0, + PROP_BUS, + PROP_DBUS_CONNECTION, + PROP_DBUS_PATH, + PROP_UNSAVED, + + LAST_PROP +}; + +enum { + UPDATED, + REMOVED, + VISIBLE, + + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct RemoteCall RemoteCall; +typedef void (*RemoteCallFetchResultCb) (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error); + + +struct RemoteCall { + NMRemoteConnection *self; + DBusGProxyCall *call; + RemoteCallFetchResultCb fetch_result_cb; + GFunc callback; + gpointer user_data; +}; + +typedef struct { + DBusGConnection *bus; + DBusGProxy *proxy; + DBusGProxy *props_proxy; + gboolean proxy_is_destroyed; + GSList *calls; + + gboolean inited; + gboolean unsaved; + + gboolean visible; +} NMRemoteConnectionPrivate; + +#define NM_REMOTE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionPrivate)) + +/** + * nm_remote_connection_error_quark: + * + * Registers an error quark for #NMRemoteConnection if necessary. + * + * Returns: the error quark used for #NMRemoteConnection errors. + **/ +GQuark +nm_remote_connection_error_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("nm-remote-connection-error-quark"); + return quark; +} + +/****************************************************************/ + +static void +_nm_remote_connection_ensure_inited (NMRemoteConnection *self) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + GError *error = NULL; + + if (!priv->inited) { + if (!g_initable_init (G_INITABLE (self), NULL, &error)) { + /* Don't warn when the call times out because the settings service can't + * be activated or whatever. + */ + if (!g_error_matches (error, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) { + g_warning ("%s: (NMRemoteConnection) error initializing: %s\n", + __func__, error->message); + } + g_error_free (error); + } + priv->inited = TRUE; + } +} + +/****************************************************************/ + +static void +remote_call_dbus_cb (DBusGProxy *proxy, DBusGProxyCall *proxy_call, gpointer user_data) +{ + RemoteCall *call = user_data; + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (call->self); + GError *error = NULL; + + g_assert ( (!proxy && !proxy_call && priv->proxy_is_destroyed) || + ( proxy && proxy_call && !priv->proxy_is_destroyed && proxy == priv->proxy) ); + + if (priv->proxy_is_destroyed) { + error = g_error_new_literal (NM_REMOTE_CONNECTION_ERROR, + NM_REMOTE_CONNECTION_ERROR_DISCONNECTED, + _("Disconnected by D-Bus")); + } + call->fetch_result_cb (call, proxy_call, error); + g_clear_error (&error); + + priv->calls = g_slist_remove (priv->calls, call); + g_object_unref (call->self); + g_free (call); +} + +static gboolean +remote_call_cleanup_cb (void *user_data) +{ + remote_call_dbus_cb (NULL, NULL, user_data); + return G_SOURCE_REMOVE; +} + +static RemoteCall * +remote_call_new (NMRemoteConnection *self, + RemoteCallFetchResultCb fetch_result_cb, + GFunc callback, + gpointer user_data) +{ + RemoteCall *call; + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + + g_assert (fetch_result_cb); + + if (priv->proxy_is_destroyed && !callback) + return NULL; + + call = g_malloc0 (sizeof (RemoteCall)); + call->self = g_object_ref (self); + call->fetch_result_cb = fetch_result_cb; + call->user_data = user_data; + call->callback = callback; + + if (priv->proxy_is_destroyed) { + g_idle_add (remote_call_cleanup_cb, call); + return NULL; + } + priv->calls = g_slist_prepend (priv->calls, call); + return call; +} + +static void +proxy_set_destroyed (NMRemoteConnection *self) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + + if (priv->proxy_is_destroyed) { + g_assert (!priv->calls); + return; + } + + priv->proxy_is_destroyed = TRUE; + + priv->calls = g_slist_reverse (priv->calls); + while (priv->calls) + remote_call_dbus_cb (NULL, NULL, priv->calls->data); +} + +static void +proxy_destroy_cb (DBusGProxy* proxy, gpointer user_data) { + proxy_set_destroyed (user_data); +} + +/****************************************************************/ + +static void +result_cb (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error) +{ + NMRemoteConnectionResultFunc func = (NMRemoteConnectionResultFunc) call->callback; + GError *local_error = NULL; + + if (!error) { + dbus_g_proxy_end_call (NM_REMOTE_CONNECTION_GET_PRIVATE (call->self)->proxy, + proxy_call, &local_error, G_TYPE_INVALID); + error = local_error; + } + if (func) + (*func) (call->self, error, call->user_data); + g_clear_error (&local_error); +} + +/** + * nm_remote_connection_commit_changes: + * @connection: the #NMRemoteConnection + * @callback: (scope async) (allow-none): a function to be called when the + * commit completes + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Send any local changes to the settings and properties of this connection to + * NetworkManager, which will immediately save them to disk. + **/ +void +nm_remote_connection_commit_changes (NMRemoteConnection *self, + NMRemoteConnectionResultFunc callback, + gpointer user_data) +{ + NMRemoteConnectionPrivate *priv; + RemoteCall *call; + GHashTable *settings; + + g_return_if_fail (NM_IS_REMOTE_CONNECTION (self)); + + priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + + call = remote_call_new (self, result_cb, (GFunc) callback, user_data); + if (!call) + return; + + settings = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ALL); + call->call = dbus_g_proxy_begin_call (priv->proxy, "Update", + remote_call_dbus_cb, call, NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, settings, + G_TYPE_INVALID); + g_assert (call->call); + g_hash_table_destroy (settings); +} + +/** + * nm_remote_connection_commit_changes_unsaved: + * @connection: the #NMRemoteConnection + * @callback: (scope async) (allow-none): a function to be called when the + * commit completes + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Send any local changes to the settings and properties of this connection to + * NetworkManager. The changes are not saved to disk until either + * nm_remote_connection_save() or nm_remote_connection_commit_changes() is + * called. + * + * Since: 0.9.10 + **/ +void +nm_remote_connection_commit_changes_unsaved (NMRemoteConnection *connection, + NMRemoteConnectionResultFunc callback, + gpointer user_data) +{ + NMRemoteConnectionPrivate *priv; + GHashTable *settings = NULL; + RemoteCall *call; + + g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection)); + + priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection); + + call = remote_call_new (connection, result_cb, (GFunc) callback, user_data); + if (!call) + return; + + settings = nm_connection_to_hash (NM_CONNECTION (connection), NM_SETTING_HASH_FLAG_ALL); + call->call = dbus_g_proxy_begin_call (priv->proxy, "UpdateUnsaved", + remote_call_dbus_cb, call, NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, settings, + G_TYPE_INVALID); + g_assert (call->call); + g_hash_table_destroy (settings); +} + +/** + * nm_remote_connection_save: + * @connection: the #NMRemoteConnection + * @callback: (scope async) (allow-none): a function to be called when the + * save completes + * @user_data: (closure): caller-specific data to be 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. + * + * Since: 0.9.10 + **/ +void +nm_remote_connection_save (NMRemoteConnection *connection, + NMRemoteConnectionResultFunc callback, + gpointer user_data) +{ + NMRemoteConnectionPrivate *priv; + RemoteCall *call; + + g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection)); + + priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection); + + call = remote_call_new (connection, result_cb, (GFunc) callback, user_data); + if (!call) + return; + + call->call = dbus_g_proxy_begin_call (priv->proxy, "Save", remote_call_dbus_cb, call, NULL, G_TYPE_INVALID); + g_assert (call->call); +} + +/** + * nm_remote_connection_delete: + * @connection: the #NMRemoteConnection + * @callback: (scope async) (allow-none): a function to be called when the delete completes + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Delete the connection. + **/ +void +nm_remote_connection_delete (NMRemoteConnection *self, + NMRemoteConnectionResultFunc callback, + gpointer user_data) +{ + NMRemoteConnectionPrivate *priv; + RemoteCall *call; + + g_return_if_fail (NM_IS_REMOTE_CONNECTION (self)); + + priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + + call = remote_call_new (self, result_cb, (GFunc) callback, user_data); + if (!call) + return; + + call->call = dbus_g_proxy_begin_call (priv->proxy, "Delete", + remote_call_dbus_cb, call, NULL, + G_TYPE_INVALID); + g_assert (call->call); +} + +static void +get_secrets_cb (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error) +{ + NMRemoteConnectionGetSecretsFunc func = (NMRemoteConnectionGetSecretsFunc) call->callback; + GHashTable *secrets = NULL; + GError *local_error = NULL; + + if (!error) { + dbus_g_proxy_end_call (NM_REMOTE_CONNECTION_GET_PRIVATE (call->self)->proxy, + proxy_call, &local_error, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &secrets, + G_TYPE_INVALID); + error = local_error; + } + if (func) + (*func) (call->self, error ? NULL : secrets, error, call->user_data); + g_clear_error (&local_error); + if (secrets) + g_hash_table_destroy (secrets); +} + + +/** + * nm_remote_connection_get_secrets: + * @connection: the #NMRemoteConnection + * @setting_name: the #NMSetting object name to get secrets for + * @callback: (scope async): a function to be called when the update completes; + * must not be %NULL + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Request the connection's secrets. + **/ +void +nm_remote_connection_get_secrets (NMRemoteConnection *self, + const char *setting_name, + NMRemoteConnectionGetSecretsFunc callback, + gpointer user_data) +{ + NMRemoteConnectionPrivate *priv; + RemoteCall *call; + + g_return_if_fail (NM_IS_REMOTE_CONNECTION (self)); + g_return_if_fail (callback != NULL); + + priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + + call = remote_call_new (self, get_secrets_cb, (GFunc) callback, user_data); + if (!call) + return; + + call->call = dbus_g_proxy_begin_call (priv->proxy, "GetSecrets", + remote_call_dbus_cb, call, NULL, + G_TYPE_STRING, setting_name, + G_TYPE_INVALID); + g_assert (call->call); +} + +/** + * 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. + * + * Since: 0.9.10 + **/ +gboolean +nm_remote_connection_get_unsaved (NMRemoteConnection *connection) +{ + g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE); + + _nm_remote_connection_ensure_inited (connection); + return NM_REMOTE_CONNECTION_GET_PRIVATE (connection)->unsaved; +} + +/****************************************************************/ + +static void +replace_settings (NMRemoteConnection *self, GHashTable *new_settings) +{ + GError *error = NULL; + + if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, &error)) + g_signal_emit (self, signals[UPDATED], 0, new_settings); + else { + g_warning ("%s: error updating connection %s settings: (%d) %s", + __func__, + nm_connection_get_path (NM_CONNECTION (self)), + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + + g_signal_emit (self, signals[REMOVED], 0); + } +} + +static void +updated_get_settings_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + NMRemoteConnection *self = user_data; + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + GHashTable *new_settings; + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &new_settings, + G_TYPE_INVALID); + if (error) { + GHashTable *hash; + + g_error_free (error); + + /* Connection is no longer visible to this user. Let the settings + * service handle this via 'visible'. The settings service will emit + * the "removed" signal for us since it handles the lifetime of this + * object. + */ + hash = g_hash_table_new (g_str_hash, g_str_equal); + nm_connection_replace_settings (NM_CONNECTION (self), hash, NULL); + g_hash_table_destroy (hash); + + priv->visible = FALSE; + g_signal_emit (self, signals[VISIBLE], 0, FALSE); + } else { + replace_settings (self, new_settings); + g_hash_table_destroy (new_settings); + + /* Settings service will handle announcing the connection to clients */ + if (priv->visible == FALSE) { + priv->visible = TRUE; + g_signal_emit (self, signals[VISIBLE], 0, TRUE); + } + } +} + +static void +updated_cb (DBusGProxy *proxy, gpointer user_data) +{ + NMRemoteConnection *self = NM_REMOTE_CONNECTION (user_data); + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + + /* The connection got updated; request the replacement settings */ + if (!priv->proxy_is_destroyed) { + dbus_g_proxy_begin_call (priv->proxy, "GetSettings", + updated_get_settings_cb, self, NULL, + G_TYPE_INVALID); + } +} + +static void +removed_cb (DBusGProxy *proxy, gpointer user_data) +{ + g_signal_emit (G_OBJECT (user_data), signals[REMOVED], 0); +} + +static void +properties_changed_cb (DBusGProxy *proxy, + GHashTable *properties, + gpointer user_data) +{ + NMRemoteConnection *self = NM_REMOTE_CONNECTION (user_data); + GHashTableIter iter; + const char *key; + GValue *value; + + g_hash_table_iter_init (&iter, properties); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) { + if (!strcmp (key, "Unsaved")) { + NM_REMOTE_CONNECTION_GET_PRIVATE (self)->unsaved = g_value_get_boolean (value); + g_object_notify (G_OBJECT (self), NM_REMOTE_CONNECTION_UNSAVED); + } + } +} + +/****************************************************************/ + +/** + * nm_remote_connection_new: + * @bus: a valid and connected D-Bus connection + * @path: the D-Bus path of the connection as exported by the settings service + * + * Creates a new object representing the remote connection. + * + * Returns: the new remote connection object on success, or %NULL on failure + **/ +NMRemoteConnection * +nm_remote_connection_new (DBusGConnection *bus, + const char *path) +{ + g_return_val_if_fail (bus != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + return (NMRemoteConnection *) g_object_new (NM_TYPE_REMOTE_CONNECTION, + NM_REMOTE_CONNECTION_BUS, bus, + NM_CONNECTION_PATH, path, + NULL); +} + +static void +constructed (GObject *object) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_remote_connection_parent_class)->constructed (object); + + g_assert (priv->bus); + g_assert (nm_connection_get_path (NM_CONNECTION (object))); + + priv->proxy = _nm_dbus_new_proxy_for_connection (priv->bus, + nm_connection_get_path (NM_CONNECTION (object)), + NM_DBUS_IFACE_SETTINGS_CONNECTION); + g_assert (priv->proxy); + dbus_g_proxy_set_default_timeout (priv->proxy, G_MAXINT); + + dbus_g_proxy_add_signal (priv->proxy, "Updated", G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "Updated", G_CALLBACK (updated_cb), object, NULL); + + dbus_g_proxy_add_signal (priv->proxy, "Removed", G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "Removed", G_CALLBACK (removed_cb), object, NULL); + + g_signal_connect (priv->proxy, "destroy", G_CALLBACK (proxy_destroy_cb), object); + + /* Monitor properties */ + dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "PropertiesChanged", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "PropertiesChanged", + G_CALLBACK (properties_changed_cb), + object, + NULL); + + priv->props_proxy = _nm_dbus_new_proxy_for_connection (priv->bus, + nm_connection_get_path (NM_CONNECTION (object)), + DBUS_INTERFACE_PROPERTIES); + g_assert (priv->props_proxy); +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable); + GHashTable *hash; + + if (!dbus_g_proxy_call (priv->proxy, "GetSettings", error, + G_TYPE_INVALID, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &hash, + G_TYPE_INVALID)) + return FALSE; + priv->visible = TRUE; + replace_settings (NM_REMOTE_CONNECTION (initable), hash); + g_hash_table_destroy (hash); + + /* Get properties */ + hash = NULL; + if (!dbus_g_proxy_call (priv->props_proxy, "GetAll", error, + G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS_CONNECTION, + G_TYPE_INVALID, + DBUS_TYPE_G_MAP_OF_VARIANT, &hash, + G_TYPE_INVALID)) + return FALSE; + properties_changed_cb (priv->props_proxy, hash, NM_REMOTE_CONNECTION (initable)); + g_hash_table_destroy (hash); + + return TRUE; +} + +typedef struct { + NMRemoteConnection *connection; + GSimpleAsyncResult *result; +} NMRemoteConnectionInitData; + +static void +init_async_complete (NMRemoteConnectionInitData *init_data, GError *error) +{ + if (error) + g_simple_async_result_take_error (init_data->result, error); + else { + g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE); + NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection)->inited = TRUE; + } + + g_simple_async_result_complete (init_data->result); + g_object_unref (init_data->result); + g_slice_free (NMRemoteConnectionInitData, init_data); +} + +static void +init_async_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMRemoteConnectionInitData *init_data = user_data; + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection); + GHashTable *props; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) { + properties_changed_cb (priv->props_proxy, props, init_data->connection); + g_hash_table_destroy (props); + } + init_async_complete (init_data, error); +} + +static void +init_get_settings_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + NMRemoteConnectionInitData *init_data = user_data; + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection); + GHashTable *settings; + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings, + G_TYPE_INVALID); + if (error) { + init_async_complete (init_data, error); + return; + } + + priv->visible = TRUE; + replace_settings (init_data->connection, settings); + g_hash_table_destroy (settings); + + /* Grab properties */ + dbus_g_proxy_begin_call (priv->props_proxy, "GetAll", + init_async_got_properties, init_data, NULL, + G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS_CONNECTION, + G_TYPE_INVALID); +} + +static void +init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + NMRemoteConnectionInitData *init_data; + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable); + + + init_data = g_slice_new0 (NMRemoteConnectionInitData); + init_data->connection = NM_REMOTE_CONNECTION (initable); + init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback, + user_data, init_async); + + dbus_g_proxy_begin_call (priv->proxy, "GetSettings", + init_get_settings_cb, init_data, NULL, + G_TYPE_INVALID); +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return TRUE; +} + +static void +nm_remote_connection_init (NMRemoteConnection *self) +{ +} + +static GObject * +constructor (GType type, guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + static GParamSpec *nm_connection_path = NULL; + static GParamSpec *nm_remote_connection_dbus_path = NULL; + int i, path_index = -1, dbus_path_index = -1; + + if (!nm_connection_path) { + nm_connection_path = + g_object_class_find_property (g_type_class_peek (NM_TYPE_CONNECTION), + NM_CONNECTION_PATH); + nm_remote_connection_dbus_path = + g_object_class_find_property (g_type_class_peek (NM_TYPE_REMOTE_CONNECTION), + NM_REMOTE_CONNECTION_DBUS_PATH); + } + + /* Find the two properties */ + for (i = 0; i < n_construct_properties; i++) { + if (construct_properties[i].pspec == nm_connection_path) + path_index = i; + else if (construct_properties[i].pspec == nm_remote_connection_dbus_path) + dbus_path_index = i; + } + g_assert (path_index != -1 && dbus_path_index != -1); + + /* If NMRemoteConnection:dbus-path is set, and NMConnection:path + * is not, then copy the value of the former to the latter. + */ + if (g_value_get_string (construct_properties[dbus_path_index].value) && + !g_value_get_string (construct_properties[path_index].value)) + construct_properties[path_index].value = construct_properties[dbus_path_index].value; + + return G_OBJECT_CLASS (nm_remote_connection_parent_class)-> + constructor (type, n_construct_properties, construct_properties); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + _nm_remote_connection_ensure_inited (NM_REMOTE_CONNECTION (object)); + + switch (prop_id) { + case PROP_UNSAVED: + g_value_set_boolean (value, NM_REMOTE_CONNECTION_GET_PRIVATE (object)->unsaved); + 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) +{ + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_BUS: + case PROP_DBUS_CONNECTION: + /* Construct only */ + /* priv->bus is set from either of two properties so that it (a) remains + * backwards compatible with the previous "bus" property, and that (b) + * it can be created just like an NMObject using the "dbus-connection", + * even though it's not a subclass of NMObject. So don't overwrite the + * a valid value that the other property set with NULL, if one of the + * properties isn't specified at construction time. + */ + if (!priv->bus) + priv->bus = g_value_dup_boxed (value); + break; + case PROP_DBUS_PATH: + /* Don't need to do anything; see constructor(). */ + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMRemoteConnection *self = NM_REMOTE_CONNECTION (object); + NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self); + + proxy_set_destroyed (self); + + if (priv->proxy) { + g_signal_handlers_disconnect_by_func (priv->proxy, proxy_destroy_cb, object); + g_clear_object (&priv->proxy); + } + g_clear_object (&priv->props_proxy); + + if (priv->bus) { + dbus_g_connection_unref (priv->bus); + priv->bus = NULL; + } + + G_OBJECT_CLASS (nm_remote_connection_parent_class)->dispose (object); +} + +static void +nm_remote_connection_class_init (NMRemoteConnectionClass *remote_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (remote_class); + + g_type_class_add_private (object_class, sizeof (NMRemoteConnectionPrivate)); + + /* virtual methods */ + object_class->constructor = constructor; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->constructed = constructed; + + /* Properties */ + /** + * NMRemoteConnection:bus: + * + * The #DBusGConnection that the #NMRemoteConnection is connected to. + */ + g_object_class_install_property + (object_class, PROP_BUS, + g_param_spec_boxed (NM_REMOTE_CONNECTION_BUS, "", "", + DBUS_TYPE_G_CONNECTION, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /* These are needed so _nm_object_create() can create NMRemoteConnections */ + g_object_class_install_property + (object_class, PROP_DBUS_CONNECTION, + g_param_spec_boxed (NM_REMOTE_CONNECTION_DBUS_CONNECTION, "", "", + DBUS_TYPE_G_CONNECTION, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_DBUS_PATH, + g_param_spec_string (NM_REMOTE_CONNECTION_DBUS_PATH, "", "", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * 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. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_UNSAVED, + g_param_spec_boolean (NM_REMOTE_CONNECTION_UNSAVED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* Signals */ + /** + * NMRemoteConnection::updated: + * @connection: a #NMConnection + * + * This signal is emitted when a connection changes, and it is + * still visible to the user. + */ + signals[UPDATED] = + g_signal_new (NM_REMOTE_CONNECTION_UPDATED, + G_TYPE_FROM_CLASS (remote_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMRemoteConnectionClass, updated), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * NMRemoteConnection::removed: + * @connection: a #NMConnection + * + * This signal is emitted when a connection is either deleted or becomes + * invisible to the current user. + */ + signals[REMOVED] = + g_signal_new (NM_REMOTE_CONNECTION_REMOVED, + G_TYPE_FROM_CLASS (remote_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMRemoteConnectionClass, removed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* Private signal */ + signals[VISIBLE] = + g_signal_new ("visible", + G_TYPE_FROM_CLASS (remote_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); +} + +static void +nm_remote_connection_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} diff --git a/libnm/nm-remote-connection.h b/libnm/nm-remote-connection.h new file mode 100644 index 0000000000..8292c23545 --- /dev/null +++ b/libnm/nm-remote-connection.h @@ -0,0 +1,149 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2011 Red Hat, Inc. + */ + +#ifndef __NM_REMOTE_CONNECTION_H__ +#define __NM_REMOTE_CONNECTION_H__ + +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +#include <nm-connection.h> + +G_BEGIN_DECLS + +#define NM_TYPE_REMOTE_CONNECTION (nm_remote_connection_get_type ()) +#define NM_REMOTE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnection)) +#define NM_REMOTE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionClass)) +#define NM_IS_REMOTE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_REMOTE_CONNECTION)) +#define NM_IS_REMOTE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_REMOTE_CONNECTION)) +#define NM_REMOTE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionClass)) + + +/** + * NMRemoteConnectionError: + * @NM_REMOTE_CONNECTION_ERROR_UNKNOWN: unknown or unclassified error + * @NM_REMOTE_CONNECTION_ERROR_DISCONNECTED: dbus disconnected + */ +typedef enum { + NM_REMOTE_CONNECTION_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_REMOTE_CONNECTION_ERROR_DISCONNECTED, /*< nick=Disconnected >*/ +} NMRemoteConnectionError; + +#define NM_REMOTE_CONNECTION_ERROR (nm_remote_connection_error_quark ()) +GQuark nm_remote_connection_error_quark (void); + +/* Properties */ +#define NM_REMOTE_CONNECTION_UNSAVED "unsaved" + +/* Signals */ +#define NM_REMOTE_CONNECTION_UPDATED "updated" +#define NM_REMOTE_CONNECTION_REMOVED "removed" + +typedef struct { + NMConnection parent; +} NMRemoteConnection; + +typedef struct { + NMConnectionClass parent_class; + + /* Signals */ + void (*updated) (NMRemoteConnection *connection, + GHashTable *new_settings); + + void (*removed) (NMRemoteConnection *connection); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMRemoteConnectionClass; + +/** + * NMRemoteConnectionResultFunc: + * @connection: the connection for which an operation was performed + * @error: on failure, a descriptive error + * @user_data: user data passed to function which began the operation + * + * Called when NetworkManager has finished an asynchronous operation on a + * connection, like commit changes, deleting, saving, etc. + */ +typedef void (*NMRemoteConnectionResultFunc) (NMRemoteConnection *connection, + GError *error, + gpointer user_data); + +/* Backwards compatibility */ +typedef NMRemoteConnectionResultFunc NMRemoteConnectionCommitFunc; +typedef NMRemoteConnectionResultFunc NMRemoteConnectionDeleteFunc; + +/** + * NMRemoteConnectionGetSecretsFunc: + * @connection: the connection for which secrets were requested + * @secrets: (element-type utf8 GLib.HashTable): on success, a hash table of + * hash tables, with each inner hash mapping a setting property to a #GValue + * containing that property's value + * @error: on failure, a descriptive error + * @user_data: user data passed to nm_remote_connection_get_secrets() + * + * Called when NetworkManager returns secrets in response to a request for + * secrets via nm_remote_connection_get_secrets(). + */ +typedef void (*NMRemoteConnectionGetSecretsFunc) (NMRemoteConnection *connection, + GHashTable *secrets, + GError *error, + gpointer user_data); + +GType nm_remote_connection_get_type (void); + +NMRemoteConnection *nm_remote_connection_new (DBusGConnection *bus, + const char *path); + +void nm_remote_connection_commit_changes (NMRemoteConnection *connection, + NMRemoteConnectionResultFunc callback, + gpointer user_data); + +NM_AVAILABLE_IN_0_9_10 +void nm_remote_connection_commit_changes_unsaved (NMRemoteConnection *connection, + NMRemoteConnectionResultFunc callback, + gpointer user_data); + +NM_AVAILABLE_IN_0_9_10 +void nm_remote_connection_save (NMRemoteConnection *connection, + NMRemoteConnectionResultFunc callback, + gpointer user_data); + +void nm_remote_connection_delete (NMRemoteConnection *connection, + NMRemoteConnectionResultFunc callback, + gpointer user_data); + +void nm_remote_connection_get_secrets (NMRemoteConnection *connection, + const char *setting_name, + NMRemoteConnectionGetSecretsFunc callback, + gpointer user_data); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_remote_connection_get_unsaved (NMRemoteConnection *connection); + +G_END_DECLS + +#endif /* __NM_REMOTE_CONNECTION__ */ diff --git a/libnm/nm-remote-settings.c b/libnm/nm-remote-settings.c new file mode 100644 index 0000000000..49d7780e6e --- /dev/null +++ b/libnm/nm-remote-settings.c @@ -0,0 +1,1566 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 Novell, Inc. + * Copyright 2009 - 2012 Red Hat, Inc. + */ + +#include <string.h> +#include <NetworkManager.h> +#include <nm-connection.h> + +#include "nm-dbus-glib-types.h" +#include "nm-remote-settings.h" +#include "nm-remote-connection-private.h" +#include "nm-object-private.h" +#include "nm-dbus-helpers-private.h" +#include "nm-glib-compat.h" +#include "nm-object-private.h" + +/** + * SECTION:nm-remote-settings + * @Short_description: A helper for NetworkManager's settings API + * @Title: NMRemoteSettings + * @See_also:#NMRemoteConnection, #NMClient + * + * The #NMRemoteSettings object represents NetworkManager's "settings" service, + * which stores network configuration and allows authenticated clients to + * add, delete, and modify that configuration. The data required to connect + * to a specific network is called a "connection" and encapsulated by the + * #NMConnection object. Once a connection is known to NetworkManager, having + * either been added by a user or read from on-disk storage, the + * #NMRemoteSettings object creates a #NMRemoteConnection object which + * represents this stored connection. Use the #NMRemoteConnection object to + * perform any operations like modification or deletion. + * + * To add a new network connection to the NetworkManager settings service, first + * build up a template #NMConnection object. Since this connection is not yet + * added to NetworkManager, it is known only to your program and is not yet + * an #NMRemoteConnection. Then ask #NMRemoteSettings to add your connection. + * When the connection is added successfully, the supplied callback is called + * and returns to your program the new #NMRemoteConnection which represents + * the stored object known to NetworkManager. + * + * |[<!-- language="C" --> + * static void + * added_cb (NMRemoteSettings *settings, + * NMRemoteConnection *remote, + * GError *error, + * gpointer user_data) + * { + * if (error) + * g_print ("Error adding connection: %s", error->message); + * else { + * g_print ("Added: %s\n", nm_connection_get_path (NM_CONNECTION (remote))); + * /* Use 'remote' with nm_remote_connection_commit_changes() to save + * * changes and nm_remote_connection_delete() to delete the connection */ + * } + * } + * + * static gboolean + * add_wired_connection (const char *human_name) + * { + * NMConnection *connection; + * NMSettingConnection *s_con; + * NMSettingWired *s_wired; + * char *uuid; + * gboolean success; + * + * connection = nm_connection_new (); + * + * /* Build up the 'connection' setting */ + * s_con = (NMSettingConnection *) nm_setting_connection_new (); + * uuid = nm_utils_uuid_generate (); + * g_object_set (G_OBJECT (s_con), + * NM_SETTING_CONNECTION_UUID, uuid, + * NM_SETTING_CONNECTION_ID, human_name, + * NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + * NULL); + * g_free (uuid); + * nm_connection_add_setting (connection, NM_SETTING (s_con)); + * + * /* Add the required 'wired' setting as this is a wired connection */ + * nm_connection_add_setting (connection, nm_setting_wired_new ()); + * + * /* Add an 'ipv4' setting using AUTO configuration (eg DHCP) */ + * s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + * g_object_set (G_OBJECT (s_ip4), + * NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + * NULL); + * nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + * + * /* Ask NetworkManager to store the connection */ + * success = nm_remote_settings_add_connection (settings, connection, added_cb, loop); + * + * /* Release the template connection; the actual stored connection will + * * be returned in added_cb() */ + * g_object_unref (connection); + * + * /* Let glib event loop run and added_cb() will be called when NetworkManager + * * is done adding the new connection. */ + * + * return success; + * } + * ]| + */ + +static void nm_remote_settings_initable_iface_init (GInitableIface *iface); +static void nm_remote_settings_async_initable_iface_init (GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (NMRemoteSettings, nm_remote_settings, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_remote_settings_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_remote_settings_async_initable_iface_init); + ) + +#define NM_REMOTE_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_REMOTE_SETTINGS, NMRemoteSettingsPrivate)) + +typedef struct { + DBusGConnection *bus; + gboolean private_bus; + gboolean inited; + + DBusGProxy *proxy; + GHashTable *connections; + GHashTable *pending; /* Connections we don't have settings for yet */ + gboolean service_running; + guint32 init_left; + + /* AddConnectionInfo objects that are waiting for the connection to become initialized */ + GSList *add_list; + + DBusGProxy *props_proxy; + char *hostname; + gboolean can_modify; + + DBusGProxy *dbus_proxy; + + DBusGProxyCall *listcon_call; +} NMRemoteSettingsPrivate; + +enum { + PROP_0, + PROP_BUS, + PROP_SERVICE_RUNNING, + PROP_HOSTNAME, + PROP_CAN_MODIFY, + + LAST_PROP +}; + +/* Signals */ +enum { + NEW_CONNECTION, + CONNECTIONS_READ, + + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +/**********************************************************************/ + +/** + * nm_remote_settings_error_quark: + * + * Registers an error quark for #NMRemoteSettings if necessary. + * + * Returns: the error quark used for #NMRemoteSettings errors. + **/ +GQuark +nm_remote_settings_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-remote-settings-error-quark"); + return quark; +} + +/**********************************************************************/ + +static void +_nm_remote_settings_ensure_inited (NMRemoteSettings *self) +{ + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + GError *error = NULL; + + if (!priv->inited) { + if (!g_initable_init (G_INITABLE (self), NULL, &error)) { + /* Don't warn when the call times out because the settings service can't + * be activated or whatever. + */ + if (!g_error_matches (error, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) { + g_warning ("%s: (NMRemoteSettings) error initializing: %s\n", + __func__, error->message); + } + g_error_free (error); + } + priv->inited = TRUE; + } +} + +/**********************************************************************/ + +typedef struct { + NMRemoteSettings *self; + NMRemoteSettingsAddConnectionFunc callback; + gpointer callback_data; + NMRemoteConnection *connection; +} AddConnectionInfo; + +static AddConnectionInfo * +add_connection_info_find (NMRemoteSettings *self, NMRemoteConnection *connection) +{ + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->add_list; iter; iter = g_slist_next (iter)) { + AddConnectionInfo *info = iter->data; + + if (info->connection == connection) + return info; + } + + return NULL; +} + +static void +add_connection_info_dispose (NMRemoteSettings *self, AddConnectionInfo *info) +{ + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + + priv->add_list = g_slist_remove (priv->add_list, info); + + g_free (info); +} + +static void +add_connection_info_complete (NMRemoteSettings *self, + AddConnectionInfo *info, + GError *error) +{ + g_return_if_fail (info != NULL); + + info->callback (info->self, error ? NULL : info->connection, error, info->callback_data); + add_connection_info_dispose (self, info); +} + +/** + * nm_remote_settings_get_connection_by_id: + * @settings: the %NMRemoteSettings + * @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. + * + * Since: 0.9.10 + **/ +NMRemoteConnection * +nm_remote_settings_get_connection_by_id (NMRemoteSettings *settings, const char *id) +{ + NMRemoteSettingsPrivate *priv; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), NULL); + g_return_val_if_fail (id != NULL, NULL); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + if (priv->service_running) { + GHashTableIter iter; + NMConnection *candidate; + + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &candidate)) { + + if (!strcmp (id, nm_connection_get_id (candidate))) + return NM_REMOTE_CONNECTION (candidate); + } + } + + return NULL; +} + +/** + * nm_remote_settings_get_connection_by_path: + * @settings: the %NMRemoteSettings + * @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 + **/ +NMRemoteConnection * +nm_remote_settings_get_connection_by_path (NMRemoteSettings *settings, const char *path) +{ + NMRemoteSettingsPrivate *priv; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), NULL); + g_return_val_if_fail (path != NULL, NULL); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + return priv->service_running ? g_hash_table_lookup (priv->connections, path) : NULL; +} + +/** + * nm_remote_settings_get_connection_by_uuid: + * @settings: the %NMRemoteSettings + * @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 + **/ +NMRemoteConnection * +nm_remote_settings_get_connection_by_uuid (NMRemoteSettings *settings, const char *uuid) +{ + NMRemoteSettingsPrivate *priv; + GHashTableIter iter; + NMRemoteConnection *candidate; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), NULL); + g_return_val_if_fail (uuid != NULL, NULL); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + if (priv->service_running) { + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) { + if (g_strcmp0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate))) == 0) + return candidate; + } + } + + return NULL; +} + +static void +connection_removed_cb (NMRemoteConnection *remote, gpointer user_data) +{ + NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + AddConnectionInfo *addinfo; + GError *add_error; + const char *path; + + /* Might have been removed while it was waiting to be initialized */ + addinfo = add_connection_info_find (self, remote); + if (addinfo) { + add_error = g_error_new_literal (NM_REMOTE_SETTINGS_ERROR, + NM_REMOTE_SETTINGS_ERROR_CONNECTION_REMOVED, + "Connection removed before it was initialized"); + add_connection_info_complete (self, addinfo, add_error); + g_error_free (add_error); + } + + path = nm_connection_get_path (NM_CONNECTION (remote)); + g_hash_table_remove (priv->connections, path); + g_hash_table_remove (priv->pending, path); +} + +static void connection_visible_cb (NMRemoteConnection *remote, + gboolean visible, + gpointer user_data); + +/* Takes a reference to the connection when adding to 'to' */ +static void +move_connection (NMRemoteSettings *self, + NMRemoteConnection *remote, + GHashTable *from, + GHashTable *to) +{ + const char *path = nm_connection_get_path (NM_CONNECTION (remote)); + + g_hash_table_insert (to, g_strdup (path), g_object_ref (remote)); + if (from) + g_hash_table_remove (from, path); + + /* Setup connection signals since removing from 'from' clears them, but + * also the first time the connection is added to a hash if 'from' is NULL. + */ + if (!g_signal_handler_find (remote, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, connection_removed_cb, NULL)) { + g_signal_connect (remote, + NM_REMOTE_CONNECTION_REMOVED, + G_CALLBACK (connection_removed_cb), + self); + } + + if (!g_signal_handler_find (remote, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, connection_visible_cb, NULL)) { + g_signal_connect (remote, + "visible", + G_CALLBACK (connection_visible_cb), + self); + } +} + +static void +connection_visible_cb (NMRemoteConnection *remote, + gboolean visible, + gpointer user_data) +{ + NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + const char *path; + + path = nm_connection_get_path (NM_CONNECTION (remote)); + g_assert (path); + + /* When a connection becomes invisible, we put it back in the pending + * hash until it becomes visible again. When it does, we move it back to + * the normal connections hash. + */ + if (visible) { + /* Connection visible to this user again */ + if (g_hash_table_lookup (priv->pending, path)) { + /* Move connection from pending to visible hash; emit for clients */ + move_connection (self, remote, priv->pending, priv->connections); + g_signal_emit (self, signals[NEW_CONNECTION], 0, remote); + } + } else { + /* Connection now invisible to this user */ + if (g_hash_table_lookup (priv->connections, path)) { + /* Move connection to pending hash and wait for it to become visible again */ + move_connection (self, remote, priv->connections, priv->pending); + + /* Signal to clients that the connection is gone; but we have to + * block our connection removed handler so we don't destroy + * the connection when the signal is emitted. + */ + g_signal_handlers_block_by_func (remote, connection_removed_cb, self); + g_signal_emit_by_name (remote, NM_REMOTE_CONNECTION_REMOVED); + g_signal_handlers_unblock_by_func (remote, connection_removed_cb, self); + } + } +} + +static void +connection_inited (GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMRemoteConnection *remote = NM_REMOTE_CONNECTION (source); + NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + AddConnectionInfo *addinfo; + const char *path; + GError *error = NULL, *local; + + path = nm_connection_get_path (NM_CONNECTION (remote)); + addinfo = add_connection_info_find (self, remote); + + if (g_async_initable_init_finish (G_ASYNC_INITABLE (remote), result, &error)) { + /* Connection is initialized and visible; expose it to clients */ + move_connection (self, remote, priv->pending, priv->connections); + + /* If there's a pending AddConnection request, complete that here before + * signaling new-connection. + */ + if (addinfo) + add_connection_info_complete (self, addinfo, NULL); + + /* Finally, let users know of the new connection now that it has all + * its settings and is valid. + */ + g_signal_emit (self, signals[NEW_CONNECTION], 0, remote); + } else { + if (addinfo) { + local = g_error_new (NM_REMOTE_SETTINGS_ERROR, + NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE, + "Connection not visible or not available: %s", + error ? error->message : "(unknown)"); + add_connection_info_complete (self, addinfo, local); + g_error_free (local); + } + + /* PermissionDenied means the connection isn't visible to this user, so + * keep it in priv->pending to be notified later of visibility changes. + * Otherwise forget it. + */ + if (!dbus_g_error_has_name (error, "org.freedesktop.NetworkManager.Settings.PermissionDenied")) + g_hash_table_remove (priv->pending, path); + + g_error_free (error); + } + + /* Let listeners know that all connections have been found */ + priv->init_left--; + if (priv->init_left == 0) + g_signal_emit (self, signals[CONNECTIONS_READ], 0); +} + +static NMRemoteConnection * +new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + NMRemoteConnection *connection = NULL; + + /* Make double-sure we don't already have it */ + connection = g_hash_table_lookup (priv->pending, path); + if (connection) + return connection; + connection = g_hash_table_lookup (priv->connections, path); + if (connection) + return connection; + + /* Create a new connection object for it */ + connection = nm_remote_connection_new (priv->bus, path); + if (connection) { + g_async_initable_init_async (G_ASYNC_INITABLE (connection), + G_PRIORITY_DEFAULT, NULL, + connection_inited, self); + + /* Add the connection to the pending table to wait for it to retrieve + * it's settings asynchronously over D-Bus. The connection isn't + * really valid until it has all its settings, so hide it until it does. + */ + move_connection (self, connection, NULL, priv->pending); + g_object_unref (connection); /* move_connection() takes a ref */ + } + return connection; +} + +static void +fetch_connections_done (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + GPtrArray *connections; + GError *error = NULL; + int i; + + g_warn_if_fail (priv->listcon_call == call); + priv->listcon_call = NULL; + + if (!dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &connections, + G_TYPE_INVALID)) { + if ( !g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN) + && !g_error_matches (error, DBUS_GERROR, DBUS_GERROR_NAME_HAS_NO_OWNER) + && priv->service_running) { + g_warning ("%s: error fetching connections: (%d) %s.", + __func__, + error->code, + error->message ? error->message : "(unknown)"); + } + g_clear_error (&error); + + /* We tried to read connections and failed */ + g_signal_emit (self, signals[CONNECTIONS_READ], 0); + return; + } + + /* Let listeners know we are done getting connections */ + if (connections->len == 0) + g_signal_emit (self, signals[CONNECTIONS_READ], 0); + else { + priv->init_left = connections->len; + for (i = 0; i < connections->len; i++) { + char *path = g_ptr_array_index (connections, i); + + new_connection_cb (proxy, path, user_data); + g_free (path); + } + } + + g_ptr_array_free (connections, TRUE); +} + +/** + * nm_remote_settings_list_connections: + * @settings: the %NMRemoteSettings + * + * Returns: (transfer container) (element-type NMRemoteConnection): a + * list containing all connections provided by the remote settings service. + * Each element of the returned list is a %NMRemoteConnection instance, which is + * owned by the %NMRemoteSettings object and should not be freed by the caller. + * The returned list is, however, owned by the caller and should be freed + * using g_slist_free() when no longer required. + **/ +GSList * +nm_remote_settings_list_connections (NMRemoteSettings *settings) +{ + NMRemoteSettingsPrivate *priv; + GSList *list = NULL; + GHashTableIter iter; + gpointer value; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), NULL); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + if (priv->service_running) { + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, NULL, &value)) + list = g_slist_prepend (list, NM_REMOTE_CONNECTION (value)); + } + + return list; +} + +static void +add_connection_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + AddConnectionInfo *info = user_data; + GError *error = NULL; + char *path = NULL; + + if (dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_G_OBJECT_PATH, &path, G_TYPE_INVALID)) { + info->connection = new_connection_cb (proxy, path, info->self); + g_assert (info->connection); + /* Wait until this connection is fully initialized before calling the callback */ + g_free (path); + } else + add_connection_info_complete (info->self, info, error); + + g_clear_error (&error); +} + +/** + * nm_remote_settings_add_connection: + * @settings: the %NMRemoteSettings + * @connection: the connection to add. Note that this object's settings will be + * added, not the object itself + * @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. The connection is immediately written to disk. @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. + * + * Returns: %TRUE if the request was successful, %FALSE if it failed + **/ +gboolean +nm_remote_settings_add_connection (NMRemoteSettings *settings, + NMConnection *connection, + NMRemoteSettingsAddConnectionFunc callback, + gpointer user_data) +{ + NMRemoteSettingsPrivate *priv; + AddConnectionInfo *info; + GHashTable *new_settings; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + if (!priv->service_running) + return FALSE; + + info = g_malloc0 (sizeof (AddConnectionInfo)); + info->self = settings; + info->callback = callback; + info->callback_data = user_data; + + new_settings = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL); + dbus_g_proxy_begin_call (priv->proxy, "AddConnection", + add_connection_done, + info, + NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, new_settings, + G_TYPE_INVALID); + g_hash_table_destroy (new_settings); + + priv->add_list = g_slist_append (priv->add_list, info); + + return TRUE; +} + +/** + * nm_remote_settings_add_connection_unsaved: + * @settings: the %NMRemoteSettings + * @connection: the connection to add. Note that this object's settings will be + * added, not the object itself + * @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. The connection is not written to disk, which may be done at + * a later time by calling the connection's nm_remote_connection_commit_changes() + * method. + * + * Returns: %TRUE if the request was successful, %FALSE if it failed + * + * Since: 0.9.10 + **/ +gboolean +nm_remote_settings_add_connection_unsaved (NMRemoteSettings *settings, + NMConnection *connection, + NMRemoteSettingsAddConnectionFunc callback, + gpointer user_data) +{ + NMRemoteSettingsPrivate *priv; + AddConnectionInfo *info; + GHashTable *new_settings; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + if (!priv->service_running) + return FALSE; + + info = g_malloc0 (sizeof (AddConnectionInfo)); + info->self = settings; + info->callback = callback; + info->callback_data = user_data; + + new_settings = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL); + dbus_g_proxy_begin_call (priv->proxy, "AddConnectionUnsaved", + add_connection_done, + info, + NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, new_settings, + G_TYPE_INVALID); + g_hash_table_destroy (new_settings); + + priv->add_list = g_slist_append (priv->add_list, info); + + return TRUE; +} + +/** + * nm_remote_settings_load_connections: + * @settings: the %NMRemoteSettings + * @filenames: %NULL-terminated array of filenames to load + * @failures: (out) (transfer full): on return, a %NULL-terminated array of + * filenames that failed to load + * @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 + * @settings'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 if NetworkManager at least tried to load @filenames, + * %FALSE if an error occurred (eg, permission denied). + * + * Since: 0.9.10 + **/ +gboolean +nm_remote_settings_load_connections (NMRemoteSettings *settings, + char **filenames, + char ***failures, + GError **error) +{ + NMRemoteSettingsPrivate *priv; + char **my_failures = NULL; + gboolean ret; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE); + g_return_val_if_fail (filenames != NULL, FALSE); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + if (!priv->service_running) { + g_set_error_literal (error, NM_REMOTE_SETTINGS_ERROR, + NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE, + "NetworkManager is not running."); + return FALSE; + } + + if (!dbus_g_proxy_call (priv->proxy, "LoadConnections", error, + G_TYPE_STRV, filenames, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &ret, + G_TYPE_STRV, &my_failures, + G_TYPE_INVALID)) + ret = FALSE; + + if (failures) { + if (my_failures && !*my_failures) + g_clear_pointer (&my_failures, g_free); + *failures = my_failures; + } else + g_strfreev (my_failures); + + return ret; +} + +/** + * nm_remote_settings_reload_connections: + * @settings: the #NMRemoteSettings + * @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 + * + * Since: 0.9.10 + **/ +gboolean +nm_remote_settings_reload_connections (NMRemoteSettings *settings, + GError **error) +{ + NMRemoteSettingsPrivate *priv; + gboolean success; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + if (!priv->service_running) { + g_set_error_literal (error, NM_REMOTE_SETTINGS_ERROR, + NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE, + "NetworkManager is not running."); + return FALSE; + } + + if (!dbus_g_proxy_call (priv->proxy, "ReloadConnections", error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &success, + G_TYPE_INVALID)) + return FALSE; + return success; +} + +static void +clear_one_hash (GHashTable *table) +{ + GHashTableIter iter; + gpointer value; + GSList *list = NULL, *list_iter; + + /* Build up the list of connections; we can't emit "removed" during hash + * table iteration because emission of the "removed" signal may trigger code + * that explicitly removes the connection from the hash table somewhere + * else. + */ + g_hash_table_iter_init (&iter, table); + while (g_hash_table_iter_next (&iter, NULL, &value)) + list = g_slist_prepend (list, NM_REMOTE_CONNECTION (value)); + + for (list_iter = list; list_iter; list_iter = g_slist_next (list_iter)) + g_signal_emit_by_name (NM_REMOTE_CONNECTION (list_iter->data), NM_REMOTE_CONNECTION_REMOVED); + g_slist_free (list); + + g_hash_table_remove_all (table); +} + +typedef struct { + NMRemoteSettings *settings; + NMRemoteSettingsSaveHostnameFunc callback; + gpointer callback_data; +} SaveHostnameInfo; + +static void +save_hostname_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + SaveHostnameInfo *info = user_data; + GError *error = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + if (info->callback != NULL) + info->callback (info->settings, error, info->callback_data); + g_clear_error (&error); +} + +/** + * nm_remote_settings_save_hostname: + * @settings: the %NMRemoteSettings + * @hostname: the new persistent hostname to set, or %NULL to clear any existing + * persistent hostname + * @callback: (scope async) (allow-none): callback to be called when the + * hostname 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. + * + * Returns: %TRUE if the request was successful, %FALSE if it failed + **/ +gboolean +nm_remote_settings_save_hostname (NMRemoteSettings *settings, + const char *hostname, + NMRemoteSettingsSaveHostnameFunc callback, + gpointer user_data) +{ + NMRemoteSettingsPrivate *priv; + SaveHostnameInfo *info; + + g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE); + g_return_val_if_fail (hostname != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + + _nm_remote_settings_ensure_inited (settings); + + if (!priv->service_running) + return FALSE; + + info = g_malloc0 (sizeof (SaveHostnameInfo)); + info->settings = settings; + info->callback = callback; + info->callback_data = user_data; + + dbus_g_proxy_begin_call (priv->proxy, "SaveHostname", + save_hostname_cb, + info, + g_free, + G_TYPE_STRING, hostname ? hostname : "", + G_TYPE_INVALID); + return TRUE; +} + +static void +properties_changed_cb (DBusGProxy *proxy, + GHashTable *properties, + gpointer user_data) +{ + NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + GHashTableIter iter; + gpointer key, tmp; + + g_hash_table_iter_init (&iter, properties); + while (g_hash_table_iter_next (&iter, &key, &tmp)) { + GValue *value = tmp; + + if (!strcmp ((const char *) key, "Hostname")) { + g_free (priv->hostname); + priv->hostname = g_value_dup_string (value); + g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_HOSTNAME); + } + + if (!strcmp ((const char *) key, "CanModify")) { + priv->can_modify = g_value_get_boolean (value); + g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_CAN_MODIFY); + } + } +} + +static void +nm_appeared_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + GHashTable *props = NULL; + + if (dbus_g_proxy_end_call (proxy, call, NULL, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) { + properties_changed_cb (priv->props_proxy, props, self); + g_hash_table_destroy (props); + } +} + +static void +name_owner_changed (DBusGProxy *proxy, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + const char *sname = NM_DBUS_SERVICE; + + if (!strcmp (name, sname)) { + if (new_owner && strlen (new_owner) > 0) { + priv->service_running = TRUE; + + priv->listcon_call = dbus_g_proxy_begin_call (priv->proxy, "ListConnections", + fetch_connections_done, self, NULL, + G_TYPE_INVALID); + + dbus_g_proxy_begin_call (priv->props_proxy, "GetAll", + nm_appeared_got_properties, self, NULL, + G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS, + G_TYPE_INVALID); + } else { + priv->service_running = FALSE; + + clear_one_hash (priv->pending); + clear_one_hash (priv->connections); + + /* Clear properties */ + g_free (priv->hostname); + priv->hostname = NULL; + g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_HOSTNAME); + + priv->can_modify = FALSE; + g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_CAN_MODIFY); + + if (priv->listcon_call) { + dbus_g_proxy_cancel_call (priv->proxy, priv->listcon_call); + priv->listcon_call = NULL; + } + } + g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_SERVICE_RUNNING); + } +} + +/****************************************************************/ + +/** + * nm_remote_settings_new: + * @bus: (allow-none): a valid and connected D-Bus connection + * + * Creates a new object representing the remote settings service. + * + * Note that this will do blocking D-Bus calls to initialize the + * settings object. You can use nm_remote_settings_new_async() if you + * want to avoid that. + * + * Returns: the new remote settings object on success, or %NULL on failure + **/ +NMRemoteSettings * +nm_remote_settings_new (DBusGConnection *bus) +{ + NMRemoteSettings *self; + + self = g_object_new (NM_TYPE_REMOTE_SETTINGS, NM_REMOTE_SETTINGS_BUS, bus, NULL); + _nm_remote_settings_ensure_inited (self); + return self; +} + +static void +remote_settings_inited (GObject *source, GAsyncResult *result, gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + GError *error = NULL; + + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source), result, &error)) + g_simple_async_result_take_error (simple, error); + else + g_simple_async_result_set_op_res_gpointer (simple, source, g_object_unref); + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +/** + * nm_remote_settings_new_async: + * @bus: (allow-none): a valid and connected D-Bus connection + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the settings object is created + * @user_data: data for @callback + * + * Creates a new object representing the remote settings service and + * begins asynchronously initializing it. @callback will be called + * when it is done; use nm_remote_settings_new_finish() to get the + * result. + **/ +void +nm_remote_settings_new_async (DBusGConnection *bus, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + NMRemoteSettings *self; + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new (NULL, callback, user_data, nm_remote_settings_new_async); + + self = g_object_new (NM_TYPE_REMOTE_SETTINGS, + NM_REMOTE_SETTINGS_BUS, bus, + NULL); + g_async_initable_init_async (G_ASYNC_INITABLE (self), G_PRIORITY_DEFAULT, + cancellable, remote_settings_inited, simple); +} + +/** + * nm_remote_settings_new_finish: + * @result: a #GAsyncResult + * @error: location for a #GError, or %NULL + * + * Gets the result of an nm_remote_settings_new_async() call. + * + * Returns: a new #NMRemoteSettings object, or %NULL on error + **/ +NMRemoteSettings * +nm_remote_settings_new_finish (GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, nm_remote_settings_new_async), NULL); + + simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + else + return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); +} + +static void +forget_connection (gpointer user_data) +{ + NMRemoteConnection *remote = NM_REMOTE_CONNECTION (user_data); + + g_signal_handlers_disconnect_matched (remote, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, connection_removed_cb, NULL); + g_signal_handlers_disconnect_matched (remote, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, connection_visible_cb, NULL); + g_object_unref (remote); +} + +static void +nm_remote_settings_init (NMRemoteSettings *self) +{ + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + + priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, forget_connection); + priv->pending = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, forget_connection); +} + +static void +constructed (GObject *object) +{ + NMRemoteSettingsPrivate *priv; + + priv = NM_REMOTE_SETTINGS_GET_PRIVATE (object); + + if (priv->private_bus == FALSE) { + /* D-Bus proxy for clearing connections on NameOwnerChanged */ + priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + g_assert (priv->dbus_proxy); + + dbus_g_object_register_marshaller (g_cclosure_marshal_generic, + G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->dbus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->dbus_proxy, + "NameOwnerChanged", + G_CALLBACK (name_owner_changed), + object, NULL); + } + + priv->proxy = _nm_dbus_new_proxy_for_connection (priv->bus, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_IFACE_SETTINGS); + g_assert (priv->proxy); + dbus_g_proxy_set_default_timeout (priv->proxy, G_MAXINT); + + dbus_g_proxy_add_signal (priv->proxy, "NewConnection", + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "NewConnection", + G_CALLBACK (new_connection_cb), + object, + NULL); + + /* D-Bus properties proxy */ + priv->props_proxy = _nm_dbus_new_proxy_for_connection (priv->bus, + NM_DBUS_PATH_SETTINGS, + "org.freedesktop.DBus.Properties"); + g_assert (priv->props_proxy); + + /* Monitor properties */ + dbus_g_object_register_marshaller (g_cclosure_marshal_generic, + G_TYPE_NONE, + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "PropertiesChanged", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "PropertiesChanged", + G_CALLBACK (properties_changed_cb), + object, + NULL); +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMRemoteSettings *settings = NM_REMOTE_SETTINGS (initable); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings); + GHashTable *props; + + if (priv->private_bus == FALSE) { + if (!dbus_g_proxy_call (priv->dbus_proxy, "NameHasOwner", error, + G_TYPE_STRING, NM_DBUS_SERVICE, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &priv->service_running, + G_TYPE_INVALID)) { + priv->service_running = FALSE; + return FALSE; + } + + /* If NM isn't running we'll grab properties from name_owner_changed() + * when it starts. + */ + if (!priv->service_running) + return TRUE; + } else + priv->service_running = TRUE; + + priv->listcon_call = dbus_g_proxy_begin_call (priv->proxy, "ListConnections", + fetch_connections_done, NM_REMOTE_SETTINGS (initable), NULL, + G_TYPE_INVALID); + + /* Get properties */ + if (!dbus_g_proxy_call (priv->props_proxy, "GetAll", error, + G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS, + G_TYPE_INVALID, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) + return FALSE; + properties_changed_cb (priv->props_proxy, props, settings); + g_hash_table_destroy (props); + + return TRUE; +} + +typedef struct { + NMRemoteSettings *settings; + GSimpleAsyncResult *result; +} NMRemoteSettingsInitData; + +static void +init_async_complete (NMRemoteSettingsInitData *init_data) +{ + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (init_data->settings); + + priv->inited = TRUE; + + g_simple_async_result_complete (init_data->result); + g_object_unref (init_data->result); + g_slice_free (NMRemoteSettingsInitData, init_data); +} + +static void +init_read_connections (NMRemoteSettings *settings, gpointer user_data) +{ + NMRemoteSettingsInitData *init_data = user_data; + + g_signal_handlers_disconnect_by_func (settings, G_CALLBACK (init_read_connections), user_data); + + init_async_complete (init_data); +} + +static void +init_async_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, + gpointer user_data) +{ + NMRemoteSettingsInitData *init_data = user_data; + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (init_data->settings); + GHashTable *props; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) { + properties_changed_cb (priv->props_proxy, props, init_data->settings); + g_hash_table_destroy (props); + g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE); + } else + g_simple_async_result_take_error (init_data->result, error); + + /* Read connections and wait for the result */ + priv->listcon_call = dbus_g_proxy_begin_call (priv->proxy, "ListConnections", + fetch_connections_done, init_data->settings, NULL, + G_TYPE_INVALID); + g_signal_connect (init_data->settings, "connections-read", + G_CALLBACK (init_read_connections), init_data); +} + +static void +init_get_properties (NMRemoteSettingsInitData *init_data) +{ + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (init_data->settings); + + dbus_g_proxy_begin_call (priv->props_proxy, "GetAll", + init_async_got_properties, init_data, NULL, + G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS, + G_TYPE_INVALID); +} + +static void +init_async_got_manager_running (DBusGProxy *proxy, DBusGProxyCall *call, + gpointer user_data) +{ + NMRemoteSettingsInitData *init_data = user_data; + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (init_data->settings); + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_BOOLEAN, &priv->service_running, + G_TYPE_INVALID)) { + g_simple_async_result_take_error (init_data->result, error); + init_async_complete (init_data); + return; + } + + if (!priv->service_running) { + g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE); + init_async_complete (init_data); + return; + } + + init_get_properties (init_data); +} + +static void +init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + NMRemoteSettingsInitData *init_data; + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (initable); + + init_data = g_slice_new0 (NMRemoteSettingsInitData); + init_data->settings = NM_REMOTE_SETTINGS (initable); + init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback, + user_data, init_async); + + if (priv->private_bus) + init_get_properties (init_data); + else { + /* Check if NM is running */ + dbus_g_proxy_begin_call (priv->dbus_proxy, "NameHasOwner", + init_async_got_manager_running, + init_data, NULL, + G_TYPE_STRING, NM_DBUS_SERVICE, + G_TYPE_INVALID); + } +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return TRUE; +} + +static void +dispose (GObject *object) +{ + NMRemoteSettings *self = NM_REMOTE_SETTINGS (object); + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self); + + while (g_slist_length (priv->add_list)) + add_connection_info_dispose (self, (AddConnectionInfo *) priv->add_list->data); + + if (priv->connections) { + g_hash_table_destroy (priv->connections); + priv->connections = NULL; + } + + if (priv->pending) { + g_hash_table_destroy (priv->pending); + priv->pending = NULL; + } + + g_free (priv->hostname); + priv->hostname = NULL; + + g_clear_object (&priv->dbus_proxy); + g_clear_object (&priv->proxy); + g_clear_object (&priv->props_proxy); + + if (priv->bus) { + dbus_g_connection_unref (priv->bus); + priv->bus = NULL; + } + + G_OBJECT_CLASS (nm_remote_settings_parent_class)->dispose (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_BUS: + /* Construct only */ + priv->bus = g_value_dup_boxed (value); + if (!priv->bus) { + priv->bus = _nm_dbus_new_connection (NULL); + priv->private_bus = _nm_dbus_is_connection_private (priv->bus); + } + 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) +{ + NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (object); + + _nm_remote_settings_ensure_inited (NM_REMOTE_SETTINGS (object)); + + switch (prop_id) { + case PROP_BUS: + g_value_set_boxed (value, priv->bus); + break; + case PROP_SERVICE_RUNNING: + g_value_set_boolean (value, priv->service_running); + break; + case PROP_HOSTNAME: + g_value_set_string (value, priv->hostname); + break; + case PROP_CAN_MODIFY: + g_value_set_boolean (value, priv->can_modify); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_remote_settings_class_init (NMRemoteSettingsClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (NMRemoteSettingsPrivate)); + + /* Virtual methods */ + object_class->constructed = constructed; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + + /* Properties */ + + /** + * NMRemoteSettings:bus: + * + * The #DBusGConnection that the #NMRemoteSettings is connected to. Defaults + * to the system bus if not specified. + */ + g_object_class_install_property + (object_class, PROP_BUS, + g_param_spec_boxed (NM_REMOTE_SETTINGS_BUS, "", "", + DBUS_TYPE_G_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NMRemoteSettings:service-running: + * + * Whether the settings service is running. + */ + g_object_class_install_property + (object_class, PROP_SERVICE_RUNNING, + g_param_spec_boolean (NM_REMOTE_SETTINGS_SERVICE_RUNNING, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMRemoteSettings:hostname: + * + * The machine hostname stored in persistent configuration. This can be + * modified by calling nm_remote_settings_save_hostname(). + */ + g_object_class_install_property + (object_class, PROP_HOSTNAME, + g_param_spec_string (NM_REMOTE_SETTINGS_HOSTNAME, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMRemoteSettings:can-modify: + * + * If %TRUE, adding and modifying connections is supported. + */ + g_object_class_install_property + (object_class, PROP_CAN_MODIFY, + g_param_spec_boolean (NM_REMOTE_SETTINGS_CAN_MODIFY, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* Signals */ + signals[NEW_CONNECTION] = + g_signal_new (NM_REMOTE_SETTINGS_NEW_CONNECTION, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMRemoteSettingsClass, new_connection), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[CONNECTIONS_READ] = + g_signal_new (NM_REMOTE_SETTINGS_CONNECTIONS_READ, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMRemoteSettingsClass, connections_read), + NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +static void +nm_remote_settings_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_remote_settings_async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} diff --git a/libnm/nm-remote-settings.h b/libnm/nm-remote-settings.h new file mode 100644 index 0000000000..92049adee0 --- /dev/null +++ b/libnm/nm-remote-settings.h @@ -0,0 +1,162 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 Novell, Inc. + * Copyright 2009 - 2011 Red Hat, Inc. + */ + +#ifndef NM_REMOTE_SETTINGS_H +#define NM_REMOTE_SETTINGS_H + +#include <gio/gio.h> +#include <dbus/dbus-glib.h> +#include <nm-connection.h> +#include <nm-remote-connection.h> + +G_BEGIN_DECLS + +#define NM_TYPE_REMOTE_SETTINGS (nm_remote_settings_get_type ()) +#define NM_REMOTE_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_REMOTE_SETTINGS, NMRemoteSettings)) +#define NM_REMOTE_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_REMOTE_SETTINGS, NMRemoteSettingsClass)) +#define NM_IS_REMOTE_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_REMOTE_SETTINGS)) +#define NM_IS_REMOTE_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_REMOTE_SETTINGS)) +#define NM_REMOTE_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_REMOTE_SETTINGS, NMRemoteSettingsClass)) + +/** + * NMRemoteSettingsError: + * @NM_REMOTE_SETTINGS_ERROR_UNKNOWN: unknown or unclassified error + * @NM_REMOTE_SETTINGS_ERROR_CONNECTION_REMOVED: the #NMRemoteConnection object + * was removed before it was completely initialized + * @NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE: the #NMRemoteConnection object + * is not visible or otherwise unreadable + * @NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE: NetworkManager is not running. + * (Since 0.9.10) + * + * Describes errors that may result from operations involving a #NMRemoteSettings. + * + **/ +typedef enum { + NM_REMOTE_SETTINGS_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ + NM_REMOTE_SETTINGS_ERROR_CONNECTION_REMOVED, /*< nick=ConnectionRemoved >*/ + NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE, /*< nick=ConnectionUnavailable >*/ + NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE, /*< nick=ServiceUnavailable >*/ +} NMRemoteSettingsError; + +#define NM_REMOTE_SETTINGS_ERROR nm_remote_settings_error_quark () +GQuark nm_remote_settings_error_quark (void); + + +#define NM_REMOTE_SETTINGS_BUS "bus" +#define NM_REMOTE_SETTINGS_SERVICE_RUNNING "service-running" +#define NM_REMOTE_SETTINGS_HOSTNAME "hostname" +#define NM_REMOTE_SETTINGS_CAN_MODIFY "can-modify" + +#define NM_REMOTE_SETTINGS_NEW_CONNECTION "new-connection" +#define NM_REMOTE_SETTINGS_CONNECTIONS_READ "connections-read" + +typedef struct _NMRemoteSettings NMRemoteSettings; +typedef struct _NMRemoteSettingsClass NMRemoteSettingsClass; + + +typedef void (*NMRemoteSettingsAddConnectionFunc) (NMRemoteSettings *settings, + NMRemoteConnection *connection, + GError *error, + gpointer user_data); + +typedef void (*NMRemoteSettingsLoadConnectionsFunc) (NMRemoteSettings *settings, + char **failures, + GError *error, + gpointer user_data); + +typedef void (*NMRemoteSettingsSaveHostnameFunc) (NMRemoteSettings *settings, + GError *error, + gpointer user_data); + + +struct _NMRemoteSettings { + GObject parent; +}; + +struct _NMRemoteSettingsClass { + GObjectClass parent; + + /* Signals */ + void (*new_connection) (NMRemoteSettings *settings, + NMRemoteConnection *connection); + + void (*connections_read) (NMRemoteSettings *settings); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +}; + +GType nm_remote_settings_get_type (void); + +NMRemoteSettings *nm_remote_settings_new (DBusGConnection *bus); + +void nm_remote_settings_new_async (DBusGConnection *bus, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +NMRemoteSettings *nm_remote_settings_new_finish (GAsyncResult *result, + GError **error); + +GSList *nm_remote_settings_list_connections (NMRemoteSettings *settings); + +NMRemoteConnection *nm_remote_settings_get_connection_by_id (NMRemoteSettings *settings, + const char *id); + +NMRemoteConnection * nm_remote_settings_get_connection_by_path (NMRemoteSettings *settings, + const char *path); + +NMRemoteConnection *nm_remote_settings_get_connection_by_uuid (NMRemoteSettings *settings, + const char *uuid); + +gboolean nm_remote_settings_add_connection (NMRemoteSettings *settings, + NMConnection *connection, + NMRemoteSettingsAddConnectionFunc callback, + gpointer user_data); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_remote_settings_add_connection_unsaved (NMRemoteSettings *settings, + NMConnection *connection, + NMRemoteSettingsAddConnectionFunc callback, + gpointer user_data); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_remote_settings_load_connections (NMRemoteSettings *settings, + char **filenames, + char ***failures, + GError **error); + +NM_AVAILABLE_IN_0_9_10 +gboolean nm_remote_settings_reload_connections (NMRemoteSettings *settings, + GError **error); + +gboolean nm_remote_settings_save_hostname (NMRemoteSettings *settings, + const char *hostname, + NMRemoteSettingsSaveHostnameFunc callback, + gpointer user_data); + +G_END_DECLS + +#endif /* NM_REMOTE_SETTINGS_H */ diff --git a/libnm/nm-secret-agent.c b/libnm/nm-secret-agent.c new file mode 100644 index 0000000000..ea6197743c --- /dev/null +++ b/libnm/nm-secret-agent.c @@ -0,0 +1,1077 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2011 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "nm-glib-compat.h" +#include "NetworkManager.h" +#include "nm-secret-agent.h" +#include "nm-glib-enum-types.h" +#include "nm-dbus-helpers-private.h" + +static void impl_secret_agent_get_secrets (NMSecretAgent *self, + GHashTable *connection_hash, + const char *connection_path, + const char *setting_name, + const char **hints, + guint32 flags, + DBusGMethodInvocation *context); + +static void impl_secret_agent_cancel_get_secrets (NMSecretAgent *self, + const char *connection_path, + const char *setting_name, + DBusGMethodInvocation *context); + +static void impl_secret_agent_save_secrets (NMSecretAgent *self, + GHashTable *connection_hash, + const char *connection_path, + DBusGMethodInvocation *context); + +static void impl_secret_agent_delete_secrets (NMSecretAgent *self, + GHashTable *connection_hash, + const char *connection_path, + DBusGMethodInvocation *context); + +#include "nm-secret-agent-glue.h" + +G_DEFINE_ABSTRACT_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) + +#define NM_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT, NMSecretAgentPrivate)) + +static gboolean auto_register_cb (gpointer user_data); + +typedef struct { + gboolean registered; + NMSecretAgentCapabilities capabilities; + + DBusGConnection *bus; + gboolean private_bus; + DBusGProxy *dbus_proxy; + DBusGProxy *manager_proxy; + DBusGProxyCall *reg_call; + + /* GetSecretsInfo structs of in-flight GetSecrets requests */ + GSList *pending_gets; + + char *nm_owner; + + char *identifier; + gboolean auto_register; + gboolean suppress_auto; + gboolean auto_register_id; +} NMSecretAgentPrivate; + +enum { + PROP_0, + PROP_IDENTIFIER, + PROP_AUTO_REGISTER, + PROP_REGISTERED, + PROP_CAPABILITIES, + + LAST_PROP +}; + +enum { + REGISTRATION_RESULT, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +/********************************************************************/ + +GQuark +nm_secret_agent_error_quark (void) +{ + static GQuark ret = 0; + + if (G_UNLIKELY (ret == 0)) + ret = g_quark_from_static_string ("nm-secret-agent-error"); + return ret; +} + +/*************************************************************/ + +static const char * +get_nm_owner (NMSecretAgent *self) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GError *error = NULL; + char *owner; + + if (!priv->nm_owner) { + if (!dbus_g_proxy_call_with_timeout (priv->dbus_proxy, + "GetNameOwner", 2000, &error, + G_TYPE_STRING, NM_DBUS_SERVICE, + G_TYPE_INVALID, + G_TYPE_STRING, &owner, + G_TYPE_INVALID)) + return NULL; + + priv->nm_owner = g_strdup (owner); + g_free (owner); + } + + return priv->nm_owner; +} + +static void +_internal_unregister (NMSecretAgent *self) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + if (priv->registered) { + dbus_g_connection_unregister_g_object (priv->bus, G_OBJECT (self)); + priv->registered = FALSE; + g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED); + } +} + +typedef struct { + char *path; + char *setting_name; + DBusGMethodInvocation *context; +} GetSecretsInfo; + +static void +get_secrets_info_finalize (NMSecretAgent *self, GetSecretsInfo *info) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + g_return_if_fail (info != NULL); + + priv->pending_gets = g_slist_remove (priv->pending_gets, info); + + g_free (info->path); + g_free (info->setting_name); + memset (info, 0, sizeof (*info)); + g_free (info); +} + +static void +name_owner_changed (DBusGProxy *proxy, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT (user_data); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + gboolean old_owner_good = (old_owner && strlen (old_owner)); + gboolean new_owner_good = (new_owner && strlen (new_owner)); + GSList *iter; + + if (strcmp (name, NM_DBUS_SERVICE) == 0) { + g_free (priv->nm_owner); + priv->nm_owner = g_strdup (new_owner); + + if (!old_owner_good && new_owner_good) { + /* NM appeared */ + auto_register_cb (self); + } else if (old_owner_good && !new_owner_good) { + /* Cancel any pending secrets requests */ + for (iter = priv->pending_gets; iter; iter = g_slist_next (iter)) { + GetSecretsInfo *info = iter->data; + + NM_SECRET_AGENT_GET_CLASS (self)->cancel_get_secrets (self, + info->path, + info->setting_name); + } + g_slist_free (priv->pending_gets); + priv->pending_gets = NULL; + + /* NM disappeared */ + _internal_unregister (self); + } else if (old_owner_good && new_owner_good && strcmp (old_owner, new_owner)) { + /* Hmm, NM magically restarted */ + _internal_unregister (self); + auto_register_cb (self); + } + } +} + +static gboolean +verify_sender (NMSecretAgent *self, + DBusGMethodInvocation *context, + GError **error) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + DBusConnection *bus; + char *sender; + const char *nm_owner; + DBusError dbus_error; + uid_t sender_uid = G_MAXUINT; + gboolean allowed = FALSE; + + g_return_val_if_fail (context != NULL, FALSE); + + /* Private bus connection is always to NetworkManager, which is always + * UID 0. + */ + if (priv->private_bus) + return TRUE; + + /* Verify the sender's UID is 0, and that the sender is the same as + * NetworkManager's bus name owner. + */ + + nm_owner = get_nm_owner (self); + if (!nm_owner) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED, + "NetworkManager bus name owner unknown."); + return FALSE; + } + + bus = dbus_g_connection_get_connection (priv->bus); + if (!bus) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED, + "Failed to get DBus connection."); + return FALSE; + } + + sender = dbus_g_method_get_sender (context); + if (!sender) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED, + "Failed to get request sender."); + return FALSE; + } + + /* Check that the sender matches the current NM bus name owner */ + if (strcmp (sender, nm_owner) != 0) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED, + "Request sender does not match NetworkManager bus name owner."); + goto out; + } + + dbus_error_init (&dbus_error); + sender_uid = dbus_bus_get_unix_user (bus, sender, &dbus_error); + if (dbus_error_is_set (&dbus_error)) { + g_set_error (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED, + "Failed to get request unix user: (%s) %s.", + dbus_error.name, dbus_error.message); + dbus_error_free (&dbus_error); + goto out; + } + + /* We only accept requests from NM, which always runs as root */ + if (0 != sender_uid) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED, + "Request sender is not root."); + goto out; + } + + allowed = TRUE; + +out: + g_free (sender); + return allowed; +} + +static gboolean +verify_request (NMSecretAgent *self, + DBusGMethodInvocation *context, + GHashTable *connection_hash, + const char *connection_path, + NMConnection **out_connection, + GError **error) +{ + NMConnection *connection = NULL; + GError *local = NULL; + + if (!verify_sender (self, context, error)) + return FALSE; + + /* No connection? If the sender verified, then we allow the request */ + if (connection_hash == NULL) + return TRUE; + + /* If we have a connection hash, we require a path too */ + if (connection_path == NULL) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "Invalid connection: no connection path given."); + return FALSE; + } + + /* Make sure the given connection is valid */ + g_assert (out_connection); + connection = nm_connection_new_from_hash (connection_hash, &local); + if (connection) { + nm_connection_set_path (connection, connection_path); + *out_connection = connection; + } else { + g_set_error (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "Invalid connection: (%d) %s", + local ? local->code : -1, + (local && local->message) ? local->message : "(unknown)"); + g_clear_error (&local); + } + + return !!connection; +} + +static void +get_secrets_cb (NMSecretAgent *self, + NMConnection *connection, + GHashTable *secrets, + GError *error, + gpointer user_data) +{ + GetSecretsInfo *info = user_data; + + if (error) + dbus_g_method_return_error (info->context, error); + else + dbus_g_method_return (info->context, secrets); + + /* Remove the request from internal tracking */ + get_secrets_info_finalize (self, info); +} + +static void +impl_secret_agent_get_secrets (NMSecretAgent *self, + GHashTable *connection_hash, + const char *connection_path, + const char *setting_name, + const char **hints, + guint32 flags, + DBusGMethodInvocation *context) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GError *error = NULL; + NMConnection *connection = NULL; + GetSecretsInfo *info; + + /* Make sure the request comes from NetworkManager and is valid */ + if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + return; + } + + info = g_malloc0 (sizeof (GetSecretsInfo)); + info->path = g_strdup (connection_path); + info->setting_name = g_strdup (setting_name); + info->context = context; + priv->pending_gets = g_slist_append (priv->pending_gets, info); + + NM_SECRET_AGENT_GET_CLASS (self)->get_secrets (self, + connection, + connection_path, + setting_name, + hints, + flags, + get_secrets_cb, + info); + g_object_unref (connection); +} + +static GetSecretsInfo * +find_get_secrets_info (GSList *list, const char *path, const char *setting_name) +{ + GSList *iter; + + for (iter = list; iter; iter = g_slist_next (iter)) { + GetSecretsInfo *candidate = iter->data; + + if ( g_strcmp0 (path, candidate->path) == 0 + && g_strcmp0 (setting_name, candidate->setting_name) == 0) + return candidate; + } + return NULL; +} + +static void +impl_secret_agent_cancel_get_secrets (NMSecretAgent *self, + const char *connection_path, + const char *setting_name, + DBusGMethodInvocation *context) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GError *error = NULL; + GetSecretsInfo *info; + + /* Make sure the request comes from NetworkManager and is valid */ + if (!verify_request (self, context, NULL, NULL, NULL, &error)) { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + return; + } + + info = find_get_secrets_info (priv->pending_gets, connection_path, setting_name); + if (!info) { + g_set_error_literal (&error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, + "No secrets request in progress for this connection."); + dbus_g_method_return_error (context, error); + g_clear_error (&error); + return; + } + + /* Send the cancel request up to the subclass and finalize it */ + NM_SECRET_AGENT_GET_CLASS (self)->cancel_get_secrets (self, + info->path, + info->setting_name); + dbus_g_method_return (context); +} + +static void +save_secrets_cb (NMSecretAgent *self, + NMConnection *connection, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +impl_secret_agent_save_secrets (NMSecretAgent *self, + GHashTable *connection_hash, + const char *connection_path, + DBusGMethodInvocation *context) +{ + GError *error = NULL; + NMConnection *connection = NULL; + + /* Make sure the request comes from NetworkManager and is valid */ + if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + return; + } + + NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self, + connection, + connection_path, + save_secrets_cb, + context); + g_object_unref (connection); +} + +static void +delete_secrets_cb (NMSecretAgent *self, + NMConnection *connection, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +impl_secret_agent_delete_secrets (NMSecretAgent *self, + GHashTable *connection_hash, + const char *connection_path, + DBusGMethodInvocation *context) +{ + GError *error = NULL; + NMConnection *connection = NULL; + + /* Make sure the request comes from NetworkManager and is valid */ + if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + return; + } + + NM_SECRET_AGENT_GET_CLASS (self)->delete_secrets (self, + connection, + connection_path, + delete_secrets_cb, + context); + g_object_unref (connection); +} + +/**************************************************************/ + +static void +reg_result (NMSecretAgent *self, GError *error) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + if (error) { + /* If registration failed we shouldn't expose ourselves on the bus */ + _internal_unregister (self); + } else { + priv->registered = TRUE; + g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED); + } + + g_signal_emit (self, signals[REGISTRATION_RESULT], 0, error); +} + +static void +reg_request_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT (user_data); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GError *error = NULL; + + priv->reg_call = NULL; + + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + reg_result (self, error); + g_clear_error (&error); +} + +static void +reg_with_caps_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT (user_data); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + priv->reg_call = NULL; + + if (dbus_g_proxy_end_call (proxy, call, NULL, G_TYPE_INVALID)) { + reg_result (self, NULL); + return; + } + + /* Might be an old NetworkManager that doesn't support capabilities; + * fall back to old Register() method instead. + */ + priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, + "Register", + reg_request_cb, + self, + NULL, + 5000, + G_TYPE_STRING, priv->identifier, + G_TYPE_INVALID); +} + +/** + * nm_secret_agent_register: + * @self: a #NMSecretAgent + * + * Registers the #NMSecretAgent 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. Registration is an + * asynchronous operation and its success or failure is indicated via the + * 'registration-result' signal. + * + * Returns: a new %TRUE if registration was successfully requested (this does + * not mean registration itself was successful), %FALSE if registration was not + * successfully requested. + **/ +gboolean +nm_secret_agent_register (NMSecretAgent *self) +{ + NMSecretAgentPrivate *priv; + NMSecretAgentClass *class; + + g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + + priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + g_return_val_if_fail (priv->registered == FALSE, FALSE); + g_return_val_if_fail (priv->reg_call == NULL, FALSE); + g_return_val_if_fail (priv->bus != NULL, FALSE); + g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + + /* Also make sure the subclass can actually respond to secrets requests */ + class = NM_SECRET_AGENT_GET_CLASS (self); + g_return_val_if_fail (class->get_secrets != NULL, FALSE); + g_return_val_if_fail (class->save_secrets != NULL, FALSE); + g_return_val_if_fail (class->delete_secrets != NULL, FALSE); + + if (!priv->nm_owner && !priv->private_bus) + return FALSE; + + priv->suppress_auto = FALSE; + + /* Export our secret agent interface before registering with the manager */ + dbus_g_connection_register_g_object (priv->bus, + NM_DBUS_PATH_SECRET_AGENT, + G_OBJECT (self)); + + priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy, + "RegisterWithCapabilities", + reg_with_caps_cb, + self, + NULL, + 5000, + G_TYPE_STRING, priv->identifier, + G_TYPE_UINT, priv->capabilities, + G_TYPE_INVALID); + return TRUE; +} + +/** + * nm_secret_agent_unregister: + * @self: a #NMSecretAgent + * + * Unregisters the #NMSecretAgent with the NetworkManager secret manager, + * indicating to NetworkManager that the agent is will no longer provide or + * store secrets on behalf of this user. + * + * Returns: a new %TRUE if unregistration was successful, %FALSE if it was not. + **/ +gboolean +nm_secret_agent_unregister (NMSecretAgent *self) +{ + NMSecretAgentPrivate *priv; + + g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + + priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + g_return_val_if_fail (priv->registered == TRUE, FALSE); + g_return_val_if_fail (priv->bus != NULL, FALSE); + g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + + if (!priv->nm_owner && !priv->private_bus) + return FALSE; + + dbus_g_proxy_call_no_reply (priv->manager_proxy, "Unregister", G_TYPE_INVALID); + + _internal_unregister (self); + priv->suppress_auto = TRUE; + + return TRUE; +} + +/** + * nm_secret_agent_get_registered: + * @self: a #NMSecretAgent + * + * Returns: a %TRUE if the agent is registered, %FALSE if it is not. + **/ +gboolean +nm_secret_agent_get_registered (NMSecretAgent *self) +{ + g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE); + + return NM_SECRET_AGENT_GET_PRIVATE (self)->registered; +} + +static gboolean +auto_register_cb (gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT (user_data); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + priv->auto_register_id = 0; + if (priv->auto_register && !priv->suppress_auto && (priv->reg_call == NULL)) + nm_secret_agent_register (self); + return FALSE; +} + +/**************************************************************/ + +/** + * nm_secret_agent_get_secrets: + * @self: a #NMSecretAgent + * @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 + * + * Asyncronously retrieve 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. + * + * Virtual: get_secrets + */ +void +nm_secret_agent_get_secrets (NMSecretAgent *self, + NMConnection *connection, + const char *setting_name, + const char **hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentGetSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SECRET_AGENT (self)); + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (nm_connection_get_path (connection)); + g_return_if_fail (setting_name != NULL); + g_return_if_fail (strlen (setting_name) > 0); + g_return_if_fail (callback != NULL); + + NM_SECRET_AGENT_GET_CLASS (self)->get_secrets (self, + connection, + nm_connection_get_path (connection), + setting_name, + hints, + flags, + callback, + user_data); +} + +/** + * nm_secret_agent_save_secrets: + * @self: a #NMSecretAgent + * @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 + * + * Asyncronously ensure that all secrets inside @connection + * are stored to disk. + * + * Virtual: save_secrets + */ +void +nm_secret_agent_save_secrets (NMSecretAgent *self, + NMConnection *connection, + NMSecretAgentSaveSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SECRET_AGENT (self)); + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (nm_connection_get_path (connection)); + + NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self, + connection, + nm_connection_get_path (connection), + callback, + user_data); +} + +/** + * nm_secret_agent_delete_secrets: + * @self: a #NMSecretAgent + * @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 ask the agent to delete all saved secrets belonging to + * @connection. + * + * Virtual: delete_secrets + */ +void +nm_secret_agent_delete_secrets (NMSecretAgent *self, + NMConnection *connection, + NMSecretAgentDeleteSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SECRET_AGENT (self)); + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (nm_connection_get_path (connection)); + + NM_SECRET_AGENT_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 void +nm_secret_agent_init (NMSecretAgent *self) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + GError *error = NULL; + + priv->bus = _nm_dbus_new_connection (&error); + if (!priv->bus) { + g_warning ("Couldn't connect to system bus: %s", error->message); + g_error_free (error); + return; + } + priv->private_bus = _nm_dbus_is_connection_private (priv->bus); + + if (priv->private_bus == FALSE) { + priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + g_assert (priv->dbus_proxy); + + dbus_g_object_register_marshaller (g_cclosure_marshal_generic, + G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->dbus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->dbus_proxy, + "NameOwnerChanged", + G_CALLBACK (name_owner_changed), + self, NULL); + + get_nm_owner (self); + } + + priv->manager_proxy = _nm_dbus_new_proxy_for_connection (priv->bus, + NM_DBUS_PATH_AGENT_MANAGER, + NM_DBUS_INTERFACE_AGENT_MANAGER); + if (!priv->manager_proxy) { + g_warning ("Couldn't create NM agent manager proxy."); + return; + } + + if (priv->nm_owner || priv->private_bus) + priv->auto_register_id = g_idle_add (auto_register_cb, self); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object); + + switch (prop_id) { + 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->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) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object); + const char *identifier; + + switch (prop_id) { + case PROP_IDENTIFIER: + identifier = g_value_get_string (value); + + g_return_if_fail (validate_identifier (identifier)); + + g_free (priv->identifier); + priv->identifier = g_strdup (identifier); + break; + case PROP_AUTO_REGISTER: + priv->auto_register = g_value_get_boolean (value); + break; + case PROP_CAPABILITIES: + priv->capabilities = g_value_get_flags (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMSecretAgent *self = NM_SECRET_AGENT (object); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + if (priv->registered) + nm_secret_agent_unregister (self); + + if (priv->auto_register_id) { + g_source_remove (priv->auto_register_id); + priv->auto_register_id = 0; + } + + g_free (priv->identifier); + priv->identifier = NULL; + g_free (priv->nm_owner); + priv->nm_owner = NULL; + + while (priv->pending_gets) + get_secrets_info_finalize (self, priv->pending_gets->data); + + g_clear_object (&priv->dbus_proxy); + g_clear_object (&priv->manager_proxy); + + if (priv->bus) { + dbus_g_connection_unref (priv->bus); + priv->bus = NULL; + } + + G_OBJECT_CLASS (nm_secret_agent_parent_class)->dispose (object); +} + +static void +nm_secret_agent_class_init (NMSecretAgentClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (NMSecretAgentPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + + /** + * NMSecretAgent: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). + **/ + g_object_class_install_property + (object_class, PROP_IDENTIFIER, + g_param_spec_string (NM_SECRET_AGENT_IDENTIFIER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSecretAgent:auto-register: + * + * If TRUE, the agent will attempt to automatically register itself after + * it is created (via an idle handler) and to re-register itself if + * NetworkManager restarts. If FALSE, the agent does not automatically + * register with NetworkManager, and nm_secret_agent_register() must be + * called. If 'auto-register' is TRUE, calling nm_secret_agent_unregister() + * will suppress auto-registration until nm_secret_agent_register() is + * called, which re-enables auto-registration. + **/ + g_object_class_install_property + (object_class, PROP_AUTO_REGISTER, + g_param_spec_boolean (NM_SECRET_AGENT_AUTO_REGISTER, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSecretAgent:registered: + * + * %TRUE if the agent is registered with NetworkManager, %FALSE if not. + **/ + g_object_class_install_property + (object_class, PROP_REGISTERED, + g_param_spec_boolean (NM_SECRET_AGENT_REGISTERED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSecretAgent:capabilities: + * + * A bitfield of %NMSecretAgentCapabilities. + **/ + g_object_class_install_property + (object_class, PROP_CAPABILITIES, + g_param_spec_flags (NM_SECRET_AGENT_CAPABILITIES, "", "", + NM_TYPE_SECRET_AGENT_CAPABILITIES, + NM_SECRET_AGENT_CAPABILITY_NONE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSecretAgent::registration-result: + * @agent: the agent that received the signal + * @error: the error, if any, that occured while registering + * + * Indicates the result of a registration request; if @error is NULL the + * request was successful. + **/ + signals[REGISTRATION_RESULT] = + g_signal_new (NM_SECRET_AGENT_REGISTRATION_RESULT, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class), + &dbus_glib_nm_secret_agent_object_info); + + dbus_g_error_domain_register (NM_SECRET_AGENT_ERROR, + NM_DBUS_INTERFACE_SECRET_AGENT, + NM_TYPE_SECRET_AGENT_ERROR); +} diff --git a/libnm/nm-secret-agent.h b/libnm/nm-secret-agent.h new file mode 100644 index 0000000000..f593b3e648 --- /dev/null +++ b/libnm/nm-secret-agent.h @@ -0,0 +1,307 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2011 Red Hat, Inc. + */ + +#ifndef NM_SECRET_AGENT_H +#define NM_SECRET_AGENT_H + +#include <nm-connection.h> + +G_BEGIN_DECLS + +#define NM_SECRET_AGENT_ERROR (nm_secret_agent_error_quark ()) + +GQuark nm_secret_agent_error_quark (void); + +/** + * NMSecretAgentError: + * @NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED: the caller (ie, NetworkManager) is not + * authorized to make this request + * @NM_SECRET_AGENT_ERROR_INVALID_CONNECTION: the connection for which secrets + * were requested could not be found + * @NM_SECRET_AGENT_ERROR_USER_CANCELED: the request was canceled by the user + * @NM_SECRET_AGENT_ERROR_AGENT_CANCELED: the agent canceled the request + * because it was requested to do so by NetworkManager + * @NM_SECRET_AGENT_ERROR_INTERNAL_ERROR: some internal error in the agent caused + * the request to fail + * @NM_SECRET_AGENT_ERROR_NO_SECRETS: the agent cannot find any secrets for this + * connection + * + * #NMSecretAgentError values are passed by secret agents back to NetworkManager + * when they encounter problems retrieving secrets on behalf of NM. + */ +typedef enum { + NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED = 0, /*< nick=NotAuthorized >*/ + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, /*< nick=InvalidConnection >*/ + NM_SECRET_AGENT_ERROR_USER_CANCELED, /*< nick=UserCanceled >*/ + NM_SECRET_AGENT_ERROR_AGENT_CANCELED, /*< nick=AgentCanceled >*/ + NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, /*< nick=InternalError >*/ + NM_SECRET_AGENT_ERROR_NO_SECRETS, /*< nick=NoSecrets >*/ +} NMSecretAgentError; + +/** + * NMSecretAgentCapabilities: + * @NM_SECRET_AGENT_CAPABILITY_NONE: the agent supports no special capabilities + * @NM_SECRET_AGENT_CAPABILITY_VPN_HINTS: the agent supports sending hints given + * by the NMSecretAgentClass::get_secrets() class method to VPN plugin + * authentication dialogs. + * @NM_SECRET_AGENT_CAPABILITY_LAST: bounds checking value; should not be used. + * + * #NMSecretAgentCapabilities indicate various capabilities of the agent. + * + * Since: 0.9.10 + */ +typedef enum /*< flags >*/ { + NM_SECRET_AGENT_CAPABILITY_NONE = 0x0, + NM_SECRET_AGENT_CAPABILITY_VPN_HINTS = 0x1, + + /* boundary value */ + NM_SECRET_AGENT_CAPABILITY_LAST = NM_SECRET_AGENT_CAPABILITY_VPN_HINTS +} NMSecretAgentCapabilities; + +/** + * NMSecretAgentGetSecretsFlags: + * @NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE: no special behavior; by default no + * user interaction is allowed and requests for secrets are fulfilled from + * persistent storage, or if no secrets are available an error is returned. + * @NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION: allows the request to + * interact with the user, possibly prompting via UI for secrets if any are + * required, or if none are found in persistent storage. + * @NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW: explicitly prompt for new + * secrets from the user. This flag signals that NetworkManager thinks any + * existing secrets are invalid or wrong. This flag implies that interaction + * is allowed. + * @NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED: set if the request was + * initiated by user-requested action via the D-Bus interface, as opposed to + * automatically initiated by NetworkManager in response to (for example) scan + * results or carrier changes. + * + * #NMSecretAgentGetSecretsFlags values modify the behavior of a GetSecrets request. + */ +typedef enum /*< flags >*/ { + NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1, + NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 0x2, + NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 0x4 +} NMSecretAgentGetSecretsFlags; + +#define NM_TYPE_SECRET_AGENT (nm_secret_agent_get_type ()) +#define NM_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgent)) +#define NM_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SECRET_AGENT, NMSecretAgentClass)) +#define NM_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRET_AGENT)) +#define NM_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SECRET_AGENT)) +#define NM_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgentClass)) + +#define NM_SECRET_AGENT_IDENTIFIER "identifier" +#define NM_SECRET_AGENT_AUTO_REGISTER "auto-register" +#define NM_SECRET_AGENT_REGISTERED "registered" +#define NM_SECRET_AGENT_CAPABILITIES "capabilities" + +#define NM_SECRET_AGENT_REGISTRATION_RESULT "registration-result" + +typedef struct { + GObject parent; +} NMSecretAgent; + +/** + * NMSecretAgentGetSecretsFunc: + * @agent: the secret agent object + * @connection: (transfer none): the connection for which secrets were requested, + * note that this object will be unrefed after the callback has returned, use + * g_object_ref()/g_object_unref() if you want to use this object after the callback + * has returned + * @secrets: (element-type utf8 GLib.HashTable): the #GHashTable containing + * the requested secrets in the same format as an #NMConnection hash (as + * created by nm_connection_to_hash() for example). Each key in @secrets + * should be the name of a #NMSetting object (like "802-11-wireless-security") + * and each value should be a #GHashTable. The sub-hashes map string:#GValue + * where the string is the setting property name (like "psk") and the value + * is the secret + * @error: if the secrets request failed, give a descriptive error here + * @user_data: caller-specific data to be passed to the function + * + * Called as a result of a request by NM to retrieve secrets. When the + * #NMSecretAgent subclass has finished retrieving secrets and is ready to + * return them, or to return an error, this function should be called with + * those secrets or the error. + * + * To easily create the hash table to return the Wi-Fi PSK, you could do + * something like this: + * <example> + * <title>Creating a secrets hash</title> + * <programlisting> + * NMConnection *secrets; + * NMSettingWirelessSecurity *s_wsec; + * GHashTable *secrets_hash; + * + * secrets = nm_connection_new (); + * s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + * g_object_set (G_OBJECT (s_wsec), + * NM_SETTING_WIRELESS_SECURITY_PSK, "my really cool PSK", + * NULL); + * nm_connection_add_setting (secrets, NM_SETTING (s_wsec)); + * secrets_hash = nm_connection_to_hash (secrets, NM_SETTING_HASH_FLAG_ALL); + * + * (call the NMSecretAgentGetSecretsFunc with secrets_hash) + * + * g_object_unref (secrets); + * g_hash_table_unref (secrets_hash); + * </programlisting> + * </example> + */ +typedef void (*NMSecretAgentGetSecretsFunc) (NMSecretAgent *agent, + NMConnection *connection, + GHashTable *secrets, + GError *error, + gpointer user_data); + +/** + * NMSecretAgentSaveSecretsFunc: + * @agent: the secret agent object + * @connection: (transfer none): the connection for which secrets were to be saved, + * note that this object will be unrefed after the callback has returned, use + * g_object_ref()/g_object_unref() if you want to use this object after the callback + * has returned + * @error: if the saving secrets failed, give a descriptive error here + * @user_data: caller-specific data to be passed to the function + * + * Called as a result of a request by NM to save secrets. When the + * #NMSecretAgent subclass has finished saving the secrets, this function + * should be called. + */ +typedef void (*NMSecretAgentSaveSecretsFunc) (NMSecretAgent *agent, + NMConnection *connection, + GError *error, + gpointer user_data); + +/** + * NMSecretAgentDeleteSecretsFunc: + * @agent: the secret agent object + * @connection: (transfer none): the connection for which secrets were to be deleted, + * note that this object will be unrefed after the callback has returned, use + * g_object_ref()/g_object_unref() if you want to use this object after the callback + * has returned + * @error: if the deleting secrets failed, give a descriptive error here + * @user_data: caller-specific data to be passed to the function + * + * Called as a result of a request by NM to delete secrets. When the + * #NMSecretAgent subclass has finished deleting the secrets, this function + * should be called. + */ +typedef void (*NMSecretAgentDeleteSecretsFunc) (NMSecretAgent *agent, + NMConnection *connection, + GError *error, + gpointer user_data); + +typedef struct { + GObjectClass parent; + + /* Virtual methods for subclasses */ + + /* Called when the subclass should retrieve and return secrets. Subclass + * must copy or reference any arguments it may require after returning from + * this method, as the arguments will freed (except for 'self', 'callback', + * and 'user_data' of course). If the request is canceled, the callback + * should still be called, but with the NM_SECRET_AGENT_ERROR_AGENT_CANCELED + * error. + */ + void (*get_secrets) (NMSecretAgent *self, + NMConnection *connection, + const char *connection_path, + const char *setting_name, + const char **hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentGetSecretsFunc callback, + gpointer user_data); + + /* Called when the subclass should cancel an outstanding request to + * get secrets for a given connection. Canceling the request MUST + * call the callback that was passed along with the initial get_secrets + * call, sending the NM_SECRET_AGENT_ERROR/NM_SECRET_AGENT_ERROR_AGENT_CANCELED + * error to that callback. + */ + void (*cancel_get_secrets) (NMSecretAgent *self, + const char *connection_path, + const char *setting_name); + + /* Called when the subclass should save the secrets contained in the + * connection to backing storage. Subclass must copy or reference any + * arguments it may require after returning from this method, as the + * arguments will freed (except for 'self', 'callback', and 'user_data' + * of course). + */ + void (*save_secrets) (NMSecretAgent *self, + NMConnection *connection, + const char *connection_path, + NMSecretAgentSaveSecretsFunc callback, + gpointer user_data); + + /* Called when the subclass should delete the secrets contained in the + * connection from backing storage. Subclass must copy or reference any + * arguments it may require after returning from this method, as the + * arguments will freed (except for 'self', 'callback', and 'user_data' + * of course). + */ + void (*delete_secrets) (NMSecretAgent *self, + NMConnection *connection, + const char *connection_path, + NMSecretAgentDeleteSecretsFunc callback, + gpointer user_data); + + /* Signals */ + void (*registration_result) (NMSecretAgent *agent, GError *error); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMSecretAgentClass; + +GType nm_secret_agent_get_type (void); + +gboolean nm_secret_agent_register (NMSecretAgent *self); + +gboolean nm_secret_agent_unregister (NMSecretAgent *self); + +gboolean nm_secret_agent_get_registered (NMSecretAgent *self); + +void nm_secret_agent_get_secrets (NMSecretAgent *self, + NMConnection *connection, + const char *setting_name, + const char **hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentGetSecretsFunc callback, + gpointer user_data); + +void nm_secret_agent_save_secrets (NMSecretAgent *self, + NMConnection *connection, + NMSecretAgentSaveSecretsFunc callback, + gpointer user_data); + +void nm_secret_agent_delete_secrets (NMSecretAgent *self, + NMConnection *connection, + NMSecretAgentDeleteSecretsFunc callback, + gpointer user_data); + +G_END_DECLS + +#endif /* NM_SECRET_AGENT_H */ diff --git a/libnm/nm-types-private.h b/libnm/nm-types-private.h new file mode 100644 index 0000000000..c34d9a8f7c --- /dev/null +++ b/libnm/nm-types-private.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Red Hat, Inc. + */ + +#ifndef NM_TYPES_PRIVATE_H +#define NM_TYPES_PRIVATE_H + +#include <dbus/dbus-glib.h> +#include "nm-types.h" +#include "nm-object-private.h" + +gboolean _nm_ssid_demarshal (GValue *value, GByteArray **dest); +gboolean _nm_uint_array_demarshal (GValue *value, GArray **dest); +gboolean _nm_string_array_demarshal (GValue *value, GPtrArray **dest); +gboolean _nm_object_array_demarshal (GValue *value, + GPtrArray **dest, + DBusGConnection *connection, + NMObjectCreatorFunc func); +gboolean _nm_ip6_address_array_demarshal (GValue *value, GSList **dest); + +#endif /* NM_TYPES_PRIVATE_H */ diff --git a/libnm/nm-types.c b/libnm/nm-types.c new file mode 100644 index 0000000000..cb0ff8ba6c --- /dev/null +++ b/libnm/nm-types.c @@ -0,0 +1,419 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 Red Hat, Inc. + */ + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include <string.h> +#include "nm-types.h" +#include "nm-types-private.h" +#include "nm-object-private.h" +#include "nm-object-cache.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-ip6-config.h" + +static gpointer +_nm_ssid_copy (GByteArray *src) +{ + GByteArray *dest; + + dest = g_byte_array_sized_new (src->len); + g_byte_array_append (dest, src->data, src->len); + return dest; +} + +static void +_nm_ssid_free (GByteArray *ssid) +{ + g_byte_array_free (ssid, TRUE); +} + +GType +nm_ssid_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static (g_intern_static_string ("NMSsid"), + (GBoxedCopyFunc) _nm_ssid_copy, + (GBoxedFreeFunc) _nm_ssid_free); + return our_type; +} + +gboolean +_nm_ssid_demarshal (GValue *value, GByteArray **dest) +{ + GByteArray *array; + + if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY)) + return FALSE; + + if (*dest) { + g_boxed_free (NM_TYPE_SSID, *dest); + *dest = NULL; + } + + array = (GByteArray *) g_value_get_boxed (value); + if (array && (array->len > 0)) { + *dest = g_byte_array_sized_new (array->len); + (*dest)->len = array->len; + memcpy ((*dest)->data, array->data, array->len); + } + + return TRUE; +} + +/*****************************/ + +static gpointer +_nm_uint_array_copy (GArray *src) +{ + GArray *dest; + + dest = g_array_sized_new (FALSE, TRUE, sizeof (guint32), src->len); + g_array_append_vals (dest, src->data, src->len); + return dest; +} + +static void +_nm_uint_array_free (GArray *array) +{ + g_array_free (array, TRUE); +} + +GType +nm_uint_array_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static (g_intern_static_string ("NMUintArray"), + (GBoxedCopyFunc) _nm_uint_array_copy, + (GBoxedFreeFunc) _nm_uint_array_free); + return our_type; +} + +gboolean +_nm_uint_array_demarshal (GValue *value, GArray **dest) +{ + GArray *array; + + if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_UINT_ARRAY)) + return FALSE; + + if (*dest) { + g_boxed_free (NM_TYPE_UINT_ARRAY, *dest); + *dest = NULL; + } + + array = (GArray *) g_value_get_boxed (value); + if (array && (array->len > 0)) { + *dest = g_array_sized_new (FALSE, TRUE, sizeof (guint32), array->len); + g_array_append_vals (*dest, array->data, array->len); + } + + return TRUE; +} + +/*****************************/ + +static gpointer +_nm_string_array_copy (GPtrArray *src) +{ + GPtrArray *dest; + int i; + + dest = g_ptr_array_sized_new (src->len); + for (i = 0; i < src->len; i++) + g_ptr_array_add (dest, g_strdup (g_ptr_array_index (src, i))); + return dest; +} + +static void +_nm_string_array_free (GPtrArray *array) +{ + int i; + + for (i = 0; i < array->len; i++) + g_free (g_ptr_array_index (array, i)); + g_ptr_array_free (array, TRUE); +} + +GType +nm_string_array_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static (g_intern_static_string ("NMStringArray"), + (GBoxedCopyFunc) _nm_string_array_copy, + (GBoxedFreeFunc) _nm_string_array_free); + return our_type; +} + +gboolean +_nm_string_array_demarshal (GValue *value, GPtrArray **dest) +{ + char **array; + + if (!G_VALUE_HOLDS (value, G_TYPE_STRV)) + return FALSE; + + if (*dest) { + g_boxed_free (NM_TYPE_STRING_ARRAY, *dest); + *dest = NULL; + } + + array = (char **) g_value_get_boxed (value); + if (array && array[0]) { + int i; + + *dest = g_ptr_array_new (); + for (i = 0; array[i]; i++) + g_ptr_array_add (*dest, g_strdup (array[i])); + } + + return TRUE; +} + +/*****************************/ + +static gpointer +_nm_object_array_copy (GPtrArray *src) +{ + GPtrArray *dest; + int i; + + dest = g_ptr_array_sized_new (src->len); + for (i = 0; i < src->len; i++) + g_ptr_array_add (dest, g_object_ref (g_ptr_array_index (src, i))); + return dest; +} + +static void +_nm_object_array_free (GPtrArray *array) +{ + int i; + + for (i = 0; i < array->len; i++) + g_object_unref (g_ptr_array_index (array, i)); + g_ptr_array_free (array, TRUE); +} + +GType +nm_object_array_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static (g_intern_static_string ("NMObjectArray"), + (GBoxedCopyFunc) _nm_object_array_copy, + (GBoxedFreeFunc) _nm_object_array_free); + return our_type; +} + +gboolean +_nm_object_array_demarshal (GValue *value, + GPtrArray **dest, + DBusGConnection *connection, + NMObjectCreatorFunc func) +{ + GPtrArray *temp = NULL; + GPtrArray *array; + + if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH)) + return FALSE; + + array = (GPtrArray *) g_value_get_boxed (value); + if (array && array->len) { + int i; + + temp = g_ptr_array_sized_new (array->len); + for (i = 0; i < array->len; i++) { + const char *path; + GObject *object; + + path = g_ptr_array_index (array, i); + object = G_OBJECT (_nm_object_cache_get (path)); + if (object) + g_ptr_array_add (temp, object); + else { + object = (*func) (connection, path); + if (object) + g_ptr_array_add (temp, object); + else + g_warning ("%s: couldn't create object for %s", __func__, path); + } + } + } else + temp = g_ptr_array_new (); + + /* Deallocate after to ensure that an object that might already + * be in the array doesn't get destroyed due to refcounting. + */ + if (*dest) + g_boxed_free (NM_TYPE_OBJECT_ARRAY, *dest); + *dest = temp; + + return TRUE; +} + +/*****************************/ + +static gpointer +_nm_ip6_address_object_array_copy (GPtrArray *src) +{ + GPtrArray *dest; + int i; + + dest = g_ptr_array_sized_new (src->len); + for (i = 0; i < src->len; i++) + g_ptr_array_add (dest, nm_ip6_address_dup (g_ptr_array_index (src, i))); + return dest; +} + +static void +_nm_ip6_address_object_array_free (GPtrArray *array) +{ + int i; + + for (i = 0; i < array->len; i++) + nm_ip6_address_unref (g_ptr_array_index (array, i)); + g_ptr_array_free (array, TRUE); +} + +GType +nm_ip6_address_object_array_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static (g_intern_static_string ("NMIP6AddressObjectArray"), + (GBoxedCopyFunc) _nm_ip6_address_object_array_copy, + (GBoxedFreeFunc) _nm_ip6_address_object_array_free); + return our_type; +} + +/*****************************/ + +static gpointer +_nm_ip6_address_array_copy (GPtrArray *src) +{ + GPtrArray *dest; + int i; + + dest = g_ptr_array_sized_new (src->len); + for (i = 0; i < src->len; i++) { + struct in6_addr *addr = g_ptr_array_index (src, i); + struct in6_addr *copy; + + copy = g_malloc0 (sizeof (struct in6_addr)); + memcpy (copy, addr, sizeof (struct in6_addr)); + g_ptr_array_add (dest, copy); + } + return dest; +} + +static void +_nm_ip6_address_array_free (GPtrArray *array) +{ + int i; + + for (i = 0; i < array->len; i++) + g_free (g_ptr_array_index (array, i)); + g_ptr_array_free (array, TRUE); +} + +GType +nm_ip6_address_array_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static (g_intern_static_string ("NMIP6AddressArray"), + (GBoxedCopyFunc) _nm_ip6_address_array_copy, + (GBoxedFreeFunc) _nm_ip6_address_array_free); + return our_type; +} + +gboolean +_nm_ip6_address_array_demarshal (GValue *value, GSList **dest) +{ + GPtrArray *array; + + if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR)) + return FALSE; + + if (*dest) { + g_slist_free_full (*dest, g_free); + *dest = NULL; + } + + array = (GPtrArray *) g_value_get_boxed (value); + if (array && array->len) { + int i; + + for (i = 0; i < array->len; i++) { + GByteArray *bytearray = (GByteArray *) g_ptr_array_index (array, i); + struct in6_addr *addr; + + addr = g_malloc0 (sizeof (struct in6_addr)); + memcpy (addr->s6_addr, bytearray->data, bytearray->len); + *dest = g_slist_append (*dest, addr); + } + } + + return TRUE; +} + +/*****************************/ + +static gpointer +_nm_ip6_route_object_array_copy (GPtrArray *src) +{ + GPtrArray *dest; + int i; + + dest = g_ptr_array_sized_new (src->len); + for (i = 0; i < src->len; i++) + g_ptr_array_add (dest, nm_ip6_route_dup (g_ptr_array_index (src, i))); + return dest; +} + +static void +_nm_ip6_route_object_array_free (GPtrArray *array) +{ + int i; + + for (i = 0; i < array->len; i++) + nm_ip6_route_unref (g_ptr_array_index (array, i)); + g_ptr_array_free (array, TRUE); +} + +GType +nm_ip6_route_object_array_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static (g_intern_static_string ("NMIP6RouteObjectArray"), + (GBoxedCopyFunc) _nm_ip6_route_object_array_copy, + (GBoxedFreeFunc) _nm_ip6_route_object_array_free); + return our_type; +} diff --git a/libnm/nm-types.h b/libnm/nm-types.h new file mode 100644 index 0000000000..81bd299e69 --- /dev/null +++ b/libnm/nm-types.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 Red Hat, Inc. + */ + +#ifndef NM_TYPES_H +#define NM_TYPES_H + +#include <glib.h> +#include <glib-object.h> + +#include <nm-glib-enum-types.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SSID (nm_ssid_get_type ()) +GType nm_ssid_get_type (void) G_GNUC_CONST; + +#define NM_TYPE_UINT_ARRAY (nm_uint_array_get_type ()) +GType nm_uint_array_get_type (void) G_GNUC_CONST; + +#define NM_TYPE_STRING_ARRAY (nm_string_array_get_type ()) +GType nm_string_array_get_type (void) G_GNUC_CONST; + +#define NM_TYPE_OBJECT_ARRAY (nm_object_array_get_type ()) +GType nm_object_array_get_type (void) G_GNUC_CONST; + +#define NM_TYPE_IP6_ADDRESS_OBJECT_ARRAY (nm_ip6_address_object_array_get_type ()) +GType nm_ip6_address_object_array_get_type (void) G_GNUC_CONST; + +#define NM_TYPE_IP6_ADDRESS_ARRAY (nm_ip6_address_array_get_type ()) +GType nm_ip6_address_array_get_type (void) G_GNUC_CONST; + +#define NM_TYPE_IP6_ROUTE_OBJECT_ARRAY (nm_ip6_route_object_array_get_type ()) +GType nm_ip6_route_object_array_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* NM_TYPES_H */ diff --git a/libnm/nm-vpn-connection.c b/libnm/nm-vpn-connection.c new file mode 100644 index 0000000000..689f58fd8d --- /dev/null +++ b/libnm/nm-vpn-connection.c @@ -0,0 +1,268 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2012 Red Hat, Inc. + */ + +#include <string.h> +#include "nm-vpn-connection.h" +#include "NetworkManager.h" +#include "nm-utils.h" +#include "nm-object-private.h" +#include "nm-active-connection.h" + +G_DEFINE_TYPE (NMVPNConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION) + +#define NM_VPN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_CONNECTION, NMVPNConnectionPrivate)) + +typedef struct { + DBusGProxy *proxy; + char *banner; + NMVPNConnectionState vpn_state; +} NMVPNConnectionPrivate; + +enum { + PROP_0, + PROP_VPN_STATE, + PROP_BANNER, + + LAST_PROP +}; + +enum { + VPN_STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +/** + * nm_vpn_connection_new: + * @connection: the #DBusGConnection + * @path: the DBus object path of the new connection + * + * Creates a new #NMVPNConnection. + * + * Returns: (transfer full): a new connection object + **/ +GObject * +nm_vpn_connection_new (DBusGConnection *connection, const char *path) +{ + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + return g_object_new (NM_TYPE_VPN_CONNECTION, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); +} + +/** + * 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) +{ + NMVPNConnectionPrivate *priv; + + g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), NULL); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn); + + /* We need to update vpn_state first in case it's unknown. */ + _nm_object_ensure_inited (NM_OBJECT (vpn)); + + if (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED) + return NULL; + + return priv->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); + + _nm_object_ensure_inited (NM_OBJECT (vpn)); + return NM_VPN_CONNECTION_GET_PRIVATE (vpn)->vpn_state; +} + +static void +vpn_state_changed_proxy (DBusGProxy *proxy, + NMVPNConnectionState vpn_state, + NMVPNConnectionStateReason reason, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (priv->vpn_state != vpn_state) { + priv->vpn_state = vpn_state; + g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason); + g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE); + } +} + +/*****************************************************************************/ + +static void +nm_vpn_connection_init (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + priv->vpn_state = NM_VPN_CONNECTION_STATE_UNKNOWN; +} + +static void +register_properties (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + const NMPropertiesInfo property_info[] = { + { NM_VPN_CONNECTION_BANNER, &priv->banner }, + { NM_VPN_CONNECTION_VPN_STATE, &priv->vpn_state }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (connection), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_vpn_connection_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_VPN_CONNECTION); + + dbus_g_object_register_marshaller (g_cclosure_marshal_generic, + G_TYPE_NONE, + G_TYPE_UINT, G_TYPE_UINT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "VpnStateChanged", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, + "VpnStateChanged", + G_CALLBACK (vpn_state_changed_proxy), + object, + NULL); + + register_properties (NM_VPN_CONNECTION (object)); +} + +static void +finalize (GObject *object) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + + g_free (priv->banner); + g_object_unref (priv->proxy); + + 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); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_VPN_STATE: + g_value_set_uint (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; + } +} + +static void +nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (connection_class); + + g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMVPNConnection:vpn-state: + * + * The VPN state of the active VPN connection. + **/ + g_object_class_install_property + (object_class, PROP_VPN_STATE, + g_param_spec_uint (NM_VPN_CONNECTION_VPN_STATE, "", "", + NM_VPN_CONNECTION_STATE_UNKNOWN, + NM_VPN_CONNECTION_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMVPNConnection:banner: + * + * The VPN login banner of the active VPN connection. + **/ + g_object_class_install_property + (object_class, PROP_BANNER, + g_param_spec_string (NM_VPN_CONNECTION_BANNER, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* signals */ + signals[VPN_STATE_CHANGED] = + g_signal_new ("vpn-state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNConnectionClass, vpn_state_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); +} diff --git a/libnm/nm-vpn-connection.h b/libnm/nm-vpn-connection.h new file mode 100644 index 0000000000..0495310f38 --- /dev/null +++ b/libnm/nm-vpn-connection.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2010 Red Hat, Inc. + */ + +#ifndef NM_VPN_CONNECTION_H +#define NM_VPN_CONNECTION_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "nm-active-connection.h" +#include "NetworkManagerVPN.h" + +G_BEGIN_DECLS + +#define NM_TYPE_VPN_CONNECTION (nm_vpn_connection_get_type ()) +#define NM_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnection)) +#define NM_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass)) +#define NM_IS_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_CONNECTION)) +#define NM_IS_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_CONNECTION)) +#define NM_VPN_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass)) + +#define NM_VPN_CONNECTION_VPN_STATE "vpn-state" +#define NM_VPN_CONNECTION_BANNER "banner" + +typedef struct { + NMActiveConnection parent; +} NMVPNConnection; + +typedef struct { + NMActiveConnectionClass parent; + + /* Signals */ + void (*vpn_state_changed) (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMVPNConnectionClass; + +GType nm_vpn_connection_get_type (void); + +GObject * nm_vpn_connection_new (DBusGConnection *connection, const char *path); + +NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *vpn); +const char * nm_vpn_connection_get_banner (NMVPNConnection *vpn); + +G_END_DECLS + +#endif /* NM_VPN_CONNECTION_H */ diff --git a/libnm/nm-vpn-plugin-ui-interface.c b/libnm/nm-vpn-plugin-ui-interface.c new file mode 100644 index 0000000000..a4a10836e8 --- /dev/null +++ b/libnm/nm-vpn-plugin-ui-interface.c @@ -0,0 +1,247 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2010 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#include "nm-vpn-plugin-ui-interface.h" + +static void +interface_init (gpointer g_iface) +{ + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Properties */ + + /** + * NMVPNPluginUiInterface:name: + * + * Short display name of the VPN plugin. + */ + g_object_interface_install_property (g_iface, + g_param_spec_string (NM_VPN_PLUGIN_UI_INTERFACE_NAME, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMVPNPluginUiInterface:desc: + * + * Longer description of the VPN plugin. + */ + g_object_interface_install_property (g_iface, + g_param_spec_string (NM_VPN_PLUGIN_UI_INTERFACE_DESC, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMVPNPluginUiInterface:service: + * + * D-Bus service name of the plugin's VPN service. + */ + g_object_interface_install_property (g_iface, + g_param_spec_string (NM_VPN_PLUGIN_UI_INTERFACE_SERVICE, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + initialized = TRUE; +} + + +GType +nm_vpn_plugin_ui_interface_get_type (void) +{ + static GType vpn_plugin_ui_interface_type = 0; + + if (!vpn_plugin_ui_interface_type) { + const GTypeInfo vpn_plugin_ui_interface_info = { + sizeof (NMVpnPluginUiInterface), /* class_size */ + interface_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + vpn_plugin_ui_interface_type = g_type_register_static (G_TYPE_INTERFACE, + "NMVpnPluginUiInterface", + &vpn_plugin_ui_interface_info, + 0); + + g_type_interface_add_prerequisite (vpn_plugin_ui_interface_type, G_TYPE_OBJECT); + } + + return vpn_plugin_ui_interface_type; +} + + +NMVpnPluginUiWidgetInterface * +nm_vpn_plugin_ui_interface_ui_factory (NMVpnPluginUiInterface *iface, + NMConnection *connection, + GError **error) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), NULL); + + return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->ui_factory (iface, connection, error); +} + +guint32 +nm_vpn_plugin_ui_interface_get_capabilities (NMVpnPluginUiInterface *iface) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), 0); + + return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->get_capabilities (iface); +} + +NMConnection * +nm_vpn_plugin_ui_interface_import (NMVpnPluginUiInterface *iface, + const char *path, + GError **error) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), NULL); + + if (nm_vpn_plugin_ui_interface_get_capabilities (iface) & NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT) { + g_return_val_if_fail (NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->import_from_file != NULL, NULL); + return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->import_from_file (iface, path, error); + } + return NULL; +} + +gboolean +nm_vpn_plugin_ui_interface_export (NMVpnPluginUiInterface *iface, + const char *path, + NMConnection *connection, + GError **error) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), FALSE); + + if (nm_vpn_plugin_ui_interface_get_capabilities (iface) & NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT) { + g_return_val_if_fail (NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->export_to_file != NULL, FALSE); + return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->export_to_file (iface, path, connection, error); + } + return FALSE; +} + +char * +nm_vpn_plugin_ui_interface_get_suggested_name (NMVpnPluginUiInterface *iface, + NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), NULL); + + if (NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->get_suggested_name) + return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->get_suggested_name (iface, connection); + return NULL; +} + +gboolean +nm_vpn_plugin_ui_interface_delete_connection (NMVpnPluginUiInterface *iface, + NMConnection *connection, + GError **error) +{ + /* Deprecated and no longer used */ + return TRUE; +} + + +static void +widget_interface_init (gpointer g_iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Signals */ + g_signal_new ("changed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVpnPluginUiWidgetInterface, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + initialized = TRUE; +} + +GType +nm_vpn_plugin_ui_widget_interface_get_type (void) +{ + static GType vpn_plugin_ui_widget_interface_type = 0; + + if (!vpn_plugin_ui_widget_interface_type) { + const GTypeInfo vpn_plugin_ui_widget_interface_info = { + sizeof (NMVpnPluginUiWidgetInterface), /* class_size */ + widget_interface_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + vpn_plugin_ui_widget_interface_type = g_type_register_static (G_TYPE_INTERFACE, + "NMVpnPluginUiWidgetInterface", + &vpn_plugin_ui_widget_interface_info, + 0); + + g_type_interface_add_prerequisite (vpn_plugin_ui_widget_interface_type, G_TYPE_OBJECT); + } + + return vpn_plugin_ui_widget_interface_type; +} + +GObject * +nm_vpn_plugin_ui_widget_interface_get_widget (NMVpnPluginUiWidgetInterface *iface) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_WIDGET_INTERFACE (iface), NULL); + + return NM_VPN_PLUGIN_UI_WIDGET_INTERFACE_GET_INTERFACE (iface)->get_widget (iface); +} + +gboolean +nm_vpn_plugin_ui_widget_interface_update_connection (NMVpnPluginUiWidgetInterface *iface, + NMConnection *connection, + GError **error) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_WIDGET_INTERFACE (iface), FALSE); + + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + return NM_VPN_PLUGIN_UI_WIDGET_INTERFACE_GET_INTERFACE (iface)->update_connection (iface, connection, error); +} + +gboolean +nm_vpn_plugin_ui_widget_interface_save_secrets (NMVpnPluginUiWidgetInterface *iface, + NMConnection *connection, + GError **error) +{ + /* Deprecated and no longer used */ + return TRUE; +} diff --git a/libnm/nm-vpn-plugin-ui-interface.h b/libnm/nm-vpn-plugin-ui-interface.h new file mode 100644 index 0000000000..9c6e49ebe6 --- /dev/null +++ b/libnm/nm-vpn-plugin-ui-interface.h @@ -0,0 +1,229 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2010 Red Hat, Inc. + * Copyright 2008 Novell, Inc. + */ + +#ifndef NM_VPN_PLUGIN_UI_INTERFACE_H +#define NM_VPN_PLUGIN_UI_INTERFACE_H + +#include <glib.h> +#include <glib-object.h> +#include <nm-connection.h> + +G_BEGIN_DECLS + +typedef struct _NMVpnPluginUiInterface NMVpnPluginUiInterface; +typedef struct _NMVpnPluginUiWidgetInterface NMVpnPluginUiWidgetInterface; + +/* Plugin's factory function that returns a GObject that implements + * NMVpnPluginUiInterface. + */ +typedef NMVpnPluginUiInterface * (*NMVpnPluginUiFactory) (GError **error); +NMVpnPluginUiInterface *nm_vpn_plugin_ui_factory (GError **error); + + +/**************************************************/ +/* Plugin interface */ +/**************************************************/ + +#define NM_TYPE_VPN_PLUGIN_UI_INTERFACE (nm_vpn_plugin_ui_interface_get_type ()) +#define NM_VPN_PLUGIN_UI_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_PLUGIN_UI_INTERFACE, NMVpnPluginUiInterface)) +#define NM_IS_VPN_PLUGIN_UI_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_PLUGIN_UI_INTERFACE)) +#define NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_VPN_PLUGIN_UI_INTERFACE, NMVpnPluginUiInterface)) + +/** + * NMVpnPluginUiCapability: + * @NM_VPN_PLUGIN_UI_CAPABILITY_NONE: unknown or no capability + * @NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT: the plugin can import new connections + * @NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT: the plugin can export connections + * @NM_VPN_PLUGIN_UI_CAPABILITY_IPV6: the plugin supports IPv6 addressing + * + * Flags that indicate to UI programs certain capabilities of the plugin. + **/ +typedef enum /*< flags >*/ { + NM_VPN_PLUGIN_UI_CAPABILITY_NONE = 0x00, + NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT = 0x01, + NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT = 0x02, + NM_VPN_PLUGIN_UI_CAPABILITY_IPV6 = 0x04 +} NMVpnPluginUiCapability; + +/* Short display name of the VPN plugin */ +#define NM_VPN_PLUGIN_UI_INTERFACE_NAME "name" + +/* Longer description of the VPN plugin */ +#define NM_VPN_PLUGIN_UI_INTERFACE_DESC "desc" + +/* D-Bus service name of the plugin's VPN service */ +#define NM_VPN_PLUGIN_UI_INTERFACE_SERVICE "service" + +/** + * NMVpnPluginUiInterfaceProp: + * @NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME: the VPN plugin's name + * @NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC: description of the VPN plugin and what + * VPN services it supports + * @NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE: the D-Bus service name used by the + * plugin's VPN service daemon + * + * #GObject property numbers that plugins should override to provide certain + * information to UI programs. + **/ +typedef enum { + /* private */ + NM_VPN_PLUGIN_UI_INTERFACE_PROP_FIRST = 0x1000, + + /* public */ + NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME = NM_VPN_PLUGIN_UI_INTERFACE_PROP_FIRST, + NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC, + NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE +} NMVpnPluginUiInterfaceProp; + + +struct _NMVpnPluginUiInterface { + GTypeInterface g_iface; + + /* Plugin's factory function that returns a GObject that implements + * NMVpnPluginUiWidgetInterface, pre-filled with values from 'connection' + * if non-NULL. + */ + NMVpnPluginUiWidgetInterface * (*ui_factory) (NMVpnPluginUiInterface *iface, + NMConnection *connection, + GError **error); + + /* Plugin's capabiltity function that returns a bitmask of capabilities + * described by NM_VPN_PLUGIN_UI_CAPABILITY_* defines. + */ + guint32 (*get_capabilities) (NMVpnPluginUiInterface *iface); + + /* Try to import a connection from the specified path. On success, return a + * partial NMConnection object. On error, return NULL and set 'error' with + * additional information. Note that 'error' can be NULL, in which case no + * additional error information should be provided. + */ + NMConnection * (*import_from_file) (NMVpnPluginUiInterface *iface, + const char *path, + GError **error); + + /* Export the given connection to the specified path. Return TRUE on success. + * On error, return FALSE and set 'error' with additional error information. + * Note that 'error' can be NULL, in which case no additional error information + * should be provided. + */ + gboolean (*export_to_file) (NMVpnPluginUiInterface *iface, + const char *path, + NMConnection *connection, + GError **error); + + /* For a given connection, return a suggested file name. Returned value should + * be NULL or a suggested file name allocated via g_malloc/g_new/etc to be freed + * by the caller. + */ + char * (*get_suggested_name) (NMVpnPluginUiInterface *iface, NMConnection *connection); + + /* Deprecated and no longer used */ + gboolean (*delete_connection) (NMVpnPluginUiInterface *iface, NMConnection *connection, GError **error); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +}; + +GType nm_vpn_plugin_ui_interface_get_type (void); + +NMVpnPluginUiWidgetInterface *nm_vpn_plugin_ui_interface_ui_factory (NMVpnPluginUiInterface *iface, + NMConnection *connection, + GError **error); + +guint32 nm_vpn_plugin_ui_interface_get_capabilities (NMVpnPluginUiInterface *iface); + +NMConnection *nm_vpn_plugin_ui_interface_import (NMVpnPluginUiInterface *iface, + const char *path, + GError **error); + +gboolean nm_vpn_plugin_ui_interface_export (NMVpnPluginUiInterface *iface, + const char *path, + NMConnection *connection, + GError **error); + +char *nm_vpn_plugin_ui_interface_get_suggested_name (NMVpnPluginUiInterface *iface, + NMConnection *connection); + +/* Deprecated and no longer used */ +NM_DEPRECATED_IN_0_9_10 +gboolean nm_vpn_plugin_ui_interface_delete_connection (NMVpnPluginUiInterface *iface, + NMConnection *connection, + GError **error); + + +/**************************************************/ +/* UI widget interface */ +/**************************************************/ + +#define NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE (nm_vpn_plugin_ui_widget_interface_get_type ()) +#define NM_VPN_PLUGIN_UI_WIDGET_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE, NMVpnPluginUiWidgetInterface)) +#define NM_IS_VPN_PLUGIN_UI_WIDGET_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE)) +#define NM_VPN_PLUGIN_UI_WIDGET_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE, NMVpnPluginUiWidgetInterface)) + +struct _NMVpnPluginUiWidgetInterface { + GTypeInterface g_iface; + + /* Return the GtkWidget for the VPN's UI */ + GObject * (*get_widget) (NMVpnPluginUiWidgetInterface *iface); + + /* Called to save the user-entered options to the connection object. Should + * return FALSE and set 'error' if the current options are invalid. 'error' + * should contain enough information for the plugin to determine which UI + * widget is invalid at a later point in time. For example, creating unique + * error codes for what error occurred and populating the message field + * of 'error' with the name of the invalid property. + */ + gboolean (*update_connection) (NMVpnPluginUiWidgetInterface *iface, + NMConnection *connection, + GError **error); + + /* Deprecated and no longer used */ + gboolean (*save_secrets) (NMVpnPluginUiWidgetInterface *iface, + NMConnection *connection, + GError **error); + + /* Emitted when the value of a UI widget changes. May trigger a validity + * check via update_connection() to write values to the connection */ + void (*changed) (NMVpnPluginUiWidgetInterface *iface); +}; + +GType nm_vpn_plugin_ui_widget_interface_get_type (void); + +GObject * nm_vpn_plugin_ui_widget_interface_get_widget (NMVpnPluginUiWidgetInterface *iface); + +gboolean nm_vpn_plugin_ui_widget_interface_update_connection (NMVpnPluginUiWidgetInterface *iface, + NMConnection *connection, + GError **error); + +/* Deprecated and no longer used */ +NM_DEPRECATED_IN_0_9_10 +gboolean nm_vpn_plugin_ui_widget_interface_save_secrets (NMVpnPluginUiWidgetInterface *iface, + NMConnection *connection, + GError **error); + +G_END_DECLS + +#endif /* NM_VPN_PLUGIN_UI_INTERFACE_H */ diff --git a/libnm/nm-vpn-plugin-utils.c b/libnm/nm-vpn-plugin-utils.c new file mode 100644 index 0000000000..0e800e9c16 --- /dev/null +++ b/libnm/nm-vpn-plugin-utils.c @@ -0,0 +1,189 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 Red Hat, Inc. + */ + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "nm-vpn-plugin-utils.h" +#include "nm-vpn-plugin.h" +#include "nm-setting-private.h" +#include "nm-dbus-glib-types.h" + +#define DATA_KEY_TAG "DATA_KEY=" +#define DATA_VAL_TAG "DATA_VAL=" +#define SECRET_KEY_TAG "SECRET_KEY=" +#define SECRET_VAL_TAG "SECRET_VAL=" + +static void +free_secret (gpointer data) +{ + char *secret = data; + + memset (secret, 0, strlen (secret)); + g_free (secret); +} + +/** + * nm_vpn_plugin_utils_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 + **/ +gboolean +nm_vpn_plugin_utils_read_vpn_details (int fd, + GHashTable **out_data, + GHashTable **out_secrets) +{ + GHashTable *data, *secrets; + gboolean success = FALSE; + char *key = NULL, *val = NULL; + GString *line; + gchar c; + + 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 (g_str_hash, g_str_equal, g_free, g_free); + secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_secret); + + line = g_string_new (NULL); + + /* Read stdin for data and secret items until we get a DONE */ + while (1) { + ssize_t nr; + GHashTable *hash = NULL; + + errno = 0; + nr = read (fd, &c, 1); + if (nr == -1) { + if (errno == EAGAIN) { + g_usleep (100); + continue; + } + break; + } + + if (c != '\n') { + g_string_append_c (line, c); + continue; + } + + /* Check for the finish marker */ + if (strcmp (line->str, "DONE") == 0) + break; + + /* Otherwise it's a data/secret item */ + if (strncmp (line->str, DATA_KEY_TAG, strlen (DATA_KEY_TAG)) == 0) { + hash = data; + key = g_strdup (line->str + strlen (DATA_KEY_TAG)); + } else if (strncmp (line->str, DATA_VAL_TAG, strlen (DATA_VAL_TAG)) == 0) { + hash = data; + val = g_strdup (line->str + strlen (DATA_VAL_TAG)); + } else if (strncmp (line->str, SECRET_KEY_TAG, strlen (SECRET_KEY_TAG)) == 0) { + hash = secrets; + key = g_strdup (line->str + strlen (SECRET_KEY_TAG)); + } else if (strncmp (line->str, SECRET_VAL_TAG, strlen (SECRET_VAL_TAG)) == 0) { + hash = secrets; + val = g_strdup (line->str + strlen (SECRET_VAL_TAG)); + } + g_string_truncate (line, 0); + + if (key && val && hash) { + g_hash_table_insert (hash, key, val); + key = NULL; + val = NULL; + success = TRUE; /* Got at least one value */ + } + } + + if (success) { + if (out_data) + *out_data = data; + else + g_hash_table_destroy (data); + + if (out_secrets) + *out_secrets = secrets; + else + g_hash_table_destroy (secrets); + } else { + g_hash_table_destroy (data); + g_hash_table_destroy (secrets); + } + + g_string_free (line, TRUE); + return success; +} + +/** + * nm_vpn_plugin_utils_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 + **/ +gboolean +nm_vpn_plugin_utils_get_secret_flags (GHashTable *data, + const char *secret_name, + NMSettingSecretFlags *out_flags) +{ + char *flag_name; + const char *val; + unsigned long tmp; + gboolean success = FALSE; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (secret_name != NULL, FALSE); + g_return_val_if_fail (out_flags != NULL, FALSE); + g_return_val_if_fail (*out_flags == NM_SETTING_SECRET_FLAG_NONE, FALSE); + + flag_name = g_strdup_printf ("%s-flags", secret_name); + + /* Try new flags value first */ + val = g_hash_table_lookup (data, flag_name); + if (val) { + errno = 0; + tmp = strtoul (val, NULL, 10); + if (errno == 0 && tmp <= NM_SETTING_SECRET_FLAGS_ALL) { + *out_flags = (NMSettingSecretFlags) tmp; + success = TRUE; + } + } + + g_free (flag_name); + return success; +} diff --git a/libnm/nm-vpn-plugin-utils.h b/libnm/nm-vpn-plugin-utils.h new file mode 100644 index 0000000000..d87ef16aa4 --- /dev/null +++ b/libnm/nm-vpn-plugin-utils.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 Red Hat, Inc. + */ + +#ifndef NM_VPN_PLUGIN_UTILS_H +#define NM_VPN_PLUGIN_UTILS_H + +#include <glib.h> +#include <nm-setting.h> + +G_BEGIN_DECLS + +gboolean nm_vpn_plugin_utils_read_vpn_details (int fd, + GHashTable **out_data, + GHashTable **out_secrets); + +gboolean nm_vpn_plugin_utils_get_secret_flags (GHashTable *data, + const char *secret_name, + NMSettingSecretFlags *out_flags); + +G_END_DECLS + +#endif /* NM_VPN_PLUGIN_UTILS_H */ diff --git a/libnm/nm-vpn-plugin.c b/libnm/nm-vpn-plugin.c new file mode 100644 index 0000000000..08fb647ada --- /dev/null +++ b/libnm/nm-vpn-plugin.c @@ -0,0 +1,1066 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2008 Red Hat, Inc. + */ + +#include <signal.h> +#include "nm-glib-compat.h" +#include "nm-vpn-plugin.h" +#include "nm-vpn-enum-types.h" +#include "nm-utils.h" +#include "nm-connection.h" +#include "nm-dbus-glib-types.h" + +static gboolean impl_vpn_plugin_connect (NMVPNPlugin *plugin, + GHashTable *connection, + GError **error); + +static gboolean impl_vpn_plugin_connect_interactive (NMVPNPlugin *plugin, + GHashTable *connection, + GHashTable *details, + GError **error); + +static gboolean impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin, + GHashTable *connection, + char **service_name, + GError **err); + +static gboolean impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin, + GHashTable *connection, + GError **err); + +static gboolean impl_vpn_plugin_disconnect (NMVPNPlugin *plugin, + GError **err); + +static gboolean impl_vpn_plugin_set_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err); + +static gboolean impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err); + +static gboolean impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err); + +static gboolean impl_vpn_plugin_set_failure (NMVPNPlugin *plugin, + char *reason, + GError **err); + +#include "nm-vpn-plugin-glue.h" + +#define NM_VPN_PLUGIN_QUIT_TIMER 20 + +G_DEFINE_ABSTRACT_TYPE (NMVPNPlugin, nm_vpn_plugin, G_TYPE_OBJECT) + +typedef struct { + NMVPNServiceState state; + + /* DBUS-y stuff */ + DBusGConnection *connection; + 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 */ + GValue banner, tundev, gateway, mtu; +} NMVPNPluginPrivate; + +#define NM_VPN_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN, NMVPNPluginPrivate)) + +enum { + STATE_CHANGED, + CONFIG, + IP4_CONFIG, + IP6_CONFIG, + LOGIN_BANNER, + FAILURE, + QUIT, + SECRETS_REQUIRED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_DBUS_SERVICE_NAME, + PROP_STATE, + + LAST_PROP +}; + +static GSList *active_plugins = NULL; + + +GQuark +nm_vpn_plugin_error_quark (void) +{ + static GQuark quark = 0; + + if (!quark) + quark = g_quark_from_static_string ("nm_vpn_plugin_error"); + + return quark; +} + + +static void +nm_vpn_plugin_set_connection (NMVPNPlugin *plugin, + DBusGConnection *connection) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + if (priv->connection) + dbus_g_connection_unref (priv->connection); + + priv->connection = connection; +} + +DBusGConnection * +nm_vpn_plugin_get_connection (NMVPNPlugin *plugin) +{ + DBusGConnection *connection; + + g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), NULL); + + connection = NM_VPN_PLUGIN_GET_PRIVATE (plugin)->connection; + + if (connection) + dbus_g_connection_ref (connection); + + return connection; +} + +NMVPNServiceState +nm_vpn_plugin_get_state (NMVPNPlugin *plugin) +{ + g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), NM_VPN_SERVICE_STATE_UNKNOWN); + + return NM_VPN_PLUGIN_GET_PRIVATE (plugin)->state; +} + +void +nm_vpn_plugin_set_state (NMVPNPlugin *plugin, + NMVPNServiceState state) +{ + NMVPNPluginPrivate *priv; + + g_return_if_fail (NM_IS_VPN_PLUGIN (plugin)); + + priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + if (priv->state != state) { + priv->state = state; + g_signal_emit (plugin, signals[STATE_CHANGED], 0, state); + } +} + +void +nm_vpn_plugin_set_login_banner (NMVPNPlugin *plugin, + const char *banner) +{ + g_return_if_fail (NM_IS_VPN_PLUGIN (plugin)); + g_return_if_fail (banner != NULL); + + g_signal_emit (plugin, signals[LOGIN_BANNER], 0, banner); +} + +void +nm_vpn_plugin_failure (NMVPNPlugin *plugin, + NMVPNPluginFailure reason) +{ + g_return_if_fail (NM_IS_VPN_PLUGIN (plugin)); + + g_signal_emit (plugin, signals[FAILURE], 0, reason); +} + +gboolean +nm_vpn_plugin_disconnect (NMVPNPlugin *plugin, GError **err) +{ + gboolean ret = FALSE; + NMVPNServiceState state; + + g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE); + + state = nm_vpn_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: + case NM_VPN_SERVICE_STATE_STARTED: + nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING); + ret = NM_VPN_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err); + nm_vpn_plugin_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_emit_quit (NMVPNPlugin *plugin) +{ + g_signal_emit (plugin, signals[QUIT], 0); +} + +static gboolean +connect_timer_expired (gpointer data) +{ + NMVPNPlugin *plugin = NM_VPN_PLUGIN (data); + GError *err = NULL; + + g_message ("Connect timer expired, disconnecting."); + nm_vpn_plugin_disconnect (plugin, &err); + if (err) { + g_warning ("Disconnect failed: %s", err->message); + g_error_free (err); + } + + return FALSE; +} + +static gboolean +quit_timer_expired (gpointer data) +{ + NMVPNPlugin *plugin = NM_VPN_PLUGIN (data); + + nm_vpn_plugin_emit_quit (plugin); + + return FALSE; +} + +static gboolean +fail_stop (gpointer data) +{ + NMVPNPlugin *plugin = NM_VPN_PLUGIN (data); + + nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED); + return FALSE; +} + +static void +schedule_fail_stop (NMVPNPlugin *plugin) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + if (priv->fail_stop_id) + g_source_remove (priv->fail_stop_id); + priv->fail_stop_id = g_idle_add (fail_stop, plugin); +} + +static void +_g_value_set (GValue *dst, GValue *src) +{ + if (src) { + GType type = G_VALUE_TYPE (src); + + if (G_IS_VALUE (dst)) + g_value_unset (dst); + g_value_init (dst, type); + g_value_copy (src, dst); + } else if (G_IS_VALUE (dst)) + g_value_unset (dst); +} + +void +nm_vpn_plugin_set_config (NMVPNPlugin *plugin, + GHashTable *config) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + GValue *val; + + g_return_if_fail (NM_IS_VPN_PLUGIN (plugin)); + g_return_if_fail (config != NULL); + + priv->got_config = TRUE; + + val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4); + if (val && g_value_get_boolean (val)) + priv->has_ip4 = TRUE; + val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6); + if (val && g_value_get_boolean (val)) + priv->has_ip6 = TRUE; + + 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. + */ + _g_value_set (&priv->banner, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_BANNER)); + _g_value_set (&priv->tundev, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_TUNDEV)); + _g_value_set (&priv->gateway, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY)); + _g_value_set (&priv->mtu, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_MTU)); + + g_signal_emit (plugin, signals[CONFIG], 0, config); +} + +void +nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, + GHashTable *ip4_config) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + GHashTable *combined_config; + GHashTableIter iter; + gpointer key, value; + + g_return_if_fail (NM_IS_VPN_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_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. + */ + combined_config = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_iter_init (&iter, ip4_config); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (combined_config, key, value); + + if (G_VALUE_TYPE (&priv->banner) != G_TYPE_INVALID) + g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner); + if (G_VALUE_TYPE (&priv->tundev) != G_TYPE_INVALID) + g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev); + if (G_VALUE_TYPE (&priv->gateway) != G_TYPE_INVALID) + g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, &priv->gateway); + if (G_VALUE_TYPE (&priv->mtu) != G_TYPE_INVALID) + g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu); + + g_signal_emit (plugin, signals[IP4_CONFIG], 0, combined_config); + g_hash_table_destroy (combined_config); + + if ( priv->has_ip4 == priv->got_ip4 + && priv->has_ip6 == priv->got_ip6) + nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +void +nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin, + GHashTable *ip6_config) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + g_return_if_fail (NM_IS_VPN_PLUGIN (plugin)); + g_return_if_fail (ip6_config != NULL); + + priv->got_ip6 = TRUE; + g_signal_emit (plugin, signals[IP6_CONFIG], 0, ip6_config); + + if ( priv->has_ip4 == priv->got_ip4 + && priv->has_ip6 == priv->got_ip6) + nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +static void +connect_timer_removed (gpointer data) +{ + NM_VPN_PLUGIN_GET_PRIVATE (data)->connect_timer = 0; +} + +static void +connect_timer_start (NMVPNPlugin *plugin) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + priv->connect_timer = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, + 60, + connect_timer_expired, + plugin, + connect_timer_removed); +} + +static gboolean +_connect_generic (NMVPNPlugin *plugin, + GHashTable *properties, + GHashTable *details, + GError **error) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + NMVPNPluginClass *vpn_class = NM_VPN_PLUGIN_GET_CLASS (plugin); + NMConnection *connection; + gboolean success = FALSE; + GError *local = NULL; + + if (priv->state != NM_VPN_SERVICE_STATE_STOPPED && + priv->state != NM_VPN_SERVICE_STATE_INIT) { + g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not start connection: wrong plugin state %d", + priv->state); + return FALSE; + } + + connection = nm_connection_new_from_hash (properties, &local); + if (!connection) { + g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: (%d) %s", + local->code, local->message); + g_clear_error (&local); + return FALSE; + } + + + priv->interactive = FALSE; + if (details && !vpn_class->connect_interactive) { + g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, + "Plugin does not implement ConnectInteractive()"); + return FALSE; + } + + nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING); + + if (details) { + priv->interactive = TRUE; + success = vpn_class->connect_interactive (plugin, connection, details, error); + } else + success = vpn_class->connect (plugin, connection, error); + + if (success) { + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start (plugin); + } else { + /* Stop the plugin from an idle handler so that the Connect + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop (plugin); + } + + g_object_unref (connection); + return success; +} + +static gboolean +impl_vpn_plugin_connect (NMVPNPlugin *plugin, + GHashTable *connection, + GError **error) +{ + return _connect_generic (plugin, connection, NULL, error); +} + +static gboolean +impl_vpn_plugin_connect_interactive (NMVPNPlugin *plugin, + GHashTable *connection, + GHashTable *details, + GError **error) +{ + return _connect_generic (plugin, connection, details, error); +} + +/***************************************************************/ + +static gboolean +impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin, + GHashTable *properties, + char **setting_name, + GError **err) +{ + gboolean ret = FALSE; + NMConnection *connection; + char *sn = NULL; + GError *ns_err = NULL; + gboolean needed = FALSE; + GError *cnfh_err = NULL; + + g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE); + g_return_val_if_fail (properties != NULL, FALSE); + + connection = nm_connection_new_from_hash (properties, &cnfh_err); + if (!connection) { + g_set_error (err, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID, + "The connection was invalid: '%s' / '%s' invalid: %d.", + g_type_name (nm_connection_lookup_setting_type_by_quark (cnfh_err->domain)), + cnfh_err->message, cnfh_err->code); + g_error_free (cnfh_err); + return FALSE; + } + + if (!NM_VPN_PLUGIN_GET_CLASS (plugin)->need_secrets) { + *setting_name = ""; + ret = TRUE; + goto out; + } + + needed = NM_VPN_PLUGIN_GET_CLASS (plugin)->need_secrets (plugin, connection, &sn, &ns_err); + if (ns_err) { + *err = g_error_copy (ns_err); + g_error_free (ns_err); + goto out; + } + + ret = TRUE; + if (needed) { + g_assert (sn); + *setting_name = g_strdup (sn); + } else { + /* No secrets required */ + *setting_name = g_strdup (""); + } + +out: + return ret; +} + +static gboolean +impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin, + GHashTable *properties, + GError **error) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + NMConnection *connection; + GError *local = NULL; + gboolean success; + + if (priv->state != NM_VPN_SERVICE_STATE_STARTING) { + g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not accept new secrets: wrong plugin state %d", + priv->state); + return FALSE; + } + + connection = nm_connection_new_from_hash (properties, &local); + if (!connection) { + g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: (%d) %s", + local->code, local->message); + g_clear_error (&local); + return FALSE; + } + + if (!NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets) { + g_set_error_literal (error, 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 FALSE; + } + + success = NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets (plugin, connection, error); + if (success) { + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start (plugin); + } else { + /* Stop the plugin from and idle handler so that the NewSecrets + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop (plugin); + } + + g_object_unref (connection); + return success; +} + +/** + * nm_vpn_plugin_secrets_required: + * @plugin: the #NMVPNPlugin + * @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: 0.9.10 + */ +void +nm_vpn_plugin_secrets_required (NMVPNPlugin *plugin, + const char *message, + const char **hints) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + /* Plugin must be able to accept the new secrets if it calls this method */ + g_return_if_fail (NM_VPN_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(). + */ + if (priv->connect_timer) + g_source_remove (priv->connect_timer); + + g_signal_emit (plugin, signals[SECRETS_REQUIRED], 0, message, hints); +} + +/***************************************************************/ + +static gboolean +impl_vpn_plugin_disconnect (NMVPNPlugin *plugin, + GError **err) +{ + return nm_vpn_plugin_disconnect (plugin, err); +} + +static gboolean +impl_vpn_plugin_set_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err) +{ + nm_vpn_plugin_set_config (plugin, config); + + return TRUE; +} + +static gboolean +impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err) +{ + nm_vpn_plugin_set_ip4_config (plugin, config); + + return TRUE; +} + +static gboolean +impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err) +{ + nm_vpn_plugin_set_ip6_config (plugin, config); + + return TRUE; +} + +static gboolean +impl_vpn_plugin_set_failure (NMVPNPlugin *plugin, + char *reason, + GError **err) +{ + nm_vpn_plugin_failure (plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG); + + return TRUE; +} + +/*********************************************************************/ + +static void +sigterm_handler (int signum) +{ + g_slist_foreach (active_plugins, (GFunc) nm_vpn_plugin_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_init (NMVPNPlugin *plugin) +{ + active_plugins = g_slist_append (active_plugins, plugin); + g_object_weak_ref (G_OBJECT (plugin), + one_plugin_destroyed, + NULL); +} + +static GObject * +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMVPNPlugin *plugin; + NMVPNPluginPrivate *priv; + DBusGConnection *connection; + DBusGProxy *proxy; + guint request_name_result; + GError *err = NULL; + + object = G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = NM_VPN_PLUGIN_GET_PRIVATE (object); + if (!priv->dbus_service_name) + goto err; + + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); + if (!connection) + goto err; + + proxy = dbus_g_proxy_new_for_name (connection, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus"); + + if (!dbus_g_proxy_call (proxy, "RequestName", &err, + G_TYPE_STRING, priv->dbus_service_name, + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &request_name_result, + G_TYPE_INVALID)) { + g_object_unref (proxy); + goto err; + } + + g_object_unref (proxy); + + dbus_g_connection_register_g_object (connection, + NM_VPN_DBUS_PLUGIN_PATH, + object); + + plugin = NM_VPN_PLUGIN (object); + + nm_vpn_plugin_set_connection (plugin, connection); + nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_INIT); + + return object; + + err: + if (err) { + g_warning ("Failed to initialize VPN plugin: %s", err->message); + g_error_free (err); + } + + if (object) + g_object_unref (object); + + return NULL; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DBUS_SERVICE_NAME: + /* Construct-only */ + priv->dbus_service_name = g_strdup (g_value_get_string (value)); + break; + case PROP_STATE: + nm_vpn_plugin_set_state (NM_VPN_PLUGIN (object), + (NMVPNServiceState) g_value_get_uint (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) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_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_uint (value, nm_vpn_plugin_get_state (NM_VPN_PLUGIN (object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMVPNPlugin *plugin = NM_VPN_PLUGIN (object); + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + NMVPNServiceState state; + GError *err = NULL; + + if (priv->fail_stop_id) { + g_source_remove (priv->fail_stop_id); + priv->fail_stop_id = 0; + } + + state = nm_vpn_plugin_get_state (plugin); + + if (state == NM_VPN_SERVICE_STATE_STARTED || + state == NM_VPN_SERVICE_STATE_STARTING) + nm_vpn_plugin_disconnect (plugin, &err); + + if (err) { + g_warning ("Error disconnecting VPN connection: %s", err->message); + g_error_free (err); + } + + G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMVPNPlugin *plugin = NM_VPN_PLUGIN (object); + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + nm_vpn_plugin_set_connection (plugin, NULL); + g_free (priv->dbus_service_name); + + if (G_IS_VALUE (&priv->banner)) + g_value_unset (&priv->banner); + if (G_IS_VALUE (&priv->tundev)) + g_value_unset (&priv->tundev); + if (G_IS_VALUE (&priv->gateway)) + g_value_unset (&priv->gateway); + if (G_IS_VALUE (&priv->mtu)) + g_value_unset (&priv->mtu); + + G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->finalize (object); +} + +static void +quit_timer_removed (gpointer data) +{ + NM_VPN_PLUGIN_GET_PRIVATE (data)->quit_timer = 0; +} + +static void +state_changed (NMVPNPlugin *plugin, NMVPNServiceState state) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + switch (state) { + case NM_VPN_SERVICE_STATE_STARTING: + /* Remove the quit timer. */ + if (priv->quit_timer) + g_source_remove (priv->quit_timer); + + if (priv->fail_stop_id) { + g_source_remove (priv->fail_stop_id); + priv->fail_stop_id = 0; + } + break; + case NM_VPN_SERVICE_STATE_STOPPED: + priv->quit_timer = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, + NM_VPN_PLUGIN_QUIT_TIMER, + quit_timer_expired, + plugin, + quit_timer_removed); + break; + default: + /* Clean up all timers we might have set up. */ + if (priv->connect_timer) + g_source_remove (priv->connect_timer); + + if (priv->quit_timer) + g_source_remove (priv->quit_timer); + + if (priv->fail_stop_id) { + g_source_remove (priv->fail_stop_id); + priv->fail_stop_id = 0; + } + break; + } +} + +static void +nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (plugin_class); + + g_type_class_add_private (object_class, sizeof (NMVPNPluginPrivate)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (plugin_class), + &dbus_glib_nm_vpn_plugin_object_info); + + /* virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + plugin_class->state_changed = state_changed; + + /* properties */ + + /** + * NMVPNPlugin:service-name: + * + * The D-Bus service name of this plugin. + */ + g_object_class_install_property + (object_class, PROP_DBUS_SERVICE_NAME, + g_param_spec_string (NM_VPN_PLUGIN_DBUS_SERVICE_NAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NMVPNPlugin:state: + * + * The state of the plugin. + */ + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_VPN_PLUGIN_STATE, "", "", + NM_VPN_SERVICE_STATE_UNKNOWN, + NM_VPN_SERVICE_STATE_STOPPED, + NM_VPN_SERVICE_STATE_INIT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /* signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNPluginClass, state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + 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 (NMVPNPluginClass, config), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + DBUS_TYPE_G_MAP_OF_VARIANT); + + signals[IP4_CONFIG] = + g_signal_new ("ip4-config", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNPluginClass, ip4_config), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + DBUS_TYPE_G_MAP_OF_VARIANT); + + signals[IP6_CONFIG] = + g_signal_new ("ip6-config", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNPluginClass, ip6_config), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + DBUS_TYPE_G_MAP_OF_VARIANT); + + signals[LOGIN_BANNER] = + g_signal_new ("login-banner", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNPluginClass, login_banner), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + 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 (NMVPNPluginClass, failure), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + 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 (NMVPNPluginClass, quit), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0, + G_TYPE_NONE); + + dbus_g_error_domain_register (NM_VPN_PLUGIN_ERROR, + NM_DBUS_VPN_ERROR_PREFIX, + NM_TYPE_VPN_PLUGIN_ERROR); + + setup_unix_signal_handler (); +} diff --git a/libnm/nm-vpn-plugin.h b/libnm/nm-vpn-plugin.h new file mode 100644 index 0000000000..323017f035 --- /dev/null +++ b/libnm/nm-vpn-plugin.h @@ -0,0 +1,175 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2008 Novell, Inc. + * Copyright 2007 - 2013 Red Hat, Inc. + */ + +#ifndef NM_VPN_PLUGIN_H +#define NM_VPN_PLUGIN_H + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include <NetworkManagerVPN.h> +#include <nm-connection.h> + +G_BEGIN_DECLS + +#define NM_TYPE_VPN_PLUGIN (nm_vpn_plugin_get_type ()) +#define NM_VPN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_PLUGIN, NMVPNPlugin)) +#define NM_VPN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_PLUGIN, NMVPNPluginClass)) +#define NM_IS_VPN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_PLUGIN)) +#define NM_IS_VPN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_PLUGIN)) +#define NM_VPN_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_PLUGIN, NMVPNPluginClass)) + +#define NM_VPN_PLUGIN_DBUS_SERVICE_NAME "service-name" +#define NM_VPN_PLUGIN_STATE "state" + +/** + * NMVPNPluginError: + * @NM_VPN_PLUGIN_ERROR_GENERAL: general failure + * @NM_VPN_PLUGIN_ERROR_STARTING_IN_PROGRESS: the plugin is already starting, + * and another connect request was received + * @NM_VPN_PLUGIN_ERROR_ALREADY_STARTED: the plugin is already connected, and + * another connect request was received + * @NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS: the plugin is already stopping, + * and another stop request was received + * @NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED: the plugin is already stopped, and + * another disconnect request was received + * @NM_VPN_PLUGIN_ERROR_WRONG_STATE: the operation could not be performed in + * this state + * @NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS: the operation could not be performed as + * the request contained malformed arguments, or arguments of unexpected type. + * Usually means that one of the VPN setting data items or secrets was not of + * the expected type (ie int, string, bool, etc). + * @NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED: a child process failed to launch + * @NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID: the operation could not be performed + * because the connection was invalid. Usually means that the connection's + * VPN setting was missing some required data item or secret. + * @NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED: the operation could not be + * performed as the plugin does not support interactive operations, such as + * ConnectInteractive() or NewSecrets() + * + * Returned by the VPN service plugin to indicate errors. + **/ +typedef enum { + NM_VPN_PLUGIN_ERROR_GENERAL, /*< nick=General >*/ + NM_VPN_PLUGIN_ERROR_STARTING_IN_PROGRESS, /*< nick=StartingInProgress >*/ + NM_VPN_PLUGIN_ERROR_ALREADY_STARTED, /*< nick=AlreadyStarted >*/ + NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS, /*< nick=StoppingInProgress >*/ + NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED, /*< nick=AlreadyStopped >*/ + NM_VPN_PLUGIN_ERROR_WRONG_STATE, /*< nick=WrongState >*/ + NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, /*< nick=BadArguments >*/ + NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, /*< nick=LaunchFailed >*/ + NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ + NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED /*< nick=InteractiveNotSupported >*/ +} NMVPNPluginError; + +#define NM_VPN_PLUGIN_ERROR (nm_vpn_plugin_error_quark ()) + +typedef struct { + GObject parent; +} NMVPNPlugin; + +typedef struct { + GObjectClass parent; + + /* virtual methods */ + gboolean (*connect) (NMVPNPlugin *plugin, + NMConnection *connection, + GError **err); + + gboolean (*need_secrets) (NMVPNPlugin *plugin, + NMConnection *connection, + char **setting_name, + GError **error); + + gboolean (*disconnect) (NMVPNPlugin *plugin, + GError **err); + + /* Signals */ + void (*state_changed) (NMVPNPlugin *plugin, + NMVPNServiceState state); + + void (*ip4_config) (NMVPNPlugin *plugin, + GHashTable *ip4_config); + + void (*login_banner) (NMVPNPlugin *plugin, + const char *banner); + + void (*failure) (NMVPNPlugin *plugin, + NMVPNPluginFailure reason); + + void (*quit) (NMVPNPlugin *plugin); + + void (*config) (NMVPNPlugin *plugin, + GHashTable *config); + + void (*ip6_config) (NMVPNPlugin *plugin, + GHashTable *config); + + /* more methods */ + gboolean (*new_secrets) (NMVPNPlugin *plugin, + NMConnection *connection, + GError **error); + + gboolean (*connect_interactive) (NMVPNPlugin *plugin, + NMConnection *connection, + GHashTable *details, + GError **error); + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); +} NMVPNPluginClass; + +GType nm_vpn_plugin_get_type (void); +GQuark nm_vpn_plugin_error_quark (void); +GType nm_vpn_plugin_error_get_type (void); + +DBusGConnection *nm_vpn_plugin_get_connection (NMVPNPlugin *plugin); +NMVPNServiceState nm_vpn_plugin_get_state (NMVPNPlugin *plugin); +void nm_vpn_plugin_set_state (NMVPNPlugin *plugin, + NMVPNServiceState state); + +NM_AVAILABLE_IN_0_9_10 +void nm_vpn_plugin_secrets_required (NMVPNPlugin *plugin, + const char *message, + const char **hints); + +void nm_vpn_plugin_set_login_banner (NMVPNPlugin *plugin, + const char *banner); + +void nm_vpn_plugin_failure (NMVPNPlugin *plugin, + NMVPNPluginFailure reason); + +void nm_vpn_plugin_set_config (NMVPNPlugin *plugin, + GHashTable *config); + +void nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, + GHashTable *ip4_config); + +void nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin, + GHashTable *ip6_config); + +gboolean nm_vpn_plugin_disconnect (NMVPNPlugin *plugin, + GError **err); + +G_END_DECLS + +#endif /* NM_VPN_PLUGIN_H */ diff --git a/libnm/nm-wimax-nsp.c b/libnm/nm-wimax-nsp.c new file mode 100644 index 0000000000..1cbd3eefd0 --- /dev/null +++ b/libnm/nm-wimax-nsp.c @@ -0,0 +1,334 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 Red Hat, Inc. + */ + +#include <config.h> +#include <string.h> + +#include "nm-glib-compat.h" + +#include <nm-connection.h> +#include <nm-setting-connection.h> +#include <nm-setting-wimax.h> + +#include "nm-wimax-nsp.h" +#include "NetworkManager.h" +#include "nm-types-private.h" +#include "nm-object-private.h" + +G_DEFINE_TYPE (NMWimaxNsp, nm_wimax_nsp, NM_TYPE_OBJECT) + +#define NM_WIMAX_NSP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_WIMAX_NSP, NMWimaxNspPrivate)) + +typedef struct { + DBusGProxy *proxy; + + char *name; + guint32 signal_quality; + NMWimaxNspNetworkType network_type; +} NMWimaxNspPrivate; + +enum { + PROP_0, + PROP_NAME, + PROP_SIGNAL_QUALITY, + PROP_NETWORK_TYPE, + + LAST_PROP +}; + +/** + * nm_wimax_nsp_new: + * @connection: the #DBusGConnection + * @path: the D-Bus object path of the WiMAX NSP + * + * Creates a new #NMWimaxNsp. + * + * Returns: (transfer full): a new WiMAX NSP + **/ +GObject * +nm_wimax_nsp_new (DBusGConnection *connection, const char *path) +{ + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + return (GObject *) g_object_new (NM_TYPE_WIMAX_NSP, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, path, + NULL); +} + +/** + * nm_wimax_nsp_get_name: + * @nsp: a #NMWimaxNsp + * + * Gets the name of the wimax NSP + * + * Returns: the name + **/ +const char * +nm_wimax_nsp_get_name (NMWimaxNsp *nsp) +{ + g_return_val_if_fail (NM_IS_WIMAX_NSP (nsp), NULL); + + _nm_object_ensure_inited (NM_OBJECT (nsp)); + return NM_WIMAX_NSP_GET_PRIVATE (nsp)->name; +} + +/** + * nm_wimax_nsp_get_signal_quality: + * @nsp: a #NMWimaxNsp + * + * Gets the WPA signal quality of the wimax NSP. + * + * Returns: the signal quality + **/ +guint32 +nm_wimax_nsp_get_signal_quality (NMWimaxNsp *nsp) +{ + g_return_val_if_fail (NM_IS_WIMAX_NSP (nsp), 0); + + _nm_object_ensure_inited (NM_OBJECT (nsp)); + return NM_WIMAX_NSP_GET_PRIVATE (nsp)->signal_quality; +} + +/** + * nm_wimax_nsp_get_network_type: + * @nsp: a #NMWimaxNsp + * + * Gets the network type of the wimax NSP. + * + * Returns: the network type + **/ +NMWimaxNspNetworkType +nm_wimax_nsp_get_network_type (NMWimaxNsp *nsp) +{ + g_return_val_if_fail (NM_IS_WIMAX_NSP (nsp), NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN); + + _nm_object_ensure_inited (NM_OBJECT (nsp)); + return NM_WIMAX_NSP_GET_PRIVATE (nsp)->network_type; +} + +/** + * 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. + **/ +gboolean +nm_wimax_nsp_connection_valid (NMWimaxNsp *nsp, NMConnection *connection) +{ + NMSettingConnection *s_con; + NMSettingWimax *s_wimax; + const char *ctype; + const char *nsp_name; + const char *setting_name; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + ctype = nm_setting_connection_get_connection_type (s_con); + if (strcmp (ctype, NM_SETTING_WIMAX_SETTING_NAME) != 0) + return FALSE; + + s_wimax = nm_connection_get_setting_wimax (connection); + if (!s_wimax) + return FALSE; + + setting_name = nm_setting_wimax_get_network_name (s_wimax); + if (!setting_name) + return FALSE; + + nsp_name = nm_wimax_nsp_get_name (nsp); + g_warn_if_fail (nsp_name != NULL); + if (g_strcmp0 (nsp_name, setting_name) != 0) + return FALSE; + + return TRUE; +} + +/** + * nm_wimax_nsp_filter_connections: + * @nsp: an #NMWimaxNsp to filter connections for + * @connections: (element-type NMConnection): a list of + * #NMConnection objects to filter + * + * Filters a given list of connections for a given #NMWimaxNsp object and + * return connections which may be activated with the access point. Any + * returned connections will match the @nsp's network name and other attributes. + * + * Returns: (transfer container) (element-type NMConnection): a + * list of #NMConnection objects that could be activated with the given @nsp. + * The elements of the list are owned by their creator and should not be freed + * by the caller, but the returned list itself is owned by the caller and should + * be freed with g_slist_free() when it is no longer required. + **/ +GSList * +nm_wimax_nsp_filter_connections (NMWimaxNsp *nsp, const GSList *connections) +{ + GSList *filtered = NULL; + const GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + if (nm_wimax_nsp_connection_valid (nsp, candidate)) + filtered = g_slist_prepend (filtered, candidate); + } + + return g_slist_reverse (filtered); +} + +/************************************************************/ + +static void +nm_wimax_nsp_init (NMWimaxNsp *nsp) +{ +} + +static void +dispose (GObject *object) +{ + NMWimaxNspPrivate *priv = NM_WIMAX_NSP_GET_PRIVATE (object); + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (nm_wimax_nsp_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMWimaxNspPrivate *priv = NM_WIMAX_NSP_GET_PRIVATE (object); + + g_free (priv->name); + + G_OBJECT_CLASS (nm_wimax_nsp_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMWimaxNsp *nsp = NM_WIMAX_NSP (object); + + _nm_object_ensure_inited (NM_OBJECT (object)); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, nm_wimax_nsp_get_name (nsp)); + break; + case PROP_SIGNAL_QUALITY: + g_value_set_uint (value, nm_wimax_nsp_get_signal_quality (nsp)); + break; + case PROP_NETWORK_TYPE: + g_value_set_uint (value, nm_wimax_nsp_get_network_type (nsp)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +register_properties (NMWimaxNsp *nsp) +{ + NMWimaxNspPrivate *priv = NM_WIMAX_NSP_GET_PRIVATE (nsp); + const NMPropertiesInfo property_info[] = { + { NM_WIMAX_NSP_NAME, &priv->name }, + { NM_WIMAX_NSP_SIGNAL_QUALITY, &priv->signal_quality }, + { NM_WIMAX_NSP_NETWORK_TYPE, &priv->network_type }, + { NULL }, + }; + + _nm_object_register_properties (NM_OBJECT (nsp), + priv->proxy, + property_info); +} + +static void +constructed (GObject *object) +{ + NMWimaxNspPrivate *priv = NM_WIMAX_NSP_GET_PRIVATE (object); + + G_OBJECT_CLASS (nm_wimax_nsp_parent_class)->constructed (object); + + priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_WIMAX_NSP); + register_properties (NM_WIMAX_NSP (object)); +} + + +static void +nm_wimax_nsp_class_init (NMWimaxNspClass *nsp_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (nsp_class); + + g_type_class_add_private (nsp_class, sizeof (NMWimaxNspPrivate)); + + /* virtual methods */ + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMWimaxNsp:name: + * + * The name of the WiMAX NSP. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, 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. + **/ + g_object_class_install_property + (object_class, PROP_NETWORK_TYPE, + g_param_spec_uint (NM_WIMAX_NSP_NETWORK_TYPE, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm/nm-wimax-nsp.h b/libnm/nm-wimax-nsp.h new file mode 100644 index 0000000000..e0cfe1e895 --- /dev/null +++ b/libnm/nm-wimax-nsp.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2011 Red Hat, Inc. + * Copyright 2009 Novell, Inc. + */ + +#ifndef NM_WIMAX_NSP_H +#define NM_WIMAX_NSP_H + +#include <glib.h> +#include <glib-object.h> +#include <NetworkManager.h> +#include "nm-object.h" + +G_BEGIN_DECLS + +#define NM_TYPE_WIMAX_NSP (nm_wimax_nsp_get_type ()) +#define NM_WIMAX_NSP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIMAX_NSP, NMWimaxNsp)) +#define NM_WIMAX_NSP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_WIMAX_NSP, NMWimaxNspClass)) +#define NM_IS_WIMAX_NSP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WIMAX_NSP)) +#define NM_IS_WIMAX_NSP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_WIMAX_NSP)) +#define NM_WIMAX_NSP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_WIMAX_NSP, NMWimaxNspClass)) + +#define NM_WIMAX_NSP_NAME "name" +#define NM_WIMAX_NSP_SIGNAL_QUALITY "signal-quality" +#define NM_WIMAX_NSP_NETWORK_TYPE "network-type" + +typedef enum { + NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN = 0, + NM_WIMAX_NSP_NETWORK_TYPE_HOME = 1, + NM_WIMAX_NSP_NETWORK_TYPE_PARTNER = 2, + NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER = 3 +} NMWimaxNspNetworkType; + +typedef struct { + NMObject parent; +} NMWimaxNsp; + +typedef struct { + NMObjectClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMWimaxNspClass; + +GType nm_wimax_nsp_get_type (void); + +GObject *nm_wimax_nsp_new (DBusGConnection *connection, const char *path); + +const char * nm_wimax_nsp_get_name (NMWimaxNsp *nsp); +guint32 nm_wimax_nsp_get_signal_quality (NMWimaxNsp *nsp); +NMWimaxNspNetworkType nm_wimax_nsp_get_network_type (NMWimaxNsp *nsp); + +GSList * nm_wimax_nsp_filter_connections (NMWimaxNsp *nsp, + const GSList *connections); + +gboolean nm_wimax_nsp_connection_valid (NMWimaxNsp *nsp, + NMConnection *connection); + +G_END_DECLS + +#endif /* NM_WIMAX_NSP_H */ diff --git a/libnm/tests/common.c b/libnm/tests/common.c new file mode 100644 index 0000000000..0dbdac54c2 --- /dev/null +++ b/libnm/tests/common.c @@ -0,0 +1,119 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + * + */ + +#include <dbus/dbus.h> +#include <glib.h> +#include <string.h> + +#include "NetworkManager.h" + +#include "common.h" + +static gboolean +name_exists (GDBusConnection *c, const char *name) +{ + GVariant *reply; + gboolean exists = FALSE; + + reply = g_dbus_connection_call_sync (c, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetNameOwner", + g_variant_new ("(s)", name), + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, + NULL, + NULL); + if (reply != NULL) { + exists = TRUE; + g_variant_unref (reply); + } + + return exists; +} + +NMTestServiceInfo * +nm_test_service_init (void) +{ + NMTestServiceInfo *info; + const char *args[2] = { TEST_NM_SERVICE, NULL }; + GError *error = NULL; + int i; + + info = g_malloc0 (sizeof (*info)); + + info->bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + + /* Spawn the test service. info->keepalive_fd will be a pipe to the service's + * stdin; if it closes, the service will exit immediately. We use this to + * make sure the service exits if the test program crashes. + */ + g_spawn_async_with_pipes (NULL, (char **) args, NULL, 0, NULL, NULL, + &info->pid, &info->keepalive_fd, NULL, NULL, &error); + g_assert_no_error (error); + + /* Wait until the service is registered on the bus */ + for (i = 100; i > 0; i--) { + if (name_exists (info->bus, "org.freedesktop.NetworkManager")) + break; + g_usleep (G_USEC_PER_SEC / 50); + } + g_assert (i > 0); + + /* Grab a proxy to our fake NM service to trigger tests */ + info->proxy = g_dbus_proxy_new_sync (info->bus, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, + NM_DBUS_SERVICE, + NM_DBUS_PATH, + "org.freedesktop.NetworkManager.LibnmGlibTest", + NULL, &error); + g_assert_no_error (error); + + return info; +} + +void +nm_test_service_cleanup (NMTestServiceInfo *info) +{ + int i; + + g_object_unref (info->proxy); + kill (info->pid, SIGTERM); + + /* Wait until the bus notices the service is gone */ + for (i = 100; i > 0; i--) { + if (!name_exists (info->bus, "org.freedesktop.NetworkManager")) + break; + g_usleep (G_USEC_PER_SEC / 50); + } + g_assert (i > 0); + + g_object_unref (info->bus); + close (info->keepalive_fd); + + memset (info, 0, sizeof (*info)); + g_free (info); +} diff --git a/libnm/tests/common.h b/libnm/tests/common.h new file mode 100644 index 0000000000..7c49d2532b --- /dev/null +++ b/libnm/tests/common.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2014 Red Hat, Inc. + */ + +#include <gio/gio.h> + +typedef struct { + GDBusConnection *bus; + GDBusProxy *proxy; + GPid pid; + int keepalive_fd; +} NMTestServiceInfo; + +NMTestServiceInfo *nm_test_service_init (void); +void nm_test_service_cleanup (NMTestServiceInfo *info); diff --git a/libnm/tests/test-nm-client.c b/libnm/tests/test-nm-client.c new file mode 100644 index 0000000000..eebf8358c7 --- /dev/null +++ b/libnm/tests/test-nm-client.c @@ -0,0 +1,898 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2014 Red Hat, Inc. + * + */ + +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <glib.h> +#include <string.h> +#include <sys/types.h> +#include <signal.h> + +#include <NetworkManager.h> +#include "nm-client.h" +#include "nm-device-wifi.h" +#include "nm-device-ethernet.h" +#include "nm-device-wimax.h" +#include "nm-glib-compat.h" + +#include "common.h" + +static GMainLoop *loop = NULL; +static NMTestServiceInfo *sinfo; + +/*******************************************************************/ + +static NMClient * +test_client_new (void) +{ + NMClient *client; + DBusGConnection *bus; + GError *error = NULL; + + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + g_assert_no_error (error); + + client = g_object_new (NM_TYPE_CLIENT, + NM_OBJECT_DBUS_CONNECTION, bus, + NM_OBJECT_DBUS_PATH, NM_DBUS_PATH, + NULL); + g_assert (client != NULL); + + dbus_g_connection_unref (bus); + + g_initable_init (G_INITABLE (client), NULL, &error); + g_assert_no_error (error); + + return client; +} + +/*******************************************************************/ + +static gboolean +loop_quit (gpointer user_data) +{ + g_main_loop_quit ((GMainLoop *) user_data); + return G_SOURCE_REMOVE; +} + +static gboolean +add_device (const char *method, const char *ifname, char **out_path) +{ + GError *error = NULL; + GVariant *ret; + + ret = g_dbus_proxy_call_sync (sinfo->proxy, + method, + g_variant_new ("(s)", ifname), + 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)"); + if (out_path) + g_variant_get (ret, "(o)", out_path); + g_variant_unref (ret); + return TRUE; +} + +/*******************************************************************/ + +typedef struct { + GMainLoop *loop; + gboolean signaled; + gboolean notified; + guint quit_count; + guint quit_id; +} DeviceAddedInfo; + +static void +device_add_check_quit (DeviceAddedInfo *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 +device_added_cb (NMClient *c, + NMDevice *device, + DeviceAddedInfo *info) +{ + g_assert (device); + g_assert_cmpstr (nm_device_get_iface (device), ==, "eth0"); + info->signaled = TRUE; + device_add_check_quit (info); +} + +static void +devices_notify_cb (NMClient *c, + GParamSpec *pspec, + DeviceAddedInfo *info) +{ + 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"); + + info->notified = TRUE; + + device_add_check_quit (info); +} + +static void +test_device_added (void) +{ + NMClient *client; + const GPtrArray *devices; + NMDevice *device; + DeviceAddedInfo info = { loop, FALSE, FALSE, 0, 0 }; + + sinfo = nm_test_service_init (); + client = test_client_new (); + + devices = nm_client_get_devices (client); + g_assert (devices == NULL); + + /* Tell the test service to add a new device */ + add_device ("AddWiredDevice", "eth0", NULL); + + g_signal_connect (client, + "device-added", + (GCallback) device_added_cb, + &info); + info.quit_count++; + + g_signal_connect (client, + "notify::devices", + (GCallback) devices_notify_cb, + &info); + info.quit_count++; + + /* Wait for libnm-glib to find the device */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + + g_assert (info.signaled); + g_assert (info.notified); + + g_signal_handlers_disconnect_by_func (client, device_added_cb, &info); + g_signal_handlers_disconnect_by_func (client, devices_notify_cb, &info); + + 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"); + + g_object_unref (client); + g_clear_pointer (&sinfo, nm_test_service_cleanup); +} + +/*******************************************************************/ + +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 +wifi_device_added_cb (NMClient *c, + NMDevice *device, + WifiApInfo *info) +{ + g_assert_cmpstr (nm_device_get_iface (device), ==, "wlan0"); + info->found = TRUE; + wifi_check_quit (info); +} + +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 == NULL); + + info->notified = TRUE; + wifi_check_quit (info); +} + +static void +test_wifi_ap_added_removed (void) +{ + NMClient *client; + NMDeviceWifi *wifi; + WifiApInfo info = { loop, FALSE, FALSE, 0, 0 }; + GVariant *ret; + GError *error = NULL; + char *expected_path = NULL; + + sinfo = nm_test_service_init (); + client = test_client_new (); + + /*************************************/ + /* Add the wifi device */ + add_device ("AddWifiDevice", "wlan0", NULL); + + g_signal_connect (client, + "device-added", + (GCallback) wifi_device_added_cb, + &info); + info.quit_count = 1; + + /* Wait for libnm-glib to find the device */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + + g_assert (info.found); + g_signal_handlers_disconnect_by_func (client, wifi_device_added_cb, &info); + + wifi = (NMDeviceWifi *) nm_client_get_device_by_iface (client, "wlan0"); + g_assert (NM_IS_DEVICE_WIFI (wifi)); + + /*************************************/ + /* Add the wifi device */ + 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-glib to find the AP */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (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); + g_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-glib to find the AP */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (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); + g_free (expected_path); + + g_object_unref (client); + g_clear_pointer (&sinfo, nm_test_service_cleanup); +} + +/*******************************************************************/ + +static const char *expected_nsp_name = "Clear"; + +typedef struct { + GMainLoop *loop; + gboolean found; + char *nsp_path; + gboolean signaled; + gboolean notified; + guint quit_id; + guint quit_count; +} WimaxNspInfo; + +static void +wimax_check_quit (WimaxNspInfo *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 +wimax_device_added_cb (NMClient *c, + NMDevice *device, + WimaxNspInfo *info) +{ + g_assert_cmpstr (nm_device_get_iface (device), ==, "wmx0"); + info->found = TRUE; + wimax_check_quit (info); +} + +static void +got_nsp_path (WimaxNspInfo *info, const char *path) +{ + if (info->nsp_path) + g_assert_cmpstr (info->nsp_path, ==, path); + else + info->nsp_path = g_strdup (path); +} + +static void +wimax_nsp_added_cb (NMDeviceWimax *w, + NMWimaxNsp *nsp, + WimaxNspInfo *info) +{ + g_assert (nsp); + g_assert_cmpstr (nm_wimax_nsp_get_name (nsp), ==, expected_nsp_name); + got_nsp_path (info, nm_object_get_path (NM_OBJECT (nsp))); + + info->signaled = TRUE; + wimax_check_quit (info); +} + +static void +wimax_nsp_add_notify_cb (NMDeviceWimax *w, + GParamSpec *pspec, + WimaxNspInfo *info) +{ + const GPtrArray *nsps; + NMWimaxNsp *nsp; + + nsps = nm_device_wimax_get_nsps (w); + g_assert (nsps); + g_assert_cmpint (nsps->len, ==, 1); + + nsp = g_ptr_array_index (nsps, 0); + g_assert (nsp); + g_assert_cmpstr (nm_wimax_nsp_get_name (nsp), ==, expected_nsp_name); + got_nsp_path (info, nm_object_get_path (NM_OBJECT (nsp))); + + info->notified = TRUE; + wimax_check_quit (info); +} + +static void +wimax_nsp_removed_cb (NMDeviceWimax *w, + NMWimaxNsp *nsp, + WimaxNspInfo *info) +{ + g_assert (nsp); + g_assert_cmpstr (info->nsp_path, ==, nm_object_get_path (NM_OBJECT (nsp))); + + info->signaled = TRUE; + wimax_check_quit (info); +} + +static void +wimax_nsp_remove_notify_cb (NMDeviceWimax *w, + GParamSpec *pspec, + WimaxNspInfo *info) +{ + const GPtrArray *nsps; + + nsps = nm_device_wimax_get_nsps (w); + g_assert (nsps == NULL); + + info->notified = TRUE; + wimax_check_quit (info); +} + +static void +test_wimax_nsp_added_removed (void) +{ + NMClient *client; + NMDeviceWimax *wimax; + WimaxNspInfo info = { loop, FALSE, FALSE, 0, 0 }; + GVariant *ret; + GError *error = NULL; + char *expected_path = NULL; + + sinfo = nm_test_service_init (); + client = test_client_new (); + + /*************************************/ + /* Add the wimax device */ + add_device ("AddWimaxDevice", "wmx0", NULL); + + g_signal_connect (client, + "device-added", + (GCallback) wimax_device_added_cb, + &info); + info.quit_count = 1; + + /* Wait for libnm-glib to find the device */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + + g_assert (info.found); + g_signal_handlers_disconnect_by_func (client, wimax_device_added_cb, &info); + + wimax = (NMDeviceWimax *) nm_client_get_device_by_iface (client, "wmx0"); + g_assert (NM_IS_DEVICE_WIMAX (wimax)); + + /*************************************/ + /* Add the wimax NSP */ + info.signaled = FALSE; + info.notified = FALSE; + info.quit_id = 0; + + ret = g_dbus_proxy_call_sync (sinfo->proxy, + "AddWimaxNsp", + g_variant_new ("(ss)", "wmx0", expected_nsp_name), + 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 (wimax, + "nsp-added", + (GCallback) wimax_nsp_added_cb, + &info); + info.quit_count = 1; + + g_signal_connect (wimax, + "notify::nsps", + (GCallback) wimax_nsp_add_notify_cb, + &info); + info.quit_count++; + + /* Wait for libnm-glib to find the AP */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + + g_assert (info.signaled); + g_assert (info.notified); + g_assert (info.nsp_path); + g_assert_cmpstr (info.nsp_path, ==, expected_path); + g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_added_cb, &info); + g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_add_notify_cb, &info); + + /*************************************/ + /* Remove the wimax NSP */ + info.signaled = FALSE; + info.notified = FALSE; + info.quit_id = 0; + + ret = g_dbus_proxy_call_sync (sinfo->proxy, + "RemoveWimaxNsp", + g_variant_new ("(so)", "wmx0", expected_path), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 3000, + NULL, + &error); + g_assert_no_error (error); + g_clear_pointer (&ret, g_variant_unref); + + g_signal_connect (wimax, + "nsp-removed", + (GCallback) wimax_nsp_removed_cb, + &info); + info.quit_count = 1; + + g_signal_connect (wimax, + "notify::nsps", + (GCallback) wimax_nsp_remove_notify_cb, + &info); + info.quit_count++; + + /* Wait for libnm-glib to find the AP */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + + g_assert (info.signaled); + g_assert (info.notified); + g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_removed_cb, &info); + g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_remove_notify_cb, &info); + + g_free (info.nsp_path); + g_free (expected_path); + + g_object_unref (client); + g_clear_pointer (&sinfo, nm_test_service_cleanup); +} + +/*******************************************************************/ + +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_added_cb (NMClient *c, + NMDevice *device, + DaInfo *info) +{ + da_check_quit (info); +} + +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) +{ + NMClient *client; + DaInfo info = { loop }; + char *paths[3] = { NULL, NULL, NULL }; + NMDevice *device; + const GPtrArray *devices; + GError *error = NULL; + GVariant *ret; + + sinfo = nm_test_service_init (); + client = test_client_new (); + + /*************************************/ + /* Add some devices */ + add_device ("AddWifiDevice", "wlan0", &paths[0]); + add_device ("AddWiredDevice", "eth0", &paths[1]); + add_device ("AddWiredDevice", "eth1", &paths[2]); + info.quit_count = 3; + + g_signal_connect (client, + "device-added", + (GCallback) da_device_added_cb, + &info); + + /* Wait for libnm-glib to find the device */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + + g_assert_cmpint (info.quit_count, ==, 0); + g_signal_handlers_disconnect_by_func (client, da_device_added_cb, &info); + + /* 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)); + + device = nm_client_get_device_by_iface (client, "eth0"); + g_assert (NM_IS_DEVICE_ETHERNET (device)); + + device = nm_client_get_device_by_iface (client, "eth1"); + g_assert (NM_IS_DEVICE_ETHERNET (device)); + + /********************************/ + /* Now remove the device in the middle */ + ret = g_dbus_proxy_call_sync (sinfo->proxy, + "RemoveDevice", + g_variant_new ("(o)", paths[1]), + 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-glib to find the device */ + info.quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (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)); + + device = nm_client_get_device_by_iface (client, "eth1"); + g_assert (NM_IS_DEVICE_ETHERNET (device)); + + g_free (paths[0]); + g_free (paths[1]); + g_free (paths[2]); + + g_object_unref (client); + g_clear_pointer (&sinfo, nm_test_service_cleanup); +} + +static void +manager_running_changed (GObject *client, + GParamSpec *pspec, + gpointer user_data) +{ + int *running_changed = user_data; + + (*running_changed)++; + g_main_loop_quit (loop); +} + +static void +test_client_manager_running (void) +{ + NMClient *client1, *client2; + guint quit_id; + int running_changed = 0; + GError *error = NULL; + + client1 = test_client_new (); + + g_assert (!nm_client_get_manager_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); + 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 = nm_test_service_init (); + client2 = test_client_new (); + + /* client2 should know that NM is running, but the previously-created + * client1 hasn't gotten the news yet. + */ + g_assert (!nm_client_get_manager_running (client1)); + g_assert (nm_client_get_manager_running (client2)); + + g_signal_connect (client1, "notify::" NM_CLIENT_MANAGER_RUNNING, + G_CALLBACK (manager_running_changed), &running_changed); + quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + g_assert_cmpint (running_changed, ==, 1); + g_assert (nm_client_get_manager_running (client1)); + g_source_remove (quit_id); + + /* And kill it */ + g_clear_pointer (&sinfo, nm_test_service_cleanup); + + g_assert (nm_client_get_manager_running (client1)); + + quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + g_assert_cmpint (running_changed, ==, 2); + g_assert (!nm_client_get_manager_running (client1)); + g_source_remove (quit_id); + + g_object_unref (client1); + g_object_unref (client2); +} + +/*******************************************************************/ + +int +main (int argc, char **argv) +{ +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + g_test_init (&argc, &argv, NULL); + + loop = g_main_loop_new (NULL, FALSE); + + g_test_add_func ("/libnm-glib/device-added", test_device_added); + g_test_add_func ("/libnm-glib/wifi-ap-added-removed", test_wifi_ap_added_removed); + g_test_add_func ("/libnm-glib/wimax-nsp-added-removed", test_wimax_nsp_added_removed); + g_test_add_func ("/libnm-glib/devices-array", test_devices_array); + g_test_add_func ("/libnm-glib/client-manager-running", test_client_manager_running); + + return g_test_run (); +} + diff --git a/libnm/tests/test-remote-settings-client.c b/libnm/tests/test-remote-settings-client.c new file mode 100644 index 0000000000..987845c1bb --- /dev/null +++ b/libnm/tests/test-remote-settings-client.c @@ -0,0 +1,456 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2011 Red Hat, Inc. + * + */ + +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <glib.h> +#include <string.h> +#include <sys/types.h> +#include <signal.h> + +#include <NetworkManager.h> + +#include <nm-setting-connection.h> +#include <nm-setting-wired.h> +#include <nm-utils.h> + +#include "nm-remote-settings.h" +#include "common.h" + +static NMTestServiceInfo *sinfo; +static NMRemoteSettings *settings = NULL; +DBusGConnection *bus = NULL; +NMRemoteConnection *remote = NULL; + +/*******************************************************************/ + +static void +add_cb (NMRemoteSettings *s, + NMRemoteConnection *connection, + GError *error, + gpointer user_data) +{ + if (error) + g_warning ("Add error: %s", error->message); + + *((gboolean *) user_data) = TRUE; + remote = connection; + g_object_add_weak_pointer (G_OBJECT (connection), (void **) &remote); +} + +#define TEST_CON_ID "blahblahblah" + +static void +test_add_connection (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + char *uuid; + gboolean success; + time_t start, now; + gboolean done = FALSE; + + connection = nm_connection_new (); + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + uuid = nm_utils_uuid_generate (); + g_object_set (G_OBJECT (s_con), + NM_SETTING_CONNECTION_ID, TEST_CON_ID, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free (uuid); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + success = nm_remote_settings_add_connection (settings, + connection, + add_cb, + &done); + g_assert (success == TRUE); + + start = time (NULL); + do { + now = time (NULL); + g_main_context_iteration (NULL, FALSE); + } while ((done == FALSE) && (now - start < 5)); + g_assert (done == TRUE); + g_assert (remote != NULL); + + /* Make sure the connection is the same as what we added */ + g_assert (nm_connection_compare (connection, + NM_CONNECTION (remote), + NM_SETTING_COMPARE_FLAG_EXACT) == TRUE); +} + +/*******************************************************************/ + +static void +set_visible_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GError *error = NULL; + gboolean success; + + success = dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + g_assert_no_error (error); + g_assert (success == TRUE); +} + +static void +invis_removed_cb (NMRemoteConnection *connection, gboolean *done) +{ + *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) +{ + time_t start, now; + GSList *list, *iter; + DBusGProxy *proxy; + gboolean done = FALSE, has_settings = FALSE; + char *path; + + g_assert (remote != NULL); + + /* Listen for the remove event when the connection becomes invisible */ + g_signal_connect (remote, "removed", G_CALLBACK (invis_removed_cb), &done); + + path = g_strdup (nm_connection_get_path (NM_CONNECTION (remote))); + proxy = dbus_g_proxy_new_for_name (bus, + NM_DBUS_SERVICE, + path, + NM_DBUS_IFACE_SETTINGS_CONNECTION); + g_assert (proxy != NULL); + + /* Bypass the NMRemoteSettings object so we can test it independently */ + dbus_g_proxy_begin_call (proxy, "SetVisible", set_visible_cb, NULL, NULL, + G_TYPE_BOOLEAN, FALSE, G_TYPE_INVALID); + + /* Wait for the connection to be removed */ + start = time (NULL); + do { + now = time (NULL); + g_main_context_iteration (NULL, FALSE); + } while ((done == FALSE) && (now - start < 5)); + g_assert (done == TRUE); + + g_assert (remote); + g_signal_handlers_disconnect_by_func (remote, G_CALLBACK (invis_removed_cb), &done); + + /* Ensure NMRemoteSettings no longer has the connection */ + list = nm_remote_settings_list_connections (settings); + for (iter = list; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + g_assert ((gpointer) 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 (remote); + nm_connection_for_each_setting_value (NM_CONNECTION (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 (NMRemoteSettings *foo, + NMRemoteConnection *connection, + NMRemoteConnection **new) +{ + *new = connection; +} + +static void +test_make_visible (void) +{ + time_t start, now; + GSList *list, *iter; + DBusGProxy *proxy; + gboolean found = FALSE; + char *path; + NMRemoteConnection *new = NULL; + + g_assert (remote != NULL); + + /* Wait for the new-connection signal when the connection is visible again */ + g_signal_connect (settings, NM_REMOTE_SETTINGS_NEW_CONNECTION, + G_CALLBACK (vis_new_connection_cb), &new); + + path = g_strdup (nm_connection_get_path (NM_CONNECTION (remote))); + proxy = dbus_g_proxy_new_for_name (bus, + NM_DBUS_SERVICE, + path, + NM_DBUS_IFACE_SETTINGS_CONNECTION); + g_assert (proxy != NULL); + + /* Bypass the NMRemoteSettings object so we can test it independently */ + dbus_g_proxy_begin_call (proxy, "SetVisible", set_visible_cb, NULL, NULL, + G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID); + + + /* Wait for the settings service to announce the connection again */ + start = time (NULL); + do { + now = time (NULL); + g_main_context_iteration (NULL, FALSE); + } while ((new == NULL) && (now - start < 5)); + + /* Ensure the new connection is the same as the one we made visible again */ + g_assert (new); + g_assert (new == remote); + + g_signal_handlers_disconnect_by_func (settings, G_CALLBACK (vis_new_connection_cb), &new); + + /* Ensure NMRemoteSettings has the connection */ + list = nm_remote_settings_list_connections (settings); + for (iter = list; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + if ((gpointer) 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 (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GError *error = NULL; + gboolean success; + + success = dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + g_assert_no_error (error); + g_assert (success == TRUE); +} + +static void +removed_cb (NMRemoteConnection *connection, gboolean *done) +{ + *done = TRUE; +} + +static void +test_remove_connection (void) +{ + NMRemoteConnection *connection; + time_t start, now; + GSList *list, *iter; + DBusGProxy *proxy; + gboolean done = FALSE; + char *path; + + /* Find a connection to delete */ + list = nm_remote_settings_list_connections (settings); + g_assert_cmpint (g_slist_length (list), >, 0); + + connection = NM_REMOTE_CONNECTION (list->data); + g_assert (connection); + g_assert (remote == connection); + path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection))); + g_signal_connect (connection, "removed", G_CALLBACK (removed_cb), &done); + + proxy = dbus_g_proxy_new_for_name (bus, + NM_DBUS_SERVICE, + path, + NM_DBUS_IFACE_SETTINGS_CONNECTION); + g_assert (proxy != NULL); + + /* Bypass the NMRemoteSettings object so we can test it independently */ + dbus_g_proxy_begin_call (proxy, "Delete", deleted_cb, NULL, NULL, G_TYPE_INVALID); + + start = time (NULL); + do { + now = time (NULL); + g_main_context_iteration (NULL, FALSE); + } while ((done == FALSE) && (now - start < 5)); + g_assert (done == TRUE); + + g_assert (!remote); + + /* Ensure NMRemoteSettings no longer has the connection */ + list = nm_remote_settings_list_connections (settings); + for (iter = list; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + g_assert ((gpointer) connection != (gpointer) candidate); + g_assert_cmpstr (path, ==, nm_connection_get_path (candidate)); + } + + g_free (path); + g_object_unref (proxy); +} + +/*******************************************************************/ + +static GMainLoop *loop; + +static gboolean +loop_quit (gpointer user_data) +{ + g_main_loop_quit (loop); + return G_SOURCE_REMOVE; +} + +static void +settings_service_running_changed (GObject *client, + GParamSpec *pspec, + gpointer user_data) +{ + int *running_changed = user_data; + + (*running_changed)++; + g_main_loop_quit (loop); +} + +static void +test_service_running (void) +{ + NMRemoteSettings *settings2; + guint quit_id; + int running_changed = 0; + gboolean running; + + loop = g_main_loop_new (NULL, FALSE); + + g_object_get (G_OBJECT (settings), + NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running, + NULL); + g_assert (running == TRUE); + + /* Now kill the test service. */ + nm_test_service_cleanup (sinfo); + + settings2 = nm_remote_settings_new (bus); + + /* settings2 should know that NM is running, but the previously-created + * settings hasn't gotten the news yet. + */ + g_object_get (G_OBJECT (settings2), + NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running, + NULL); + g_assert (running == FALSE); + g_object_get (G_OBJECT (settings), + NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running, + NULL); + g_assert (running == TRUE); + + g_signal_connect (settings, "notify::" NM_REMOTE_SETTINGS_SERVICE_RUNNING, + G_CALLBACK (settings_service_running_changed), &running_changed); + quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + g_assert_cmpint (running_changed, ==, 1); + g_source_remove (quit_id); + + g_object_get (G_OBJECT (settings2), + NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running, + NULL); + g_assert (running == FALSE); + + /* Now restart it */ + sinfo = nm_test_service_init (); + + quit_id = g_timeout_add_seconds (5, loop_quit, loop); + g_main_loop_run (loop); + g_assert_cmpint (running_changed, ==, 2); + g_source_remove (quit_id); + + g_object_get (G_OBJECT (settings2), + NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running, + NULL); + g_assert (running == TRUE); + + g_object_unref (settings2); +} + +/*******************************************************************/ + +int +main (int argc, char **argv) +{ + int ret; + GError *error = NULL; + +#if !GLIB_CHECK_VERSION (2, 35, 0) + g_type_init (); +#endif + + g_test_init (&argc, &argv, NULL); + + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + g_assert_no_error (error); + + sinfo = nm_test_service_init (); + + settings = nm_remote_settings_new (bus); + g_assert (settings != NULL); + + /* FIXME: these tests assume that they get run in order, but g_test_run() + * does not actually guarantee that! + */ + g_test_add_func ("/remote_settings/add_connection", test_add_connection); + g_test_add_func ("/remote_settings/make_invisible", test_make_invisible); + g_test_add_func ("/remote_settings/make_visible", test_make_visible); + g_test_add_func ("/remote_settings/remove_connection", test_remove_connection); + g_test_add_func ("/remote_settings/service_running", test_service_running); + + ret = g_test_run (); + + nm_test_service_cleanup (sinfo); + g_object_unref (settings); + dbus_g_connection_unref (bus); + + return ret; +} + |