NetworkManager/libnm-glib/nm-device.c
Dan Williams 2140dad5e0 core: consolidate mobile broadband device types
These days more and more devices are showing up that support a
number of different access technology families in the same hardware,
like Qualcomm Gobi (CDMA and GSM), Pantech UM190 (CDMA and GSM),
Pantech UML290 (CDMA and LTE), LG VL600 (CDMA and LTE), Sierra
320U (GSM and LTE), etc.  The previous scheme of having device
classes based on access technology family simply cannot handle
this hardware and attempting to add LTE to both the CDMA and GSM
device classes would result in a bunch of code duplication that
we don't want.  There's a better way...

Instead, combine both CDMA and GSM device classes into a generic
"Modem" device class that provides capabilities indicating what
access technology families a modem supports, and what families
it supports immediately without a firmware reload.  (Gobi devices
for example require a firmware reload before they can switch
between GSM and CDMA).  This provides the necessary flexibility
to the client and allows us to keep the API stable when the
same consolidation change is made in ModemManager.

The current code doesn't yet allow multi-mode operation internally,
but the API is now what we want it to be and won't need to be
changed.
2011-02-25 10:16:17 -06:00

1398 lines
35 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* libnm_glib -- Access network status & information from glib applications
*
* 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 (C) 2007 - 2008 Novell, Inc.
* Copyright (C) 2007 - 2011 Red Hat, Inc.
*/
#include <string.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
#include "NetworkManager.h"
#include "nm-device-ethernet.h"
#include "nm-device-wifi.h"
#include "nm-device-modem.h"
#include "nm-device-bt.h"
#include "nm-device-wimax.h"
#include "nm-device.h"
#include "nm-device-private.h"
#include "nm-object-private.h"
#include "nm-object-cache.h"
#include "nm-marshal.h"
#include "nm-device-bindings.h"
G_DEFINE_TYPE (NMDevice, nm_device, NM_TYPE_OBJECT)
#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
typedef struct {
gboolean disposed;
DBusGProxy *proxy;
char *iface;
char *ip_iface;
NMDeviceType device_type;
char *udi;
char *driver;
NMDeviceCapabilities capabilities;
gboolean managed;
gboolean firmware_missing;
NMIP4Config *ip4_config;
gboolean null_ip4_config;
NMDHCP4Config *dhcp4_config;
gboolean null_dhcp4_config;
NMIP6Config *ip6_config;
gboolean null_ip6_config;
NMDHCP6Config *dhcp6_config;
gboolean null_dhcp6_config;
NMDeviceState state;
GUdevClient *client;
char *product;
char *vendor;
} NMDevicePrivate;
enum {
PROP_0,
PROP_INTERFACE,
PROP_UDI,
PROP_DRIVER,
PROP_CAPABILITIES,
PROP_MANAGED,
PROP_FIRMWARE_MISSING,
PROP_IP4_CONFIG,
PROP_DHCP4_CONFIG,
PROP_IP6_CONFIG,
PROP_STATE,
PROP_PRODUCT,
PROP_VENDOR,
PROP_DHCP6_CONFIG,
PROP_IP_INTERFACE,
PROP_DEVICE_TYPE,
LAST_PROP
};
enum {
STATE_CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
static void
nm_device_init (NMDevice *device)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
priv->state = NM_DEVICE_STATE_UNKNOWN;
}
static gboolean
demarshal_ip4_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
const char *path;
NMIP4Config *config = NULL;
DBusGConnection *connection;
if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
return FALSE;
priv->null_ip4_config = FALSE;
path = g_value_get_boxed (value);
if (path) {
if (!strcmp (path, "/"))
priv->null_ip4_config = TRUE;
else {
config = NM_IP4_CONFIG (_nm_object_cache_get (path));
if (!config) {
connection = nm_object_get_connection (object);
config = NM_IP4_CONFIG (nm_ip4_config_new (connection, path));
}
}
}
if (priv->ip4_config) {
g_object_unref (priv->ip4_config);
priv->ip4_config = NULL;
}
if (config)
priv->ip4_config = config;
_nm_object_queue_notify (object, NM_DEVICE_IP4_CONFIG);
return TRUE;
}
static gboolean
demarshal_dhcp4_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
const char *path;
NMDHCP4Config *config = NULL;
DBusGConnection *connection;
if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
return FALSE;
priv->null_dhcp4_config = FALSE;
path = g_value_get_boxed (value);
if (path) {
if (!strcmp (path, "/"))
priv->null_dhcp4_config = TRUE;
else {
config = NM_DHCP4_CONFIG (_nm_object_cache_get (path));
if (!config) {
connection = nm_object_get_connection (object);
config = NM_DHCP4_CONFIG (nm_dhcp4_config_new (connection, path));
}
}
}
if (priv->dhcp4_config) {
g_object_unref (priv->dhcp4_config);
priv->dhcp4_config = NULL;
}
if (config)
priv->dhcp4_config = config;
_nm_object_queue_notify (object, NM_DEVICE_DHCP4_CONFIG);
return TRUE;
}
static gboolean
demarshal_ip6_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
const char *path;
NMIP6Config *config = NULL;
DBusGConnection *connection;
if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
return FALSE;
priv->null_ip6_config = FALSE;
path = g_value_get_boxed (value);
if (path) {
if (!strcmp (path, "/"))
priv->null_ip6_config = TRUE;
else {
config = NM_IP6_CONFIG (_nm_object_cache_get (path));
if (!config) {
connection = nm_object_get_connection (object);
config = NM_IP6_CONFIG (nm_ip6_config_new (connection, path));
}
}
}
if (priv->ip6_config) {
g_object_unref (priv->ip6_config);
priv->ip6_config = NULL;
}
if (config)
priv->ip6_config = config;
_nm_object_queue_notify (object, NM_DEVICE_IP6_CONFIG);
return TRUE;
}
static gboolean
demarshal_dhcp6_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
const char *path;
NMDHCP6Config *config = NULL;
DBusGConnection *connection;
if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
return FALSE;
priv->null_dhcp6_config = FALSE;
path = g_value_get_boxed (value);
if (path) {
if (!strcmp (path, "/"))
priv->null_dhcp6_config = TRUE;
else {
config = NM_DHCP6_CONFIG (_nm_object_cache_get (path));
if (!config) {
connection = nm_object_get_connection (object);
config = NM_DHCP6_CONFIG (nm_dhcp6_config_new (connection, path));
}
}
}
if (priv->dhcp6_config) {
g_object_unref (priv->dhcp6_config);
priv->dhcp6_config = NULL;
}
if (config)
priv->dhcp6_config = config;
_nm_object_queue_notify (object, NM_DEVICE_DHCP6_CONFIG);
return TRUE;
}
static void
register_for_property_changed (NMDevice *device)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
const NMPropertiesChangedInfo property_changed_info[] = {
{ NM_DEVICE_UDI, _nm_object_demarshal_generic, &priv->udi },
{ NM_DEVICE_INTERFACE, _nm_object_demarshal_generic, &priv->iface },
{ NM_DEVICE_IP_INTERFACE, _nm_object_demarshal_generic, &priv->ip_iface },
{ NM_DEVICE_DRIVER, _nm_object_demarshal_generic, &priv->driver },
{ NM_DEVICE_CAPABILITIES, _nm_object_demarshal_generic, &priv->capabilities },
{ NM_DEVICE_MANAGED, _nm_object_demarshal_generic, &priv->managed },
{ NM_DEVICE_FIRMWARE_MISSING, _nm_object_demarshal_generic, &priv->firmware_missing },
{ NM_DEVICE_IP4_CONFIG, demarshal_ip4_config, &priv->ip4_config },
{ NM_DEVICE_DHCP4_CONFIG, demarshal_dhcp4_config, &priv->dhcp4_config },
{ NM_DEVICE_IP6_CONFIG, demarshal_ip6_config, &priv->ip6_config },
{ NM_DEVICE_DHCP6_CONFIG, demarshal_dhcp6_config, &priv->dhcp6_config },
{ NULL },
};
_nm_object_handle_properties_changed (NM_OBJECT (device),
priv->proxy,
property_changed_info);
}
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 (priv->state != new_state) {
priv->state = new_state;
g_signal_emit (self, signals[STATE_CHANGED], 0, new_state, old_state, reason);
_nm_object_queue_notify (NM_OBJECT (self), "state");
}
}
static GObject*
constructor (GType type,
guint n_construct_params,
GObjectConstructParam *construct_params)
{
NMObject *object;
NMDevicePrivate *priv;
object = (NMObject *) G_OBJECT_CLASS (nm_device_parent_class)->constructor (type,
n_construct_params,
construct_params);
if (!object)
return NULL;
priv = NM_DEVICE_GET_PRIVATE (object);
priv->proxy = dbus_g_proxy_new_for_name (nm_object_get_connection (object),
NM_DBUS_SERVICE,
nm_object_get_path (object),
NM_DBUS_INTERFACE_DEVICE);
register_for_property_changed (NM_DEVICE (object));
dbus_g_object_register_marshaller (_nm_marshal_VOID__UINT_UINT_UINT,
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);
return G_OBJECT (object);
}
static void
dispose (GObject *object)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
if (priv->disposed) {
G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
return;
}
priv->disposed = TRUE;
g_object_unref (priv->proxy);
if (priv->ip4_config)
g_object_unref (priv->ip4_config);
if (priv->dhcp4_config)
g_object_unref (priv->dhcp4_config);
if (priv->ip6_config)
g_object_unref (priv->ip6_config);
if (priv->dhcp6_config)
g_object_unref (priv->dhcp6_config);
if (priv->client)
g_object_unref (priv->client);
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->product);
g_free (priv->vendor);
G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
}
static void
get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
NMDevice *device = NM_DEVICE (object);
switch (prop_id) {
case PROP_DEVICE_TYPE:
g_value_set_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_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_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_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;
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);
switch (prop_id) {
case PROP_DEVICE_TYPE:
/* Construct only */
priv->device_type = g_value_get_uint (value);
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->constructor = constructor;
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose;
object_class->finalize = finalize;
/* properties */
/**
* NMDevice:interface:
*
* The interface of the device.
**/
g_object_class_install_property
(object_class, PROP_INTERFACE,
g_param_spec_string (NM_DEVICE_INTERFACE,
"Interface",
"Interface name",
NULL,
G_PARAM_READABLE));
/**
* 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,
"IP Interface",
"IP Interface name",
NULL,
G_PARAM_READABLE));
/**
* 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,
"Device Type",
"Numeric device type (ie ethernet, wifi, etc)",
NM_DEVICE_TYPE_UNKNOWN, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/**
* NMDevice:udi:
*
* The Unique Device Identifier of the device.
**/
g_object_class_install_property
(object_class, PROP_UDI,
g_param_spec_string (NM_DEVICE_UDI,
"UDI",
"Unique Device Identifier",
NULL,
G_PARAM_READABLE));
/**
* NMDevice:driver:
*
* The driver of the device.
**/
g_object_class_install_property
(object_class, PROP_DRIVER,
g_param_spec_string (NM_DEVICE_DRIVER,
"Driver",
"Driver",
NULL,
G_PARAM_READABLE));
/**
* NMDevice:capabilities:
*
* The capabilities of the device.
**/
g_object_class_install_property
(object_class, PROP_CAPABILITIES,
g_param_spec_uint (NM_DEVICE_CAPABILITIES,
"Capabilities",
"Capabilities",
0, G_MAXUINT32, 0,
G_PARAM_READABLE));
/**
* 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,
"Managed",
"Managed",
FALSE,
G_PARAM_READABLE));
/**
* 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,
"FirmwareMissing",
"Firmware missing",
FALSE,
G_PARAM_READABLE));
/**
* 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,
"IP4 Config",
"IP4 Config",
NM_TYPE_IP4_CONFIG,
G_PARAM_READABLE));
/**
* 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,
"DHCP4 Config",
"DHCP4 Config",
NM_TYPE_DHCP4_CONFIG,
G_PARAM_READABLE));
/**
* 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,
"IP6 Config",
"IP6 Config",
NM_TYPE_IP6_CONFIG,
G_PARAM_READABLE));
/**
* 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,
"DHCP6 Config",
"DHCP6 Config",
NM_TYPE_DHCP6_CONFIG,
G_PARAM_READABLE));
/**
* NMDevice:state:
*
* The state of the device.
**/
g_object_class_install_property
(object_class, PROP_STATE,
g_param_spec_uint (NM_DEVICE_STATE,
"State",
"State",
0, G_MAXUINT32, 0,
G_PARAM_READABLE));
/**
* NMDevice:vendor:
*
* The vendor string of the device.
**/
g_object_class_install_property
(object_class, PROP_VENDOR,
g_param_spec_string (NM_DEVICE_VENDOR,
"Vendor",
"Vendor string",
NULL,
G_PARAM_READABLE));
/**
* NMDevice:product:
*
* The product string of the device.
**/
g_object_class_install_property
(object_class, PROP_PRODUCT,
g_param_spec_string (NM_DEVICE_PRODUCT,
"Product",
"Product string",
NULL,
G_PARAM_READABLE));
/* signals */
/**
* NMDevice::state-changed:
* @device: the client that received the signal
* @state: the new state of the device
*
* 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,
_nm_marshal_VOID__UINT_UINT_UINT,
G_TYPE_NONE, 3,
G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
}
/**
* nm_device_new:
* @connection: the #DBusGConnection
* @path: the DBus object path of the device
*
* Creates a new #NMDevice.
*
* Returns: a new device
**/
GObject *
nm_device_new (DBusGConnection *connection, const char *path)
{
DBusGProxy *proxy;
GError *err = NULL;
GValue value = {0,};
GType dtype = 0;
NMDevice *device = NULL;
NMDeviceType nm_dtype;
g_return_val_if_fail (connection != NULL, NULL);
g_return_val_if_fail (path != NULL, NULL);
proxy = dbus_g_proxy_new_for_name (connection,
NM_DBUS_SERVICE,
path,
"org.freedesktop.DBus.Properties");
if (!proxy) {
g_warning ("%s: couldn't create D-Bus object proxy.", __func__);
return NULL;
}
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);
goto out;
}
nm_dtype = g_value_get_uint (&value);
switch (nm_dtype) {
case NM_DEVICE_TYPE_ETHERNET:
dtype = NM_TYPE_DEVICE_ETHERNET;
break;
case NM_DEVICE_TYPE_WIFI:
dtype = NM_TYPE_DEVICE_WIFI;
break;
case NM_DEVICE_TYPE_MODEM:
dtype = NM_TYPE_DEVICE_MODEM;
break;
case NM_DEVICE_TYPE_BT:
dtype = NM_TYPE_DEVICE_BT;
break;
case NM_DEVICE_TYPE_WIMAX:
dtype = NM_TYPE_DEVICE_WIMAX;
break;
default:
g_warning ("Unknown device type %d", g_value_get_uint (&value));
break;
}
if (dtype) {
device = (NMDevice *) g_object_new (dtype,
NM_OBJECT_DBUS_CONNECTION, connection,
NM_OBJECT_DBUS_PATH, path,
NM_DEVICE_DEVICE_TYPE, nm_dtype,
NULL);
}
out:
g_object_unref (proxy);
return G_OBJECT (device);
}
/**
* 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)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
priv = NM_DEVICE_GET_PRIVATE (device);
if (!priv->iface) {
priv->iface = _nm_object_get_string_property (NM_OBJECT (device),
NM_DBUS_INTERFACE_DEVICE,
"Interface",
NULL);
}
return priv->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)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
priv = NM_DEVICE_GET_PRIVATE (device);
if (!priv->ip_iface) {
priv->ip_iface = _nm_object_get_string_property (NM_OBJECT (device),
NM_DBUS_INTERFACE_DEVICE,
"IpInterface",
NULL);
}
return priv->ip_iface;
}
/**
* nm_device_get_device_type:
* @device: a #NMDevice
*
* Returns the numeric type of the #NMDevice, ie ethernet, wifi, 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)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
priv = NM_DEVICE_GET_PRIVATE (device);
if (!priv->udi) {
priv->udi = _nm_object_get_string_property (NM_OBJECT (device),
NM_DBUS_INTERFACE_DEVICE,
"Udi",
NULL);
}
return priv->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)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
priv = NM_DEVICE_GET_PRIVATE (device);
if (!priv->driver) {
priv->driver = _nm_object_get_string_property (NM_OBJECT (device),
NM_DBUS_INTERFACE_DEVICE,
"Driver",
NULL);
}
return priv->driver;
}
/**
* nm_device_get_capabilities:
* @device: a #NMDevice
*
* Gets the device' capabilities.
*
* Returns: the capabilities
**/
NMDeviceCapabilities
nm_device_get_capabilities (NMDevice *device)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (device), 0);
priv = NM_DEVICE_GET_PRIVATE (device);
if (!priv->capabilities) {
priv->capabilities = _nm_object_get_uint_property (NM_OBJECT (device),
NM_DBUS_INTERFACE_DEVICE,
"Capabilities",
NULL);
}
return priv->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)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (device), 0);
priv = NM_DEVICE_GET_PRIVATE (device);
if (!priv->managed) {
priv->managed = _nm_object_get_boolean_property (NM_OBJECT (device),
NM_DBUS_INTERFACE_DEVICE,
"Managed",
NULL);
}
return priv->managed;
}
/**
* 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)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (device), 0);
priv = NM_DEVICE_GET_PRIVATE (device);
if (!priv->firmware_missing) {
priv->firmware_missing = _nm_object_get_boolean_property (NM_OBJECT (device),
NM_DBUS_INTERFACE_DEVICE,
"FirmwareMissing",
NULL);
}
return priv->firmware_missing;
}
/**
* nm_device_get_ip4_config:
* @device: a #NMDevice
*
* Gets the current #NMIP4Config associated with the #NMDevice.
*
* Returns: (transfer none): the #NMIP4Config or %NULL if the device is not activated.
**/
NMIP4Config *
nm_device_get_ip4_config (NMDevice *device)
{
NMDevicePrivate *priv;
char *path;
GValue value = { 0, };
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
priv = NM_DEVICE_GET_PRIVATE (device);
if (priv->ip4_config)
return priv->ip4_config;
if (priv->null_ip4_config)
return NULL;
path = _nm_object_get_object_path_property (NM_OBJECT (device), NM_DBUS_INTERFACE_DEVICE, "Ip4Config", NULL);
if (path) {
g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
g_value_take_boxed (&value, path);
demarshal_ip4_config (NM_OBJECT (device), NULL, &value, &priv->ip4_config);
g_value_unset (&value);
}
return priv->ip4_config;
}
/**
* nm_device_get_dhcp4_config:
* @device: a #NMDevice
*
* Gets the current #NMDHCP4Config associated with the #NMDevice.
*
* Returns: (transfer none): the #NMDHCPConfig or %NULL if the device is not activated or not
* using DHCP.
**/
NMDHCP4Config *
nm_device_get_dhcp4_config (NMDevice *device)
{
NMDevicePrivate *priv;
char *path;
GValue value = { 0, };
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
priv = NM_DEVICE_GET_PRIVATE (device);
if (priv->dhcp4_config)
return priv->dhcp4_config;
if (priv->null_dhcp4_config)
return NULL;
path = _nm_object_get_object_path_property (NM_OBJECT (device), NM_DBUS_INTERFACE_DEVICE, "Dhcp4Config", NULL);
if (path) {
g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
g_value_take_boxed (&value, path);
demarshal_dhcp4_config (NM_OBJECT (device), NULL, &value, &priv->dhcp4_config);
g_value_unset (&value);
}
return priv->dhcp4_config;
}
/**
* nm_device_get_ip6_config:
* @device: a #NMDevice
*
* Gets the current #NMIP6Config associated with the #NMDevice.
*
* Returns: (transfer none): the #NMIP6Config or %NULL if the device is not activated.
**/
NMIP6Config *
nm_device_get_ip6_config (NMDevice *device)
{
NMDevicePrivate *priv;
char *path;
GValue value = { 0, };
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
priv = NM_DEVICE_GET_PRIVATE (device);
if (priv->ip6_config)
return priv->ip6_config;
if (priv->null_ip6_config)
return NULL;
path = _nm_object_get_object_path_property (NM_OBJECT (device), NM_DBUS_INTERFACE_DEVICE, "Ip6Config", NULL);
if (path) {
g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
g_value_take_boxed (&value, path);
demarshal_ip6_config (NM_OBJECT (device), NULL, &value, &priv->ip6_config);
g_value_unset (&value);
}
return priv->ip6_config;
}
/**
* nm_device_get_dhcp6_config:
* @device: a #NMDevice
*
* Gets the current #NMDHCP6Config associated with the #NMDevice.
*
* Returns: (transfer none): the #NMDHCPConfig or %NULL if the device is not activated or not
* using DHCP.
**/
NMDHCP6Config *
nm_device_get_dhcp6_config (NMDevice *device)
{
NMDevicePrivate *priv;
char *path;
GValue value = { 0, };
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
priv = NM_DEVICE_GET_PRIVATE (device);
if (priv->dhcp6_config)
return priv->dhcp6_config;
if (priv->null_dhcp6_config)
return NULL;
path = _nm_object_get_object_path_property (NM_OBJECT (device), NM_DBUS_INTERFACE_DEVICE, "Dhcp6Config", NULL);
if (path) {
g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
g_value_take_boxed (&value, path);
demarshal_dhcp6_config (NM_OBJECT (device), NULL, &value, &priv->dhcp6_config);
g_value_unset (&value);
}
return priv->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)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
priv = NM_DEVICE_GET_PRIVATE (device);
if (priv->state == NM_DEVICE_STATE_UNKNOWN) {
priv->state = _nm_object_get_uint_property (NM_OBJECT (device),
NM_DBUS_INTERFACE_DEVICE,
"State",
NULL);
}
return priv->state;
}
/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
static int hex2num (char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int hex2byte (const char *hex)
{
int a, b;
a = hex2num(*hex++);
if (a < 0)
return -1;
b = hex2num(*hex++);
if (b < 0)
return -1;
return (a << 4) | b;
}
/* End from hostap */
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) hex2byte (p + 2);
p += 4;
len -= 4;
} else {
*n++ = *p++;
len--;
}
}
return unescaped;
}
static void
nm_device_update_description (NMDevice *device)
{
NMDevicePrivate *priv;
const char *subsys[3] = { "net", "tty", NULL };
GUdevDevice *udev_device = NULL, *tmpdev, *olddev;
const char *ifname;
guint32 count = 0;
const char *vendor, *model;
g_return_if_fail (NM_IS_DEVICE (device));
priv = NM_DEVICE_GET_PRIVATE (device);
if (!priv->client) {
priv->client = g_udev_client_new (subsys);
if (!priv->client)
return;
}
ifname = nm_device_get_iface (device);
if (!ifname)
return;
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;
g_free (priv->product);
priv->product = NULL;
g_free (priv->vendor);
priv->vendor = 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 && (!priv->vendor || !priv->product)) {
if (!priv->vendor)
priv->vendor = get_decoded_property (tmpdev, "ID_VENDOR_ENC");
if (!priv->product)
priv->product = get_decoded_property (tmpdev, "ID_MODEL_ENC");
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);
/* If we didn't get strings directly from the device, try database strings */
/* Again, ref the original device as we need to unref it every iteration
* since g_udev_device_get_parent() returns a refed object.
*/
tmpdev = g_object_ref (udev_device);
count = 0;
while ((count++ < 3) && tmpdev && (!priv->vendor || !priv->product)) {
if (!priv->vendor) {
vendor = g_udev_device_get_property (tmpdev, "ID_VENDOR_FROM_DATABASE");
if (vendor)
priv->vendor = g_strdup (vendor);
}
if (!priv->product) {
model = g_udev_device_get_property (tmpdev, "ID_MODEL_FROM_DATABASE");
if (model)
priv->product = g_strdup (model);
}
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);
_nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_VENDOR);
_nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_PRODUCT);
}
/**
* 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)
nm_device_update_description (device);
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)
nm_device_update_description (device);
return priv->vendor;
}
typedef struct {
NMDevice *device;
NMDeviceDeactivateFn fn;
gpointer user_data;
} DeactivateInfo;
static void
deactivate_cb (DBusGProxy *proxy,
GError *error,
gpointer user_data)
{
DeactivateInfo *info = user_data;
if (info->fn)
info->fn (info->device, error, info->user_data);
else if (error) {
g_warning ("%s: device %s deactivation failed: (%d) %s",
__func__,
nm_object_get_path (NM_OBJECT (info->device)),
error ? error->code : -1,
error && error->message ? error->message : "(unknown)");
}
g_object_unref (info->device);
g_slice_free (DeactivateInfo, info);
}
/**
* nm_device_disconnect:
* @device: a #NMDevice
* @callback: (scope async): callback to be called when disconnect operation completes
* @user_data: 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,
NMDeviceDeactivateFn callback,
gpointer user_data)
{
DeactivateInfo *info;
g_return_if_fail (NM_IS_DEVICE (device));
info = g_slice_new (DeactivateInfo);
info->fn = callback;
info->user_data = user_data;
info->device = g_object_ref (device);
org_freedesktop_NetworkManager_Device_disconnect_async (NM_DEVICE_GET_PRIVATE (device)->proxy,
deactivate_cb,
info);
}