mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-08 10:40:30 +01:00
Virtual device types can only have an ifname in the "Device" entry, but the code was accidentally filling in the MAC address too if the device currently existed (which then made the entry value invalid).
589 lines
17 KiB
C
589 lines
17 KiB
C
/* -*- 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 of the
|
|
* License, 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Copyright 2013 Red Hat, Inc.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:nmt-device-entry
|
|
* @short_description: #NmtNewtEntry for identifying a device
|
|
*
|
|
* #NmtDeviceEntry provides a widget for identifying a device, either
|
|
* by interface name or by hardware address. The user can enter either
|
|
* value, and the entry's #NmtDeviceEntry:interface-name or
|
|
* #NmtDeviceEntry:mac-address property will be set accordingly. If
|
|
* the entry recognizes the interface name or mac address typed in as
|
|
* matching a known #NMDevice, then it will also display the other
|
|
* property in parentheses.
|
|
*
|
|
* FIXME: #NmtDeviceEntry is currently an #NmtPageGrid object, so that
|
|
* we can possibly eventually add a button to its "extra" field, that
|
|
* would pop up a form for selecting a device. But if we're not going
|
|
* to implement that then we should make it just an #NmtNewtEntry.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <glib/gi18n-lib.h>
|
|
#include <nm-device.h>
|
|
#include <nm-device-infiniband.h>
|
|
#include <nm-utils.h>
|
|
|
|
#include "nmtui.h"
|
|
#include "nmt-device-entry.h"
|
|
|
|
G_DEFINE_TYPE (NmtDeviceEntry, nmt_device_entry, NMT_TYPE_PAGE_GRID)
|
|
|
|
#define NMT_DEVICE_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryPrivate))
|
|
|
|
typedef struct {
|
|
GType hardware_type;
|
|
NmtDeviceEntryDeviceFilter device_filter;
|
|
gpointer device_filter_data;
|
|
int arptype;
|
|
|
|
char *interface_name;
|
|
GByteArray *mac_address;
|
|
|
|
char *label;
|
|
NmtNewtEntry *entry;
|
|
NmtNewtWidget *button;
|
|
|
|
gboolean updating;
|
|
} NmtDeviceEntryPrivate;
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_LABEL,
|
|
PROP_WIDTH,
|
|
PROP_HARDWARE_TYPE,
|
|
PROP_INTERFACE_NAME,
|
|
PROP_MAC_ADDRESS,
|
|
|
|
LAST_PROP
|
|
};
|
|
|
|
/**
|
|
* nmt_device_entry_new:
|
|
* @label: the label for the entry
|
|
* @width: the width of the entry
|
|
* @hardware_type: the type of #NMDevice to be selected, or
|
|
* %G_TYPE_NONE if this is for a virtual device type.
|
|
*
|
|
* Creates a new #NmtDeviceEntry, for identifying a device of type
|
|
* @hardware_type. If @hardware_type is %G_TYPE_NONE (and you do not
|
|
* set a #NmtDeviceEntryDeviceFilter), then this will only allow
|
|
* specifying an interface name, not a hardware address.
|
|
*
|
|
* Returns: a new #NmtDeviceEntry.
|
|
*/
|
|
NmtNewtWidget *
|
|
nmt_device_entry_new (const char *label,
|
|
int width,
|
|
GType hardware_type)
|
|
{
|
|
return g_object_new (NMT_TYPE_DEVICE_ENTRY,
|
|
"label", label,
|
|
"width", width,
|
|
"hardware-type", hardware_type,
|
|
NULL);
|
|
}
|
|
|
|
static gboolean
|
|
device_entry_parse (NmtDeviceEntry *deventry,
|
|
const char *text,
|
|
char **interface_name,
|
|
char **mac_address)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
|
|
char **words;
|
|
int len;
|
|
|
|
*interface_name = *mac_address = NULL;
|
|
if (!*text)
|
|
return TRUE;
|
|
|
|
if (priv->hardware_type == G_TYPE_NONE && !priv->device_filter) {
|
|
if (nm_utils_iface_valid_name (text)) {
|
|
*interface_name = g_strdup (text);
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
words = g_strsplit (text, " ", -1);
|
|
if (g_strv_length (words) > 2) {
|
|
g_strfreev (words);
|
|
return FALSE;
|
|
}
|
|
|
|
if (words[1]) {
|
|
len = strlen (words[1]);
|
|
if (len < 3 || words[1][0] != '(' || words[1][len - 1] != ')')
|
|
goto fail;
|
|
|
|
memmove (words[1], words[1] + 1, len - 2);
|
|
words[1][len - 2] = '\0';
|
|
}
|
|
|
|
if ( nm_utils_hwaddr_aton (words[0], priv->arptype, buf)
|
|
&& (!words[1] || nm_utils_iface_valid_name (words[1]))) {
|
|
*mac_address = words[0];
|
|
*interface_name = NULL;
|
|
g_free (words);
|
|
return TRUE;
|
|
} else if ( nm_utils_iface_valid_name (words[0])
|
|
&& (!words[1] || nm_utils_hwaddr_aton (words[1], priv->arptype, buf))) {
|
|
*interface_name = words[0];
|
|
*mac_address = NULL;
|
|
g_free (words);
|
|
return TRUE;
|
|
}
|
|
|
|
fail:
|
|
g_strfreev (words);
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
device_entry_validate (NmtNewtEntry *entry,
|
|
const char *text,
|
|
gpointer user_data)
|
|
{
|
|
NmtDeviceEntry *deventry = user_data;
|
|
char *ifname, *mac;
|
|
|
|
if (!device_entry_parse (deventry, text, &ifname, &mac))
|
|
return FALSE;
|
|
|
|
g_free (ifname);
|
|
g_free (mac);
|
|
return TRUE;
|
|
}
|
|
|
|
static NMDevice *
|
|
find_device_by_interface_name (NmtDeviceEntry *deventry,
|
|
const char *interface_name)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
const GPtrArray *devices;
|
|
NMDevice *device = NULL;
|
|
int i;
|
|
|
|
devices = nm_client_get_devices (nm_client);
|
|
if (!devices)
|
|
return NULL;
|
|
|
|
for (i = 0; i < devices->len && !device; i++) {
|
|
NMDevice *candidate = devices->pdata[i];
|
|
|
|
if ( priv->hardware_type != G_TYPE_NONE
|
|
&& !G_TYPE_CHECK_INSTANCE_TYPE (candidate, priv->hardware_type))
|
|
continue;
|
|
|
|
if ( priv->device_filter
|
|
&& !priv->device_filter (deventry, candidate, priv->device_filter_data))
|
|
continue;
|
|
|
|
if (!g_strcmp0 (interface_name, nm_device_get_iface (candidate)))
|
|
device = candidate;
|
|
}
|
|
|
|
return device;
|
|
}
|
|
|
|
static NMDevice *
|
|
find_device_by_mac_address (NmtDeviceEntry *deventry,
|
|
const char *mac_address)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
const GPtrArray *devices;
|
|
NMDevice *device = NULL;
|
|
int i;
|
|
|
|
devices = nm_client_get_devices (nm_client);
|
|
if (!devices)
|
|
return NULL;
|
|
|
|
for (i = 0; i < devices->len && !device; i++) {
|
|
NMDevice *candidate = devices->pdata[i];
|
|
char *hwaddr;
|
|
|
|
if ( priv->hardware_type != G_TYPE_NONE
|
|
&& !G_TYPE_CHECK_INSTANCE_TYPE (candidate, priv->hardware_type))
|
|
continue;
|
|
|
|
if ( priv->device_filter
|
|
&& !priv->device_filter (deventry, candidate, priv->device_filter_data))
|
|
continue;
|
|
|
|
g_object_get (G_OBJECT (candidate), "hw-address", &hwaddr, NULL);
|
|
if (hwaddr && !g_ascii_strcasecmp (mac_address, hwaddr))
|
|
device = candidate;
|
|
g_free (hwaddr);
|
|
}
|
|
|
|
return device;
|
|
}
|
|
|
|
static void
|
|
update_entry (NmtDeviceEntry *deventry)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
const char *ifname;
|
|
char *mac, *text;
|
|
NMDevice *ifname_device, *mac_device;
|
|
|
|
if (priv->interface_name) {
|
|
ifname = priv->interface_name;
|
|
ifname_device = find_device_by_interface_name (deventry, priv->interface_name);
|
|
} else {
|
|
ifname = NULL;
|
|
ifname_device = NULL;
|
|
}
|
|
|
|
if (priv->mac_address) {
|
|
mac = nm_utils_hwaddr_ntoa (priv->mac_address->data, priv->arptype);
|
|
mac_device = find_device_by_mac_address (deventry, mac);
|
|
} else {
|
|
mac = NULL;
|
|
mac_device = NULL;
|
|
}
|
|
|
|
if (!ifname && mac_device)
|
|
ifname = nm_device_get_iface (mac_device);
|
|
if (!mac && ifname_device && (priv->hardware_type != G_TYPE_NONE))
|
|
g_object_get (G_OBJECT (ifname_device), "hw-address", &mac, NULL);
|
|
|
|
if (ifname_device && mac_device && ifname_device != mac_device) {
|
|
/* Mismatch! */
|
|
text = g_strdup_printf ("%s != %s", priv->interface_name, mac);
|
|
} else if (ifname && mac) {
|
|
if (ifname_device)
|
|
text = g_strdup_printf ("%s (%s)", ifname, mac);
|
|
else
|
|
text = g_strdup_printf ("%s (%s)", mac, ifname);
|
|
} else if (ifname)
|
|
text = g_strdup (ifname);
|
|
else if (mac)
|
|
text = g_strdup (mac);
|
|
else
|
|
text = g_strdup ("");
|
|
|
|
priv->updating = TRUE;
|
|
g_object_set (G_OBJECT (priv->entry), "text", text, NULL);
|
|
priv->updating = FALSE;
|
|
g_free (text);
|
|
|
|
g_free (mac);
|
|
}
|
|
|
|
static gboolean
|
|
nmt_device_entry_set_interface_name (NmtDeviceEntry *deventry,
|
|
const char *interface_name)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
|
|
if (g_strcmp0 (interface_name, priv->interface_name) != 0) {
|
|
g_free (priv->interface_name);
|
|
priv->interface_name = g_strdup (interface_name);
|
|
|
|
g_object_notify (G_OBJECT (deventry), "interface-name");
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
nmt_device_entry_set_mac_address (NmtDeviceEntry *deventry,
|
|
GByteArray *mac_address)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
gboolean changed;
|
|
|
|
if (mac_address)
|
|
g_return_val_if_fail (mac_address->len == nm_utils_hwaddr_len (priv->arptype), FALSE);
|
|
|
|
if (mac_address && !priv->mac_address) {
|
|
priv->mac_address = g_boxed_copy (DBUS_TYPE_G_UCHAR_ARRAY, mac_address);
|
|
changed = TRUE;
|
|
} else if (!mac_address && priv->mac_address) {
|
|
g_clear_pointer (&priv->mac_address, g_byte_array_unref);
|
|
changed = TRUE;
|
|
} else if ( mac_address && priv->mac_address
|
|
&& memcmp (mac_address->data, priv->mac_address->data, mac_address->len) != 0) {
|
|
g_byte_array_unref (priv->mac_address);
|
|
priv->mac_address = g_boxed_copy (DBUS_TYPE_G_UCHAR_ARRAY, mac_address);
|
|
changed = TRUE;
|
|
} else
|
|
changed = FALSE;
|
|
|
|
if (changed)
|
|
g_object_notify (G_OBJECT (deventry), "mac-address");
|
|
return changed;
|
|
}
|
|
|
|
static void
|
|
entry_text_changed (GObject *object,
|
|
GParamSpec *pspec,
|
|
gpointer deventry)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
const char *text;
|
|
char *ifname, *mac;
|
|
|
|
if (priv->updating)
|
|
return;
|
|
|
|
text = nmt_newt_entry_get_text (priv->entry);
|
|
if (!device_entry_parse (deventry, text, &ifname, &mac))
|
|
return;
|
|
|
|
if (ifname) {
|
|
nmt_device_entry_set_interface_name (deventry, ifname);
|
|
g_free (ifname);
|
|
} else
|
|
nmt_device_entry_set_interface_name (deventry, NULL);
|
|
|
|
if (mac) {
|
|
GByteArray *mac_address;
|
|
|
|
mac_address = nm_utils_hwaddr_atoba (mac, priv->arptype);
|
|
nmt_device_entry_set_mac_address (deventry, mac_address);
|
|
g_byte_array_unref (mac_address);
|
|
g_free (mac);
|
|
} else
|
|
nmt_device_entry_set_mac_address (deventry, NULL);
|
|
}
|
|
|
|
static void
|
|
nmt_device_entry_init (NmtDeviceEntry *deventry)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
NmtNewtWidget *entry;
|
|
|
|
priv->hardware_type = G_TYPE_NONE;
|
|
|
|
entry = nmt_newt_entry_new (-1, 0);
|
|
priv->entry = NMT_NEWT_ENTRY (entry);
|
|
nmt_newt_entry_set_validator (priv->entry, device_entry_validate, deventry);
|
|
g_signal_connect (priv->entry, "notify::text",
|
|
G_CALLBACK (entry_text_changed), deventry);
|
|
|
|
#if 0
|
|
priv->button = nmt_newt_button_new (_("Select..."));
|
|
g_signal_connect (priv->button, "clicked",
|
|
G_CALLBACK (do_select_dialog), deventry);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
nmt_device_entry_constructed (GObject *object)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object);
|
|
|
|
nmt_page_grid_append (NMT_PAGE_GRID (object), priv->label, NMT_NEWT_WIDGET (priv->entry), NULL);
|
|
|
|
G_OBJECT_CLASS (nmt_device_entry_parent_class)->constructed (object);
|
|
}
|
|
|
|
static void
|
|
nmt_device_entry_finalize (GObject *object)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object);
|
|
|
|
g_free (priv->interface_name);
|
|
if (priv->mac_address)
|
|
g_byte_array_unref (priv->mac_address);
|
|
|
|
G_OBJECT_CLASS (nmt_device_entry_parent_class)->finalize (object);
|
|
}
|
|
|
|
/**
|
|
* NmtDeviceEntryDeviceFilter:
|
|
* @deventry: the #NmtDeviceEntry
|
|
* @device: an #NMDevice
|
|
* @user_data: user data
|
|
*
|
|
* Filter function for determining which devices can be specified
|
|
* on an entry.
|
|
*
|
|
* Returns: %TRUE if @device is acceptable for @deventry
|
|
*/
|
|
|
|
/**
|
|
* nmt_device_entry_set_device_filter:
|
|
* @deventry: the #NmtDeviceEntry
|
|
* @filter: the filter
|
|
* @user_data: data for @filter
|
|
*
|
|
* Sets a device filter on @deventry. Only devices that pass @filter
|
|
* will be recognized by @deventry.
|
|
*
|
|
* If the entry's #NmtDeviceEntry:hardware-type is not %G_TYPE_NONE,
|
|
* then only devices that both match the hardware type and are
|
|
* accepted by the filter will be allowed.
|
|
*/
|
|
void
|
|
nmt_device_entry_set_device_filter (NmtDeviceEntry *deventry,
|
|
NmtDeviceEntryDeviceFilter filter,
|
|
gpointer user_data)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
|
|
priv->device_filter = filter;
|
|
priv->device_filter_data = user_data;
|
|
}
|
|
|
|
static void
|
|
nmt_device_entry_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
NmtDeviceEntry *deventry = NMT_DEVICE_ENTRY (object);
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
|
|
const char *interface_name;
|
|
GByteArray *mac_address;
|
|
|
|
switch (prop_id) {
|
|
case PROP_LABEL:
|
|
priv->label = g_value_dup_string (value);
|
|
break;
|
|
case PROP_WIDTH:
|
|
nmt_newt_entry_set_width (priv->entry, g_value_get_int (value));
|
|
break;
|
|
case PROP_HARDWARE_TYPE:
|
|
priv->hardware_type = g_value_get_gtype (value);
|
|
priv->arptype = (priv->hardware_type == NM_TYPE_DEVICE_INFINIBAND) ? ARPHRD_INFINIBAND : ARPHRD_ETHER;
|
|
break;
|
|
case PROP_INTERFACE_NAME:
|
|
interface_name = g_value_get_string (value);
|
|
if (nmt_device_entry_set_interface_name (deventry, interface_name))
|
|
update_entry (deventry);
|
|
break;
|
|
case PROP_MAC_ADDRESS:
|
|
mac_address = g_value_get_boxed (value);
|
|
if (nmt_device_entry_set_mac_address (deventry, mac_address))
|
|
update_entry (deventry);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nmt_device_entry_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_LABEL:
|
|
g_value_set_string (value, priv->label);
|
|
break;
|
|
case PROP_WIDTH:
|
|
g_value_set_int (value, nmt_newt_entry_get_width (priv->entry));
|
|
break;
|
|
case PROP_HARDWARE_TYPE:
|
|
g_value_set_gtype (value, priv->hardware_type);
|
|
break;
|
|
case PROP_INTERFACE_NAME:
|
|
g_value_set_string (value, priv->interface_name);
|
|
break;
|
|
case PROP_MAC_ADDRESS:
|
|
g_value_set_boxed (value, priv->mac_address);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nmt_device_entry_class_init (NmtDeviceEntryClass *deventry_class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (deventry_class);
|
|
|
|
g_type_class_add_private (deventry_class, sizeof (NmtDeviceEntryPrivate));
|
|
|
|
/* virtual methods */
|
|
object_class->constructed = nmt_device_entry_constructed;
|
|
object_class->set_property = nmt_device_entry_set_property;
|
|
object_class->get_property = nmt_device_entry_get_property;
|
|
object_class->finalize = nmt_device_entry_finalize;
|
|
|
|
/**
|
|
* NmtDeviceEntry:label:
|
|
*
|
|
* The entry's label
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_LABEL,
|
|
g_param_spec_string ("label", "", "",
|
|
NULL,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
/**
|
|
* NmtDeviceEntry:width:
|
|
*
|
|
* The entry's width in characters
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_WIDTH,
|
|
g_param_spec_int ("width", "", "",
|
|
-1, 80, -1,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
/**
|
|
* NmtDeviceEntry:hardware-type:
|
|
*
|
|
* The type of #NMDevice to limit the entry to, or %G_TYPE_NONE
|
|
* if the entry is for a virtual device and should not accept
|
|
* hardware addresses.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_HARDWARE_TYPE,
|
|
g_param_spec_gtype ("hardware-type", "", "",
|
|
G_TYPE_NONE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
/**
|
|
* NmtDeviceEntry:interface-name:
|
|
*
|
|
* The interface name of the device identified by the entry.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_INTERFACE_NAME,
|
|
g_param_spec_string ("interface-name", "", "",
|
|
NULL,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
/**
|
|
* NmtDeviceEntry:mac-address:
|
|
*
|
|
* The hardware address of the device identified by the entry.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_MAC_ADDRESS,
|
|
g_param_spec_boxed ("mac-address", "", "",
|
|
DBUS_TYPE_G_UCHAR_ARRAY,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
}
|