summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2014-06-06 13:56:08 -0500
committerDan Williams <dcbw@redhat.com>2014-06-06 14:15:21 -0500
commit67b228d16bf8523e37c36f45d48da406ba5b461e (patch)
tree2666f91fbf56cebe17ce11a4304f8edad90b26ea
parent26a65f4fe410f88c4cf748e4c965833e8fb44e63 (diff)
parent02252224e286dd3849467567ecbf0238a54e77a0 (diff)
downloadNetworkManager-67b228d16bf8523e37c36f45d48da406ba5b461e.tar.gz
merge: add blocking dispatcher pre-up and pre-down events (bgo #387832)
https://bugzilla.gnome.org/show_bug.cgi?id=387832 https://bugzilla.redhat.com/show_bug.cgi?id=1048345 https://bugzilla.redhat.com/show_bug.cgi?id=982734
-rw-r--r--callouts/Makefile.am10
-rw-r--r--callouts/nm-dispatcher-api.h (renamed from callouts/nm-dispatcher-action.h)17
-rw-r--r--callouts/nm-dispatcher-utils.c2
-rw-r--r--callouts/nm-dispatcher.c (renamed from callouts/nm-dispatcher-action.c)50
-rw-r--r--callouts/org.freedesktop.nm_dispatcher.service.in2
-rw-r--r--callouts/tests/test-dispatcher-envp.c2
-rw-r--r--contrib/fedora/rpm/NetworkManager.spec2
-rw-r--r--data/NetworkManager-dispatcher.service.in2
-rw-r--r--man/NetworkManager.xml49
-rw-r--r--src/devices/nm-device.c4047
-rw-r--r--src/devices/nm-device.h21
-rw-r--r--src/main.c3
-rw-r--r--src/nm-dispatcher.c521
-rw-r--r--src/nm-dispatcher.h39
-rw-r--r--src/nm-manager.c46
-rw-r--r--src/nm-policy.c2
-rw-r--r--src/vpn-manager/nm-vpn-connection.c442
-rw-r--r--src/vpn-manager/nm-vpn-connection.h10
-rw-r--r--src/vpn-manager/nm-vpn-manager.c142
-rw-r--r--src/vpn-manager/nm-vpn-service.c365
-rw-r--r--src/vpn-manager/nm-vpn-service.h8
21 files changed, 3044 insertions, 2738 deletions
diff --git a/callouts/Makefile.am b/callouts/Makefile.am
index 8b018be703..a7ef429849 100644
--- a/callouts/Makefile.am
+++ b/callouts/Makefile.am
@@ -26,7 +26,7 @@ dbusservice_DATA = \
nm-avahi-autoipd.conf
libexec_PROGRAMS = \
- nm-dispatcher.action \
+ nm-dispatcher \
nm-avahi-autoipd.action
@@ -38,13 +38,13 @@ nm_avahi_autoipd_action_LDADD = \
$(GLIB_LIBS)
-nm_dispatcher_action_SOURCES = \
- nm-dispatcher-action.c \
- nm-dispatcher-action.h \
+nm_dispatcher_SOURCES = \
+ nm-dispatcher.c \
+ nm-dispatcher-api.h \
nm-dispatcher-utils.c \
nm-dispatcher-utils.h
-nm_dispatcher_action_LDADD = \
+nm_dispatcher_LDADD = \
$(top_builddir)/libnm-util/libnm-util.la \
$(DBUS_LIBS) \
$(GLIB_LIBS)
diff --git a/callouts/nm-dispatcher-action.h b/callouts/nm-dispatcher-api.h
index 9fea487ada..eb6b33fe0c 100644
--- a/callouts/nm-dispatcher-action.h
+++ b/callouts/nm-dispatcher-api.h
@@ -20,6 +20,10 @@
#include <dbus/dbus-glib.h>
+#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d"
+#define NMD_PRE_UP_DIR NMD_SCRIPT_DIR "/pre-up.d"
+#define NMD_PRE_DOWN_DIR NMD_SCRIPT_DIR "/pre-down.d"
+
/* dbus-glib types for dispatcher call return value */
#define DISPATCHER_TYPE_RESULT (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID))
#define DISPATCHER_TYPE_RESULT_ARRAY (dbus_g_type_get_collection ("GPtrArray", DISPATCHER_TYPE_RESULT))
@@ -36,6 +40,19 @@
#define NMD_DEVICE_PROPS_STATE "state"
#define NMD_DEVICE_PROPS_PATH "path"
+/* Actions */
+#define NMD_ACTION_HOSTNAME "hostname"
+#define NMD_ACTION_PRE_UP "pre-up"
+#define NMD_ACTION_UP "up"
+#define NMD_ACTION_PRE_DOWN "pre-down"
+#define NMD_ACTION_DOWN "down"
+#define NMD_ACTION_VPN_PRE_UP "vpn-pre-up"
+#define NMD_ACTION_VPN_UP "vpn-up"
+#define NMD_ACTION_VPN_PRE_DOWN "vpn-pre-down"
+#define NMD_ACTION_VPN_DOWN "vpn-down"
+#define NMD_ACTION_DHCP4_CHANGE "dhcp4-change"
+#define NMD_ACTION_DHCP6_CHANGE "dhcp6-change"
+
typedef enum {
DISPATCH_RESULT_UNKNOWN = 0,
DISPATCH_RESULT_SUCCESS = 1,
diff --git a/callouts/nm-dispatcher-utils.c b/callouts/nm-dispatcher-utils.c
index 1f99a0b6c1..b1d11daf3d 100644
--- a/callouts/nm-dispatcher-utils.c
+++ b/callouts/nm-dispatcher-utils.c
@@ -30,7 +30,7 @@
#include <nm-setting-ip6-config.h>
#include <nm-setting-connection.h>
-#include "nm-dispatcher-action.h"
+#include "nm-dispatcher-api.h"
#include "nm-utils.h"
#include "nm-dispatcher-utils.h"
diff --git a/callouts/nm-dispatcher-action.c b/callouts/nm-dispatcher.c
index 337a5c616c..d66a0d8f94 100644
--- a/callouts/nm-dispatcher-action.c
+++ b/callouts/nm-dispatcher.c
@@ -37,12 +37,10 @@
#include <dbus/dbus-glib.h>
-#include "nm-dispatcher-action.h"
+#include "nm-dispatcher-api.h"
#include "nm-dispatcher-utils.h"
#include "nm-glib-compat.h"
-#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d"
-
static GMainLoop *loop = NULL;
static gboolean debug = FALSE;
@@ -322,33 +320,33 @@ script_timeout_cb (gpointer user_data)
}
static inline gboolean
-check_permissions (struct stat *s, GError **error)
+check_permissions (struct stat *s, const char **out_error_msg)
{
g_return_val_if_fail (s != NULL, FALSE);
- g_return_val_if_fail (error != NULL, FALSE);
- g_return_val_if_fail (*error == NULL, FALSE);
+ g_return_val_if_fail (out_error_msg != NULL, FALSE);
+ g_return_val_if_fail (*out_error_msg == NULL, FALSE);
/* Only accept regular files */
if (!S_ISREG (s->st_mode)) {
- g_set_error (error, 0, 0, "not a regular file.");
+ *out_error_msg = "not a regular file.";
return FALSE;
}
/* Only accept files owned by root */
if (s->st_uid != 0) {
- g_set_error (error, 0, 0, "not owned by root.");
+ *out_error_msg = "not owned by root.";
return FALSE;
}
/* Only accept files not writable by group or other, and not SUID */
if (s->st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) {
- g_set_error (error, 0, 0, "writable by group or other, or set-UID.");
+ *out_error_msg = "writable by group or other, or set-UID.";
return FALSE;
}
/* Only accept files executable by the owner */
if (!(s->st_mode & S_IXUSR)) {
- g_set_error (error, 0, 0, "not executable by owner.");
+ *out_error_msg = "not executable by owner.";
return FALSE;
}
@@ -385,6 +383,8 @@ child_setup (gpointer user_data G_GNUC_UNUSED)
setpgid (pid, pid);
}
+#define SCRIPT_TIMEOUT 600 /* 10 minutes */
+
static void
dispatch_one_script (Request *request)
{
@@ -402,7 +402,7 @@ dispatch_one_script (Request *request)
if (g_spawn_async ("/", argv, request->envp, G_SPAWN_DO_NOT_REAP_CHILD, child_setup, request, &script->pid, &error)) {
request->script_watch_id = g_child_watch_add (script->pid, (GChildWatchFunc) script_watch_cb, script);
- request->script_timeout_id = g_timeout_add_seconds (20, script_timeout_cb, script);
+ request->script_timeout_id = g_timeout_add_seconds (SCRIPT_TIMEOUT, script_timeout_cb, script);
} else {
g_warning ("Failed to execute script '%s': (%d) %s",
script->script, error->code, error->message);
@@ -416,16 +416,26 @@ dispatch_one_script (Request *request)
}
static GSList *
-find_scripts (void)
+find_scripts (const char *str_action)
{
GDir *dir;
const char *filename;
GSList *sorted = NULL;
GError *error = NULL;
+ const char *dirname;
+
+ if ( strcmp (str_action, NMD_ACTION_PRE_UP) == 0
+ || strcmp (str_action, NMD_ACTION_VPN_PRE_UP) == 0)
+ dirname = NMD_PRE_UP_DIR;
+ else if ( strcmp (str_action, NMD_ACTION_PRE_DOWN) == 0
+ || strcmp (str_action, NMD_ACTION_VPN_PRE_DOWN) == 0)
+ dirname = NMD_PRE_DOWN_DIR;
+ else
+ dirname = NMD_SCRIPT_DIR;
- if (!(dir = g_dir_open (NMD_SCRIPT_DIR, 0, &error))) {
+ if (!(dir = g_dir_open (dirname, 0, &error))) {
g_warning ("Failed to open dispatcher directory '%s': (%d) %s",
- NMD_SCRIPT_DIR, error->code, error->message);
+ dirname, error->code, error->message);
g_error_free (error);
return NULL;
}
@@ -434,19 +444,19 @@ find_scripts (void)
char *path;
struct stat st;
int err;
+ const char *err_msg = NULL;
if (!check_filename (filename))
continue;
- path = g_build_filename (NMD_SCRIPT_DIR, filename, NULL);
+ path = g_build_filename (dirname, filename, NULL);
err = stat (path, &st);
if (err)
g_warning ("Failed to stat '%s': %d", path, err);
- else if (!check_permissions (&st, &error)) {
- g_warning ("Cannot execute '%s': %s", path, error->message);
- g_clear_error (&error);
- } else {
+ else if (!check_permissions (&st, &err_msg))
+ g_warning ("Cannot execute '%s': %s", path, err_msg);
+ else {
/* success */
sorted = g_slist_insert_sorted (sorted, path, (GCompareFunc) g_strcmp0);
}
@@ -478,7 +488,7 @@ impl_dispatch (Handler *h,
char **p;
char *iface = NULL;
- sorted_scripts = find_scripts ();
+ sorted_scripts = find_scripts (str_action);
if (!sorted_scripts) {
dbus_g_method_return (context, g_ptr_array_new ());
diff --git a/callouts/org.freedesktop.nm_dispatcher.service.in b/callouts/org.freedesktop.nm_dispatcher.service.in
index 9feb3b468b..ff037cca9f 100644
--- a/callouts/org.freedesktop.nm_dispatcher.service.in
+++ b/callouts/org.freedesktop.nm_dispatcher.service.in
@@ -1,6 +1,6 @@
[D-BUS Service]
Name=org.freedesktop.nm_dispatcher
-Exec=@libexecdir@/nm-dispatcher.action
+Exec=@libexecdir@/nm-dispatcher
User=root
SystemdService=dbus-org.freedesktop.nm-dispatcher.service
diff --git a/callouts/tests/test-dispatcher-envp.c b/callouts/tests/test-dispatcher-envp.c
index 8dd18430ed..a91ae2b27a 100644
--- a/callouts/tests/test-dispatcher-envp.c
+++ b/callouts/tests/test-dispatcher-envp.c
@@ -29,7 +29,7 @@
#include "nm-setting-connection.h"
#include "nm-dispatcher-utils.h"
#include "nm-dbus-glib-types.h"
-#include "nm-dispatcher-action.h"
+#include "nm-dispatcher-api.h"
#include "nm-utils.h"
/*******************************************/
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index 616f219b32..3a2f29b2b4 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -441,7 +441,7 @@ fi
%{_bindir}/nm-online
%{_libexecdir}/nm-dhcp-helper
%{_libexecdir}/nm-avahi-autoipd.action
-%{_libexecdir}/nm-dispatcher.action
+%{_libexecdir}/nm-dispatcher
%dir %{_libdir}/NetworkManager
%{_libdir}/NetworkManager/libnm-settings-plugin*.so
%{_mandir}/man1/*
diff --git a/data/NetworkManager-dispatcher.service.in b/data/NetworkManager-dispatcher.service.in
index e1b90036f3..c450478bac 100644
--- a/data/NetworkManager-dispatcher.service.in
+++ b/data/NetworkManager-dispatcher.service.in
@@ -4,7 +4,7 @@ Description=Network Manager Script Dispatcher Service
[Service]
Type=dbus
BusName=org.freedesktop.nm_dispatcher
-ExecStart=@libexecdir@/nm-dispatcher.action
+ExecStart=@libexecdir@/nm-dispatcher
# We want to allow scripts to spawn long-running daemons, so tell
# systemd to not clean up when nm-dispatcher exits
diff --git a/man/NetworkManager.xml b/man/NetworkManager.xml
index 70e49aa16a..229c390cdc 100644
--- a/man/NetworkManager.xml
+++ b/man/NetworkManager.xml
@@ -53,9 +53,9 @@
<title>Dispatcher scripts</title>
<para>
NetworkManager will execute scripts in the
- /etc/NetworkManager/dispatcher.d directory in alphabetical order
- in response to network events. Each script should be a regular
- executable file, owned by root. Furthermore, it must not be
+ /etc/NetworkManager/dispatcher.d directory or subdirectories in
+ alphabetical order in response to network events. Each script should
+ be a regular executable file owned by root. Furthermore, it must not be
writable by group or other, and not setuid.
</para>
<para>
@@ -65,22 +65,65 @@
<para>The actions are:</para>
<variablelist class="dispatcher-options">
<varlistentry>
+ <term><varname>pre-up</varname></term>
+ <listitem><para>The interface is connected to the network but is not
+ yet fully activated. Scripts acting on this event must be placed or
+ symlinked into the /etc/NetworkManager/dispatcher.d/pre-up.d directory,
+ and NetworkManager will wait for script execution to complete before
+ indicating to applications that the interface is fully activated.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>up</varname></term>
<listitem><para>The interface has been activated.</para></listitem>
</varlistentry>
<varlistentry>
+ <term><varname>pre-down</varname></term>
+ <listitem><para>The interface will be deactivated but has not yet been
+ disconnected from the network. Scripts acting on this event must be
+ placed or symlinked into the /etc/NetworkManager/dispatcher.d/pre-down.d
+ directory, and NetworkManager will wait for script execution to complete
+ before disconnecting the interface from its network. Note that this
+ event is not emitted for forced disconnections, like when carrier is
+ lost or a wireless signal fades. It is only emitted when there is
+ an opportunity to cleanly handle a network disconnection event.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>down</varname></term>
<listitem><para>
The interface has been deactivated.
</para></listitem>
</varlistentry>
<varlistentry>
+ <term><varname>vpn-pre-up</varname></term>
+ <listitem><para>The VPN is connected to the network but is not yet
+ fully activated. Scripts acting on this event must be placed or
+ symlinked into the /etc/NetworkManager/dispatcher.d/pre-up.d directory,
+ and NetworkManager will wait for script execution to complete before
+ indicating to applications that the VPN is fully activated.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>vpn-up</varname></term>
<listitem><para>
A VPN connection has been activated.
</para></listitem>
</varlistentry>
<varlistentry>
+ <term><varname>vpn-pre-down</varname></term>
+ <listitem><para>The VPN will be deactivated but has not yet been
+ disconnected from the network. Scripts acting on this event must be
+ placed or symlinked into the /etc/NetworkManager/dispatcher.d/pre-down.d
+ directory, and NetworkManager will wait for script execution to complete
+ before disconnecting the VPN from its network. Note that this
+ event is not emitted for forced disconnections, like when the VPN
+ terminates unexpectedly or general connectivity is lost. It is only
+ emitted when there is an opportunity to cleanly handle a VPN
+ disconnection event.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>vpn-down</varname></term>
<listitem><para>
A VPN connection has been deactivated.
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 445611ddca..a6b4b548f0 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -78,24 +78,12 @@ static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *con
#include "nm-device-glue.h"
-#define DBUS_G_TYPE_UINT_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
-
-/* default to installed helper, but can be modified for testing */
-const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action";
-
-/***********************************************************/
-#define NM_DEVICE_ERROR (nm_device_error_quark ())
+static void nm_device_config_device_interface_init (NMConfigDeviceInterface *iface);
-static GQuark
-nm_device_error_quark (void)
-{
- static GQuark quark = 0;
- if (!quark)
- quark = g_quark_from_static_string ("nm-device-error");
- return quark;
-}
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMDevice, nm_device, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (NM_TYPE_CONFIG_DEVICE, nm_device_config_device_interface_init))
-/***********************************************************/
+#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
enum {
STATE_CHANGED,
@@ -146,17 +134,8 @@ enum {
LAST_PROP
};
-#define DEFAULT_AUTOCONNECT TRUE
-
/***********************************************************/
-static void nm_device_config_device_interface_init (NMConfigDeviceInterface *iface);
-
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMDevice, nm_device, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (NM_TYPE_CONFIG_DEVICE, nm_device_config_device_interface_init))
-
-#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
-
#define PENDING_ACTION_DHCP4 "dhcp4"
#define PENDING_ACTION_DHCP6 "dhcp6"
#define PENDING_ACTION_AUTOCONF6 "autoconf6"
@@ -196,8 +175,6 @@ typedef struct {
} DeleteOnDeactivateData;
typedef struct {
- gboolean disposed;
- gboolean initialized;
gboolean in_state_changed;
NMDeviceState state;
@@ -239,6 +216,7 @@ typedef struct {
guint act_source6_id;
gpointer act_source6_func;
guint recheck_assume_id;
+ guint dispatcher_id;
/* Link stuff */
guint link_connected_id;
@@ -335,291 +313,166 @@ static gboolean nm_device_set_ip6_config (NMDevice *dev,
gboolean commit,
NMDeviceStateReason *reason);
-static gboolean check_connection_available (NMDevice *device,
- NMConnection *connection,
- const char *specific_object);
-
-static gboolean spec_match_list (NMDevice *device, const GSList *specs);
-
-static void _clear_available_connections (NMDevice *device, gboolean do_signal);
-
-static void dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release);
-static void dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release);
-
-static const char *reason_to_string (NMDeviceStateReason reason);
-
-static void ip_check_gw_ping_cleanup (NMDevice *self);
-
-static void cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
-static void cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
-static void cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
-
-static const char *state_to_string (NMDeviceState state);
-
-static void link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDevice *device);
-static void check_carrier (NMDevice *device);
-
-static void nm_device_queued_ip_config_change_clear (NMDevice *self);
-static void update_ip_config (NMDevice *self, gboolean initial);
-static void device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformSignalChangeType change_type, NMPlatformReason reason, gpointer user_data);
-
+static gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure);
static void nm_device_slave_notify_enslave (NMDevice *dev, gboolean success);
static void nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason);
static void addrconf6_start_with_link_ready (NMDevice *self);
-static void
-nm_device_init (NMDevice *self)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+static gboolean nm_device_get_default_unmanaged (NMDevice *device);
- priv->type = NM_DEVICE_TYPE_UNKNOWN;
- priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
- priv->state = NM_DEVICE_STATE_UNMANAGED;
- priv->state_reason = NM_DEVICE_STATE_REASON_NONE;
- priv->dhcp_timeout = 0;
- priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
- priv->autoconnect = DEFAULT_AUTOCONNECT;
- priv->unmanaged_flags = NM_UNMANAGED_INTERNAL;
- priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
- priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
-}
+static void _set_state_full (NMDevice *device,
+ NMDeviceState state,
+ NMDeviceStateReason reason,
+ gboolean quitting);
-static inline gboolean
-nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value)
+/***********************************************************/
+
+static GQuark
+nm_device_error_quark (void)
{
- return nm_platform_sysctl_set (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value);
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("nm-device-error");
+ return quark;
}
-static const char *ip6_properties_to_save[] = {
- "accept_ra",
- "accept_ra_defrtr",
- "accept_ra_pinfo",
- "accept_ra_rtr_pref",
- "disable_ipv6",
- "hop_limit",
- "use_tempaddr",
-};
+#define NM_DEVICE_ERROR (nm_device_error_quark ())
-static void
-save_ip6_properties (NMDevice *self)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- const char *ifname = nm_device_get_ip_iface (self);
- char *value;
- int i;
+/***********************************************************/
- g_hash_table_remove_all (priv->ip6_saved_properties);
+#define QUEUED_PREFIX "queued state change to "
- for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) {
- value = nm_platform_sysctl_get (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i]));
- if (value) {
- g_hash_table_insert (priv->ip6_saved_properties,
- (char *) ip6_properties_to_save[i],
- value);
- }
- }
-}
+static const char *state_table[] = {
+ [NM_DEVICE_STATE_UNKNOWN] = QUEUED_PREFIX "unknown",
+ [NM_DEVICE_STATE_UNMANAGED] = QUEUED_PREFIX "unmanaged",
+ [NM_DEVICE_STATE_UNAVAILABLE] = QUEUED_PREFIX "unavailable",
+ [NM_DEVICE_STATE_DISCONNECTED] = QUEUED_PREFIX "disconnected",
+ [NM_DEVICE_STATE_PREPARE] = QUEUED_PREFIX "prepare",
+ [NM_DEVICE_STATE_CONFIG] = QUEUED_PREFIX "config",
+ [NM_DEVICE_STATE_NEED_AUTH] = QUEUED_PREFIX "need-auth",
+ [NM_DEVICE_STATE_IP_CONFIG] = QUEUED_PREFIX "ip-config",
+ [NM_DEVICE_STATE_IP_CHECK] = QUEUED_PREFIX "ip-check",
+ [NM_DEVICE_STATE_SECONDARIES] = QUEUED_PREFIX "secondaries",
+ [NM_DEVICE_STATE_ACTIVATED] = QUEUED_PREFIX "activated",
+ [NM_DEVICE_STATE_DEACTIVATING] = QUEUED_PREFIX "deactivating",
+ [NM_DEVICE_STATE_FAILED] = QUEUED_PREFIX "failed",
+};
-static void
-restore_ip6_properties (NMDevice *self)
+static const char *
+queued_state_to_string (NMDeviceState state)
{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- GHashTableIter iter;
- gpointer key, value;
-
- g_hash_table_iter_init (&iter, priv->ip6_saved_properties);
- while (g_hash_table_iter_next (&iter, &key, &value))
- nm_device_ipv6_sysctl_set (self, key, value);
+ if (state >= 0 && state < G_N_ELEMENTS (state_table))
+ return state_table[state];
+ return state_table[NM_DEVICE_STATE_UNKNOWN];
}
-/*
- * Get driver info from SIOCETHTOOL ioctl() for 'iface'
- * Returns driver and firmware versions to 'driver_version and' 'firmware_version'
- */
-static gboolean
-device_get_driver_info (const char *iface, char **driver_version, char **firmware_version)
+static const char *
+state_to_string (NMDeviceState state)
{
- struct ethtool_drvinfo drvinfo;
- struct ifreq req;
- int fd;
-
- fd = socket (PF_INET, SOCK_DGRAM, 0);
- if (fd < 0) {
- nm_log_warn (LOGD_HW, "couldn't open control socket.");
- return FALSE;
- }
-
- /* Get driver and firmware version info */
- memset (&drvinfo, 0, sizeof (drvinfo));
- memset (&req, 0, sizeof (struct ifreq));
- strncpy (req.ifr_name, iface, IFNAMSIZ);
- drvinfo.cmd = ETHTOOL_GDRVINFO;
- req.ifr_data = &drvinfo;
-
- errno = 0;
- if (ioctl (fd, SIOCETHTOOL, &req) < 0) {
- nm_log_dbg (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d",
- iface, errno);
- close (fd);
- return FALSE;
- }
- if (driver_version)
- *driver_version = g_strdup (drvinfo.version);
- if (firmware_version)
- *firmware_version = g_strdup (drvinfo.fw_version);
-
- close (fd);
- return TRUE;
+ return queued_state_to_string (state) + strlen (QUEUED_PREFIX);
}
-static gboolean
-device_has_capability (NMDevice *device, NMDeviceCapabilities caps)
-{
- return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & caps);
-}
+static const char *reason_table[] = {
+ [NM_DEVICE_STATE_REASON_NONE] = "none",
+ [NM_DEVICE_STATE_REASON_NOW_MANAGED] = "managed",
+ [NM_DEVICE_STATE_REASON_NOW_UNMANAGED] = "unmanaged",
+ [NM_DEVICE_STATE_REASON_CONFIG_FAILED] = "config-failed",
+ [NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE] = "ip-config-unavailable",
+ [NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED] = "ip-config-expired",
+ [NM_DEVICE_STATE_REASON_NO_SECRETS] = "no-secrets",
+ [NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT] = "supplicant-disconnect",
+ [NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED] = "supplicant-config-failed",
+ [NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED] = "supplicant-failed",
+ [NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT] = "supplicant-timeout",
+ [NM_DEVICE_STATE_REASON_PPP_START_FAILED] = "ppp-start-failed",
+ [NM_DEVICE_STATE_REASON_PPP_DISCONNECT] = "ppp-disconnect",
+ [NM_DEVICE_STATE_REASON_PPP_FAILED] = "ppp-failed",
+ [NM_DEVICE_STATE_REASON_DHCP_START_FAILED] = "dhcp-start-failed",
+ [NM_DEVICE_STATE_REASON_DHCP_ERROR] = "dhcp-error",
+ [NM_DEVICE_STATE_REASON_DHCP_FAILED] = "dhcp-failed",
+ [NM_DEVICE_STATE_REASON_SHARED_START_FAILED] = "sharing-start-failed",
+ [NM_DEVICE_STATE_REASON_SHARED_FAILED] = "sharing-failed",
+ [NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED] = "autoip-start-failed",
+ [NM_DEVICE_STATE_REASON_AUTOIP_ERROR] = "autoip-error",
+ [NM_DEVICE_STATE_REASON_AUTOIP_FAILED] = "autoip-failed",
+ [NM_DEVICE_STATE_REASON_MODEM_BUSY] = "modem-busy",
+ [NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE] = "modem-no-dialtone",
+ [NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER] = "modem-no-carrier",
+ [NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT] = "modem-dial-timeout",
+ [NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED] = "modem-dial-failed",
+ [NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED] = "modem-init-failed",
+ [NM_DEVICE_STATE_REASON_GSM_APN_FAILED] = "gsm-apn-failed",
+ [NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING] = "gsm-registration-idle",
+ [NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED] = "gsm-registration-denied",
+ [NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT] = "gsm-registration-timeout",
+ [NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED] = "gsm-registration-failed",
+ [NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED] = "gsm-pin-check-failed",
+ [NM_DEVICE_STATE_REASON_FIRMWARE_MISSING] = "firmware-missing",
+ [NM_DEVICE_STATE_REASON_REMOVED] = "removed",
+ [NM_DEVICE_STATE_REASON_SLEEPING] = "sleeping",
+ [NM_DEVICE_STATE_REASON_CONNECTION_REMOVED] = "connection-removed",
+ [NM_DEVICE_STATE_REASON_USER_REQUESTED] = "user-requested",
+ [NM_DEVICE_STATE_REASON_CARRIER] = "carrier-changed",
+ [NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED] = "connection-assumed",
+ [NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE] = "supplicant-available",
+ [NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND] = "modem-not-found",
+ [NM_DEVICE_STATE_REASON_BT_FAILED] = "bluetooth-failed",
+ [NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED] = "gsm-sim-not-inserted",
+ [NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED] = "gsm-sim-pin-required",
+ [NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED] = "gsm-sim-puk-required",
+ [NM_DEVICE_STATE_REASON_GSM_SIM_WRONG] = "gsm-sim-wrong",
+ [NM_DEVICE_STATE_REASON_INFINIBAND_MODE] = "infiniband-mode",
+ [NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED] = "dependency-failed",
+ [NM_DEVICE_STATE_REASON_BR2684_FAILED] = "br2684-bridge-failed",
+ [NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE] = "modem-manager-unavailable",
+ [NM_DEVICE_STATE_REASON_SSID_NOT_FOUND] = "ssid-not-found",
+ [NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED] = "secondary-connection-failed",
+ [NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED] = "dcb-fcoe-failed",
+ [NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED] = "teamd-control-failed",
+ [NM_DEVICE_STATE_REASON_MODEM_FAILED] = "modem-failed",
+ [NM_DEVICE_STATE_REASON_MODEM_AVAILABLE] = "modem-available",
+ [NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT] = "sim-pin-incorrect",
+};
-static GObject*
-constructor (GType type,
- guint n_construct_params,
- GObjectConstructParam *construct_params)
+static const char *
+reason_to_string (NMDeviceStateReason reason)
{
- GObject *object;
- NMDevice *dev;
- NMDevicePrivate *priv;
- NMPlatform *platform;
- static guint32 id = 0;
-
- object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type,
- n_construct_params,
- construct_params);
- if (!object)
- return NULL;
-
- dev = NM_DEVICE (object);
- priv = NM_DEVICE_GET_PRIVATE (dev);
-
- if (!priv->iface) {
- nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring");
- goto error;
- }
-
- if (!priv->udi) {
- /* Use a placeholder UDI until we get a real one */
- priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++);
- }
-
- if (NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities)
- priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev);
-
- priv->fw_manager = nm_firewall_manager_get ();
-
- device_get_driver_info (priv->iface, &priv->driver_version, &priv->firmware_version);
-
- /* Watch for external IP config changes */
- platform = nm_platform_get ();
- g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev);
- g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev);
- g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev);
- g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev);
- g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), dev);
-
- priv->initialized = TRUE;
- return object;
-
-error:
- g_object_unref (dev);
- return NULL;
+ if (reason >= 0 && reason < G_N_ELEMENTS (reason_table))
+ return reason_table[reason];
+ return reason_table[NM_DEVICE_STATE_REASON_UNKNOWN];
}
-static void
-constructed (GObject *object)
-{
- NMDevice *dev = NM_DEVICE (object);
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
-
- nm_device_update_hw_address (dev);
-
- if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address)
- NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev);
-
- if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address)
- NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev);
-
- /* Have to call update_initial_hw_address() before calling get_ignore_carrier() */
- if (device_has_capability (dev, NM_DEVICE_CAP_CARRIER_DETECT)) {
- priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), NM_CONFIG_DEVICE (dev));
-
- check_carrier (dev);
- nm_log_info (LOGD_HW,
- "(%s): carrier is %s%s",
- nm_device_get_iface (NM_DEVICE (dev)),
- priv->carrier ? "ON" : "OFF",
- priv->ignore_carrier ? " (but ignored)" : "");
- } else {
- /* Fake online link when carrier detection is not available. */
- priv->carrier = TRUE;
- }
-
- if (priv->ifindex > 0) {
- priv->is_software = nm_platform_link_is_software (priv->ifindex);
- priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex);
- }
-
- if (priv->ifindex > 0)
- priv->mtu = nm_platform_link_get_mtu (priv->ifindex);
-
- priv->con_provider = nm_connection_provider_get ();
- g_assert (priv->con_provider);
- g_signal_connect (priv->con_provider,
- NM_CP_SIGNAL_CONNECTION_ADDED,
- G_CALLBACK (cp_connection_added),
- dev);
-
- g_signal_connect (priv->con_provider,
- NM_CP_SIGNAL_CONNECTION_REMOVED,
- G_CALLBACK (cp_connection_removed),
- dev);
-
- g_signal_connect (priv->con_provider,
- NM_CP_SIGNAL_CONNECTION_UPDATED,
- G_CALLBACK (cp_connection_updated),
- dev);
-
- if (G_OBJECT_CLASS (nm_device_parent_class)->constructed)
- G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
-}
+/***********************************************************/
-static gboolean
-nm_device_is_up (NMDevice *self)
+static inline gboolean
+nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value)
{
- g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-
- if (NM_DEVICE_GET_CLASS (self)->is_up)
- return NM_DEVICE_GET_CLASS (self)->is_up (self);
-
- return TRUE;
+ return nm_platform_sysctl_set (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value);
}
static gboolean
-is_up (NMDevice *device)
+device_has_capability (NMDevice *device, NMDeviceCapabilities caps)
{
- int ifindex = nm_device_get_ip_ifindex (device);
-
- return ifindex > 0 ? nm_platform_link_is_up (ifindex) : TRUE;
+ return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & caps);
}
+/***********************************************************/
+
void
-nm_device_set_path (NMDevice *self, const char *path)
+nm_device_dbus_export (NMDevice *device)
{
+ static guint32 devcount = 0;
NMDevicePrivate *priv;
- g_return_if_fail (self != NULL);
+ g_return_if_fail (NM_IS_DEVICE (device));
- priv = NM_DEVICE_GET_PRIVATE (self);
+ priv = NM_DEVICE_GET_PRIVATE (device);
g_return_if_fail (priv->path == NULL);
- priv->path = g_strdup (path);
+ priv->path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++);
+ nm_log_info (LOGD_DEVICE, "(%s): exported as %s", priv->iface, priv->path);
+ nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, device);
}
const char *
@@ -638,9 +491,6 @@ nm_device_get_udi (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->udi;
}
-/*
- * Get/set functions for iface
- */
const char *
nm_device_get_iface (NMDevice *self)
{
@@ -725,43 +575,6 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
g_free (old_ip_iface);
}
-static guint
-get_hw_address_length (NMDevice *dev, gboolean *out_permanent)
-{
- size_t len;
-
- if (nm_platform_link_get_address (nm_device_get_ip_ifindex (dev), &len))
- return len;
- else
- return 0;
-}
-
-static guint
-nm_device_get_hw_address_length (NMDevice *dev, gboolean *out_permanent)
-{
- return NM_DEVICE_GET_CLASS (dev)->get_hw_address_length (dev, out_permanent);
-}
-
-const guint8 *
-nm_device_get_hw_address (NMDevice *dev, guint *out_len)
-{
- NMDevicePrivate *priv;
-
- g_return_val_if_fail (NM_IS_DEVICE (dev), NULL);
- priv = NM_DEVICE_GET_PRIVATE (dev);
-
- if (out_len)
- *out_len = priv->hw_addr_len;
-
- if (priv->hw_addr_len == 0)
- return NULL;
- else
- return priv->hw_addr;
-}
-
-/*
- * Get/set functions for driver
- */
const char *
nm_device_get_driver (NMDevice *self)
{
@@ -778,18 +591,6 @@ nm_device_get_driver_version (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->driver_version;
}
-const char *
-nm_device_get_firmware_version (NMDevice *self)
-{
- g_return_val_if_fail (self != NULL, NULL);
-
- return NM_DEVICE_GET_PRIVATE (self)->firmware_version;
-}
-
-
-/*
- * Get/set functions for type
- */
NMDeviceType
nm_device_get_device_type (NMDevice *self)
{
@@ -857,6 +658,44 @@ nm_device_get_type_desc (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->type_desc;
}
+gboolean
+nm_device_has_carrier (NMDevice *device)
+{
+ return NM_DEVICE_GET_PRIVATE (device)->carrier;
+}
+
+NMActRequest *
+nm_device_get_act_request (NMDevice *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ return NM_DEVICE_GET_PRIVATE (self)->act_request;
+}
+
+NMConnection *
+nm_device_get_connection (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ return priv->act_request ? nm_act_request_get_connection (priv->act_request) : NULL;
+}
+
+RfKillType
+nm_device_get_rfkill_type (NMDevice *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+ return NM_DEVICE_GET_PRIVATE (self)->rfkill_type;
+}
+
+static const char *
+nm_device_get_physical_port_id (NMDevice *device)
+{
+ return NM_DEVICE_GET_PRIVATE (device)->physical_port_id;
+}
+
+/***********************************************************/
+
static gboolean
nm_device_uses_generated_connection (NMDevice *self)
{
@@ -1020,7 +859,7 @@ carrier_changed (NMDevice *device, gboolean carrier)
if (priv->ignore_carrier && !carrier)
return;
- if (nm_device_is_master (device)) {
+ if (priv->is_master) {
/* Bridge/bond/team carrier does not affect its own activation,
* but when carrier comes on, if there are slaves waiting,
* it will restart them.
@@ -1069,12 +908,6 @@ carrier_changed (NMDevice *device, gboolean carrier)
}
}
-gboolean
-nm_device_has_carrier (NMDevice *device)
-{
- return NM_DEVICE_GET_PRIVATE (device)->carrier;
-}
-
#define LINK_DISCONNECT_DELAY 4
static gboolean
@@ -1338,15 +1171,6 @@ nm_device_owns_iface (NMDevice *device, const char *iface)
}
static void
-check_carrier (NMDevice *device)
-{
- int ifindex = nm_device_get_ip_ifindex (device);
-
- if (!device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
- nm_device_set_carrier (device, nm_platform_link_is_connected (ifindex));
-}
-
-static void
slave_state_changed (NMDevice *slave,
NMDeviceState slave_new_state,
NMDeviceState slave_old_state,
@@ -1400,7 +1224,7 @@ slave_state_changed (NMDevice *slave,
*
* Returns: %TRUE on success, %FALSE on failure
*/
-gboolean
+static gboolean
nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
@@ -1509,18 +1333,6 @@ nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave,
}
}
-/**
- * nm_device_is_master:
- * @dev: the device
- *
- * Returns: whether @dev can enslave other devices (eg, bridge or bond or team)
- */
-gboolean
-nm_device_is_master (NMDevice *dev)
-{
- return NM_DEVICE_GET_PRIVATE (dev)->is_master;
-}
-
/* release all slaves */
static void
nm_device_master_release_slaves (NMDevice *self)
@@ -1673,28 +1485,6 @@ nm_device_get_enslaved (NMDevice *device)
return NM_DEVICE_GET_PRIVATE (device)->enslaved;
}
-/*
- * nm_device_get_act_request
- *
- * Return the devices activation request, if any.
- *
- */
-NMActRequest *
-nm_device_get_act_request (NMDevice *self)
-{
- g_return_val_if_fail (self != NULL, NULL);
-
- return NM_DEVICE_GET_PRIVATE (self)->act_request;
-}
-
-NMConnection *
-nm_device_get_connection (NMDevice *self)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
- return priv->act_request ? nm_act_request_get_connection (priv->act_request) : NULL;
-}
-
static gboolean
is_available (NMDevice *device)
{
@@ -1750,12 +1540,12 @@ nm_device_set_enabled (NMDevice *self, gboolean enabled)
NM_DEVICE_GET_CLASS (self)->set_enabled (self, enabled);
}
-RfKillType
-nm_device_get_rfkill_type (NMDevice *self)
+gboolean
+nm_device_get_autoconnect (NMDevice *device)
{
- g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
- return NM_DEVICE_GET_PRIVATE (self)->rfkill_type;
+ return NM_DEVICE_GET_PRIVATE (device)->autoconnect;
}
static gboolean
@@ -2061,6 +1851,18 @@ nm_device_check_connection_compatible (NMDevice *device, NMConnection *connectio
return NM_DEVICE_GET_CLASS (device)->check_connection_compatible (device, connection);
}
+static gboolean
+string_in_list (const char *str, const char **array, gsize array_len)
+{
+ gsize i;
+
+ for (i = 0; i < array_len; i++) {
+ if (strcmp (str, array[i]) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
/**
* nm_device_can_assume_connections:
* @device: #NMDevice instance
@@ -2068,19 +1870,70 @@ nm_device_check_connection_compatible (NMDevice *device, NMConnection *connectio
* This is a convenience function to determine whether connection assumption
* is available for this device.
*
- * Use this function when you need to determine whether full cleanup should
- * be performed for this device or whether the device should be kept running
- * between NetworkManager runs.
+ * Returns: %TRUE if the device is capable of assuming connections, %FALSE if not
+ */
+static gboolean
+nm_device_can_assume_connections (NMDevice *device)
+{
+ return !!NM_DEVICE_GET_CLASS (device)->update_connection;
+}
+
+/**
+ * nm_device_can_assume_active_connection:
+ * @device: #NMDevice instance
*
- * Returns: %TRUE for assumable connections and %FALS for full-cleanup connections.
+ * This is a convenience function to determine whether the device's active
+ * connection can be assumed if NetworkManager restarts. This method returns
+ * %TRUE if and only if the device can assume connections, and the device has
+ * an active connection, and that active connection can be assumed.
*
- * FIXME: Consider turning this method into (a) a device capability or (b) a class
- * method.
+ * Returns: %TRUE if the device's active connection can be assumed, or %FALSE
+ * if there is no active connection or the active connection cannot be
+ * assumed.
*/
gboolean
-nm_device_can_assume_connections (NMDevice *device)
+nm_device_can_assume_active_connection (NMDevice *device)
{
- return !!NM_DEVICE_GET_CLASS (device)->update_connection;
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ NMConnection *connection;
+ const char *method;
+ const char *assumable_ip6_methods[] = {
+ NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+ NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+ NM_SETTING_IP6_CONFIG_METHOD_DHCP,
+ NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
+ NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
+ };
+ const char *assumable_ip4_methods[] = {
+ NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+ NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+ NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
+ };
+
+ if (!nm_device_can_assume_connections (device))
+ return FALSE;
+
+ connection = nm_device_get_connection (device);
+ if (!connection)
+ return FALSE;
+
+ /* Can't assume connections that aren't yet configured
+ * FIXME: what about bridges/bonds waiting for slaves?
+ */
+ if (priv->state < NM_DEVICE_STATE_IP_CONFIG)
+ return FALSE;
+ if (priv->ip4_state != IP_DONE && priv->ip6_state != IP_DONE)
+ return FALSE;
+
+ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+ if (!string_in_list (method, assumable_ip6_methods, G_N_ELEMENTS (assumable_ip6_methods)))
+ return FALSE;
+
+ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+ if (!string_in_list (method, assumable_ip4_methods, G_N_ELEMENTS (assumable_ip4_methods)))
+ return FALSE;
+
+ return TRUE;
}
static gboolean
@@ -2633,6 +2486,9 @@ aipd_child_setup (gpointer user_data G_GNUC_UNUSED)
nm_unblock_posix_signals (NULL);
}
+/* default to installed helper, but can be modified for testing */
+const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action";
+
static NMActStageReturn
aipd_start (NMDevice *self, NMDeviceStateReason *reason)
{
@@ -2708,6 +2564,37 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason)
/* DHCPv4 stuff */
static void
+dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->dhcp4_config) {
+ g_clear_object (&priv->dhcp4_config);
+ g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
+ }
+
+ if (priv->dhcp4_client) {
+ /* Stop any ongoing DHCP transaction on this device */
+ if (priv->dhcp4_state_sigid) {
+ g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid);
+ priv->dhcp4_state_sigid = 0;
+ }
+
+ if (priv->dhcp4_timeout_sigid) {
+ g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid);
+ priv->dhcp4_timeout_sigid = 0;
+ }
+
+ nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
+
+ if (stop)
+ nm_dhcp_client_stop (priv->dhcp4_client, release);
+
+ g_clear_object (&priv->dhcp4_client);
+ }
+}
+
+static void
dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data)
{
nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data),
@@ -2773,6 +2660,7 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
nm_device_get_connection (self),
self,
NULL,
+ NULL,
NULL);
}
}
@@ -3061,7 +2949,7 @@ act_stage3_ip4_config_start (NMDevice *self,
g_assert_cmpstr (method, ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED);
if ( strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0
- && nm_device_is_master (self)
+ && priv->is_master
&& !priv->carrier) {
nm_log_info (LOGD_IP4 | LOGD_DEVICE,
"(%s): IPv4 config waiting until carrier is on",
@@ -3117,6 +3005,39 @@ act_stage3_ip4_config_start (NMDevice *self,
/* DHCPv6 stuff */
static void
+dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
+ g_clear_object (&priv->dhcp6_ip6_config);
+
+ if (priv->dhcp6_config) {
+ g_clear_object (&priv->dhcp6_config);
+ g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+ }
+
+ if (priv->dhcp6_client) {
+ if (priv->dhcp6_state_sigid) {
+ g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid);
+ priv->dhcp6_state_sigid = 0;
+ }
+
+ if (priv->dhcp6_timeout_sigid) {
+ g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid);
+ priv->dhcp6_timeout_sigid = 0;
+ }
+
+ nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
+
+ if (stop)
+ nm_dhcp_client_stop (priv->dhcp6_client, release);
+
+ g_clear_object (&priv->dhcp6_client);
+ }
+}
+
+static void
dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data)
{
nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data),
@@ -3187,7 +3108,7 @@ dhcp6_lease_change (NMDevice *device)
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
} else {
/* Notify dispatcher scripts of new DHCPv6 config */
- nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, device, NULL, NULL);
+ nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, device, NULL, NULL, NULL);
}
}
@@ -3485,8 +3406,6 @@ linklocal6_start (NMDevice *self)
/******************************************/
-static void dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release);
-
static void
print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr)
{
@@ -3773,6 +3692,48 @@ addrconf6_cleanup (NMDevice *self)
/******************************************/
+static const char *ip6_properties_to_save[] = {
+ "accept_ra",
+ "accept_ra_defrtr",
+ "accept_ra_pinfo",
+ "accept_ra_rtr_pref",
+ "disable_ipv6",
+ "hop_limit",
+ "use_tempaddr",
+};
+
+static void
+save_ip6_properties (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ const char *ifname = nm_device_get_ip_iface (self);
+ char *value;
+ int i;
+
+ g_hash_table_remove_all (priv->ip6_saved_properties);
+
+ for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) {
+ value = nm_platform_sysctl_get (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i]));
+ if (value) {
+ g_hash_table_insert (priv->ip6_saved_properties,
+ (char *) ip6_properties_to_save[i],
+ value);
+ }
+ }
+}
+
+static void
+restore_ip6_properties (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, priv->ip6_saved_properties);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ nm_device_ipv6_sysctl_set (self, key, value);
+}
+
static NMSettingIP6ConfigPrivacy
use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
{
@@ -3868,7 +3829,7 @@ act_stage3_ip6_config_start (NMDevice *self,
g_assert_cmpstr (method, ==, NM_SETTING_IP6_CONFIG_METHOD_IGNORE);
if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0
- && nm_device_is_master (self)
+ && priv->is_master
&& !priv->carrier) {
nm_log_info (LOGD_IP6 | LOGD_DEVICE,
"(%s): IPv6 config waiting until carrier is on", ip_iface);
@@ -4606,6 +4567,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
nm_device_get_connection (self),
self,
NULL,
+ NULL,
NULL);
}
@@ -4700,6 +4662,7 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
nm_device_get_connection (self),
self,
NULL,
+ NULL,
NULL);
}
@@ -4772,78 +4735,6 @@ clear_act_request (NMDevice *self)
}
static void
-dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
- if (priv->dhcp4_config) {
- g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
- g_object_unref (priv->dhcp4_config);
- priv->dhcp4_config = NULL;
- }
-
- if (priv->dhcp4_client) {
- /* Stop any ongoing DHCP transaction on this device */
- if (priv->dhcp4_state_sigid) {
- g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid);
- priv->dhcp4_state_sigid = 0;
- }
-
- if (priv->dhcp4_timeout_sigid) {
- g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid);
- priv->dhcp4_timeout_sigid = 0;
- }
-
- nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
-
- if (stop)
- nm_dhcp_client_stop (priv->dhcp4_client, release);
-
- g_object_unref (priv->dhcp4_client);
- priv->dhcp4_client = NULL;
- }
-}
-
-static void
-dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
- priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
-
- if (priv->dhcp6_ip6_config) {
- g_object_unref (priv->dhcp6_ip6_config);
- priv->dhcp6_ip6_config = NULL;
- }
-
- if (priv->dhcp6_config) {
- g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
- g_object_unref (priv->dhcp6_config);
- priv->dhcp6_config = NULL;
- }
-
- if (priv->dhcp6_client) {
- if (priv->dhcp6_state_sigid) {
- g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid);
- priv->dhcp6_state_sigid = 0;
- }
-
- if (priv->dhcp6_timeout_sigid) {
- g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid);
- priv->dhcp6_timeout_sigid = 0;
- }
-
- nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
-
- if (stop)
- nm_dhcp_client_stop (priv->dhcp6_client, release);
-
- g_object_unref (priv->dhcp6_client);
- priv->dhcp6_client = NULL;
- }
-}
-
-static void
dnsmasq_cleanup (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -4887,6 +4778,20 @@ _update_ip4_address (NMDevice *self)
close (fd);
}
+gboolean
+nm_device_get_is_nm_owned (NMDevice *device)
+{
+ return NM_DEVICE_GET_PRIVATE (device)->is_nm_owned;
+}
+
+void
+nm_device_set_nm_owned (NMDevice *device)
+{
+ g_return_if_fail (NM_IS_DEVICE (device));
+
+ NM_DEVICE_GET_PRIVATE (device)->is_nm_owned = TRUE;
+}
+
/*
* delete_on_deactivate_link_delete
*
@@ -4934,12 +4839,12 @@ delete_on_deactivate_unschedule (NMDevice *self)
static void
delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
{
- NMDevicePrivate *priv;
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
DeleteOnDeactivateData *data;
if (ifindex <= 0)
return;
- if (!nm_device_get_is_nm_owned (self))
+ if (!priv->is_nm_owned)
return;
if (!nm_device_is_software (self))
return;
@@ -4949,8 +4854,6 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
return;
delete_on_deactivate_unschedule (self); /* always cancel and reschedule */
- priv = NM_DEVICE_GET_PRIVATE (self);
-
data = g_new (DeleteOnDeactivateData, 1);
g_object_add_weak_pointer (G_OBJECT (self), (void **) &data->device);
data->device = self;
@@ -4962,143 +4865,6 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
ifindex, nm_device_get_iface (self), data->idle_add_id);
}
-gboolean
-nm_device_get_is_nm_owned (NMDevice *device)
-{
- g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
- return NM_DEVICE_GET_PRIVATE (device)->is_nm_owned;
-}
-
-gboolean
-nm_device_set_is_nm_owned (NMDevice *device,
- gboolean is_nm_owned)
-{
- NMDevicePrivate *priv;
-
- g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
-
- priv = NM_DEVICE_GET_PRIVATE (device);
-
- if (is_nm_owned == priv->is_nm_owned)
- return TRUE;
- if (!is_nm_owned)
- return FALSE;
- priv->is_nm_owned = TRUE;
- return TRUE;
-}
-
-/*
- * nm_device_cleanup
- *
- * Remove a device's routing table entries and IP addresses.
- *
- */
-static void
-nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
-{
- NMDevicePrivate *priv;
- NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
- NMConnection *connection = NULL;
- int ifindex;
-
- g_return_if_fail (NM_IS_DEVICE (self));
-
- if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) {
- nm_log_info (LOGD_DEVICE, "(%s): preparing device",
- nm_device_get_iface (self));
- } else {
- nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason '%s') [%d]",
- nm_device_get_iface (self), reason_to_string (reason), reason);
- }
-
- /* Save whether or not we tried IPv6 for later */
- priv = NM_DEVICE_GET_PRIVATE (self);
-
- /* Clean up when device was deactivated during call to firewall */
- if (priv->fw_call) {
- nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call);
- priv->fw_call = NULL;
- }
-
- if (priv->act_request)
- connection = nm_act_request_get_connection (priv->act_request);
- if (connection) {
- nm_firewall_manager_remove_from_zone (priv->fw_manager,
- nm_device_get_ip_iface (self),
- NULL);
- }
-
- ip_check_gw_ping_cleanup (self);
-
- /* Break the activation chain */
- activation_source_clear (self, TRUE, AF_INET);
- activation_source_clear (self, TRUE, AF_INET6);
-
- /* Clear any queued transitions */
- nm_device_queued_state_clear (self);
- nm_device_queued_ip_config_change_clear (self);
-
- priv->ip4_state = priv->ip6_state = IP_NONE;
-
- dhcp4_cleanup (self, TRUE, FALSE);
- arp_cleanup (self);
- dhcp6_cleanup (self, TRUE, FALSE);
- linklocal6_cleanup (self);
- addrconf6_cleanup (self);
- dnsmasq_cleanup (self);
- aipd_cleanup (self);
-
- /* Turn off kernel IPv6 */
- nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
- nm_device_ipv6_sysctl_set (self, "accept_ra", "0");
- nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0");
-
- /* Call device type-specific deactivation */
- if (NM_DEVICE_GET_CLASS (self)->deactivate)
- NM_DEVICE_GET_CLASS (self)->deactivate (self);
-
- /* master: release slaves */
- nm_device_master_release_slaves (self);
-
- /* slave: mark no longer enslaved */
- g_clear_object (&priv->master);
- priv->enslaved = FALSE;
- g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
-
- /* Tear down an existing activation request */
- clear_act_request (self);
-
- /* Take out any entries in the routing table and any IP address the device had. */
- ifindex = nm_device_get_ip_ifindex (self);
- if (ifindex > 0) {
- nm_platform_route_flush (ifindex);
- nm_platform_address_flush (ifindex);
- }
-
- /* Clean up nameservers and addresses */
- nm_device_set_ip4_config (self, NULL, TRUE, &ignored);
- nm_device_set_ip6_config (self, NULL, TRUE, &ignored);
- g_clear_object (&priv->ext_ip4_config);
- g_clear_object (&priv->vpn4_config);
- g_clear_object (&priv->vpn6_config);
- g_clear_object (&priv->ext_ip6_config);
-
- /* Clear legacy IPv4 address property */
- priv->ip4_address = 0;
- g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS);
-
- /* Only clear ip_iface after flushing all routes and addreses, since
- * those are identified by ip_iface, not by iface (which might be a tty
- * or ATM device).
- */
- nm_device_set_ip_iface (self, NULL);
-
- /* Check if the device was deactivated, and if so, delete_link.
- * Don't call delete_link synchronously because we are currently
- * handling a state change -- which is not reentrant. */
- delete_on_deactivate_check_and_schedule (self, ifindex);
-}
-
static void
disconnect_cb (NMDevice *device,
DBusGMethodInvocation *context,
@@ -5492,6 +5258,36 @@ nm_device_get_ip6_config (NMDevice *self)
/****************************************************************/
static void
+ip_check_pre_up_done (guint call_id, gpointer user_data)
+{
+ NMDevice *self = NM_DEVICE (user_data);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ g_return_if_fail (call_id == priv->dispatcher_id);
+
+ priv->dispatcher_id = 0;
+ nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
+}
+
+static void
+ip_check_pre_up (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ g_warn_if_fail (priv->dispatcher_id == 0);
+
+ if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_UP,
+ nm_device_get_connection (self),
+ self,
+ ip_check_pre_up_done,
+ self,
+ &priv->dispatcher_id)) {
+ /* Just proceed on errors */
+ ip_check_pre_up_done (0, self);
+ }
+}
+
+static void
ip_check_gw_ping_cleanup (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -5545,9 +5341,9 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data)
} else
nm_log_warn (log_domain, "(%s): ping stopped unexpectedly with status %d", iface, status);
- /* We've got connectivity, proceed to secondaries */
+ /* We've got connectivity, proceed to pre_up */
ip_check_gw_ping_cleanup (self);
- nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
+ ip_check_pre_up (self);
}
static gboolean
@@ -5562,7 +5358,7 @@ ip_check_ping_timeout_cb (gpointer user_data)
nm_device_get_iface (self));
ip_check_gw_ping_cleanup (self);
- nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
+ ip_check_pre_up (self);
return FALSE;
}
@@ -5665,9 +5461,9 @@ nm_device_start_ip_check (NMDevice *self)
if (buf[0])
spawn_ping (self, log_domain, ping_binary, buf, timeout);
- /* If no ping was started, just advance to SECONDARIES */
+ /* If no ping was started, just advance to pre_up */
if (!priv->gw_ping.pid)
- nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
+ ip_check_pre_up (self);
}
/****************************************************************/
@@ -5682,6 +5478,25 @@ carrier_wait_timeout (gpointer user_data)
return G_SOURCE_REMOVE;
}
+static gboolean
+nm_device_is_up (NMDevice *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+ if (NM_DEVICE_GET_CLASS (self)->is_up)
+ return NM_DEVICE_GET_CLASS (self)->is_up (self);
+
+ return TRUE;
+}
+
+static gboolean
+is_up (NMDevice *device)
+{
+ int ifindex = nm_device_get_ip_ifindex (device);
+
+ return ifindex > 0 ? nm_platform_link_is_up (ifindex) : TRUE;
+}
+
gboolean
nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
{
@@ -5739,6 +5554,15 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
return TRUE;
}
+static void
+check_carrier (NMDevice *device)
+{
+ int ifindex = nm_device_get_ip_ifindex (device);
+
+ if (!device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
+ nm_device_set_carrier (device, nm_platform_link_is_connected (ifindex));
+}
+
static gboolean
bring_up (NMDevice *device, gboolean *no_firmware)
{
@@ -5810,760 +5634,6 @@ take_down (NMDevice *device)
return FALSE;
}
-static void
-dispose (GObject *object)
-{
- NMDevice *self = NM_DEVICE (object);
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- gboolean deconfigure = TRUE;
- NMPlatform *platform;
-
- if (priv->disposed || !priv->initialized)
- goto out;
-
- priv->disposed = TRUE;
-
- /* Don't down can-assume-connection capable devices that are activated with
- * a connection that can be assumed.
- */
- if (nm_device_can_assume_connections (self) && (priv->state == NM_DEVICE_STATE_ACTIVATED)) {
- NMConnection *connection;
- const char *method;
-
- connection = nm_device_get_connection (self);
- if (connection) {
- /* Only static or DHCP IPv4 connections can be left up.
- * All IPv6 connections can be left up, so we don't have
- * to check that.
- */
- method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
- if ( !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)
- || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)
- || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
- deconfigure = FALSE;
- }
- }
-
- ip_check_gw_ping_cleanup (self);
-
- /* Clear any queued transitions */
- nm_device_queued_state_clear (self);
- nm_device_queued_ip_config_change_clear (self);
-
- /* Clean up and stop address configuration */
- dhcp4_cleanup (self, deconfigure, FALSE);
- arp_cleanup (self);
- dhcp6_cleanup (self, deconfigure, FALSE);
- linklocal6_cleanup (self);
- addrconf6_cleanup (self);
- dnsmasq_cleanup (self);
-
- g_warn_if_fail (priv->slaves == NULL);
- g_assert (priv->master_ready_id == 0);
-
- /* Take the device itself down and clear its IP configuration */
- if (nm_device_get_managed (self) && deconfigure) {
- NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
-
- if (nm_device_get_act_request (self))
- nm_device_cleanup (self, NM_DEVICE_STATE_REASON_REMOVED);
- nm_device_set_ip4_config (self, NULL, TRUE, &ignored);
- nm_device_set_ip6_config (self, NULL, TRUE, &ignored);
-
- nm_device_take_down (self, FALSE);
-
- restore_ip6_properties (self);
-
- /* do a final check whether we should delete_link */
- delete_on_deactivate_check_and_schedule (self, nm_device_get_ip_ifindex (self));
- }
- g_clear_object (&priv->dev_ip4_config);
- g_clear_object (&priv->ext_ip4_config);
- g_clear_object (&priv->vpn4_config);
- g_clear_object (&priv->ip4_config);
-
- g_clear_object (&priv->ip6_config);
- g_clear_object (&priv->ac_ip6_config);
- g_clear_object (&priv->dhcp6_ip6_config);
- g_clear_object (&priv->vpn6_config);
- g_clear_object (&priv->ext_ip6_config);
-
- g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref);
-
- if (priv->recheck_assume_id) {
- g_source_remove (priv->recheck_assume_id);
- priv->recheck_assume_id = 0;
- }
-
- link_disconnect_action_cancel (self);
-
- if (priv->con_provider) {
- g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_added, self);
- g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_removed, self);
- g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_updated, self);
- priv->con_provider = NULL;
- }
-
- g_hash_table_unref (priv->available_connections);
- priv->available_connections = NULL;
-
- if (priv->carrier_wait_id) {
- g_source_remove (priv->carrier_wait_id);
- priv->carrier_wait_id = 0;
- }
-
- g_clear_pointer (&priv->physical_port_id, g_free);
-
- activation_source_clear (self, TRUE, AF_INET);
- activation_source_clear (self, TRUE, AF_INET6);
-
- clear_act_request (self);
- g_clear_object (&priv->queued_act_request);
-
- platform = nm_platform_get ();
- g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self);
- g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
-
-out:
- G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
-}
-
-static void
-finalize (GObject *object)
-{
- NMDevice *self = NM_DEVICE (object);
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
- if (priv->fw_manager)
- g_object_unref (priv->fw_manager);
-
- g_slist_free_full (priv->pending_actions, g_free);
-
- g_free (priv->udi);
- g_free (priv->path);
- g_free (priv->iface);
- g_free (priv->ip_iface);
- g_free (priv->driver);
- g_free (priv->driver_version);
- g_free (priv->firmware_version);
- g_free (priv->type_desc);
- if (priv->dhcp_anycast_address)
- g_byte_array_free (priv->dhcp_anycast_address, TRUE);
-
- G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
-}
-
-
-static void
-set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
- NMPlatformLink *platform_device;
- const char *hw_addr;
-
- switch (prop_id) {
- case PROP_PLATFORM_DEVICE:
- platform_device = g_value_get_pointer (value);
- if (platform_device) {
- g_free (priv->udi);
- priv->udi = g_strdup (platform_device->udi);
- g_free (priv->iface);
- priv->iface = g_strdup (platform_device->name);
- priv->ifindex = platform_device->ifindex;
- g_free (priv->driver);
- priv->driver = g_strdup (platform_device->driver);
- }
- break;
- case PROP_UDI:
- if (g_value_get_string (value)) {
- g_free (priv->udi);
- priv->udi = g_value_dup_string (value);
- }
- break;
- case PROP_IFACE:
- if (g_value_get_string (value)) {
- g_free (priv->iface);
- priv->ifindex = 0;
- priv->iface = g_value_dup_string (value);
-
- /* Only look up the ifindex if it appears to be an actual kernel
- * interface name. eg Bluetooth devices won't have one until we know
- * the IP interface.
- */
- if (priv->iface && !strchr (priv->iface, ':')) {
- priv->ifindex = nm_platform_link_get_ifindex (priv->iface);
- if (priv->ifindex <= 0)
- nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface);
- }
- }
- break;
- case PROP_DRIVER:
- if (g_value_get_string (value)) {
- g_free (priv->driver);
- priv->driver = g_value_dup_string (value);
- }
- break;
- case PROP_DRIVER_VERSION:
- g_free (priv->driver_version);
- priv->driver_version = g_strdup (g_value_get_string (value));
- break;
- case PROP_FIRMWARE_VERSION:
- g_free (priv->firmware_version);
- priv->firmware_version = g_strdup (g_value_get_string (value));
- break;
- case PROP_MTU:
- priv->mtu = g_value_get_uint (value);
- break;
- case PROP_IP4_ADDRESS:
- priv->ip4_address = g_value_get_uint (value);
- break;
- case PROP_AUTOCONNECT:
- priv->autoconnect = g_value_get_boolean (value);
- break;
- case PROP_FIRMWARE_MISSING:
- priv->firmware_missing = g_value_get_boolean (value);
- break;
- case PROP_DEVICE_TYPE:
- g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN);
- priv->type = g_value_get_uint (value);
- break;
- case PROP_TYPE_DESC:
- g_free (priv->type_desc);
- priv->type_desc = g_value_dup_string (value);
- break;
- case PROP_RFKILL_TYPE:
- priv->rfkill_type = g_value_get_uint (value);
- break;
- case PROP_IS_MASTER:
- priv->is_master = g_value_get_boolean (value);
- break;
- case PROP_HW_ADDRESS:
- priv->hw_addr_len = nm_device_get_hw_address_length (NM_DEVICE (object), NULL);
-
- hw_addr = g_value_get_string (value);
- if (!hw_addr)
- break;
- if (priv->hw_addr_len == 0) {
- g_warn_if_fail (*hw_addr == '\0');
- break;
- }
-
- if (!nm_utils_hwaddr_aton_len (hw_addr, priv->hw_addr, priv->hw_addr_len)) {
- g_warning ("Could not parse hw-address '%s'", hw_addr);
- memset (priv->hw_addr, 0, sizeof (priv->hw_addr));
- }
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static gboolean
-ip_config_valid (NMDeviceState state)
-{
- return (state == NM_DEVICE_STATE_UNMANAGED) ||
- (state >= NM_DEVICE_STATE_IP_CHECK &&
- state <= NM_DEVICE_STATE_DEACTIVATING);
-}
-
-static void
-get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- NMDevice *self = NM_DEVICE (object);
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- const char *ac_path = NULL;
- GPtrArray *array;
- GHashTableIter iter;
- NMConnection *connection;
-
- switch (prop_id) {
- case PROP_UDI:
- g_value_set_string (value, priv->udi);
- break;
- case PROP_IFACE:
- g_value_set_string (value, priv->iface);
- break;
- case PROP_IP_IFACE:
- if (ip_config_valid (priv->state))
- g_value_set_string (value, nm_device_get_ip_iface (self));
- else
- g_value_set_string (value, NULL);
- break;
- case PROP_IFINDEX:
- g_value_set_int (value, priv->ifindex);
- break;
- case PROP_DRIVER:
- g_value_set_string (value, priv->driver);
- break;
- case PROP_DRIVER_VERSION:
- g_value_set_string (value, priv->driver_version);
- break;
- case PROP_FIRMWARE_VERSION:
- g_value_set_string (value, priv->firmware_version);
- break;
- case PROP_CAPABILITIES:
- g_value_set_uint (value, (priv->capabilities & ~NM_DEVICE_CAP_INTERNAL_MASK));
- break;
- case PROP_IP4_ADDRESS:
- g_value_set_uint (value, priv->ip4_address);
- break;
- case PROP_CARRIER:
- g_value_set_boolean (value, priv->carrier);
- break;
- case PROP_MTU:
- g_value_set_uint (value, priv->mtu);
- break;
- case PROP_IP4_CONFIG:
- if (ip_config_valid (priv->state) && priv->ip4_config)
- g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config));
- else
- g_value_set_boxed (value, "/");
- break;
- case PROP_DHCP4_CONFIG:
- if (ip_config_valid (priv->state) && priv->dhcp4_client)
- g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config));
- else
- g_value_set_boxed (value, "/");
- break;
- case PROP_IP6_CONFIG:
- if (ip_config_valid (priv->state) && priv->ip6_config)
- g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config));
- else
- g_value_set_boxed (value, "/");
- break;
- case PROP_DHCP6_CONFIG:
- if (ip_config_valid (priv->state) && priv->dhcp6_client)
- g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config));
- else
- g_value_set_boxed (value, "/");
- break;
- case PROP_STATE:
- g_value_set_uint (value, priv->state);
- break;
- case PROP_STATE_REASON:
- g_value_take_boxed (value, dbus_g_type_specialized_construct (DBUS_G_TYPE_UINT_STRUCT));
- dbus_g_type_struct_set (value,
- 0, priv->state,
- 1, priv->state_reason,
- G_MAXUINT);
- break;
- case PROP_ACTIVE_CONNECTION:
- if (priv->act_request)
- ac_path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (priv->act_request));
- g_value_set_boxed (value, ac_path ? ac_path : "/");
- break;
- case PROP_DEVICE_TYPE:
- g_value_set_uint (value, priv->type);
- break;
- case PROP_MANAGED:
- g_value_set_boolean (value, nm_device_get_managed (self));
- break;
- case PROP_AUTOCONNECT:
- g_value_set_boolean (value, priv->autoconnect);
- break;
- case PROP_FIRMWARE_MISSING:
- g_value_set_boolean (value, priv->firmware_missing);
- break;
- case PROP_TYPE_DESC:
- g_value_set_string (value, priv->type_desc);
- break;
- case PROP_RFKILL_TYPE:
- g_value_set_uint (value, priv->rfkill_type);
- break;
- case PROP_AVAILABLE_CONNECTIONS:
- array = g_ptr_array_sized_new (g_hash_table_size (priv->available_connections));
- g_hash_table_iter_init (&iter, priv->available_connections);
- while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL))
- g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection)));
- g_value_take_boxed (value, array);
- break;
- case PROP_PHYSICAL_PORT_ID:
- g_value_set_string (value, priv->physical_port_id);
- break;
- case PROP_IS_MASTER:
- g_value_set_boolean (value, priv->is_master);
- break;
- case PROP_MASTER:
- g_value_set_object (value, priv->master);
- break;
- case PROP_HW_ADDRESS:
- if (priv->hw_addr_len)
- g_value_take_string (value, nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len));
- else
- g_value_set_string (value, NULL);
- break;
- case PROP_HAS_PENDING_ACTION:
- g_value_set_boolean (value, nm_device_has_pending_action (self));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-nm_device_class_init (NMDeviceClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (object_class, sizeof (NMDevicePrivate));
-
- /* Virtual methods */
- object_class->dispose = dispose;
- object_class->finalize = finalize;
- object_class->set_property = set_property;
- object_class->get_property = get_property;
- object_class->constructor = constructor;
- object_class->constructed = constructed;
-
- klass->link_changed = link_changed;
-
- klass->is_available = is_available;
- klass->act_stage1_prepare = act_stage1_prepare;
- klass->act_stage2_config = act_stage2_config;
- klass->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
- klass->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
- klass->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout;
- klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout;
- klass->have_any_ready_slaves = have_any_ready_slaves;
-
- klass->spec_match_list = spec_match_list;
- klass->can_auto_connect = can_auto_connect;
- klass->check_connection_compatible = check_connection_compatible;
- klass->check_connection_available = check_connection_available;
- klass->is_up = is_up;
- klass->bring_up = bring_up;
- klass->take_down = take_down;
- klass->carrier_changed = carrier_changed;
- klass->get_hw_address_length = get_hw_address_length;
-
- /* Properties */
- g_object_class_install_property
- (object_class, PROP_PLATFORM_DEVICE,
- g_param_spec_pointer (NM_DEVICE_PLATFORM_DEVICE,
- "Platform Device",
- "NMPlatform device object",
- G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_UDI,
- g_param_spec_string (NM_DEVICE_UDI,
- "UDI",
- "Unique Device Identifier",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
- g_object_class_install_property
- (object_class, PROP_IFACE,
- g_param_spec_string (NM_DEVICE_IFACE,
- "Interface",
- "Interface",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_IP_IFACE,
- g_param_spec_string (NM_DEVICE_IP_IFACE,
- "IP Interface",
- "IP Interface",
- NULL,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_DRIVER,
- g_param_spec_string (NM_DEVICE_DRIVER,
- "Driver",
- "Driver",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_DRIVER_VERSION,
- g_param_spec_string (NM_DEVICE_DRIVER_VERSION,
- "Driver Version",
- "Driver Version",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_FIRMWARE_VERSION,
- g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION,
- "Firmware Version",
- "Firmware Version",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_CAPABILITIES,
- g_param_spec_uint (NM_DEVICE_CAPABILITIES,
- "Capabilities",
- "Capabilities",
- 0, G_MAXUINT32, NM_DEVICE_CAP_NONE,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_CARRIER,
- g_param_spec_boolean (NM_DEVICE_CARRIER,
- "Carrier",
- "Carrier",
- FALSE,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_MTU,
- g_param_spec_uint (NM_DEVICE_MTU,
- "MTU",
- "MTU",
- 0, G_MAXUINT32, 1500,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_IP4_ADDRESS,
- g_param_spec_uint (NM_DEVICE_IP4_ADDRESS,
- "IP4 address",
- "IP4 address",
- 0, G_MAXUINT32, 0, /* FIXME */
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_IP4_CONFIG,
- g_param_spec_boxed (NM_DEVICE_IP4_CONFIG,
- "IP4 Config",
- "IP4 Config",
- DBUS_TYPE_G_OBJECT_PATH,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_DHCP4_CONFIG,
- g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG,
- "DHCP4 Config",
- "DHCP4 Config",
- DBUS_TYPE_G_OBJECT_PATH,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_IP6_CONFIG,
- g_param_spec_boxed (NM_DEVICE_IP6_CONFIG,
- "IP6 Config",
- "IP6 Config",
- DBUS_TYPE_G_OBJECT_PATH,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_DHCP6_CONFIG,
- g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG,
- "DHCP6 Config",
- "DHCP6 Config",
- DBUS_TYPE_G_OBJECT_PATH,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_STATE,
- g_param_spec_uint (NM_DEVICE_STATE,
- "State",
- "State",
- 0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN,
- G_PARAM_READABLE));
- g_object_class_install_property
- (object_class, PROP_STATE_REASON,
- g_param_spec_boxed (NM_DEVICE_STATE_REASON,
- "StateReason",
- "StateReason",
- DBUS_G_TYPE_UINT_STRUCT,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_ACTIVE_CONNECTION,
- g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION,
- "ActiveConnection",
- "ActiveConnection",
- DBUS_TYPE_G_OBJECT_PATH,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_DEVICE_TYPE,
- g_param_spec_uint (NM_DEVICE_DEVICE_TYPE,
- "DeviceType",
- "DeviceType",
- 0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_MANAGED,
- g_param_spec_boolean (NM_DEVICE_MANAGED,
- "Managed",
- "Managed",
- FALSE,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_AUTOCONNECT,
- g_param_spec_boolean (NM_DEVICE_AUTOCONNECT,
- "Autoconnect",
- "Autoconnect",
- DEFAULT_AUTOCONNECT,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_FIRMWARE_MISSING,
- g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING,
- "FirmwareMissing",
- "Firmware missing",
- FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_TYPE_DESC,
- g_param_spec_string (NM_DEVICE_TYPE_DESC,
- "Type Description",
- "Device type description",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_RFKILL_TYPE,
- g_param_spec_uint (NM_DEVICE_RFKILL_TYPE,
- "Rfkill Type",
- "Type of rfkill switch (if any) supported by this device",
- RFKILL_TYPE_WLAN,
- RFKILL_TYPE_MAX,
- RFKILL_TYPE_UNKNOWN,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_IFINDEX,
- g_param_spec_int (NM_DEVICE_IFINDEX,
- "Ifindex",
- "Ifindex",
- 0, G_MAXINT, 0,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_AVAILABLE_CONNECTIONS,
- g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS,
- "AvailableConnections",
- "AvailableConnections",
- DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_PHYSICAL_PORT_ID,
- g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID,
- "PhysicalPortId",
- "PhysicalPortId",
- NULL,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_IS_MASTER,
- g_param_spec_boolean (NM_DEVICE_IS_MASTER,
- "IsMaster",
- "IsMaster",
- FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_MASTER,
- g_param_spec_object (NM_DEVICE_MASTER,
- "Master",
- "Master",
- NM_TYPE_DEVICE,
- G_PARAM_READABLE));
-
- g_object_class_install_property
- (object_class, PROP_HW_ADDRESS,
- g_param_spec_string (NM_DEVICE_HW_ADDRESS,
- "Hardware Address",
- "Hardware address",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_HAS_PENDING_ACTION,
- g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION,
- "Has pending action",
- "Has pending action",
- FALSE,
- G_PARAM_READABLE));
-
- /* Signals */
- signals[STATE_CHANGED] =
- g_signal_new ("state-changed",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMDeviceClass, state_changed),
- NULL, NULL, NULL,
- G_TYPE_NONE, 3,
- G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
-
- signals[AUTOCONNECT_ALLOWED] =
- g_signal_new ("autoconnect-allowed",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- 0,
- autoconnect_allowed_accumulator, NULL, NULL,
- G_TYPE_BOOLEAN, 0);
-
- signals[AUTH_REQUEST] =
- g_signal_new (NM_DEVICE_AUTH_REQUEST,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- /* dbus-glib context, connection, permission, allow_interaction, callback, user_data */
- G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER);
-
- signals[IP4_CONFIG_CHANGED] =
- g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
-
- signals[IP6_CONFIG_CHANGED] =
- g_signal_new (NM_DEVICE_IP6_CONFIG_CHANGED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
-
- signals[REMOVED] =
- g_signal_new (NM_DEVICE_REMOVED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-
- signals[RECHECK_AUTO_ACTIVATE] =
- g_signal_new (NM_DEVICE_RECHECK_AUTO_ACTIVATE,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-
- signals[RECHECK_ASSUME] =
- g_signal_new (NM_DEVICE_RECHECK_ASSUME,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-
- nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
- G_TYPE_FROM_CLASS (klass),
- &dbus_glib_nm_device_object_info);
-
- dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR);
-}
-
-static void
-nm_device_config_device_interface_init (NMConfigDeviceInterface *iface)
-{
- iface->spec_match_list = (gboolean (*) (NMConfigDevice *, const GSList *)) nm_device_spec_match_list;
- iface->get_hw_address = (const guint8 * (*) (NMConfigDevice *, guint *)) nm_device_get_hw_address;
-}
-
void
nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing)
{
@@ -6584,508 +5654,6 @@ nm_device_get_firmware_missing (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->firmware_missing;
}
-#define QUEUED_PREFIX "queued state change to "
-
-static const char *
-queued_state_to_string (NMDeviceState state)
-{
- switch (state) {
- case NM_DEVICE_STATE_UNMANAGED:
- return QUEUED_PREFIX "unmanaged";
- case NM_DEVICE_STATE_UNAVAILABLE:
- return QUEUED_PREFIX "unavailable";
- case NM_DEVICE_STATE_DISCONNECTED:
- return QUEUED_PREFIX "disconnected";
- case NM_DEVICE_STATE_PREPARE:
- return QUEUED_PREFIX "prepare";
- case NM_DEVICE_STATE_CONFIG:
- return QUEUED_PREFIX "config";
- case NM_DEVICE_STATE_NEED_AUTH:
- return QUEUED_PREFIX "need-auth";
- case NM_DEVICE_STATE_IP_CONFIG:
- return QUEUED_PREFIX "ip-config";
- case NM_DEVICE_STATE_IP_CHECK:
- return QUEUED_PREFIX "ip-check";
- case NM_DEVICE_STATE_SECONDARIES:
- return QUEUED_PREFIX "secondaries";
- case NM_DEVICE_STATE_ACTIVATED:
- return QUEUED_PREFIX "activated";
- case NM_DEVICE_STATE_DEACTIVATING:
- return QUEUED_PREFIX "deactivating";
- case NM_DEVICE_STATE_FAILED:
- return QUEUED_PREFIX "failed";
- default:
- break;
- }
- return QUEUED_PREFIX "unknown";
-}
-
-static const char *
-state_to_string (NMDeviceState state)
-{
- return queued_state_to_string (state) + strlen (QUEUED_PREFIX);
-}
-
-static const char *
-reason_to_string (NMDeviceStateReason reason)
-{
- switch (reason) {
- case NM_DEVICE_STATE_REASON_NONE:
- return "none";
- case NM_DEVICE_STATE_REASON_NOW_MANAGED:
- return "managed";
- case NM_DEVICE_STATE_REASON_NOW_UNMANAGED:
- return "unmanaged";
- case NM_DEVICE_STATE_REASON_CONFIG_FAILED:
- return "config-failed";
- case NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE:
- return "ip-config-unavailable";
- case NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED:
- return "ip-config-expired";
- case NM_DEVICE_STATE_REASON_NO_SECRETS:
- return "no-secrets";
- case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT:
- return "supplicant-disconnect";
- case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED:
- return "supplicant-config-failed";
- case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED:
- return "supplicant-failed";
- case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT:
- return "supplicant-timeout";
- case NM_DEVICE_STATE_REASON_PPP_START_FAILED:
- return "ppp-start-failed";
- case NM_DEVICE_STATE_REASON_PPP_DISCONNECT:
- return "ppp-disconnect";
- case NM_DEVICE_STATE_REASON_PPP_FAILED:
- return "ppp-failed";
- case NM_DEVICE_STATE_REASON_DHCP_START_FAILED:
- return "dhcp-start-failed";
- case NM_DEVICE_STATE_REASON_DHCP_ERROR:
- return "dhcp-error";
- case NM_DEVICE_STATE_REASON_DHCP_FAILED:
- return "dhcp-failed";
- case NM_DEVICE_STATE_REASON_SHARED_START_FAILED:
- return "sharing-start-failed";
- case NM_DEVICE_STATE_REASON_SHARED_FAILED:
- return "sharing-failed";
- case NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED:
- return "autoip-start-failed";
- case NM_DEVICE_STATE_REASON_AUTOIP_ERROR:
- return "autoip-error";
- case NM_DEVICE_STATE_REASON_AUTOIP_FAILED:
- return "autoip-failed";
- case NM_DEVICE_STATE_REASON_MODEM_BUSY:
- return "modem-busy";
- case NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE:
- return "modem-no-dialtone";
- case NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER:
- return "modem-no-carrier";
- case NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT:
- return "modem-dial-timeout";
- case NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED:
- return "modem-dial-failed";
- case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED:
- return "modem-init-failed";
- case NM_DEVICE_STATE_REASON_GSM_APN_FAILED:
- return "gsm-apn-failed";
- case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING:
- return "gsm-registration-idle";
- case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED:
- return "gsm-registration-denied";
- case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT:
- return "gsm-registration-timeout";
- case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED:
- return "gsm-registration-failed";
- case NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED:
- return "gsm-pin-check-failed";
- case NM_DEVICE_STATE_REASON_FIRMWARE_MISSING:
- return "firmware-missing";
- case NM_DEVICE_STATE_REASON_REMOVED:
- return "removed";
- case NM_DEVICE_STATE_REASON_SLEEPING:
- return "sleeping";
- case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED:
- return "connection-removed";
- case NM_DEVICE_STATE_REASON_USER_REQUESTED:
- return "user-requested";
- case NM_DEVICE_STATE_REASON_CARRIER:
- return "carrier-changed";
- case NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED:
- return "connection-assumed";
- case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE:
- return "supplicant-available";
- case NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND:
- return "modem-not-found";
- case NM_DEVICE_STATE_REASON_BT_FAILED:
- return "bluetooth-failed";
- case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED:
- return "gsm-sim-not-inserted";
- case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED:
- return "gsm-sim-pin-required";
- case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED:
- return "gsm-sim-puk-required";
- case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG:
- return "gsm-sim-wrong";
- case NM_DEVICE_STATE_REASON_INFINIBAND_MODE:
- return "infiniband-mode";
- case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
- return "dependency-failed";
- case NM_DEVICE_STATE_REASON_BR2684_FAILED:
- return "br2684-bridge-failed";
- case NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE:
- return "modem-manager-unavailable";
- case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND:
- return "SSID not found";
- case NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED:
- return "secondary-connection-failed";
- case NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED:
- return "DCB-FCoE-failed";
- case NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED:
- return "teamd-control-failed";
- case NM_DEVICE_STATE_REASON_MODEM_FAILED:
- return "modem-failed";
- case NM_DEVICE_STATE_REASON_MODEM_AVAILABLE:
- return "modem-available";
- case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT:
- return "sim-pin-incorrect";
- default:
- break;
- }
- return "unknown";
-}
-
-static void
-notify_ip_properties (NMDevice *device)
-{
- g_object_notify (G_OBJECT (device), NM_DEVICE_IP_IFACE);
- g_object_notify (G_OBJECT (device), NM_DEVICE_IP4_CONFIG);
- g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG);
- g_object_notify (G_OBJECT (device), NM_DEVICE_IP6_CONFIG);
- g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG);
-}
-
-void
-nm_device_state_changed (NMDevice *device,
- NMDeviceState state,
- NMDeviceStateReason reason)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- NMDeviceState old_state;
- NMActRequest *req;
- gboolean no_firmware = FALSE;
- NMConnection *connection;
-
- /* Track re-entry */
- g_warn_if_fail (priv->in_state_changed == FALSE);
- priv->in_state_changed = TRUE;
-
- g_return_if_fail (NM_IS_DEVICE (device));
-
- /* Do nothing if state isn't changing, but as a special case allow
- * re-setting UNAVAILABLE if the device is missing firmware so that we
- * can retry device initialization.
- */
- if ( (priv->state == state)
- && !(state == NM_DEVICE_STATE_UNAVAILABLE && priv->firmware_missing)) {
- priv->in_state_changed = FALSE;
- return;
- }
-
- old_state = priv->state;
- priv->state = state;
- priv->state_reason = reason;
-
- nm_log_info (LOGD_DEVICE, "(%s): device state change: %s -> %s (reason '%s') [%d %d %d]",
- nm_device_get_iface (device),
- state_to_string (old_state),
- state_to_string (state),
- reason_to_string (reason),
- old_state,
- state,
- reason);
-
- /* Clear any queued transitions */
- nm_device_queued_state_clear (device);
-
- /* Cache the activation request for the dispatcher */
- req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
-
- if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
- _clear_available_connections (device, TRUE);
- g_clear_object (&priv->queued_act_request);
- }
-
- /* Update the available connections list when a device first becomes available */
- if ( state >= NM_DEVICE_STATE_DISCONNECTED
- && old_state < NM_DEVICE_STATE_DISCONNECTED)
- nm_device_recheck_available_connections (device);
-
- /* Handle the new state here; but anything that could trigger
- * another state change should be done below.
- */
- switch (state) {
- case NM_DEVICE_STATE_UNMANAGED:
- nm_device_set_firmware_missing (device, FALSE);
- if (old_state > NM_DEVICE_STATE_UNMANAGED) {
- /* Clean up if the device is now unmanaged but was activated */
- if (nm_device_get_act_request (device))
- nm_device_cleanup (device, reason);
- nm_device_take_down (device, TRUE);
- restore_ip6_properties (device);
- }
- break;
- case NM_DEVICE_STATE_UNAVAILABLE:
- if (old_state == NM_DEVICE_STATE_UNMANAGED) {
- save_ip6_properties (device);
- if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
- nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
- nm_device_ipv6_sysctl_set (device, "accept_ra_defrtr", "0");
- nm_device_ipv6_sysctl_set (device, "accept_ra_pinfo", "0");
- nm_device_ipv6_sysctl_set (device, "accept_ra_rtr_pref", "0");
- nm_device_ipv6_sysctl_set (device, "use_tempaddr", "0");
- }
- }
-
- if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) {
- if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware)
- nm_log_warn (LOGD_HW, "(%s): firmware may be missing.", nm_device_get_iface (device));
- nm_device_set_firmware_missing (device, no_firmware ? TRUE : FALSE);
- }
- /* Ensure the device gets deactivated in response to stuff like
- * carrier changes or rfkill. But don't deactivate devices that are
- * about to assume a connection since that defeats the purpose of
- * assuming the device's existing connection.
- *
- * Note that we "deactivate" the device even when coming from
- * UNMANAGED, to ensure that it's in a clean state.
- */
- if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED)
- nm_device_cleanup (device, reason);
- break;
- case NM_DEVICE_STATE_DISCONNECTED:
- if (old_state > NM_DEVICE_STATE_UNAVAILABLE)
- nm_device_cleanup (device, reason);
- break;
- default:
- break;
- }
-
- /* Reset autoconnect flag when the device is activating or connected. */
- if ( state >= NM_DEVICE_STATE_PREPARE
- && state <= NM_DEVICE_STATE_ACTIVATED)
- priv->autoconnect = TRUE;
-
- g_object_notify (G_OBJECT (device), NM_DEVICE_STATE);
- g_object_notify (G_OBJECT (device), NM_DEVICE_STATE_REASON);
- g_signal_emit_by_name (device, "state-changed", state, old_state, reason);
-
- /* Post-process the event after internal notification */
-
- switch (state) {
- case NM_DEVICE_STATE_UNAVAILABLE:
- /* If the device can activate now (ie, it's got a carrier, the supplicant
- * is active, or whatever) schedule a delayed transition to DISCONNECTED
- * to get things rolling. The device can't transition immediately because
- * we can't change states again from the state handler for a variety of
- * reasons.
- */
- if (nm_device_is_available (device)) {
- nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED",
- nm_device_get_iface (device));
- nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
- } else {
- if (old_state == NM_DEVICE_STATE_UNMANAGED) {
- nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED",
- nm_device_get_iface (device));
- } else if ( old_state > NM_DEVICE_STATE_UNAVAILABLE
- && nm_device_get_default_unmanaged (device))
- nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
- }
- break;
- case NM_DEVICE_STATE_DEACTIVATING:
- nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, reason);
- break;
- case NM_DEVICE_STATE_DISCONNECTED:
- if (priv->queued_act_request) {
- NMActRequest *queued_req;
-
- queued_req = priv->queued_act_request;
- priv->queued_act_request = NULL;
- _device_activate (device, queued_req);
- g_object_unref (queued_req);
- } else if ( old_state > NM_DEVICE_STATE_DISCONNECTED
- && nm_device_get_default_unmanaged (device))
- nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
- break;
- case NM_DEVICE_STATE_ACTIVATED:
- nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.",
- nm_device_get_iface (device));
- nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), device, NULL, NULL);
- break;
- case NM_DEVICE_STATE_FAILED:
- connection = nm_device_get_connection (device);
- nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
- "Activation (%s) failed for connection '%s'",
- nm_device_get_iface (device),
- connection ? nm_connection_get_id (connection) : "<unknown>");
-
- /* Notify any slaves of the unexpected failure */
- nm_device_master_release_slaves (device);
-
- /* If the connection doesn't yet have a timestamp, set it to zero so that
- * we can distinguish between connections we've tried to activate and have
- * failed (zero timestamp), connections that succeeded (non-zero timestamp),
- * and those we haven't tried yet (no timestamp).
- */
- if (connection && !nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), NULL)) {
- nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (connection),
- (guint64) 0,
- TRUE);
- }
-
- /* Schedule the transition to DISCONNECTED. The device can't transition
- * immediately because we can't change states again from the state
- * handler for a variety of reasons.
- */
- nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
- break;
- case NM_DEVICE_STATE_IP_CHECK:
- nm_device_start_ip_check (device);
-
- /* IP-related properties are only valid when the device has IP configuration;
- * now that it does, ensure their change notifications are emitted.
- */
- notify_ip_properties (device);
- break;
- case NM_DEVICE_STATE_SECONDARIES:
- ip_check_gw_ping_cleanup (device);
- nm_log_dbg (LOGD_DEVICE, "(%s): device entered SECONDARIES state",
- nm_device_get_iface (device));
- break;
- default:
- break;
- }
-
- if (state > NM_DEVICE_STATE_DISCONNECTED)
- delete_on_deactivate_unschedule (device);
-
- if (old_state == NM_DEVICE_STATE_ACTIVATED)
- nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL);
-
- /* IP-related properties are only valid when the device has IP configuration.
- * If it no longer does, ensure their change notifications are emitted.
- */
- if (ip_config_valid (old_state) && !ip_config_valid (state))
- notify_ip_properties (device);
-
- /* Dispose of the cached activation request */
- if (req)
- g_object_unref (req);
-
- priv->in_state_changed = FALSE;
-}
-
-static gboolean
-queued_set_state (gpointer user_data)
-{
- NMDevice *self = NM_DEVICE (user_data);
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- NMDeviceState new_state;
- NMDeviceStateReason new_reason;
-
- if (priv->queued_state.id) {
- nm_log_dbg (LOGD_DEVICE, "(%s): running queued state change to %s (id %d)",
- nm_device_get_iface (self),
- state_to_string (priv->queued_state.state),
- priv->queued_state.id);
-
- /* Clear queued state struct before triggering state change, since
- * the state change may queue another state.
- */
- priv->queued_state.id = 0;
- new_state = priv->queued_state.state;
- new_reason = priv->queued_state.reason;
- nm_device_queued_state_clear (self);
-
- nm_device_state_changed (self, new_state, new_reason);
- nm_device_remove_pending_action (self, queued_state_to_string (new_state), TRUE);
- } else {
- g_warn_if_fail (priv->queued_state.state == NM_DEVICE_STATE_UNKNOWN);
- g_warn_if_fail (priv->queued_state.reason == NM_DEVICE_STATE_REASON_NONE);
- }
- return FALSE;
-}
-
-void
-nm_device_queue_state (NMDevice *self,
- NMDeviceState state,
- NMDeviceStateReason reason)
-{
- NMDevicePrivate *priv;
-
- g_return_if_fail (NM_IS_DEVICE (self));
-
- priv = NM_DEVICE_GET_PRIVATE (self);
-
- if (priv->queued_state.id && priv->queued_state.state == state)
- return;
-
- /* Add pending action for the new state before clearing the queued states, so
- * that we don't accidently pop all pending states and reach 'startup complete' */
- nm_device_add_pending_action (self, queued_state_to_string (state), TRUE);
-
- /* We should only ever have one delayed state transition at a time */
- if (priv->queued_state.id) {
- nm_log_warn (LOGD_DEVICE, "(%s): overwriting previously queued state change to %s (%s)",
- nm_device_get_iface (self),
- state_to_string (priv->queued_state.state),
- reason_to_string (priv->queued_state.reason));
- nm_device_queued_state_clear (self);
- }
-
- priv->queued_state.state = state;
- priv->queued_state.reason = reason;
- priv->queued_state.id = g_idle_add (queued_set_state, self);
-
- nm_log_dbg (LOGD_DEVICE, "(%s): queued state change to %s due to %s (id %d)",
- nm_device_get_iface (self), state_to_string (state), reason_to_string (reason),
- priv->queued_state.id);
-}
-
-NMDeviceState
-nm_device_queued_state_peek (NMDevice *self)
-{
- NMDevicePrivate *priv;
-
- g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_STATE_UNKNOWN);
-
- priv = NM_DEVICE_GET_PRIVATE (self);
-
- return priv->queued_state.id ? priv->queued_state.state : NM_DEVICE_STATE_UNKNOWN;
-}
-
-void
-nm_device_queued_state_clear (NMDevice *self)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
- if (priv->queued_state.id) {
- nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)",
- nm_device_get_iface (self), priv->queued_state.id);
- g_source_remove (priv->queued_state.id);
- nm_device_remove_pending_action (self, queued_state_to_string (priv->queued_state.state), TRUE);
- }
- memset (&priv->queued_state, 0, sizeof (priv->queued_state));
-}
-
-NMDeviceState
-nm_device_get_state (NMDevice *device)
-{
- g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
-
- return NM_DEVICE_GET_PRIVATE (device)->state;
-}
-
static NMIP4Config *
find_ip4_lease_config (NMDevice *device,
NMConnection *connection,
@@ -7281,7 +5849,7 @@ device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object,
}
}
-void
+static void
nm_device_queued_ip_config_change_clear (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -7323,27 +5891,27 @@ nm_device_get_managed (NMDevice *device)
}
/**
- * nm_device_get_default_unmanaged():
+ * nm_device_get_unmanaged_flag():
* @device: the #NMDevice
*
- * Returns: %TRUE if the device is by default unmanaged
+ * Returns: %TRUE if the device is unmanaged for @flag.
*/
gboolean
-nm_device_get_default_unmanaged (NMDevice *device)
+nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag)
{
- return nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT);
+ return NM_DEVICE_GET_PRIVATE (device)->unmanaged_flags & flag;
}
/**
- * nm_device_get_unmanaged_flag():
+ * nm_device_get_default_unmanaged():
* @device: the #NMDevice
*
- * Returns: %TRUE if the device is unmanaged for @flag.
+ * Returns: %TRUE if the device is by default unmanaged
*/
-gboolean
-nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag)
+static gboolean
+nm_device_get_default_unmanaged (NMDevice *device)
{
- return NM_DEVICE_GET_PRIVATE (device)->unmanaged_flags & flag;
+ return nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT);
}
void
@@ -7381,6 +5949,21 @@ nm_device_set_unmanaged (NMDevice *device,
}
}
+void
+nm_device_set_unmanaged_quitting (NMDevice *device)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+
+ /* It's OK to block here because we're quitting */
+ if (nm_device_is_activating (device) || priv->state == NM_DEVICE_STATE_ACTIVATED)
+ _set_state_full (device, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_REMOVED, TRUE);
+
+ nm_device_set_unmanaged (device,
+ NM_UNMANAGED_INTERNAL,
+ TRUE,
+ NM_DEVICE_STATE_REASON_REMOVED);
+}
+
/**
* nm_device_set_initial_unmanaged_flag():
* @device: the #NMDevice
@@ -7410,60 +5993,6 @@ nm_device_set_initial_unmanaged_flag (NMDevice *device,
priv->unmanaged_flags &= ~flag;
}
-/**
- * nm_device_spec_match_list:
- * @device: an #NMDevice
- * @specs: (element-type utf8): a list of device specs
- *
- * Checks if @device matches any of the specifications in @specs. The
- * currently-supported spec types are:
- *
- * "mac:00:11:22:33:44:55" - matches a device with the given
- * hardware address
- *
- * "interface-name:foo0" - matches a device with the given
- * interface name
- *
- * "s390-subchannels:00.11.22" - matches a device with the given
- * z/VM / s390 subchannels.
- *
- * "*" - matches any device
- *
- * Returns: #TRUE if @device matches one of the specs in @specs
- */
-gboolean
-nm_device_spec_match_list (NMDevice *device, const GSList *specs)
-{
- g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
-
- if (!specs)
- return FALSE;
-
- return NM_DEVICE_GET_CLASS (device)->spec_match_list (device, specs);
-}
-
-static gboolean
-spec_match_list (NMDevice *device, const GSList *specs)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- char *hwaddr_str;
- gboolean matched = FALSE;
-
- if (nm_match_spec_string (specs, "*"))
- return TRUE;
-
- if (priv->hw_addr_len) {
- hwaddr_str = nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len);
- matched = nm_match_spec_hwaddr (specs, hwaddr_str);
- g_free (hwaddr_str);
- }
-
- if (!matched)
- matched = nm_match_spec_interface_name (specs, nm_device_get_iface (device));
-
- return matched;
-}
-
void
nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout)
{
@@ -7492,14 +6021,6 @@ nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr)
}
}
-gboolean
-nm_device_get_autoconnect (NMDevice *device)
-{
- g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
-
- return NM_DEVICE_GET_PRIVATE (device)->autoconnect;
-}
-
/**
* nm_device_connection_is_available():
* @device: the #NMDevice
@@ -7736,6 +6257,683 @@ nm_device_supports_vlans (NMDevice *device)
return nm_platform_link_supports_vlans (nm_device_get_ifindex (device));
}
+/**
+ * nm_device_add_pending_action():
+ * @device: the #NMDevice to add the pending action to
+ * @action: a static string that identifies the action
+ * @assert_not_yet_pending: if %TRUE, assert that the @action is currently not yet pending.
+ * Otherwise, ignore duplicate scheduling of the same action silently.
+ *
+ * Adds a pending action to the device.
+ *
+ * Returns: %TRUE if the action was added (and not already added before). %FALSE
+ * if the same action is already scheduled. In the latter case, the action was not scheduled
+ * a second time.
+ */
+gboolean
+nm_device_add_pending_action (NMDevice *device, const char *action, gboolean assert_not_yet_pending)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ GSList *iter;
+ guint count = 0;
+
+ g_return_val_if_fail (action, FALSE);
+
+ /* Check if the action is already pending. Cannot add duplicate actions */
+ for (iter = priv->pending_actions; iter; iter = iter->next) {
+ if (!strcmp (action, iter->data)) {
+ if (assert_not_yet_pending) {
+ nm_log_warn (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending",
+ nm_device_get_iface (device),
+ count + g_slist_length (iter),
+ action);
+ g_return_val_if_reached (FALSE);
+ } else {
+ nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending (expected)",
+ nm_device_get_iface (device),
+ count + g_slist_length (iter),
+ action);
+ }
+ return FALSE;
+ }
+ count++;
+ }
+
+ priv->pending_actions = g_slist_append (priv->pending_actions, g_strdup (action));
+ count++;
+
+ nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s'",
+ nm_device_get_iface (device),
+ count,
+ action);
+
+ if (count == 1)
+ g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
+
+ return TRUE;
+}
+
+/**
+ * nm_device_remove_pending_action():
+ * @device: the #NMDevice to remove the pending action from
+ * @action: a static string that identifies the action
+ * @assert_is_pending: if %TRUE, assert that the @action is pending.
+ * If %FALSE, don't do anything if the current action is not pending and
+ * return %FALSE.
+ *
+ * Removes a pending action previously added by nm_device_add_pending_action().
+ *
+ * Returns: whether the @action was pending and is now removed.
+ */
+gboolean
+nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ GSList *iter;
+ guint count = 0;
+
+ g_return_val_if_fail (action, FALSE);
+
+ for (iter = priv->pending_actions; iter; iter = iter->next) {
+ if (!strcmp (action, iter->data)) {
+ nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s'",
+ nm_device_get_iface (device),
+ count + g_slist_length (iter->next), /* length excluding 'iter' */
+ action);
+ g_free (iter->data);
+ priv->pending_actions = g_slist_delete_link (priv->pending_actions, iter);
+ if (priv->pending_actions == NULL)
+ g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
+ return TRUE;
+ }
+ count++;
+ }
+
+ if (assert_is_pending) {
+ nm_log_warn (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending",
+ nm_device_get_iface (device),
+ count,
+ action);
+ g_return_val_if_reached (FALSE);
+ } else {
+ nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending (expected)",
+ nm_device_get_iface (device),
+ count,
+ action);
+ }
+ return FALSE;
+}
+
+gboolean
+nm_device_has_pending_action (NMDevice *device)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+
+ return !!priv->pending_actions;
+}
+
+/***********************************************************/
+
+static void
+_cleanup_generic_pre (NMDevice *self, gboolean deconfigure)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ /* Clean up when device was deactivated during call to firewall */
+ if (priv->fw_manager) {
+ NMConnection *connection;
+
+ if (priv->fw_call) {
+ nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call);
+ priv->fw_call = NULL;
+ }
+
+ connection = nm_device_get_connection (self);
+ if (deconfigure && connection) {
+ nm_firewall_manager_remove_from_zone (priv->fw_manager,
+ nm_device_get_ip_iface (self),
+ NULL);
+ }
+ }
+
+ ip_check_gw_ping_cleanup (self);
+
+ /* Break the activation chain */
+ activation_source_clear (self, TRUE, AF_INET);
+ activation_source_clear (self, TRUE, AF_INET6);
+
+ /* Clear any queued transitions */
+ nm_device_queued_state_clear (self);
+ nm_device_queued_ip_config_change_clear (self);
+
+ priv->ip4_state = priv->ip6_state = IP_NONE;
+
+ dhcp4_cleanup (self, deconfigure, FALSE);
+ arp_cleanup (self);
+ dhcp6_cleanup (self, deconfigure, FALSE);
+ linklocal6_cleanup (self);
+ addrconf6_cleanup (self);
+ dnsmasq_cleanup (self);
+ aipd_cleanup (self);
+}
+
+static void
+_cleanup_generic_post (NMDevice *self, gboolean deconfigure)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
+
+ /* Clean up IP configs; this does not actually deconfigure the
+ * interface; the caller must flush routes and addresses explicitly.
+ */
+ nm_device_set_ip4_config (self, NULL, TRUE, &ignored);
+ nm_device_set_ip6_config (self, NULL, TRUE, &ignored);
+ g_clear_object (&priv->dev_ip4_config);
+ g_clear_object (&priv->ext_ip4_config);
+ g_clear_object (&priv->vpn4_config);
+ g_clear_object (&priv->ip4_config);
+ g_clear_object (&priv->ac_ip6_config);
+ g_clear_object (&priv->ext_ip6_config);
+ g_clear_object (&priv->vpn6_config);
+ g_clear_object (&priv->ip6_config);
+
+ clear_act_request (self);
+
+ /* Clear legacy IPv4 address property */
+ if (priv->ip4_address) {
+ priv->ip4_address = 0;
+ g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS);
+ }
+
+ if (deconfigure) {
+ /* Check if the device was deactivated, and if so, delete_link.
+ * Don't call delete_link synchronously because we are currently
+ * handling a state change -- which is not reentrant. */
+ delete_on_deactivate_check_and_schedule (self, nm_device_get_ip_ifindex (self));
+ }
+
+ /* ip_iface should be cleared after flushing all routes and addreses, since
+ * those are identified by ip_iface, not by iface (which might be a tty
+ * or ATM device).
+ */
+ nm_device_set_ip_iface (self, NULL);
+}
+
+/*
+ * nm_device_cleanup
+ *
+ * Remove a device's routing table entries and IP addresses.
+ *
+ */
+static void
+nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
+{
+ NMDevicePrivate *priv;
+ int ifindex;
+
+ g_return_if_fail (NM_IS_DEVICE (self));
+
+ if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) {
+ nm_log_info (LOGD_DEVICE, "(%s): preparing device",
+ nm_device_get_iface (self));
+ } else {
+ nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason '%s') [%d]",
+ nm_device_get_iface (self), reason_to_string (reason), reason);
+ }
+
+ /* Save whether or not we tried IPv6 for later */
+ priv = NM_DEVICE_GET_PRIVATE (self);
+
+ _cleanup_generic_pre (self, TRUE);
+
+ /* Turn off kernel IPv6 */
+ nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
+ nm_device_ipv6_sysctl_set (self, "accept_ra", "0");
+ nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0");
+
+ /* Call device type-specific deactivation */
+ if (NM_DEVICE_GET_CLASS (self)->deactivate)
+ NM_DEVICE_GET_CLASS (self)->deactivate (self);
+
+ /* master: release slaves */
+ nm_device_master_release_slaves (self);
+
+ /* slave: mark no longer enslaved */
+ g_clear_object (&priv->master);
+ priv->enslaved = FALSE;
+ g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
+
+ /* Take out any entries in the routing table and any IP address the device had. */
+ ifindex = nm_device_get_ip_ifindex (self);
+ if (ifindex > 0) {
+ nm_platform_route_flush (ifindex);
+ nm_platform_address_flush (ifindex);
+ }
+
+ _cleanup_generic_post (self, TRUE);
+}
+
+/***********************************************************/
+
+static void
+dispatcher_pre_down_done (guint call_id, gpointer user_data)
+{
+ NMDevice *self = NM_DEVICE (user_data);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ g_return_if_fail (call_id == priv->dispatcher_id);
+
+ priv->dispatcher_id = 0;
+ nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
+}
+
+static void
+dispatcher_cleanup (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->dispatcher_id) {
+ nm_dispatcher_call_cancel (priv->dispatcher_id);
+ priv->dispatcher_id = 0;
+ }
+}
+
+static gboolean
+ip_config_valid (NMDeviceState state)
+{
+ return (state == NM_DEVICE_STATE_UNMANAGED) ||
+ (state >= NM_DEVICE_STATE_IP_CHECK &&
+ state <= NM_DEVICE_STATE_DEACTIVATING);
+}
+
+static void
+notify_ip_properties (NMDevice *device)
+{
+ g_object_notify (G_OBJECT (device), NM_DEVICE_IP_IFACE);
+ g_object_notify (G_OBJECT (device), NM_DEVICE_IP4_CONFIG);
+ g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG);
+ g_object_notify (G_OBJECT (device), NM_DEVICE_IP6_CONFIG);
+ g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG);
+}
+
+static void
+_set_state_full (NMDevice *device,
+ NMDeviceState state,
+ NMDeviceStateReason reason,
+ gboolean quitting)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ NMDeviceState old_state;
+ NMActRequest *req;
+ gboolean no_firmware = FALSE;
+ NMConnection *connection;
+
+ /* Track re-entry */
+ g_warn_if_fail (priv->in_state_changed == FALSE);
+ priv->in_state_changed = TRUE;
+
+ g_return_if_fail (NM_IS_DEVICE (device));
+
+ /* Do nothing if state isn't changing, but as a special case allow
+ * re-setting UNAVAILABLE if the device is missing firmware so that we
+ * can retry device initialization.
+ */
+ if ( (priv->state == state)
+ && !(state == NM_DEVICE_STATE_UNAVAILABLE && priv->firmware_missing)) {
+ priv->in_state_changed = FALSE;
+ return;
+ }
+
+ old_state = priv->state;
+ priv->state = state;
+ priv->state_reason = reason;
+
+ nm_log_info (LOGD_DEVICE, "(%s): device state change: %s -> %s (reason '%s') [%d %d %d]",
+ nm_device_get_iface (device),
+ state_to_string (old_state),
+ state_to_string (state),
+ reason_to_string (reason),
+ old_state,
+ state,
+ reason);
+
+ /* Clear any queued transitions */
+ nm_device_queued_state_clear (device);
+
+ dispatcher_cleanup (device);
+
+ /* Cache the activation request for the dispatcher */
+ req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
+
+ if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
+ _clear_available_connections (device, TRUE);
+ g_clear_object (&priv->queued_act_request);
+ }
+
+ /* Update the available connections list when a device first becomes available */
+ if ( state >= NM_DEVICE_STATE_DISCONNECTED
+ && old_state < NM_DEVICE_STATE_DISCONNECTED)
+ nm_device_recheck_available_connections (device);
+
+ /* Handle the new state here; but anything that could trigger
+ * another state change should be done below.
+ */
+ switch (state) {
+ case NM_DEVICE_STATE_UNMANAGED:
+ nm_device_set_firmware_missing (device, FALSE);
+ if (old_state > NM_DEVICE_STATE_UNMANAGED) {
+ /* Clean up if the device is now unmanaged but was activated */
+ if (nm_device_get_act_request (device))
+ nm_device_cleanup (device, reason);
+ nm_device_take_down (device, TRUE);
+ restore_ip6_properties (device);
+ }
+ break;
+ case NM_DEVICE_STATE_UNAVAILABLE:
+ if (old_state == NM_DEVICE_STATE_UNMANAGED) {
+ save_ip6_properties (device);
+ if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
+ nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
+ nm_device_ipv6_sysctl_set (device, "accept_ra_defrtr", "0");
+ nm_device_ipv6_sysctl_set (device, "accept_ra_pinfo", "0");
+ nm_device_ipv6_sysctl_set (device, "accept_ra_rtr_pref", "0");
+ nm_device_ipv6_sysctl_set (device, "use_tempaddr", "0");
+ }
+ }
+
+ if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) {
+ if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware)
+ nm_log_warn (LOGD_HW, "(%s): firmware may be missing.", nm_device_get_iface (device));
+ nm_device_set_firmware_missing (device, no_firmware ? TRUE : FALSE);
+ }
+ /* Ensure the device gets deactivated in response to stuff like
+ * carrier changes or rfkill. But don't deactivate devices that are
+ * about to assume a connection since that defeats the purpose of
+ * assuming the device's existing connection.
+ *
+ * Note that we "deactivate" the device even when coming from
+ * UNMANAGED, to ensure that it's in a clean state.
+ */
+ if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED)
+ nm_device_cleanup (device, reason);
+ break;
+ case NM_DEVICE_STATE_DISCONNECTED:
+ if (old_state > NM_DEVICE_STATE_UNAVAILABLE)
+ nm_device_cleanup (device, reason);
+ break;
+ default:
+ break;
+ }
+
+ /* Reset autoconnect flag when the device is activating or connected. */
+ if ( state >= NM_DEVICE_STATE_PREPARE
+ && state <= NM_DEVICE_STATE_ACTIVATED)
+ priv->autoconnect = TRUE;
+
+ g_object_notify (G_OBJECT (device), NM_DEVICE_STATE);
+ g_object_notify (G_OBJECT (device), NM_DEVICE_STATE_REASON);
+ g_signal_emit_by_name (device, "state-changed", state, old_state, reason);
+
+ /* Post-process the event after internal notification */
+
+ switch (state) {
+ case NM_DEVICE_STATE_UNAVAILABLE:
+ /* If the device can activate now (ie, it's got a carrier, the supplicant
+ * is active, or whatever) schedule a delayed transition to DISCONNECTED
+ * to get things rolling. The device can't transition immediately because
+ * we can't change states again from the state handler for a variety of
+ * reasons.
+ */
+ if (nm_device_is_available (device)) {
+ nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED",
+ nm_device_get_iface (device));
+ nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
+ } else {
+ if (old_state == NM_DEVICE_STATE_UNMANAGED) {
+ nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED",
+ nm_device_get_iface (device));
+ } else if ( old_state > NM_DEVICE_STATE_UNAVAILABLE
+ && nm_device_get_default_unmanaged (device))
+ nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
+ }
+ break;
+ case NM_DEVICE_STATE_DEACTIVATING:
+ if (quitting) {
+ nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN,
+ nm_act_request_get_connection (req),
+ device);
+ } else {
+ if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN,
+ nm_act_request_get_connection (req),
+ device,
+ dispatcher_pre_down_done,
+ device,
+ &priv->dispatcher_id)) {
+ /* Just proceed on errors */
+ dispatcher_pre_down_done (0, device);
+ }
+ }
+ break;
+ case NM_DEVICE_STATE_DISCONNECTED:
+ if (priv->queued_act_request) {
+ NMActRequest *queued_req;
+
+ queued_req = priv->queued_act_request;
+ priv->queued_act_request = NULL;
+ _device_activate (device, queued_req);
+ g_object_unref (queued_req);
+ } else if ( old_state > NM_DEVICE_STATE_DISCONNECTED
+ && nm_device_get_default_unmanaged (device))
+ nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
+ break;
+ case NM_DEVICE_STATE_ACTIVATED:
+ nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.",
+ nm_device_get_iface (device));
+ nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), device, NULL, NULL, NULL);
+ break;
+ case NM_DEVICE_STATE_FAILED:
+ connection = nm_device_get_connection (device);
+ nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+ "Activation (%s) failed for connection '%s'",
+ nm_device_get_iface (device),
+ connection ? nm_connection_get_id (connection) : "<unknown>");
+
+ /* Notify any slaves of the unexpected failure */
+ nm_device_master_release_slaves (device);
+
+ /* If the connection doesn't yet have a timestamp, set it to zero so that
+ * we can distinguish between connections we've tried to activate and have
+ * failed (zero timestamp), connections that succeeded (non-zero timestamp),
+ * and those we haven't tried yet (no timestamp).
+ */
+ if (connection && !nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), NULL)) {
+ nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (connection),
+ (guint64) 0,
+ TRUE);
+ }
+
+ /* Schedule the transition to DISCONNECTED. The device can't transition
+ * immediately because we can't change states again from the state
+ * handler for a variety of reasons.
+ */
+ nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
+ break;
+ case NM_DEVICE_STATE_IP_CHECK:
+ nm_device_start_ip_check (device);
+
+ /* IP-related properties are only valid when the device has IP configuration;
+ * now that it does, ensure their change notifications are emitted.
+ */
+ notify_ip_properties (device);
+ break;
+ case NM_DEVICE_STATE_SECONDARIES:
+ ip_check_gw_ping_cleanup (device);
+ nm_log_dbg (LOGD_DEVICE, "(%s): device entered SECONDARIES state",
+ nm_device_get_iface (device));
+ break;
+ default:
+ break;
+ }
+
+ if (state > NM_DEVICE_STATE_DISCONNECTED)
+ delete_on_deactivate_unschedule (device);
+
+ if ( (old_state == NM_DEVICE_STATE_ACTIVATED || old_state == NM_DEVICE_STATE_DEACTIVATING)
+ && (state != NM_DEVICE_STATE_DEACTIVATING)) {
+ if (quitting)
+ nm_dispatcher_call_sync (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device);
+ else
+ nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL, NULL);
+ }
+
+ /* IP-related properties are only valid when the device has IP configuration.
+ * If it no longer does, ensure their change notifications are emitted.
+ */
+ if (ip_config_valid (old_state) && !ip_config_valid (state))
+ notify_ip_properties (device);
+
+ /* Dispose of the cached activation request */
+ if (req)
+ g_object_unref (req);
+
+ priv->in_state_changed = FALSE;
+}
+
+void
+nm_device_state_changed (NMDevice *device,
+ NMDeviceState state,
+ NMDeviceStateReason reason)
+{
+ _set_state_full (device, state, reason, FALSE);
+}
+
+static gboolean
+queued_set_state (gpointer user_data)
+{
+ NMDevice *self = NM_DEVICE (user_data);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMDeviceState new_state;
+ NMDeviceStateReason new_reason;
+
+ if (priv->queued_state.id) {
+ nm_log_dbg (LOGD_DEVICE, "(%s): running queued state change to %s (id %d)",
+ nm_device_get_iface (self),
+ state_to_string (priv->queued_state.state),
+ priv->queued_state.id);
+
+ /* Clear queued state struct before triggering state change, since
+ * the state change may queue another state.
+ */
+ priv->queued_state.id = 0;
+ new_state = priv->queued_state.state;
+ new_reason = priv->queued_state.reason;
+ nm_device_queued_state_clear (self);
+
+ nm_device_state_changed (self, new_state, new_reason);
+ nm_device_remove_pending_action (self, queued_state_to_string (new_state), TRUE);
+ } else {
+ g_warn_if_fail (priv->queued_state.state == NM_DEVICE_STATE_UNKNOWN);
+ g_warn_if_fail (priv->queued_state.reason == NM_DEVICE_STATE_REASON_NONE);
+ }
+ return FALSE;
+}
+
+void
+nm_device_queue_state (NMDevice *self,
+ NMDeviceState state,
+ NMDeviceStateReason reason)
+{
+ NMDevicePrivate *priv;
+
+ g_return_if_fail (NM_IS_DEVICE (self));
+
+ priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->queued_state.id && priv->queued_state.state == state)
+ return;
+
+ /* Add pending action for the new state before clearing the queued states, so
+ * that we don't accidently pop all pending states and reach 'startup complete' */
+ nm_device_add_pending_action (self, queued_state_to_string (state), TRUE);
+
+ /* We should only ever have one delayed state transition at a time */
+ if (priv->queued_state.id) {
+ nm_log_warn (LOGD_DEVICE, "(%s): overwriting previously queued state change to %s (%s)",
+ nm_device_get_iface (self),
+ state_to_string (priv->queued_state.state),
+ reason_to_string (priv->queued_state.reason));
+ nm_device_queued_state_clear (self);
+ }
+
+ priv->queued_state.state = state;
+ priv->queued_state.reason = reason;
+ priv->queued_state.id = g_idle_add (queued_set_state, self);
+
+ nm_log_dbg (LOGD_DEVICE, "(%s): queued state change to %s due to %s (id %d)",
+ nm_device_get_iface (self), state_to_string (state), reason_to_string (reason),
+ priv->queued_state.id);
+}
+
+NMDeviceState
+nm_device_queued_state_peek (NMDevice *self)
+{
+ NMDevicePrivate *priv;
+
+ g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_STATE_UNKNOWN);
+
+ priv = NM_DEVICE_GET_PRIVATE (self);
+
+ return priv->queued_state.id ? priv->queued_state.state : NM_DEVICE_STATE_UNKNOWN;
+}
+
+void
+nm_device_queued_state_clear (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->queued_state.id) {
+ nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)",
+ nm_device_get_iface (self), priv->queued_state.id);
+ g_source_remove (priv->queued_state.id);
+ nm_device_remove_pending_action (self, queued_state_to_string (priv->queued_state.state), TRUE);
+ }
+ memset (&priv->queued_state, 0, sizeof (priv->queued_state));
+}
+
+NMDeviceState
+nm_device_get_state (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
+
+ return NM_DEVICE_GET_PRIVATE (device)->state;
+}
+
+/***********************************************************/
+/* NMConfigDevice interface related stuff */
+
+static guint
+nm_device_get_hw_address_length (NMDevice *dev, gboolean *out_permanent)
+{
+ return NM_DEVICE_GET_CLASS (dev)->get_hw_address_length (dev, out_permanent);
+}
+
+const guint8 *
+nm_device_get_hw_address (NMDevice *dev, guint *out_len)
+{
+ NMDevicePrivate *priv;
+
+ g_return_val_if_fail (NM_IS_DEVICE (dev), NULL);
+ priv = NM_DEVICE_GET_PRIVATE (dev);
+
+ if (out_len)
+ *out_len = priv->hw_addr_len;
+
+ if (priv->hw_addr_len == 0)
+ return NULL;
+ else
+ return priv->hw_addr;
+}
+
gboolean
nm_device_update_hw_address (NMDevice *dev)
{
@@ -7844,137 +7042,912 @@ nm_device_set_hw_addr (NMDevice *device, const guint8 *addr,
}
/**
- * nm_device_add_pending_action():
- * @device: the #NMDevice to add the pending action to
- * @action: a static string that identifies the action
- * @assert_not_yet_pending: if %TRUE, assert that the @action is currently not yet pending.
- * Otherwise, ignore duplicate scheduling of the same action silently.
+ * nm_device_spec_match_list:
+ * @device: an #NMDevice
+ * @specs: (element-type utf8): a list of device specs
*
- * Adds a pending action to the device.
+ * Checks if @device matches any of the specifications in @specs. The
+ * currently-supported spec types are:
*
- * Returns: %TRUE if the action was added (and not already added before). %FALSE
- * if the same action is already scheduled. In the latter case, the action was not scheduled
- * a second time.
+ * "mac:00:11:22:33:44:55" - matches a device with the given
+ * hardware address
+ *
+ * "interface-name:foo0" - matches a device with the given
+ * interface name
+ *
+ * "s390-subchannels:00.11.22" - matches a device with the given
+ * z/VM / s390 subchannels.
+ *
+ * "*" - matches any device
+ *
+ * Returns: #TRUE if @device matches one of the specs in @specs
*/
gboolean
-nm_device_add_pending_action (NMDevice *device, const char *action, gboolean assert_not_yet_pending)
+nm_device_spec_match_list (NMDevice *device, const GSList *specs)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+ if (!specs)
+ return FALSE;
+
+ return NM_DEVICE_GET_CLASS (device)->spec_match_list (device, specs);
+}
+
+static gboolean
+spec_match_list (NMDevice *device, const GSList *specs)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- GSList *iter;
- guint count = 0;
+ char *hwaddr_str;
+ gboolean matched = FALSE;
- g_return_val_if_fail (action, FALSE);
+ if (nm_match_spec_string (specs, "*"))
+ return TRUE;
- /* Check if the action is already pending. Cannot add duplicate actions */
- for (iter = priv->pending_actions; iter; iter = iter->next) {
- if (!strcmp (action, iter->data)) {
- if (assert_not_yet_pending) {
- nm_log_warn (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending",
- nm_device_get_iface (device),
- count + g_slist_length (iter),
- action);
- g_return_val_if_reached (FALSE);
- } else {
- nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending (expected)",
- nm_device_get_iface (device),
- count + g_slist_length (iter),
- action);
- }
- return FALSE;
- }
- count++;
+ if (priv->hw_addr_len) {
+ hwaddr_str = nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len);
+ matched = nm_match_spec_hwaddr (specs, hwaddr_str);
+ g_free (hwaddr_str);
}
- priv->pending_actions = g_slist_append (priv->pending_actions, g_strdup (action));
- count++;
+ if (!matched)
+ matched = nm_match_spec_interface_name (specs, nm_device_get_iface (device));
- nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s'",
- nm_device_get_iface (device),
- count,
- action);
+ return matched;
+}
- if (count == 1)
- g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
+static guint
+get_hw_address_length (NMDevice *dev, gboolean *out_permanent)
+{
+ size_t len;
- return TRUE;
+ if (nm_platform_link_get_address (nm_device_get_ip_ifindex (dev), &len))
+ return len;
+ else
+ return 0;
}
-/**
- * nm_device_remove_pending_action():
- * @device: the #NMDevice to remove the pending action from
- * @action: a static string that identifies the action
- * @assert_is_pending: if %TRUE, assert that the @action is pending.
- * If %FALSE, don't do anything if the current action is not pending and
- * return %FALSE.
- *
- * Removes a pending action previously added by nm_device_add_pending_action().
- *
- * Returns: whether the @action was pending and is now removed.
+/***********************************************************/
+
+#define DEFAULT_AUTOCONNECT TRUE
+
+static void
+nm_device_init (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ priv->type = NM_DEVICE_TYPE_UNKNOWN;
+ priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
+ priv->state = NM_DEVICE_STATE_UNMANAGED;
+ priv->state_reason = NM_DEVICE_STATE_REASON_NONE;
+ priv->dhcp_timeout = 0;
+ priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
+ priv->autoconnect = DEFAULT_AUTOCONNECT;
+ priv->unmanaged_flags = NM_UNMANAGED_INTERNAL;
+ priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
+ priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+}
+
+static void
+nm_device_config_device_interface_init (NMConfigDeviceInterface *iface)
+{
+ iface->spec_match_list = (gboolean (*) (NMConfigDevice *, const GSList *)) nm_device_spec_match_list;
+ iface->get_hw_address = (const guint8 * (*) (NMConfigDevice *, guint *)) nm_device_get_hw_address;
+}
+
+/*
+ * Get driver info from SIOCETHTOOL ioctl() for 'iface'
+ * Returns driver and firmware versions to 'driver_version and' 'firmware_version'
*/
-gboolean
-nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending)
+static gboolean
+device_get_driver_info (const char *iface, char **driver_version, char **firmware_version)
{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- GSList *iter;
- guint count = 0;
+ struct ethtool_drvinfo drvinfo;
+ struct ifreq req;
+ int fd;
- g_return_val_if_fail (action, FALSE);
+ fd = socket (PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ nm_log_warn (LOGD_HW, "couldn't open control socket.");
+ return FALSE;
+ }
- for (iter = priv->pending_actions; iter; iter = iter->next) {
- if (!strcmp (action, iter->data)) {
- nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s'",
- nm_device_get_iface (device),
- count + g_slist_length (iter->next), /* length excluding 'iter' */
- action);
- g_free (iter->data);
- priv->pending_actions = g_slist_delete_link (priv->pending_actions, iter);
- if (priv->pending_actions == NULL)
- g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
- return TRUE;
- }
- count++;
+ /* Get driver and firmware version info */
+ memset (&drvinfo, 0, sizeof (drvinfo));
+ memset (&req, 0, sizeof (struct ifreq));
+ strncpy (req.ifr_name, iface, IFNAMSIZ);
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ req.ifr_data = &drvinfo;
+
+ errno = 0;
+ if (ioctl (fd, SIOCETHTOOL, &req) < 0) {
+ nm_log_dbg (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d",
+ iface, errno);
+ close (fd);
+ return FALSE;
}
+ if (driver_version)
+ *driver_version = g_strdup (drvinfo.version);
+ if (firmware_version)
+ *firmware_version = g_strdup (drvinfo.fw_version);
- if (assert_is_pending) {
- nm_log_warn (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending",
- nm_device_get_iface (device),
- count,
- action);
- g_return_val_if_reached (FALSE);
+ close (fd);
+ return TRUE;
+}
+
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ NMDevice *dev;
+ NMDevicePrivate *priv;
+ NMPlatform *platform;
+ static guint32 id = 0;
+
+ object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ if (!object)
+ return NULL;
+
+ dev = NM_DEVICE (object);
+ priv = NM_DEVICE_GET_PRIVATE (dev);
+
+ if (!priv->iface) {
+ nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring");
+ goto error;
+ }
+
+ if (!priv->udi) {
+ /* Use a placeholder UDI until we get a real one */
+ priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++);
+ }
+
+ if (NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities)
+ priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev);
+
+ priv->fw_manager = nm_firewall_manager_get ();
+
+ device_get_driver_info (priv->iface, &priv->driver_version, &priv->firmware_version);
+
+ /* Watch for external IP config changes */
+ platform = nm_platform_get ();
+ g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev);
+ g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev);
+ g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev);
+ g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev);
+ g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), dev);
+
+ return object;
+
+error:
+ g_object_unref (dev);
+ return NULL;
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDevice *dev = NM_DEVICE (object);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
+
+ nm_device_update_hw_address (dev);
+
+ if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address)
+ NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev);
+
+ if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address)
+ NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev);
+
+ /* Have to call update_initial_hw_address() before calling get_ignore_carrier() */
+ if (device_has_capability (dev, NM_DEVICE_CAP_CARRIER_DETECT)) {
+ priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), NM_CONFIG_DEVICE (dev));
+
+ check_carrier (dev);
+ nm_log_info (LOGD_HW,
+ "(%s): carrier is %s%s",
+ nm_device_get_iface (NM_DEVICE (dev)),
+ priv->carrier ? "ON" : "OFF",
+ priv->ignore_carrier ? " (but ignored)" : "");
} else {
- nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending (expected)",
- nm_device_get_iface (device),
- count,
- action);
+ /* Fake online link when carrier detection is not available. */
+ priv->carrier = TRUE;
}
- return FALSE;
+
+ if (priv->ifindex > 0) {
+ priv->is_software = nm_platform_link_is_software (priv->ifindex);
+ priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex);
+ }
+
+ if (priv->ifindex > 0)
+ priv->mtu = nm_platform_link_get_mtu (priv->ifindex);
+
+ priv->con_provider = nm_connection_provider_get ();
+ g_assert (priv->con_provider);
+ g_signal_connect (priv->con_provider,
+ NM_CP_SIGNAL_CONNECTION_ADDED,
+ G_CALLBACK (cp_connection_added),
+ dev);
+
+ g_signal_connect (priv->con_provider,
+ NM_CP_SIGNAL_CONNECTION_REMOVED,
+ G_CALLBACK (cp_connection_removed),
+ dev);
+
+ g_signal_connect (priv->con_provider,
+ NM_CP_SIGNAL_CONNECTION_UPDATED,
+ G_CALLBACK (cp_connection_updated),
+ dev);
+
+ G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
}
-gboolean
-nm_device_has_pending_action (NMDevice *device)
+static void
+dispose (GObject *object)
{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ NMDevice *self = NM_DEVICE (object);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMPlatform *platform;
- return !!priv->pending_actions;
+ dispatcher_cleanup (self);
+
+ _cleanup_generic_pre (self, FALSE);
+
+ g_warn_if_fail (priv->slaves == NULL);
+ g_assert (priv->master_ready_id == 0);
+
+ _cleanup_generic_post (self, FALSE);
+
+ g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref);
+
+ if (priv->recheck_assume_id) {
+ g_source_remove (priv->recheck_assume_id);
+ priv->recheck_assume_id = 0;
+ }
+
+ link_disconnect_action_cancel (self);
+
+ if (priv->con_provider) {
+ g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_added, self);
+ g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_removed, self);
+ g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_updated, self);
+ priv->con_provider = NULL;
+ }
+
+ g_hash_table_unref (priv->available_connections);
+ priv->available_connections = NULL;
+
+ if (priv->carrier_wait_id) {
+ g_source_remove (priv->carrier_wait_id);
+ priv->carrier_wait_id = 0;
+ }
+
+ g_clear_object (&priv->queued_act_request);
+
+ platform = nm_platform_get ();
+ g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self);
+ g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
+
+ g_clear_object (&priv->fw_manager);
+
+ G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
}
-const char *
-nm_device_get_physical_port_id (NMDevice *device)
+static void
+finalize (GObject *object)
{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ NMDevice *self = NM_DEVICE (object);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ g_slist_free_full (priv->pending_actions, g_free);
+ g_clear_pointer (&priv->physical_port_id, g_free);
+ g_free (priv->udi);
+ g_free (priv->path);
+ g_free (priv->iface);
+ g_free (priv->ip_iface);
+ g_free (priv->driver);
+ g_free (priv->driver_version);
+ g_free (priv->firmware_version);
+ g_free (priv->type_desc);
+ if (priv->dhcp_anycast_address)
+ g_byte_array_free (priv->dhcp_anycast_address, TRUE);
- return priv->physical_port_id;
+ G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
}
-/**
- * nm_device_get_mtu:
- * @device: the #NMDevice
- *
- * Returns: MTU of the #NMDevice
- */
-guint32
-nm_device_get_mtu (NMDevice *device)
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
+ NMPlatformLink *platform_device;
+ const char *hw_addr;
+
+ switch (prop_id) {
+ case PROP_PLATFORM_DEVICE:
+ platform_device = g_value_get_pointer (value);
+ if (platform_device) {
+ g_free (priv->udi);
+ priv->udi = g_strdup (platform_device->udi);
+ g_free (priv->iface);
+ priv->iface = g_strdup (platform_device->name);
+ priv->ifindex = platform_device->ifindex;
+ g_free (priv->driver);
+ priv->driver = g_strdup (platform_device->driver);
+ }
+ break;
+ case PROP_UDI:
+ if (g_value_get_string (value)) {
+ g_free (priv->udi);
+ priv->udi = g_value_dup_string (value);
+ }
+ break;
+ case PROP_IFACE:
+ if (g_value_get_string (value)) {
+ g_free (priv->iface);
+ priv->ifindex = 0;
+ priv->iface = g_value_dup_string (value);
+
+ /* Only look up the ifindex if it appears to be an actual kernel
+ * interface name. eg Bluetooth devices won't have one until we know
+ * the IP interface.
+ */
+ if (priv->iface && !strchr (priv->iface, ':')) {
+ priv->ifindex = nm_platform_link_get_ifindex (priv->iface);
+ if (priv->ifindex <= 0)
+ nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface);
+ }
+ }
+ break;
+ case PROP_DRIVER:
+ if (g_value_get_string (value)) {
+ g_free (priv->driver);
+ priv->driver = g_value_dup_string (value);
+ }
+ break;
+ case PROP_DRIVER_VERSION:
+ g_free (priv->driver_version);
+ priv->driver_version = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_FIRMWARE_VERSION:
+ g_free (priv->firmware_version);
+ priv->firmware_version = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_MTU:
+ priv->mtu = g_value_get_uint (value);
+ break;
+ case PROP_IP4_ADDRESS:
+ priv->ip4_address = g_value_get_uint (value);
+ break;
+ case PROP_AUTOCONNECT:
+ priv->autoconnect = g_value_get_boolean (value);
+ break;
+ case PROP_FIRMWARE_MISSING:
+ priv->firmware_missing = g_value_get_boolean (value);
+ break;
+ case PROP_DEVICE_TYPE:
+ g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN);
+ priv->type = g_value_get_uint (value);
+ break;
+ case PROP_TYPE_DESC:
+ g_free (priv->type_desc);
+ priv->type_desc = g_value_dup_string (value);
+ break;
+ case PROP_RFKILL_TYPE:
+ priv->rfkill_type = g_value_get_uint (value);
+ break;
+ case PROP_IS_MASTER:
+ priv->is_master = g_value_get_boolean (value);
+ break;
+ case PROP_HW_ADDRESS:
+ priv->hw_addr_len = nm_device_get_hw_address_length (NM_DEVICE (object), NULL);
+
+ hw_addr = g_value_get_string (value);
+ if (!hw_addr)
+ break;
+ if (priv->hw_addr_len == 0) {
+ g_warn_if_fail (*hw_addr == '\0');
+ break;
+ }
+
+ if (!nm_utils_hwaddr_aton_len (hw_addr, priv->hw_addr, priv->hw_addr_len)) {
+ g_warning ("Could not parse hw-address '%s'", hw_addr);
+ memset (priv->hw_addr, 0, sizeof (priv->hw_addr));
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+#define DBUS_TYPE_STATE_REASON_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMDevice *self = NM_DEVICE (object);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ const char *ac_path = NULL;
+ GPtrArray *array;
+ GHashTableIter iter;
+ NMConnection *connection;
+
+ switch (prop_id) {
+ case PROP_UDI:
+ g_value_set_string (value, priv->udi);
+ break;
+ case PROP_IFACE:
+ g_value_set_string (value, priv->iface);
+ break;
+ case PROP_IP_IFACE:
+ if (ip_config_valid (priv->state))
+ g_value_set_string (value, nm_device_get_ip_iface (self));
+ else
+ g_value_set_string (value, NULL);
+ break;
+ case PROP_IFINDEX:
+ g_value_set_int (value, priv->ifindex);
+ break;
+ case PROP_DRIVER:
+ g_value_set_string (value, priv->driver);
+ break;
+ case PROP_DRIVER_VERSION:
+ g_value_set_string (value, priv->driver_version);
+ break;
+ case PROP_FIRMWARE_VERSION:
+ g_value_set_string (value, priv->firmware_version);
+ break;
+ case PROP_CAPABILITIES:
+ g_value_set_uint (value, (priv->capabilities & ~NM_DEVICE_CAP_INTERNAL_MASK));
+ break;
+ case PROP_IP4_ADDRESS:
+ g_value_set_uint (value, priv->ip4_address);
+ break;
+ case PROP_CARRIER:
+ g_value_set_boolean (value, priv->carrier);
+ break;
+ case PROP_MTU:
+ g_value_set_uint (value, priv->mtu);
+ break;
+ case PROP_IP4_CONFIG:
+ if (ip_config_valid (priv->state) && priv->ip4_config)
+ g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config));
+ else
+ g_value_set_boxed (value, "/");
+ break;
+ case PROP_DHCP4_CONFIG:
+ if (ip_config_valid (priv->state) && priv->dhcp4_client)
+ g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config));
+ else
+ g_value_set_boxed (value, "/");
+ break;
+ case PROP_IP6_CONFIG:
+ if (ip_config_valid (priv->state) && priv->ip6_config)
+ g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config));
+ else
+ g_value_set_boxed (value, "/");
+ break;
+ case PROP_DHCP6_CONFIG:
+ if (ip_config_valid (priv->state) && priv->dhcp6_client)
+ g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config));
+ else
+ g_value_set_boxed (value, "/");
+ break;
+ case PROP_STATE:
+ g_value_set_uint (value, priv->state);
+ break;
+ case PROP_STATE_REASON:
+ g_value_take_boxed (value, dbus_g_type_specialized_construct (DBUS_TYPE_STATE_REASON_STRUCT));
+ dbus_g_type_struct_set (value, 0, priv->state, 1, priv->state_reason, G_MAXUINT);
+ break;
+ case PROP_ACTIVE_CONNECTION:
+ if (priv->act_request)
+ ac_path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (priv->act_request));
+ g_value_set_boxed (value, ac_path ? ac_path : "/");
+ break;
+ case PROP_DEVICE_TYPE:
+ g_value_set_uint (value, priv->type);
+ break;
+ case PROP_MANAGED:
+ g_value_set_boolean (value, nm_device_get_managed (self));
+ break;
+ case PROP_AUTOCONNECT:
+ g_value_set_boolean (value, priv->autoconnect);
+ break;
+ case PROP_FIRMWARE_MISSING:
+ g_value_set_boolean (value, priv->firmware_missing);
+ break;
+ case PROP_TYPE_DESC:
+ g_value_set_string (value, priv->type_desc);
+ break;
+ case PROP_RFKILL_TYPE:
+ g_value_set_uint (value, priv->rfkill_type);
+ break;
+ case PROP_AVAILABLE_CONNECTIONS:
+ array = g_ptr_array_sized_new (g_hash_table_size (priv->available_connections));
+ g_hash_table_iter_init (&iter, priv->available_connections);
+ while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL))
+ g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection)));
+ g_value_take_boxed (value, array);
+ break;
+ case PROP_PHYSICAL_PORT_ID:
+ g_value_set_string (value, priv->physical_port_id);
+ break;
+ case PROP_IS_MASTER:
+ g_value_set_boolean (value, priv->is_master);
+ break;
+ case PROP_MASTER:
+ g_value_set_object (value, priv->master);
+ break;
+ case PROP_HW_ADDRESS:
+ if (priv->hw_addr_len)
+ g_value_take_string (value, nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len));
+ else
+ g_value_set_string (value, NULL);
+ break;
+ case PROP_HAS_PENDING_ACTION:
+ g_value_set_boolean (value, nm_device_has_pending_action (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_class_init (NMDeviceClass *klass)
{
- return NM_DEVICE_GET_PRIVATE (device)->mtu;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (NMDevicePrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->constructor = constructor;
+ object_class->constructed = constructed;
+
+ klass->link_changed = link_changed;
+
+ klass->is_available = is_available;
+ klass->act_stage1_prepare = act_stage1_prepare;
+ klass->act_stage2_config = act_stage2_config;
+ klass->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+ klass->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
+ klass->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout;
+ klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout;
+ klass->have_any_ready_slaves = have_any_ready_slaves;
+
+ klass->spec_match_list = spec_match_list;
+ klass->can_auto_connect = can_auto_connect;
+ klass->check_connection_compatible = check_connection_compatible;
+ klass->check_connection_available = check_connection_available;
+ klass->is_up = is_up;
+ klass->bring_up = bring_up;
+ klass->take_down = take_down;
+ klass->carrier_changed = carrier_changed;
+ klass->get_hw_address_length = get_hw_address_length;
+
+ /* Properties */
+ g_object_class_install_property
+ (object_class, PROP_PLATFORM_DEVICE,
+ g_param_spec_pointer (NM_DEVICE_PLATFORM_DEVICE,
+ "Platform Device",
+ "NMPlatform device object",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_UDI,
+ g_param_spec_string (NM_DEVICE_UDI,
+ "UDI",
+ "Unique Device Identifier",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property
+ (object_class, PROP_IFACE,
+ g_param_spec_string (NM_DEVICE_IFACE,
+ "Interface",
+ "Interface",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_IP_IFACE,
+ g_param_spec_string (NM_DEVICE_IP_IFACE,
+ "IP Interface",
+ "IP Interface",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_DRIVER,
+ g_param_spec_string (NM_DEVICE_DRIVER,
+ "Driver",
+ "Driver",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_DRIVER_VERSION,
+ g_param_spec_string (NM_DEVICE_DRIVER_VERSION,
+ "Driver Version",
+ "Driver Version",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_FIRMWARE_VERSION,
+ g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION,
+ "Firmware Version",
+ "Firmware Version",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_CAPABILITIES,
+ g_param_spec_uint (NM_DEVICE_CAPABILITIES,
+ "Capabilities",
+ "Capabilities",
+ 0, G_MAXUINT32, NM_DEVICE_CAP_NONE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_CARRIER,
+ g_param_spec_boolean (NM_DEVICE_CARRIER,
+ "Carrier",
+ "Carrier",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_MTU,
+ g_param_spec_uint (NM_DEVICE_MTU,
+ "MTU",
+ "MTU",
+ 0, G_MAXUINT32, 1500,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_IP4_ADDRESS,
+ g_param_spec_uint (NM_DEVICE_IP4_ADDRESS,
+ "IP4 address",
+ "IP4 address",
+ 0, G_MAXUINT32, 0, /* FIXME */
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_IP4_CONFIG,
+ g_param_spec_boxed (NM_DEVICE_IP4_CONFIG,
+ "IP4 Config",
+ "IP4 Config",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_DHCP4_CONFIG,
+ g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG,
+ "DHCP4 Config",
+ "DHCP4 Config",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_IP6_CONFIG,
+ g_param_spec_boxed (NM_DEVICE_IP6_CONFIG,
+ "IP6 Config",
+ "IP6 Config",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_DHCP6_CONFIG,
+ g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG,
+ "DHCP6 Config",
+ "DHCP6 Config",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_STATE,
+ g_param_spec_uint (NM_DEVICE_STATE,
+ "State",
+ "State",
+ 0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_STATE_REASON,
+ g_param_spec_boxed (NM_DEVICE_STATE_REASON,
+ "StateReason",
+ "StateReason",
+ DBUS_TYPE_STATE_REASON_STRUCT,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_CONNECTION,
+ g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION,
+ "ActiveConnection",
+ "ActiveConnection",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_DEVICE_TYPE,
+ g_param_spec_uint (NM_DEVICE_DEVICE_TYPE,
+ "DeviceType",
+ "DeviceType",
+ 0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_MANAGED,
+ g_param_spec_boolean (NM_DEVICE_MANAGED,
+ "Managed",
+ "Managed",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_AUTOCONNECT,
+ g_param_spec_boolean (NM_DEVICE_AUTOCONNECT,
+ "Autoconnect",
+ "Autoconnect",
+ DEFAULT_AUTOCONNECT,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_FIRMWARE_MISSING,
+ g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING,
+ "FirmwareMissing",
+ "Firmware missing",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_TYPE_DESC,
+ g_param_spec_string (NM_DEVICE_TYPE_DESC,
+ "Type Description",
+ "Device type description",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_RFKILL_TYPE,
+ g_param_spec_uint (NM_DEVICE_RFKILL_TYPE,
+ "Rfkill Type",
+ "Type of rfkill switch (if any) supported by this device",
+ RFKILL_TYPE_WLAN,
+ RFKILL_TYPE_MAX,
+ RFKILL_TYPE_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_IFINDEX,
+ g_param_spec_int (NM_DEVICE_IFINDEX,
+ "Ifindex",
+ "Ifindex",
+ 0, G_MAXINT, 0,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_AVAILABLE_CONNECTIONS,
+ g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS,
+ "AvailableConnections",
+ "AvailableConnections",
+ DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_PHYSICAL_PORT_ID,
+ g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID,
+ "PhysicalPortId",
+ "PhysicalPortId",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_IS_MASTER,
+ g_param_spec_boolean (NM_DEVICE_IS_MASTER,
+ "IsMaster",
+ "IsMaster",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_MASTER,
+ g_param_spec_object (NM_DEVICE_MASTER,
+ "Master",
+ "Master",
+ NM_TYPE_DEVICE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_HW_ADDRESS,
+ "Hardware Address",
+ "Hardware address",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_HAS_PENDING_ACTION,
+ g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION,
+ "Has pending action",
+ "Has pending action",
+ FALSE,
+ G_PARAM_READABLE));
+
+ /* Signals */
+ signals[STATE_CHANGED] =
+ g_signal_new ("state-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NMDeviceClass, state_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3,
+ G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[AUTOCONNECT_ALLOWED] =
+ g_signal_new ("autoconnect-allowed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ autoconnect_allowed_accumulator, NULL, NULL,
+ G_TYPE_BOOLEAN, 0);
+
+ signals[AUTH_REQUEST] =
+ g_signal_new (NM_DEVICE_AUTH_REQUEST,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ /* dbus-glib context, connection, permission, allow_interaction, callback, user_data */
+ G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER);
+
+ signals[IP4_CONFIG_CHANGED] =
+ g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
+
+ signals[IP6_CONFIG_CHANGED] =
+ g_signal_new (NM_DEVICE_IP6_CONFIG_CHANGED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
+
+ signals[REMOVED] =
+ g_signal_new (NM_DEVICE_REMOVED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals[RECHECK_AUTO_ACTIVATE] =
+ g_signal_new (NM_DEVICE_RECHECK_AUTO_ACTIVATE,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals[RECHECK_ASSUME] =
+ g_signal_new (NM_DEVICE_RECHECK_ASSUME,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
+ G_TYPE_FROM_CLASS (klass),
+ &dbus_glib_nm_device_object_info);
+
+ dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR);
}
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 1e2f39a31e..f74486e6fc 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -218,7 +218,7 @@ typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device,
GType nm_device_get_type (void);
const char * nm_device_get_path (NMDevice *dev);
-void nm_device_set_path (NMDevice *dev, const char *path);
+void nm_device_dbus_export (NMDevice *device);
const char * nm_device_get_udi (NMDevice *dev);
const char * nm_device_get_iface (NMDevice *dev);
@@ -228,7 +228,6 @@ const char * nm_device_get_ip_iface (NMDevice *dev);
int nm_device_get_ip_ifindex(NMDevice *dev);
const char * nm_device_get_driver (NMDevice *dev);
const char * nm_device_get_driver_version (NMDevice *dev);
-const char * nm_device_get_firmware_version (NMDevice *dev);
const char * nm_device_get_type_desc (NMDevice *dev);
NMDeviceType nm_device_get_device_type (NMDevice *dev);
@@ -248,9 +247,7 @@ void nm_device_set_vpn6_config (NMDevice *dev, NMIP6Config *config)
void nm_device_capture_initial_config (NMDevice *dev);
/* Master */
-gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure);
GSList * nm_device_master_get_slaves (NMDevice *dev);
-gboolean nm_device_is_master (NMDevice *dev);
/* Slave */
NMDevice * nm_device_get_master (NMDevice *dev);
@@ -275,10 +272,7 @@ gboolean nm_device_complete_connection (NMDevice *device,
gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection);
-gboolean nm_device_can_assume_connections (NMDevice *device);
-
-NMConnection * nm_device_find_assumable_connection (NMDevice *device,
- const GSList *connections);
+gboolean nm_device_can_assume_active_connection (NMDevice *device);
gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs);
@@ -313,19 +307,18 @@ typedef enum {
} NMUnmanagedFlags;
gboolean nm_device_get_managed (NMDevice *device);
-gboolean nm_device_get_default_unmanaged (NMDevice *device);
gboolean nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag);
void nm_device_set_unmanaged (NMDevice *device,
NMUnmanagedFlags flag,
gboolean unmanaged,
NMDeviceStateReason reason);
+void nm_device_set_unmanaged_quitting (NMDevice *device);
void nm_device_set_initial_unmanaged_flag (NMDevice *device,
NMUnmanagedFlags flag,
gboolean unmanaged);
gboolean nm_device_get_is_nm_owned (NMDevice *device);
-gboolean nm_device_set_is_nm_owned (NMDevice *device,
- gboolean is_nm_owned);
+void nm_device_set_nm_owned (NMDevice *device);
gboolean nm_device_get_autoconnect (NMDevice *device);
@@ -341,8 +334,6 @@ void nm_device_queue_state (NMDevice *self,
NMDeviceState state,
NMDeviceStateReason reason);
-void nm_device_queue_ip_config_change (NMDevice *self);
-
gboolean nm_device_get_firmware_missing (NMDevice *self);
void nm_device_queue_activation (NMDevice *device, NMActRequest *req);
@@ -356,10 +347,6 @@ gboolean nm_device_has_pending_action (NMDevice *device);
GPtrArray *nm_device_get_available_connections (NMDevice *device,
const char *specific_object);
-const char *nm_device_get_physical_port_id (NMDevice *device);
-
-guint32 nm_device_get_mtu (NMDevice *device);
-
gboolean nm_device_connection_is_available (NMDevice *device,
NMConnection *connection,
gboolean allow_device_override);
diff --git a/src/main.c b/src/main.c
index 4bd2b4324c..0ba65b0b8a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -54,6 +54,7 @@
#include "nm-config.h"
#include "nm-posix-signals.h"
#include "nm-session-monitor.h"
+#include "nm-dispatcher.h"
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
@@ -604,6 +605,8 @@ main (int argc, char *argv[])
dhcp_mgr = nm_dhcp_manager_get ();
g_assert (dhcp_mgr != NULL);
+ nm_dispatcher_init ();
+
settings = nm_settings_new (&error);
if (!settings) {
nm_log_err (LOGD_CORE, "failed to initialize settings storage: %s",
diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c
index 63773862c6..7a0c8de178 100644
--- a/src/nm-dispatcher.c
+++ b/src/nm-dispatcher.c
@@ -23,7 +23,7 @@
#include <string.h>
#include "nm-dispatcher.h"
-#include "nm-dispatcher-action.h"
+#include "nm-dispatcher-api.h"
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
#include "nm-logging.h"
@@ -31,7 +31,10 @@
#include "nm-dbus-glib-types.h"
#include "nm-glib-compat.h"
-static GSList *requests = NULL;
+#define CALL_TIMEOUT (1000 * 60 * 10) /* 10 mintues for all scripts */
+
+static gboolean do_dispatch = TRUE;
+static GHashTable *requests = NULL;
static void
dump_object_to_props (GObject *object, GHashTable *hash)
@@ -132,17 +135,37 @@ fill_vpn_props (NMIP4Config *ip4_config,
}
typedef struct {
+ guint request_id;
DispatcherFunc callback;
gpointer user_data;
+ guint idle_id;
} DispatchInfo;
static void
dispatcher_info_free (DispatchInfo *info)
{
- requests = g_slist_remove (requests, info);
+ if (info->idle_id)
+ g_source_remove (info->idle_id);
g_free (info);
}
+static void
+_ensure_requests (void)
+{
+ if (G_UNLIKELY (requests == NULL)) {
+ requests = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify) dispatcher_info_free);
+ }
+}
+
+static void
+dispatcher_info_cleanup (DispatchInfo *info)
+{
+ g_hash_table_remove (requests, GUINT_TO_POINTER (info->request_id));
+}
+
static const char *
dispatch_result_to_string (DispatchResult result)
{
@@ -161,115 +184,149 @@ dispatch_result_to_string (DispatchResult result)
g_assert_not_reached ();
}
+static gboolean
+validate_element (guint request_id, GValue *val, GType expected_type, guint idx, guint eltnum)
+{
+ if (G_VALUE_TYPE (val) != expected_type) {
+ nm_log_dbg (LOGD_DISPATCH, "(%u) result %d element %d invalid type %s",
+ request_id, idx, eltnum, G_VALUE_TYPE_NAME (val));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+dispatcher_results_process (guint request_id, GPtrArray *results)
+{
+ guint i;
+
+ g_return_if_fail (results != NULL);
+
+ for (i = 0; i < results->len; i++) {
+ GValueArray *item = g_ptr_array_index (results, i);
+ GValue *tmp;
+ const char *script, *err;
+ DispatchResult result;
+
+ if (item->n_values != 3) {
+ nm_log_dbg (LOGD_DISPATCH, "(%u) unexpected number of items in "
+ "dispatcher result (got %d, expected 3)",
+ request_id, item->n_values);
+ continue;
+ }
+
+ /* Script */
+ tmp = g_value_array_get_nth (item, 0);
+ if (!validate_element (request_id, tmp, G_TYPE_STRING, i, 0))
+ continue;
+ script = g_value_get_string (tmp);
+ if (!script || strncmp (script, NMD_SCRIPT_DIR "/", STRLEN (NMD_SCRIPT_DIR "/")))
+ continue;
+
+ /* Result */
+ tmp = g_value_array_get_nth (item, 1);
+ if (!validate_element (request_id, tmp, G_TYPE_UINT, i, 1))
+ continue;
+ result = g_value_get_uint (tmp);
+
+ /* Error */
+ tmp = g_value_array_get_nth (item, 2);
+ if (!validate_element (request_id, tmp, G_TYPE_STRING, i, 2))
+ continue;
+ err = g_value_get_string (tmp);
+
+
+ if (result == DISPATCH_RESULT_SUCCESS) {
+ nm_log_dbg (LOGD_DISPATCH, "(%u) %s succeeded",
+ request_id,
+ script + STRLEN (NMD_SCRIPT_DIR "/"));
+ } else {
+ nm_log_warn (LOGD_DISPATCH, "(%u) %s failed (%s): %s",
+ request_id,
+ script + STRLEN (NMD_SCRIPT_DIR "/"),
+ dispatch_result_to_string (result),
+ err ? err : "");
+ }
+ }
+}
+
+static void
+free_results (GPtrArray *results)
+{
+ g_return_if_fail (results != NULL);
+ g_ptr_array_foreach (results, (GFunc) g_value_array_free, NULL);
+ g_ptr_array_free (results, TRUE);
+}
+
static void
dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
{
DispatchInfo *info = user_data;
GError *error = NULL;
GPtrArray *results = NULL;
- guint i;
if (dbus_g_proxy_end_call (proxy, call, &error,
DISPATCHER_TYPE_RESULT_ARRAY, &results,
G_TYPE_INVALID)) {
- for (i = 0; results && (i < results->len); i++) {
- GValueArray *item = g_ptr_array_index (results, i);
- GValue *tmp;
- const char *script, *err;
- DispatchResult result;
-
- if (item->n_values != 3) {
- nm_log_dbg (LOGD_DISPATCH, "Unexpected number of items in "
- "dispatcher result (got %d, expectd 3)",
- item->n_values);
- goto next;
- }
-
- /* Script */
- tmp = g_value_array_get_nth (item, 0);
- if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) {
- nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 0 invalid type %s",
- i, G_VALUE_TYPE_NAME (tmp));
- goto next;
- }
- script = g_value_get_string (tmp);
-
- /* Result */
- tmp = g_value_array_get_nth (item, 1);
- if (G_VALUE_TYPE (tmp) != G_TYPE_UINT) {
- nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 1 invalid type %s",
- i, G_VALUE_TYPE_NAME (tmp));
- goto next;
- }
- result = g_value_get_uint (tmp);
-
- /* Error */
- tmp = g_value_array_get_nth (item, 2);
- if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) {
- nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 2 invalid type %s",
- i, G_VALUE_TYPE_NAME (tmp));
- goto next;
- }
- err = g_value_get_string (tmp);
-
- if (result != DISPATCH_RESULT_SUCCESS) {
- nm_log_warn (LOGD_DISPATCH, "Dispatcher script \"%s\" failed with %s: %s",
- script, dispatch_result_to_string (result), err);
- }
-
-next:
- g_value_array_free (item);
- }
- g_ptr_array_free (results, TRUE);
+ dispatcher_results_process (info->request_id, results);
+ free_results (results);
} else {
g_assert (error);
- nm_log_warn (LOGD_DISPATCH, "Dispatcher failed: (%d) %s", error->code, error->message);
+ nm_log_warn (LOGD_DISPATCH, "(%u) failed: (%d) %s",
+ info->request_id, error->code, error->message);
}
if (info->callback)
- info->callback (info, info->user_data);
+ info->callback (info->request_id, info->user_data);
g_clear_error (&error);
g_object_unref (proxy);
}
+static const char *action_table[] = {
+ [DISPATCHER_ACTION_HOSTNAME] = NMD_ACTION_HOSTNAME,
+ [DISPATCHER_ACTION_PRE_UP] = NMD_ACTION_PRE_UP,
+ [DISPATCHER_ACTION_UP] = NMD_ACTION_UP,
+ [DISPATCHER_ACTION_PRE_DOWN] = NMD_ACTION_PRE_DOWN,
+ [DISPATCHER_ACTION_DOWN] = NMD_ACTION_DOWN,
+ [DISPATCHER_ACTION_VPN_PRE_UP] = NMD_ACTION_VPN_PRE_UP,
+ [DISPATCHER_ACTION_VPN_UP] = NMD_ACTION_VPN_UP,
+ [DISPATCHER_ACTION_VPN_PRE_DOWN] = NMD_ACTION_VPN_PRE_DOWN,
+ [DISPATCHER_ACTION_VPN_DOWN] = NMD_ACTION_VPN_DOWN,
+ [DISPATCHER_ACTION_DHCP4_CHANGE] = NMD_ACTION_DHCP4_CHANGE,
+ [DISPATCHER_ACTION_DHCP6_CHANGE] = NMD_ACTION_DHCP6_CHANGE,
+};
+
static const char *
action_to_string (DispatcherAction action)
{
- switch (action) {
- case DISPATCHER_ACTION_HOSTNAME:
- return "hostname";
- case DISPATCHER_ACTION_UP:
- return "up";
- case DISPATCHER_ACTION_PRE_DOWN:
- return "pre-down";
- case DISPATCHER_ACTION_DOWN:
- return "down";
- case DISPATCHER_ACTION_VPN_UP:
- return "vpn-up";
- case DISPATCHER_ACTION_VPN_PRE_DOWN:
- return "vpn-pre-down";
- case DISPATCHER_ACTION_VPN_DOWN:
- return "vpn-down";
- case DISPATCHER_ACTION_DHCP4_CHANGE:
- return "dhcp4-change";
- case DISPATCHER_ACTION_DHCP6_CHANGE:
- return "dhcp6-change";
- default:
- break;
- }
- g_assert_not_reached ();
+ g_assert (action >= 0 && action < G_N_ELEMENTS (action_table));
+ return action_table[action];
+}
+
+static gboolean
+dispatcher_idle_cb (gpointer user_data)
+{
+ DispatchInfo *info = user_data;
+
+ info->idle_id = 0;
+ if (info->callback)
+ info->callback (info->request_id, info->user_data);
+ dispatcher_info_cleanup (info);
+ return G_SOURCE_REMOVE;
}
-static gconstpointer
+static gboolean
_dispatcher_call (DispatcherAction action,
+ gboolean blocking,
NMConnection *connection,
NMDevice *device,
const char *vpn_iface,
NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config,
DispatcherFunc callback,
- gpointer user_data)
+ gpointer user_data,
+ guint *out_call_id)
{
DBusGProxy *proxy;
DBusGConnection *g_connection;
@@ -282,22 +339,49 @@ _dispatcher_call (DispatcherAction action,
GHashTable *device_dhcp6_props;
GHashTable *vpn_ip4_props;
GHashTable *vpn_ip6_props;
- DispatchInfo *info;
+ DispatchInfo *info = NULL;
+ gboolean success = FALSE;
+ GError *error = NULL;
+ static guint request_counter = 0;
+ guint reqid = ++request_counter;
+
+ /* Wrapping protection */
+ if (G_UNLIKELY (!reqid))
+ reqid = ++request_counter;
+
+ g_assert (!blocking || (!callback && !user_data));
+
+ _ensure_requests ();
/* All actions except 'hostname' require a device */
if (action == DISPATCHER_ACTION_HOSTNAME) {
- nm_log_dbg (LOGD_DISPATCH, "dispatching action '%s'",
- action_to_string (action));
+ nm_log_dbg (LOGD_DISPATCH, "(%u) dispatching action '%s'",
+ reqid, action_to_string (action));
} else {
- g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
- nm_log_dbg (LOGD_DISPATCH, "(%s) dispatching action '%s'",
- nm_device_get_iface (device), action_to_string (action));
+ nm_log_dbg (LOGD_DISPATCH, "(%u) (%s) dispatching action '%s'",
+ reqid,
+ vpn_iface ? vpn_iface : nm_device_get_iface (device),
+ action_to_string (action));
}
/* VPN actions require at least an IPv4 config (for now) */
if (action == DISPATCHER_ACTION_VPN_UP)
- g_return_val_if_fail (vpn_ip4_config != NULL, NULL);
+ g_return_val_if_fail (vpn_ip4_config != NULL, FALSE);
+
+ if (do_dispatch == FALSE) {
+ if (blocking == FALSE && (out_call_id || callback)) {
+ info = g_malloc0 (sizeof (*info));
+ info->request_id = reqid;
+ info->callback = callback;
+ info->user_data = user_data;
+ info->idle_id = g_idle_add (dispatcher_idle_cb, info);
+ }
+ nm_log_dbg (LOGD_DISPATCH, "(%u) ignoring request; no scripts in " NMD_SCRIPT_DIR, reqid);
+ success = TRUE;
+ goto done;
+ }
g_connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
proxy = dbus_g_proxy_new_for_name (g_connection,
@@ -305,8 +389,8 @@ _dispatcher_call (DispatcherAction action,
NM_DISPATCHER_DBUS_PATH,
NM_DISPATCHER_DBUS_IFACE);
if (!proxy) {
- nm_log_err (LOGD_DISPATCH, "could not get dispatcher proxy!");
- return NULL;
+ nm_log_err (LOGD_DISPATCH, "(%u) could not get dispatcher proxy!", reqid);
+ return FALSE;
}
if (connection) {
@@ -341,29 +425,61 @@ _dispatcher_call (DispatcherAction action,
fill_vpn_props (vpn_ip4_config, vpn_ip6_config, vpn_ip4_props, vpn_ip6_props);
}
- info = g_malloc0 (sizeof (*info));
- info->callback = callback;
- info->user_data = user_data;
-
/* Send the action to the dispatcher */
- dbus_g_proxy_begin_call_with_timeout (proxy, "Action",
- dispatcher_done_cb,
- info,
- (GDestroyNotify) dispatcher_info_free,
- 30000,
- G_TYPE_STRING, action_to_string (action),
- DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
- DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
- DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
- DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
- DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
- DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
- DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
- G_TYPE_STRING, vpn_iface ? vpn_iface : "",
- DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
- DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
- G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH),
- G_TYPE_INVALID);
+ if (blocking) {
+ GPtrArray *results = NULL;
+
+ success = dbus_g_proxy_call_with_timeout (proxy, "Action",
+ CALL_TIMEOUT,
+ &error,
+ G_TYPE_STRING, action_to_string (action),
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
+ DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
+ G_TYPE_STRING, vpn_iface ? vpn_iface : "",
+ DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
+ G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH),
+ G_TYPE_INVALID,
+ DISPATCHER_TYPE_RESULT_ARRAY, &results,
+ G_TYPE_INVALID);
+ if (success) {
+ dispatcher_results_process (reqid, results);
+ free_results (results);
+ } else {
+ nm_log_warn (LOGD_DISPATCH, "(%u) failed: (%d) %s", reqid, error->code, error->message);
+ g_error_free (error);
+ }
+ } else {
+ info = g_malloc0 (sizeof (*info));
+ info->request_id = reqid;
+ info->callback = callback;
+ info->user_data = user_data;
+ dbus_g_proxy_begin_call_with_timeout (proxy, "Action",
+ dispatcher_done_cb,
+ info,
+ (GDestroyNotify) dispatcher_info_cleanup,
+ CALL_TIMEOUT,
+ G_TYPE_STRING, action_to_string (action),
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
+ DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
+ G_TYPE_STRING, vpn_iface ? vpn_iface : "",
+ DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
+ DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
+ G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH),
+ G_TYPE_INVALID);
+ success = TRUE;
+ }
+
g_hash_table_destroy (connection_hash);
g_hash_table_destroy (connection_props);
g_hash_table_destroy (device_props);
@@ -374,45 +490,192 @@ _dispatcher_call (DispatcherAction action,
g_hash_table_destroy (vpn_ip4_props);
g_hash_table_destroy (vpn_ip6_props);
- /* Track the request in case of cancelation */
- requests = g_slist_append (requests, info);
+done:
+ if (success && info) {
+ /* Track the request in case of cancelation */
+ g_hash_table_insert (requests, GUINT_TO_POINTER (info->request_id), info);
+ if (out_call_id)
+ *out_call_id = info->request_id;
+ } else if (out_call_id)
+ *out_call_id = 0;
- return info;
+ return success;
}
-gconstpointer
+/**
+ * nm_dispatcher_call:
+ * @action: the %DispatcherAction
+ * @connection: the #NMConnection the action applies to
+ * @device: the #NMDevice the action applies to
+ * @callback: a caller-supplied callback to execute when done
+ * @user_data: caller-supplied pointer passed to @callback
+ * @out_call_id: on success, a call identifier which can be passed to
+ * nm_dispatcher_call_cancel()
+ *
+ * This method always invokes the dispatcher action asynchronously. To ignore
+ * the result, pass %NULL to @callback.
+ *
+ * Returns: %TRUE if the action was dispatched, %FALSE on failure
+ */
+gboolean
nm_dispatcher_call (DispatcherAction action,
NMConnection *connection,
NMDevice *device,
DispatcherFunc callback,
- gpointer user_data)
+ gpointer user_data,
+ guint *out_call_id)
+{
+ return _dispatcher_call (action, FALSE, connection, device, NULL, NULL,
+ NULL, callback, user_data, out_call_id);
+}
+
+/**
+ * nm_dispatcher_call_sync():
+ * @action: the %DispatcherAction
+ * @connection: the #NMConnection the action applies to
+ * @device: the #NMDevice the action applies to
+ *
+ * This method always invokes the dispatcher action synchronously and it may
+ * take a long time to return.
+ *
+ * Returns: %TRUE if the action was dispatched, %FALSE on failure
+ */
+gboolean
+nm_dispatcher_call_sync (DispatcherAction action,
+ NMConnection *connection,
+ NMDevice *device)
{
- return _dispatcher_call (action, connection, device, NULL, NULL, NULL, callback, user_data);
+ return _dispatcher_call (action, TRUE, connection, device, NULL, NULL,
+ NULL, NULL, NULL, NULL);
}
-gconstpointer
+/**
+ * nm_dispatcher_call_vpn():
+ * @action: the %DispatcherAction
+ * @connection: the #NMConnection the action applies to
+ * @parent_device: the parent #NMDevice of the VPN connection
+ * @vpn_iface: the IP interface of the VPN tunnel, if any
+ * @vpn_ip4_config: the #NMIP4Config of the VPN connection
+ * @vpn_ip6_config: the #NMIP6Config of the VPN connection
+ * @callback: a caller-supplied callback to execute when done
+ * @user_data: caller-supplied pointer passed to @callback
+ * @out_call_id: on success, a call identifier which can be passed to
+ * nm_dispatcher_call_cancel()
+ *
+ * This method always invokes the dispatcher action asynchronously. To ignore
+ * the result, pass %NULL to @callback.
+ *
+ * Returns: %TRUE if the action was dispatched, %FALSE on failure
+ */
+gboolean
nm_dispatcher_call_vpn (DispatcherAction action,
NMConnection *connection,
- NMDevice *device,
+ NMDevice *parent_device,
const char *vpn_iface,
NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config,
DispatcherFunc callback,
- gpointer user_data)
+ gpointer user_data,
+ guint *out_call_id)
+{
+ return _dispatcher_call (action, FALSE, connection, parent_device, vpn_iface,
+ vpn_ip4_config, vpn_ip6_config, callback, user_data, out_call_id);
+}
+
+/**
+ * nm_dispatcher_call_vpn_sync():
+ * @action: the %DispatcherAction
+ * @connection: the #NMConnection the action applies to
+ * @parent_device: the parent #NMDevice of the VPN connection
+ * @vpn_iface: the IP interface of the VPN tunnel, if any
+ * @vpn_ip4_config: the #NMIP4Config of the VPN connection
+ * @vpn_ip6_config: the #NMIP6Config of the VPN connection
+ *
+ * This method always invokes the dispatcher action synchronously and it may
+ * take a long time to return.
+ *
+ * Returns: %TRUE if the action was dispatched, %FALSE on failure
+ */
+gboolean
+nm_dispatcher_call_vpn_sync (DispatcherAction action,
+ NMConnection *connection,
+ NMDevice *parent_device,
+ const char *vpn_iface,
+ NMIP4Config *vpn_ip4_config,
+ NMIP6Config *vpn_ip6_config)
{
- return _dispatcher_call (action, connection, device, vpn_iface, vpn_ip4_config, vpn_ip6_config, callback, user_data);
+ return _dispatcher_call (action, TRUE, connection, parent_device, vpn_iface,
+ vpn_ip4_config, vpn_ip6_config, NULL, NULL, NULL);
}
void
-nm_dispatcher_call_cancel (gconstpointer call)
+nm_dispatcher_call_cancel (guint call_id)
{
- /* 'call' is really a DispatchInfo pointer, just opaque to callers.
- * Look it up in our requests list, but don't access it directly before
- * we've made sure it's a valid request,since it may have long since been
- * freed. Canceling just means the callback doesn't get called, so set
- * the DispatcherInfo's callback to NULL.
+ DispatchInfo *info;
+
+ _ensure_requests ();
+
+ /* Canceling just means the callback doesn't get called, so set the
+ * DispatcherInfo's callback to NULL.
*/
- if (g_slist_find (requests, call))
- ((DispatchInfo *) call)->callback = NULL;
+ info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id));
+ if (info)
+ info->callback = NULL;
+ else
+ g_return_if_reached ();
+}
+
+typedef struct {
+ const char *dir;
+ GFileMonitor *monitor;
+ gboolean has_scripts;
+} Monitor;
+
+static Monitor monitors[3] = {
+ { NMD_SCRIPT_DIR, NULL, TRUE },
+ { NMD_PRE_UP_DIR, NULL, TRUE },
+ { NMD_PRE_DOWN_DIR, NULL, TRUE }
+};
+
+static void
+dispatcher_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ Monitor *item)
+{
+ GDir *dir;
+ guint i;
+
+ /* Default to dispatching on any errors */
+ item->has_scripts = TRUE;
+
+ dir = g_dir_open (item->dir, 0, NULL);
+ if (dir) {
+ item->has_scripts = !!g_dir_read_name (dir);
+ g_dir_close (dir);
+ }
+
+ /* Recheck all dirs for scripts and update global variable */
+ do_dispatch = FALSE;
+ for (i = 0; i < G_N_ELEMENTS (monitors); i++)
+ do_dispatch |= monitors[i].has_scripts;
+}
+
+void
+nm_dispatcher_init (void)
+{
+ GFile *file;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (monitors); i++) {
+ file = g_file_new_for_path (monitors[i].dir);
+ monitors[i].monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitors[i].monitor) {
+ g_signal_connect (monitors[i].monitor, "changed", G_CALLBACK (dispatcher_dir_changed), &monitors[i]);
+ dispatcher_dir_changed (monitors[i].monitor, file, NULL, 0, &monitors[i]);
+ }
+ g_object_unref (file);
+ }
}
diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h
index 05a6c87514..464f6310cb 100644
--- a/src/nm-dispatcher.h
+++ b/src/nm-dispatcher.h
@@ -32,9 +32,11 @@
typedef enum {
DISPATCHER_ACTION_HOSTNAME,
+ DISPATCHER_ACTION_PRE_UP,
DISPATCHER_ACTION_UP,
DISPATCHER_ACTION_PRE_DOWN,
DISPATCHER_ACTION_DOWN,
+ DISPATCHER_ACTION_VPN_PRE_UP,
DISPATCHER_ACTION_VPN_UP,
DISPATCHER_ACTION_VPN_PRE_DOWN,
DISPATCHER_ACTION_VPN_DOWN,
@@ -42,23 +44,38 @@ typedef enum {
DISPATCHER_ACTION_DHCP6_CHANGE
} DispatcherAction;
-typedef void (*DispatcherFunc) (gconstpointer call, gpointer user_data);
+typedef void (*DispatcherFunc) (guint call_id, gpointer user_data);
-gconstpointer nm_dispatcher_call (DispatcherAction action,
+gboolean nm_dispatcher_call (DispatcherAction action,
+ NMConnection *connection,
+ NMDevice *device,
+ DispatcherFunc callback,
+ gpointer user_data,
+ guint *out_call_id);
+
+gboolean nm_dispatcher_call_sync (DispatcherAction action,
NMConnection *connection,
- NMDevice *device,
- DispatcherFunc callback,
- gpointer user_data);
+ NMDevice *device);
+
+gboolean nm_dispatcher_call_vpn (DispatcherAction action,
+ NMConnection *connection,
+ NMDevice *parent_device,
+ const char *vpn_iface,
+ NMIP4Config *vpn_ip4_config,
+ NMIP6Config *vpn_ip6_config,
+ DispatcherFunc callback,
+ gpointer user_data,
+ guint *out_call_id);
-gconstpointer nm_dispatcher_call_vpn (DispatcherAction action,
+gboolean nm_dispatcher_call_vpn_sync (DispatcherAction action,
NMConnection *connection,
- NMDevice *device,
+ NMDevice *parent_device,
const char *vpn_iface,
NMIP4Config *vpn_ip4_config,
- NMIP6Config *vpn_ip6_config,
- DispatcherFunc callback,
- gpointer user_data);
+ NMIP6Config *vpn_ip6_config);
+
+void nm_dispatcher_call_cancel (guint call_id);
-void nm_dispatcher_call_cancel (gconstpointer call);
+void nm_dispatcher_init (void);
#endif /* NM_DISPATCHER_H */
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 47245b648c..6fc74df14a 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -727,20 +727,27 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting)
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
if (nm_device_get_managed (device)) {
- /* Leave configured interfaces up when quitting so they can be
- * taken over again if NM starts up, and to ensure connectivity while
- * NM is gone. Assumed connections don't get taken down even if they
- * haven't been fully activated.
- */
-
- if ( !nm_device_can_assume_connections (device)
- || (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED)
- || !quitting) {
- NMActRequest *req = nm_device_get_act_request (device);
+ NMActRequest *req = nm_device_get_act_request (device);
+ gboolean unmanage = FALSE;
- if (!req || !nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (req)))
- nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
- }
+ /* Leave activated interfaces up when quitting so their configuration
+ * can be taken over when NM restarts. This ensures connectivity while
+ * NM is stopped. Devices which do not support connection assumption
+ * cannot be left up.
+ */
+ if (!quitting) /* Forced removal; device already gone */
+ unmanage = TRUE;
+ else if (!nm_device_can_assume_active_connection (device))
+ unmanage = TRUE;
+ else if (!req)
+ unmanage = TRUE;
+
+ if (unmanage) {
+ if (quitting)
+ nm_device_set_unmanaged_quitting (device);
+ else
+ nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
+ }
}
g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager);
@@ -754,8 +761,7 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting)
nm_dbus_manager_unregister_object (priv->dbus_mgr, device);
g_object_unref (device);
- if (priv->startup)
- check_if_startup_complete (manager);
+ check_if_startup_complete (manager);
}
static void
@@ -1087,7 +1093,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
}
if (device) {
- nm_device_set_is_nm_owned (device, TRUE);
+ nm_device_set_nm_owned (device);
add_device (self, device, FALSE);
g_object_unref (device);
}
@@ -1683,8 +1689,6 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
const char *iface, *driver, *type_desc;
- char *path;
- static guint32 devcount = 0;
const GSList *unmanaged_specs;
gboolean user_unmanaged, sleeping;
NMConnection *connection = NULL;
@@ -1761,11 +1765,7 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con)
sleeping = manager_sleeping (self);
nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping);
- path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++);
- nm_device_set_path (device, path);
- nm_dbus_manager_register_object (priv->dbus_mgr, path, device);
- nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path);
- g_free (path);
+ nm_device_dbus_export (device);
/* Don't generate a connection e.g. for devices NM just created, or
* for the loopback, or when we're sleeping. */
diff --git a/src/nm-policy.c b/src/nm-policy.c
index f307f999c6..b54b94928e 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -326,7 +326,7 @@ _set_hostname (NMPolicy *policy,
nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
if (set_system_hostname (priv->cur_hostname, msg))
- nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL);
+ nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL);
}
static void
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 17664ad1a9..f7476b411a 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -62,17 +62,32 @@ typedef enum {
SECRETS_REQ_LAST
} SecretsReq;
-typedef struct {
- gboolean disposed;
+/* Internal VPN states, private to NMVPNConnection */
+typedef enum {
+ STATE_UNKNOWN = 0,
+ STATE_WAITING,
+ STATE_PREPARE,
+ STATE_NEED_AUTH,
+ STATE_CONNECT,
+ STATE_IP_CONFIG_GET,
+ STATE_PRE_UP,
+ STATE_ACTIVATED,
+ STATE_DEACTIVATING,
+ STATE_DISCONNECTED,
+ STATE_FAILED,
+} VpnState;
+typedef struct {
NMConnection *connection;
guint32 secrets_id;
SecretsReq secrets_idx;
char *username;
- NMVPNConnectionState vpn_state;
+ VpnState vpn_state;
+ guint dispatcher_id;
NMVPNConnectionStateReason failure_reason;
+
DBusGProxy *proxy;
GHashTable *connect_hash;
guint connect_timeout;
@@ -121,20 +136,65 @@ static void plugin_interactive_secrets_required (DBusGProxy *proxy,
const char **secrets,
gpointer user_data);
+static void _set_vpn_state (NMVPNConnection *connection,
+ VpnState vpn_state,
+ NMVPNConnectionStateReason reason,
+ gboolean quitting);
+
+/*********************************************************************/
+
+static NMVPNConnectionState
+_state_to_nm_vpn_state (VpnState state)
+{
+ switch (state) {
+ case STATE_WAITING:
+ case STATE_PREPARE:
+ return NM_VPN_CONNECTION_STATE_PREPARE;
+ case STATE_NEED_AUTH:
+ return NM_VPN_CONNECTION_STATE_NEED_AUTH;
+ case STATE_CONNECT:
+ return NM_VPN_CONNECTION_STATE_CONNECT;
+ case STATE_IP_CONFIG_GET:
+ case STATE_PRE_UP:
+ return NM_VPN_CONNECTION_STATE_IP_CONFIG_GET;
+ case STATE_ACTIVATED:
+ return NM_VPN_CONNECTION_STATE_ACTIVATED;
+ case STATE_DEACTIVATING: {
+ /* Map DEACTIVATING to ACTIVATED to preserve external API behavior,
+ * since our API has no DEACTIVATING state of its own. Since this can
+ * take some time, and the VPN isn't actually disconnected until it
+ * hits the DISCONNECTED state, to clients it should still appear
+ * connected.
+ */
+ return NM_VPN_CONNECTION_STATE_ACTIVATED;
+ }
+ case STATE_DISCONNECTED:
+ return NM_VPN_CONNECTION_STATE_DISCONNECTED;
+ case STATE_FAILED:
+ return NM_VPN_CONNECTION_STATE_FAILED;
+ default:
+ return STATE_UNKNOWN;
+ }
+}
+
static NMActiveConnectionState
-ac_state_from_vpn_state (NMVPNConnectionState vpn_state)
+_state_to_ac_state (VpnState vpn_state)
{
/* Set the NMActiveConnection state based on VPN state */
switch (vpn_state) {
- case NM_VPN_CONNECTION_STATE_PREPARE:
- case NM_VPN_CONNECTION_STATE_NEED_AUTH:
- case NM_VPN_CONNECTION_STATE_CONNECT:
- case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
+ case STATE_WAITING:
+ case STATE_PREPARE:
+ case STATE_NEED_AUTH:
+ case STATE_CONNECT:
+ case STATE_IP_CONFIG_GET:
+ case STATE_PRE_UP:
return NM_ACTIVE_CONNECTION_STATE_ACTIVATING;
- case NM_VPN_CONNECTION_STATE_ACTIVATED:
+ case STATE_ACTIVATED:
return NM_ACTIVE_CONNECTION_STATE_ACTIVATED;
- case NM_VPN_CONNECTION_STATE_FAILED:
- case NM_VPN_CONNECTION_STATE_DISCONNECTED:
+ case STATE_DEACTIVATING:
+ return NM_ACTIVE_CONNECTION_STATE_DEACTIVATING;
+ case STATE_DISCONNECTED:
+ case STATE_FAILED:
return NM_ACTIVE_CONNECTION_STATE_DEACTIVATED;
default:
break;
@@ -190,12 +250,45 @@ vpn_cleanup (NMVPNConnection *connection, NMDevice *parent_dev)
}
static void
-nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
- NMVPNConnectionState vpn_state,
- NMVPNConnectionStateReason reason)
+dispatcher_pre_down_done (guint call_id, gpointer user_data)
+{
+ NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+ priv->dispatcher_id = 0;
+ _set_vpn_state (self, STATE_DISCONNECTED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+}
+
+static void
+dispatcher_pre_up_done (guint call_id, gpointer user_data)
+{
+ NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+ priv->dispatcher_id = 0;
+ _set_vpn_state (self, STATE_ACTIVATED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+}
+
+static void
+dispatcher_cleanup (NMVPNConnection *self)
+{
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+ if (priv->dispatcher_id) {
+ nm_dispatcher_call_cancel (priv->dispatcher_id);
+ priv->dispatcher_id = 0;
+ }
+}
+
+static void
+_set_vpn_state (NMVPNConnection *connection,
+ VpnState vpn_state,
+ NMVPNConnectionStateReason reason,
+ gboolean quitting)
{
NMVPNConnectionPrivate *priv;
- NMVPNConnectionState old_vpn_state;
+ VpnState old_vpn_state;
+ NMVPNConnectionState new_external_state, old_external_state;
NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
@@ -216,7 +309,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
/* Update active connection base class state */
nm_active_connection_set_state (NM_ACTIVE_CONNECTION (connection),
- ac_state_from_vpn_state (vpn_state));
+ _state_to_ac_state (vpn_state));
/* Clear any in-progress secrets request */
if (priv->secrets_id) {
@@ -224,24 +317,47 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
priv->secrets_id = 0;
}
+ dispatcher_cleanup (connection);
+
/* The connection gets destroyed by the VPN manager when it enters the
* disconnected/failed state, but we need to keep it around for a bit
* to send out signals and handle the dispatcher. So ref it.
*/
g_object_ref (connection);
- g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason);
- g_signal_emit (connection, signals[INTERNAL_STATE_CHANGED], 0, vpn_state, old_vpn_state, reason);
- g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
+ old_external_state = _state_to_nm_vpn_state (old_vpn_state);
+ new_external_state = _state_to_nm_vpn_state (priv->vpn_state);
+ if (new_external_state != old_external_state) {
+ g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, new_external_state, reason);
+ g_signal_emit (connection, signals[INTERNAL_STATE_CHANGED], 0,
+ new_external_state,
+ old_external_state,
+ reason);
+ g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
+ }
switch (vpn_state) {
- case NM_VPN_CONNECTION_STATE_NEED_AUTH:
+ case STATE_NEED_AUTH:
/* Do nothing; not part of 'default' because we don't want to touch
* priv->secrets_req as NEED_AUTH is re-entered during interactive
* secrets.
*/
break;
- case NM_VPN_CONNECTION_STATE_ACTIVATED:
+ case STATE_PRE_UP:
+ if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_UP,
+ priv->connection,
+ parent_dev,
+ priv->ip_iface,
+ priv->ip4_config,
+ priv->ip6_config,
+ dispatcher_pre_up_done,
+ connection,
+ &priv->dispatcher_id)) {
+ /* Just proceed on errors */
+ dispatcher_pre_up_done (0, connection);
+ }
+ break;
+ case STATE_ACTIVATED:
/* Secrets no longer needed now that we're connected */
nm_connection_clear_secrets (priv->connection);
@@ -253,20 +369,55 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
priv->ip4_config,
priv->ip6_config,
NULL,
+ NULL,
NULL);
break;
- case NM_VPN_CONNECTION_STATE_FAILED:
- case NM_VPN_CONNECTION_STATE_DISCONNECTED:
- if (old_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) {
+ case STATE_DEACTIVATING:
+ if (quitting) {
+ nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_PRE_DOWN,
+ priv->connection,
+ parent_dev,
+ priv->ip_iface,
+ priv->ip4_config,
+ priv->ip6_config);
+ } else {
+ if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_DOWN,
+ priv->connection,
+ parent_dev,
+ priv->ip_iface,
+ priv->ip4_config,
+ priv->ip6_config,
+ dispatcher_pre_down_done,
+ connection,
+ &priv->dispatcher_id)) {
+ /* Just proceed on errors */
+ dispatcher_pre_down_done (0, connection);
+ }
+ }
+ break;
+ case STATE_FAILED:
+ case STATE_DISCONNECTED:
+ if ( old_vpn_state >= STATE_ACTIVATED
+ && old_vpn_state <= STATE_DEACTIVATING) {
/* Let dispatcher scripts know we're about to go down */
- nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN,
- priv->connection,
- parent_dev,
- priv->ip_iface,
- NULL,
- NULL,
- NULL,
- NULL);
+ if (quitting) {
+ nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_DOWN,
+ priv->connection,
+ parent_dev,
+ priv->ip_iface,
+ NULL,
+ NULL);
+ } else {
+ nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN,
+ priv->connection,
+ parent_dev,
+ priv->ip_iface,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ }
}
/* Tear down and clean up the connection */
@@ -290,14 +441,20 @@ device_state_changed (NMActiveConnection *active,
NMDeviceState old_state)
{
if (new_state <= NM_DEVICE_STATE_DISCONNECTED) {
- nm_vpn_connection_set_vpn_state (NM_VPN_CONNECTION (active),
- NM_VPN_CONNECTION_STATE_DISCONNECTED,
- NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED);
+ _set_vpn_state (NM_VPN_CONNECTION (active),
+ STATE_DISCONNECTED,
+ NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
+ FALSE);
} else if (new_state == NM_DEVICE_STATE_FAILED) {
- nm_vpn_connection_set_vpn_state (NM_VPN_CONNECTION (active),
- NM_VPN_CONNECTION_STATE_FAILED,
- NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED);
+ _set_vpn_state (NM_VPN_CONNECTION (active),
+ STATE_FAILED,
+ NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
+ FALSE);
}
+
+ /* FIXME: map device DEACTIVATING state to VPN DEACTIVATING state and
+ * block device deactivation on VPN deactivation.
+ */
}
static void
@@ -488,27 +645,25 @@ vpn_service_state_to_string (NMVPNServiceState state)
return "unknown";
}
+static const char *state_table[] = {
+ [STATE_UNKNOWN] = "unknown",
+ [STATE_WAITING] = "waiting",
+ [STATE_PREPARE] = "prepare",
+ [STATE_NEED_AUTH] = "need-auth",
+ [STATE_CONNECT] = "connect",
+ [STATE_IP_CONFIG_GET] = "ip-config-get",
+ [STATE_PRE_UP] = "pre-up",
+ [STATE_ACTIVATED] = "activated",
+ [STATE_DEACTIVATING] = "deactivating",
+ [STATE_DISCONNECTED] = "disconnected",
+ [STATE_FAILED] = "failed",
+};
+
static const char *
-vpn_state_to_string (NMVPNConnectionState state)
+vpn_state_to_string (VpnState state)
{
- switch (state) {
- case NM_VPN_CONNECTION_STATE_PREPARE:
- return "prepare";
- case NM_VPN_CONNECTION_STATE_NEED_AUTH:
- return "need-auth";
- case NM_VPN_CONNECTION_STATE_CONNECT:
- return "connect";
- case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
- return "ip-config-get";
- case NM_VPN_CONNECTION_STATE_ACTIVATED:
- return "activated";
- case NM_VPN_CONNECTION_STATE_FAILED:
- return "failed";
- case NM_VPN_CONNECTION_STATE_DISCONNECTED:
- return "disconnected";
- default:
- break;
- }
+ if (state >= 0 && state < G_N_ELEMENTS (state_table))
+ return state_table[state];
return "unknown";
}
@@ -561,23 +716,13 @@ plugin_state_changed (DBusGProxy *proxy,
*/
nm_connection_clear_secrets (priv->connection);
- switch (nm_vpn_connection_get_vpn_state (connection)) {
- case NM_VPN_CONNECTION_STATE_PREPARE:
- case NM_VPN_CONNECTION_STATE_NEED_AUTH:
- case NM_VPN_CONNECTION_STATE_CONNECT:
- case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
- case NM_VPN_CONNECTION_STATE_ACTIVATED:
+ if ((priv->vpn_state >= STATE_WAITING) && (priv->vpn_state <= STATE_ACTIVATED)) {
nm_log_info (LOGD_VPN, "VPN plugin state change reason: %s (%d)",
vpn_reason_to_string (priv->failure_reason), priv->failure_reason);
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_FAILED,
- priv->failure_reason);
+ _set_vpn_state (connection, STATE_FAILED, priv->failure_reason, FALSE);
/* Reset the failure reason */
priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN;
- break;
- default:
- break;
}
}
}
@@ -742,9 +887,7 @@ nm_vpn_connection_apply_config (NMVPNConnection *connection)
nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
nm_connection_get_id (priv->connection));
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_ACTIVATED,
- NM_VPN_CONNECTION_STATE_REASON_NONE);
+ _set_vpn_state (connection, STATE_PRE_UP, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
return TRUE;
}
@@ -784,9 +927,7 @@ nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection,
nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.",
nm_connection_get_id (priv->connection));
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_FAILED,
- NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID);
+ _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE);
}
#define LOG_INVALID_ARG(property) \
@@ -880,11 +1021,8 @@ nm_vpn_connection_config_get (DBusGProxy *proxy,
nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.",
nm_connection_get_id (priv->connection));
- if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) {
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
- NM_VPN_CONNECTION_STATE_REASON_NONE);
- }
+ if (priv->vpn_state == STATE_CONNECT)
+ _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
if (!process_generic_config (connection, config_hash))
return;
@@ -937,11 +1075,8 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
GValue *val;
int i;
- if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) {
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
- NM_VPN_CONNECTION_STATE_REASON_NONE);
- }
+ if (priv->vpn_state == STATE_CONNECT)
+ _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
if (priv->has_ip4) {
nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.",
@@ -1096,11 +1231,8 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
nm_log_info (LOGD_VPN, "VPN connection '%s' (IP6 Config Get) reply received.",
nm_connection_get_id (priv->connection));
- if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) {
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
- NM_VPN_CONNECTION_STATE_REASON_NONE);
- }
+ if (priv->vpn_state == STATE_CONNECT)
+ _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
if (g_hash_table_size (config_hash) == 0) {
priv->has_ip6 = FALSE;
@@ -1233,19 +1365,15 @@ connect_timeout_cb (gpointer user_data)
{
NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
- NMVPNConnectionState state;
priv->connect_timeout = 0;
/* Cancel activation if it's taken too long */
- state = nm_vpn_connection_get_vpn_state (connection);
- if (state == NM_VPN_CONNECTION_STATE_CONNECT ||
- state == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) {
+ if (priv->vpn_state == STATE_CONNECT ||
+ priv->vpn_state == STATE_IP_CONFIG_GET) {
nm_log_warn (LOGD_VPN, "VPN connection '%s' connect timeout exceeded.",
nm_connection_get_id (priv->connection));
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_FAILED,
- NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT);
+ _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
}
return FALSE;
@@ -1282,9 +1410,7 @@ connect_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.",
nm_connection_get_id (priv->connection), err->message);
g_error_free (err);
- nm_vpn_connection_set_vpn_state (self,
- NM_VPN_CONNECTION_STATE_FAILED,
- NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
+ _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
}
static void
@@ -1313,9 +1439,7 @@ connect_interactive_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data
nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect interactively: '%s'.",
nm_connection_get_id (priv->connection), err->message);
g_error_free (err);
- nm_vpn_connection_set_vpn_state (self,
- NM_VPN_CONNECTION_STATE_FAILED,
- NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
+ _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
}
}
@@ -1355,9 +1479,9 @@ really_activate (NMVPNConnection *connection, const char *username)
GHashTable *details;
g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
- g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_NEED_AUTH);
priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+ g_return_if_fail (priv->vpn_state == STATE_NEED_AUTH);
dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID);
@@ -1410,9 +1534,7 @@ really_activate (NMVPNConnection *connection, const char *username)
g_object_unref (agent_mgr);
g_hash_table_destroy (details);
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_CONNECT,
- NM_VPN_CONNECTION_STATE_REASON_NONE);
+ _set_vpn_state (connection, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
}
void
@@ -1422,10 +1544,11 @@ nm_vpn_connection_activate (NMVPNConnection *connection)
DBusGConnection *bus;
g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
- g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_PREPARE);
priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+ _set_vpn_state (connection, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+
bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
priv->proxy = dbus_g_proxy_new_for_name (bus,
nm_vpn_connection_get_service (connection),
@@ -1450,9 +1573,7 @@ nm_vpn_connection_activate (NMVPNConnection *connection)
G_CALLBACK (plugin_interactive_secrets_required),
connection, NULL);
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_NEED_AUTH,
- NM_VPN_CONNECTION_STATE_REASON_NONE);
+ _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
/* Kick off the secrets requests; first we get existing system secrets
* and ask the plugin if these are sufficient, next we get all existing
@@ -1475,7 +1596,7 @@ nm_vpn_connection_get_vpn_state (NMVPNConnection *connection)
{
g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NM_VPN_CONNECTION_STATE_UNKNOWN);
- return NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state;
+ return _state_to_nm_vpn_state (NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state);
}
const char *
@@ -1535,25 +1656,31 @@ nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection)
}
void
-nm_vpn_connection_fail (NMVPNConnection *connection,
- NMVPNConnectionStateReason reason)
+nm_vpn_connection_disconnect (NMVPNConnection *connection,
+ NMVPNConnectionStateReason reason,
+ gboolean quitting)
{
g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_FAILED,
- reason);
+ _set_vpn_state (connection, STATE_DISCONNECTED, reason, quitting);
}
-void
-nm_vpn_connection_disconnect (NMVPNConnection *connection,
- NMVPNConnectionStateReason reason)
+gboolean
+nm_vpn_connection_deactivate (NMVPNConnection *connection,
+ NMVPNConnectionStateReason reason,
+ gboolean quitting)
{
- g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+ NMVPNConnectionPrivate *priv;
+ gboolean success = FALSE;
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_DISCONNECTED,
- reason);
+ g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), FALSE);
+
+ priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+ if (priv->vpn_state > STATE_UNKNOWN && priv->vpn_state <= STATE_DEACTIVATING) {
+ _set_vpn_state (connection, STATE_DEACTIVATING, reason, quitting);
+ success = TRUE;
+ }
+ return success;
}
/******************************************************************************/
@@ -1576,7 +1703,7 @@ plugin_need_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_dat
priv->secrets_idx + 1,
g_quark_to_string (error->domain),
error->message);
- nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+ _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
g_error_free (error);
return;
}
@@ -1588,7 +1715,7 @@ plugin_need_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_dat
nm_log_err (LOGD_VPN, "(%s/%s) final secrets request failed to provide sufficient secrets",
nm_connection_get_uuid (priv->connection),
nm_connection_get_id (priv->connection));
- nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+ _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
} else {
nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required",
nm_connection_get_uuid (priv->connection),
@@ -1620,7 +1747,7 @@ plugin_new_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data
nm_connection_get_id (priv->connection),
g_quark_to_string (error->domain),
error->message);
- nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+ _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
g_error_free (error);
}
}
@@ -1645,7 +1772,7 @@ get_secrets_cb (NMSettingsConnection *connection,
if (error) {
nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s",
priv->secrets_idx + 1, error->code, error->message);
- nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+ _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
} else {
/* Cache the username for later */
if (agent_username) {
@@ -1729,7 +1856,7 @@ get_secrets (NMVPNConnection *self,
nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s",
priv->secrets_idx + 1, error->code, error->message);
}
- nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+ _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
g_clear_error (&error);
}
}
@@ -1749,13 +1876,11 @@ plugin_interactive_secrets_required (DBusGProxy *proxy,
nm_log_info (LOGD_VPN, "VPN plugin requested secrets; state %s (%d)",
vpn_state_to_string (priv->vpn_state), priv->vpn_state);
- g_return_if_fail (priv->vpn_state == NM_VPN_CONNECTION_STATE_CONNECT ||
- priv->vpn_state == NM_VPN_CONNECTION_STATE_NEED_AUTH);
+ g_return_if_fail (priv->vpn_state == STATE_CONNECT ||
+ priv->vpn_state == STATE_NEED_AUTH);
priv->secrets_idx = SECRETS_REQ_INTERACTIVE;
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_NEED_AUTH,
- NM_VPN_CONNECTION_STATE_REASON_NONE);
+ _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
/* Copy hints and add message to the end */
hints = g_malloc0 (sizeof (char *) * (secrets_len + 2));
@@ -1775,7 +1900,7 @@ nm_vpn_connection_init (NMVPNConnection *self)
{
NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
- priv->vpn_state = NM_VPN_CONNECTION_STATE_PREPARE;
+ priv->vpn_state = STATE_WAITING;
priv->secrets_idx = SECRETS_REQ_SYSTEM;
}
@@ -1795,38 +1920,28 @@ dispose (GObject *object)
{
NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
- if (priv->disposed) {
- G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
- return;
- }
- priv->disposed = TRUE;
-
- if (priv->connect_hash)
+ if (priv->connect_hash) {
g_hash_table_destroy (priv->connect_hash);
+ priv->connect_hash = NULL;
+ }
- if (priv->ip6_internal_gw)
- g_free (priv->ip6_internal_gw);
- if (priv->ip6_external_gw)
- g_free (priv->ip6_external_gw);
-
- if (priv->ip4_config)
- g_object_unref (priv->ip4_config);
- if (priv->ip6_config)
- g_object_unref (priv->ip6_config);
-
- if (priv->connect_timeout)
+ if (priv->connect_timeout) {
g_source_remove (priv->connect_timeout);
+ priv->connect_timeout = 0;
+ }
- if (priv->proxy)
- g_object_unref (priv->proxy);
+ dispatcher_cleanup (NM_VPN_CONNECTION (object));
if (priv->secrets_id) {
nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection),
priv->secrets_id);
+ priv->secrets_id = 0;
}
+ g_clear_object (&priv->ip4_config);
+ g_clear_object (&priv->ip6_config);
+ g_clear_object (&priv->proxy);
g_clear_object (&priv->connection);
- g_free (priv->username);
G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
}
@@ -1838,10 +1953,19 @@ finalize (GObject *object)
g_free (priv->banner);
g_free (priv->ip_iface);
+ g_free (priv->username);
+ g_free (priv->ip6_internal_gw);
+ g_free (priv->ip6_external_gw);
G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object);
}
+static gboolean
+ip_config_valid (VpnState state)
+{
+ return (state == STATE_PRE_UP || state == STATE_ACTIVATED);
+}
+
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
@@ -1851,19 +1975,19 @@ get_property (GObject *object, guint prop_id,
switch (prop_id) {
case PROP_VPN_STATE:
- g_value_set_uint (value, priv->vpn_state);
+ g_value_set_uint (value, _state_to_nm_vpn_state (priv->vpn_state));
break;
case PROP_BANNER:
g_value_set_string (value, priv->banner ? priv->banner : "");
break;
case PROP_IP4_CONFIG:
- if (priv->vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED && priv->ip4_config)
+ if (ip_config_valid (priv->vpn_state) && priv->ip4_config)
g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config));
else
g_value_set_boxed (value, "/");
break;
case PROP_IP6_CONFIG:
- if (priv->vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED && priv->ip6_config)
+ if (ip_config_valid (priv->vpn_state) && priv->ip6_config)
g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config));
else
g_value_set_boxed (value, "/");
diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h
index b98dcbe629..c9c88cda14 100644
--- a/src/vpn-manager/nm-vpn-connection.h
+++ b/src/vpn-manager/nm-vpn-connection.h
@@ -74,10 +74,14 @@ void nm_vpn_connection_activate (NMVPNConnection *connect
NMConnection * nm_vpn_connection_get_connection (NMVPNConnection *connection);
NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *connection);
const char * nm_vpn_connection_get_banner (NMVPNConnection *connection);
-void nm_vpn_connection_fail (NMVPNConnection *connection,
- NMVPNConnectionStateReason reason);
+
+gboolean nm_vpn_connection_deactivate (NMVPNConnection *connection,
+ NMVPNConnectionStateReason reason,
+ gboolean quitting);
void nm_vpn_connection_disconnect (NMVPNConnection *connection,
- NMVPNConnectionStateReason reason);
+ NMVPNConnectionStateReason reason,
+ gboolean quitting);
+
NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection);
NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection);
const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection);
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
index 6c63edc1e7..5550d4d6c6 100644
--- a/src/vpn-manager/nm-vpn-manager.c
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -38,8 +38,6 @@ G_DEFINE_TYPE (NMVPNManager, nm_vpn_manager, G_TYPE_OBJECT)
#define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVPNManagerPrivate))
typedef struct {
- gboolean disposed;
-
GHashTable *services;
GFileMonitor *monitor;
guint monitor_id;
@@ -77,39 +75,11 @@ get_service_by_namefile (NMVPNManager *self, const char *namefile)
return NULL;
}
-static NMVPNConnection *
-find_active_vpn_connection (NMVPNManager *self, NMConnection *connection)
-{
- NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
- GHashTableIter iter;
- gpointer data;
- const GSList *active, *aiter;
- NMVPNConnection *found = NULL;
-
- g_return_val_if_fail (connection, NULL);
- g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
- g_hash_table_iter_init (&iter, priv->services);
- while (g_hash_table_iter_next (&iter, NULL, &data) && (found == NULL)) {
- active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
- for (aiter = active; aiter; aiter = g_slist_next (aiter)) {
- NMVPNConnection *vpn = NM_VPN_CONNECTION (aiter->data);
-
- if (nm_vpn_connection_get_connection (vpn) == connection) {
- found = vpn;
- break;
- }
- }
- }
- return found;
-}
-
gboolean
nm_vpn_manager_activate_connection (NMVPNManager *manager,
NMVPNConnection *vpn,
GError **error)
{
- NMVPNConnection *existing = NULL;
NMConnection *connection;
NMSettingVPN *s_vpn;
NMVPNService *service;
@@ -145,11 +115,6 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
return FALSE;
}
- existing = find_active_vpn_connection (manager,
- nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (vpn)));
- if (existing)
- nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
-
return nm_vpn_service_activate (service, vpn, error);
}
@@ -158,51 +123,7 @@ nm_vpn_manager_deactivate_connection (NMVPNManager *self,
NMVPNConnection *connection,
NMVPNConnectionStateReason reason)
{
- NMVPNManagerPrivate *priv;
- GHashTableIter iter;
- gpointer data;
- const GSList *active, *aiter;
- gboolean success = FALSE;
-
- g_return_val_if_fail (self, FALSE);
- g_return_val_if_fail (NM_IS_VPN_MANAGER (self), FALSE);
- g_return_val_if_fail (connection != NULL, FALSE);
-
- priv = NM_VPN_MANAGER_GET_PRIVATE (self);
- g_hash_table_iter_init (&iter, priv->services);
- while (g_hash_table_iter_next (&iter, NULL, &data) && (success == FALSE)) {
- active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
- for (aiter = active; aiter; aiter = g_slist_next (aiter)) {
- NMVPNConnection *candidate = aiter->data;
-
- if (connection == candidate) {
- nm_vpn_connection_disconnect (connection, reason);
- success = TRUE;
- break;
- }
- }
- }
-
- return success;
-}
-
-static char *
-service_name_from_file (const char *path)
-{
- GKeyFile *kf = NULL;
- char *service_name = NULL;
-
- g_return_val_if_fail (g_path_is_absolute (path), NULL);
-
- if (!g_str_has_suffix (path, ".name"))
- return NULL;
-
- kf = g_key_file_new ();
- if (g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
- service_name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
-
- g_key_file_free (kf);
- return service_name;
+ return nm_vpn_connection_deactivate (connection, reason, FALSE);
}
static void
@@ -210,34 +131,32 @@ try_add_service (NMVPNManager *self, const char *namefile)
{
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
NMVPNService *service = NULL;
+ GHashTableIter iter;
GError *error = NULL;
const char *service_name;
- char *tmp;
g_return_if_fail (g_path_is_absolute (namefile));
/* Make sure we don't add dupes */
- tmp = service_name_from_file (namefile);
- if (tmp)
- service = g_hash_table_lookup (priv->services, tmp);
- g_free (tmp);
- if (service)
- return;
+ g_hash_table_iter_init (&iter, priv->services);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) {
+ if (g_strcmp0 (namefile, nm_vpn_service_get_name_file (service)) == 0)
+ return;
+ }
- /* New service, add it */
+ /* New service */
service = nm_vpn_service_new (namefile, &error);
- if (!service) {
+ if (service) {
+ service_name = nm_vpn_service_get_dbus_service (service);
+ g_hash_table_insert (priv->services, (char *) service_name, service);
+ nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name);
+ } else {
nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s",
namefile,
error ? error->code : -1,
error && error->message ? error->message : "(unknown)");
g_clear_error (&error);
- return;
}
-
- service_name = nm_vpn_service_get_dbus_service (service);
- g_hash_table_insert (priv->services, (char *) service_name, service);
- nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name);
}
static void
@@ -267,7 +186,7 @@ vpn_dir_changed (GFileMonitor *monitor,
const char *service_name = nm_vpn_service_get_dbus_service (service);
/* Stop active VPN connections and destroy the service */
- nm_vpn_service_connections_stop (service, TRUE,
+ nm_vpn_service_stop_connections (service, FALSE,
NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name);
g_hash_table_remove (priv->services, service_name);
@@ -339,21 +258,36 @@ nm_vpn_manager_init (NMVPNManager *self)
}
static void
+stop_all_services (NMVPNManager *self)
+{
+ NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ GHashTableIter iter;
+ NMVPNService *service;
+
+ g_hash_table_iter_init (&iter, priv->services);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) {
+ nm_vpn_service_stop_connections (service,
+ TRUE,
+ NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+ }
+}
+
+static void
dispose (GObject *object)
{
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
- if (!priv->disposed) {
- priv->disposed = TRUE;
-
- if (priv->monitor) {
- if (priv->monitor_id)
- g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
- g_file_monitor_cancel (priv->monitor);
- g_object_unref (priv->monitor);
- }
+ if (priv->monitor) {
+ if (priv->monitor_id)
+ g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
+ g_file_monitor_cancel (priv->monitor);
+ g_clear_object (&priv->monitor);
+ }
+ if (priv->services) {
+ stop_all_services (NM_VPN_MANAGER (object));
g_hash_table_destroy (priv->services);
+ priv->services = NULL;
}
G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object);
diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c
index 8ecd9a1176..9eb6b96824 100644
--- a/src/vpn-manager/nm-vpn-service.c
+++ b/src/vpn-manager/nm-vpn-service.c
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2005 - 2012 Red Hat, Inc.
+ * Copyright (C) 2005 - 2014 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
@@ -38,30 +38,30 @@
G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT)
typedef struct {
- gboolean disposed;
-
- NMDBusManager *dbus_mgr;
char *name;
char *dbus_service;
char *program;
char *namefile;
- GPid pid;
- GSList *connections;
+ NMVPNConnection *active;
+ GSList *pending;
+
guint start_timeout;
- guint quit_timeout;
- guint child_watch;
- gulong name_owner_id;
+ gboolean service_running;
} NMVPNServicePrivate;
#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVPNServicePrivate))
+#define VPN_CONNECTION_GROUP "VPN Connection"
+
+static gboolean start_pending_vpn (NMVPNService *self);
+
NMVPNService *
nm_vpn_service_new (const char *namefile, GError **error)
{
- NMVPNService *self = NULL;
+ NMVPNService *self;
+ NMVPNServicePrivate *priv;
GKeyFile *kf;
- char *dbus_service = NULL, *program = NULL, *name = NULL;
g_return_val_if_fail (namefile != NULL, NULL);
g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
@@ -72,36 +72,31 @@ nm_vpn_service_new (const char *namefile, GError **error)
return NULL;
}
- dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
- if (!dbus_service) {
- g_set_error (error, 0, 0, "VPN service file %s had no 'service' key", namefile);
- goto out;
- }
+ self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
+ priv = NM_VPN_SERVICE_GET_PRIVATE (self);
+ priv->namefile = g_strdup (namefile);
- program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", NULL);
- if (!program) {
- g_set_error (error, 0, 0, "VPN service file %s had no 'program' key", namefile);
- goto out;
- }
+ priv->dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", error);
+ if (!priv->dbus_service)
+ goto error;
- name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", NULL);
- if (!name) {
- g_set_error (error, 0, 0, "VPN service file %s had no 'name' key", namefile);
- goto out;
- }
+ priv->program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", error);
+ if (!priv->program)
+ goto error;
- self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
- NM_VPN_SERVICE_GET_PRIVATE (self)->name = g_strdup (name);
- NM_VPN_SERVICE_GET_PRIVATE (self)->dbus_service = g_strdup (dbus_service);
- NM_VPN_SERVICE_GET_PRIVATE (self)->program = g_strdup (program);
- NM_VPN_SERVICE_GET_PRIVATE (self)->namefile = g_strdup (namefile);
+ priv->name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", error);
+ if (!priv->name)
+ goto error;
+
+ priv->service_running = nm_dbus_manager_name_has_owner (nm_dbus_manager_get (), priv->dbus_service);
- out:
g_key_file_free (kf);
- g_free (dbus_service);
- g_free (program);
- g_free (name);
return self;
+
+error:
+ g_object_unref (self);
+ g_key_file_free (kf);
+ return NULL;
}
const char *
@@ -120,46 +115,60 @@ nm_vpn_service_get_name_file (NMVPNService *service)
return NM_VPN_SERVICE_GET_PRIVATE (service)->namefile;
}
+static void
+connection_vpn_state_changed (NMVPNConnection *connection,
+ NMVPNConnectionState new_state,
+ NMVPNConnectionState old_state,
+ NMVPNConnectionStateReason reason,
+ gpointer user_data)
+{
+ NMVPNService *self = NM_VPN_SERVICE (user_data);
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
+
+ if (new_state == NM_VPN_CONNECTION_STATE_FAILED ||
+ new_state == NM_VPN_CONNECTION_STATE_DISCONNECTED) {
+ g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_vpn_state_changed), self);
+ if (connection == priv->active) {
+ priv->active = NULL;
+ start_pending_vpn (self);
+ } else
+ priv->pending = g_slist_remove (priv->pending, connection);
+ g_object_unref (connection);
+ }
+}
+
void
-nm_vpn_service_connections_stop (NMVPNService *service,
- gboolean fail,
+nm_vpn_service_stop_connections (NMVPNService *service,
+ gboolean quitting,
NMVPNConnectionStateReason reason)
{
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
- GSList *iter, *copy;
+ GSList *iter;
- /* Copy because stopping the connection may remove it from the list
- * in the NMVPNService objects' VPN connection state handler.
+ /* Just add priv->active to the beginning of priv->pending,
+ * since we're going to clear priv->pending immediately anyway.
*/
- copy = g_slist_copy (priv->connections);
- for (iter = copy; iter; iter = iter->next) {
- if (fail)
- nm_vpn_connection_fail (NM_VPN_CONNECTION (iter->data), reason);
- else
- nm_vpn_connection_disconnect (NM_VPN_CONNECTION (iter->data), reason);
+ if (priv->active) {
+ priv->pending = g_slist_prepend (priv->pending, priv->active);
+ priv->active = NULL;
}
- g_slist_free (copy);
-}
-static void
-clear_quit_timeout (NMVPNService *self)
-{
- NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
+ for (iter = priv->pending; iter; iter = iter->next) {
+ NMVPNConnection *vpn = NM_VPN_CONNECTION (iter->data);
- if (priv->quit_timeout) {
- g_source_remove (priv->quit_timeout);
- priv->quit_timeout = 0;
+ g_signal_handlers_disconnect_by_func (vpn, G_CALLBACK (connection_vpn_state_changed), service);
+ if (quitting) {
+ /* Deactivate to allow pre-down before disconnecting */
+ nm_vpn_connection_deactivate (vpn, reason, quitting);
+ }
+ nm_vpn_connection_disconnect (vpn, reason, quitting);
+ g_object_unref (vpn);
}
+ g_clear_pointer (&priv->pending, g_slist_free);
}
-/*
- * nm_vpn_service_child_setup
- *
- * Set the process group ID of the newly forked process
- *
- */
static void
-nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED)
+_daemon_setup (gpointer user_data G_GNUC_UNUSED)
{
/* We are in the child process at this point */
pid_t pid = getpid ();
@@ -172,73 +181,37 @@ nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED)
nm_unblock_posix_signals (NULL);
}
-static void
-vpn_service_watch_cb (GPid pid, gint status, gpointer user_data)
-{
- NMVPNService *service = NM_VPN_SERVICE (user_data);
- NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
-
- if (WIFEXITED (status)) {
- guint err = WEXITSTATUS (status);
-
- if (err != 0) {
- nm_log_warn (LOGD_VPN, "VPN service '%s' exited with error: %d",
- priv->name, WSTOPSIG (status));
- }
- } else if (WIFSTOPPED (status)) {
- nm_log_warn (LOGD_VPN, "VPN service '%s' stopped unexpectedly with signal %d",
- priv->name, WSTOPSIG (status));
- } else if (WIFSIGNALED (status)) {
- nm_log_warn (LOGD_VPN, "VPN service '%s' died with signal %d",
- priv->name, WTERMSIG (status));
- } else {
- nm_log_warn (LOGD_VPN, "VPN service '%s' died from an unknown cause",
- priv->name);
- }
-
- priv->pid = 0;
- priv->child_watch = 0;
- clear_quit_timeout (service);
-
- nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
-}
-
static gboolean
-nm_vpn_service_timeout (gpointer data)
+_daemon_exec_timeout (gpointer data)
{
NMVPNService *self = NM_VPN_SERVICE (data);
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", priv->name);
priv->start_timeout = 0;
- nm_vpn_service_connections_stop (self, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
- return FALSE;
+ nm_vpn_service_stop_connections (self, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
+ return G_SOURCE_REMOVE;
}
static gboolean
nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
{
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+ GPid pid;
char *vpn_argv[2];
gboolean success = FALSE;
GError *spawn_error = NULL;
g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE);
- g_return_val_if_fail (error != NULL, FALSE);
- g_return_val_if_fail (*error == NULL, FALSE);
vpn_argv[0] = priv->program;
vpn_argv[1] = NULL;
- success = g_spawn_async (NULL, vpn_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
- nm_vpn_service_child_setup, NULL, &priv->pid,
- &spawn_error);
+ success = g_spawn_async (NULL, vpn_argv, NULL, 0, _daemon_setup, NULL, &pid, &spawn_error);
if (success) {
nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d",
- priv->name, priv->dbus_service, priv->pid);
-
- priv->child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service);
- priv->start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service);
+ priv->name, priv->dbus_service, pid);
+ priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, service);
} else {
nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.",
priv->name,
@@ -249,7 +222,7 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED,
"%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error");
- nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
+ nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
if (spawn_error)
g_error_free (spawn_error);
}
@@ -258,69 +231,42 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
}
static gboolean
-ensure_killed (gpointer data)
+start_active_vpn (NMVPNService *self, GError **error)
{
- int pid = GPOINTER_TO_INT (data);
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
- if (kill (pid, 0) == 0)
- kill (pid, SIGKILL);
+ if (!priv->active)
+ return TRUE;
- /* ensure the child is reaped */
- nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid);
- waitpid (pid, NULL, 0);
- nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid);
+ if (priv->service_running) {
+ /* Just activate the VPN */
+ nm_vpn_connection_activate (priv->active);
+ return TRUE;
+ } else if (priv->start_timeout == 0) {
+ /* VPN service not running, start it */
+ nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name);
+ return nm_vpn_service_daemon_exec (self, error);
+ }
- return FALSE;
+ /* Already started VPN service, waiting for it to appear on D-Bus */
+ return TRUE;
}
static gboolean
-service_quit (gpointer user_data)
+start_pending_vpn (NMVPNService *self)
{
- NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data);
-
- if (priv->pid) {
- if (kill (priv->pid, SIGTERM) == 0)
- g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
- else {
- kill (priv->pid, SIGKILL);
-
- /* ensure the child is reaped */
- nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid);
- waitpid (priv->pid, NULL, 0);
- nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid);
- }
- priv->pid = 0;
- }
- priv->quit_timeout = 0;
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
- return FALSE;
-}
+ g_assert (priv->active == NULL);
-static void
-connection_vpn_state_changed (NMVPNConnection *connection,
- NMVPNConnectionState new_state,
- NMVPNConnectionState old_state,
- NMVPNConnectionStateReason reason,
- gpointer user_data)
-{
- NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data);
+ if (!priv->pending)
+ return TRUE;
- switch (new_state) {
- case NM_VPN_CONNECTION_STATE_FAILED:
- case NM_VPN_CONNECTION_STATE_DISCONNECTED:
- /* Remove the connection from our list */
- priv->connections = g_slist_remove (priv->connections, connection);
- g_object_unref (connection);
+ /* Make next VPN active */
+ priv->active = g_slist_nth_data (priv->pending, 0);
+ priv->pending = g_slist_remove (priv->pending, priv->active);
- if (priv->connections == NULL) {
- /* Tell the service to quit in a few seconds */
- if (!priv->quit_timeout)
- priv->quit_timeout = g_timeout_add_seconds (5, service_quit, user_data);
- }
- break;
- default:
- break;
- }
+ return start_active_vpn (self, NULL);
}
gboolean
@@ -337,45 +283,36 @@ nm_vpn_service_activate (NMVPNService *service,
priv = NM_VPN_SERVICE_GET_PRIVATE (service);
- clear_quit_timeout (service);
-
g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
G_CALLBACK (connection_vpn_state_changed),
service);
- priv->connections = g_slist_prepend (priv->connections, g_object_ref (vpn));
+ /* Queue up the new VPN connection */
+ priv->pending = g_slist_append (priv->pending, g_object_ref (vpn));
- if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service))
- nm_vpn_connection_activate (vpn);
- else if (priv->start_timeout == 0) {
- nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name);
- if (!nm_vpn_service_daemon_exec (service, error))
- return FALSE;
+ /* Tell the active VPN to deactivate and wait for it to quit before we
+ * start the next VPN. The just-queued VPN will then be started from
+ * connection_vpn_state_changed().
+ */
+ if (priv->active) {
+ nm_vpn_connection_deactivate (priv->active, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE);
+ return TRUE;
}
- return TRUE;
-}
-
-const GSList *
-nm_vpn_service_get_active_connections (NMVPNService *service)
-{
- g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
-
- return NM_VPN_SERVICE_GET_PRIVATE (service)->connections;
+ /* Otherwise start the next VPN */
+ return start_pending_vpn (service);
}
static void
-nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
- const char *name,
- const char *old,
- const char *new,
- gpointer user_data)
+_name_owner_changed (NMDBusManager *mgr,
+ const char *name,
+ const char *old,
+ const char *new,
+ gpointer user_data)
{
NMVPNService *service = NM_VPN_SERVICE (user_data);
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
- gboolean old_owner_good;
- gboolean new_owner_good;
- GSList *iter;
+ gboolean old_owner_good, new_owner_good, success;
if (strcmp (name, priv->dbus_service))
return;
@@ -386,20 +323,21 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
priv->start_timeout = 0;
}
- old_owner_good = (old && (strlen (old) > 0));
- new_owner_good = (new && (strlen (new) > 0));
+ old_owner_good = (old && old[0]);
+ new_owner_good = (new && new[0]);
if (!old_owner_good && new_owner_good) {
- /* service just appeared */
+ /* service appeared */
+ priv->service_running = TRUE;
nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name);
- clear_quit_timeout (service);
-
- for (iter = priv->connections; iter; iter = iter->next)
- nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data));
+ /* Expect success because the VPN service has already appeared */
+ success = start_active_vpn (service, NULL);
+ g_warn_if_fail (success);
} else if (old_owner_good && !new_owner_good) {
/* service went away */
+ priv->service_running = FALSE;
nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", priv->name);
- nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+ nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
}
}
@@ -408,13 +346,10 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
static void
nm_vpn_service_init (NMVPNService *self)
{
- NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
-
- priv->dbus_mgr = nm_dbus_manager_get ();
- priv->name_owner_id = g_signal_connect (priv->dbus_mgr,
- NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
- G_CALLBACK (nm_vpn_service_name_owner_changed),
- self);
+ g_signal_connect (nm_dbus_manager_get (),
+ NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
+ G_CALLBACK (_name_owner_changed),
+ self);
}
static void
@@ -423,34 +358,33 @@ dispose (GObject *object)
NMVPNService *self = NM_VPN_SERVICE (object);
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
- if (priv->disposed)
- goto out;
- priv->disposed = TRUE;
-
- if (priv->start_timeout)
+ if (priv->start_timeout) {
g_source_remove (priv->start_timeout);
+ priv->start_timeout = 0;
+ }
- nm_vpn_service_connections_stop (NM_VPN_SERVICE (object),
- FALSE,
- NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
-
- g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
+ /* VPNService owner is required to stop connections before releasing */
+ g_assert (priv->active == NULL);
+ g_assert (priv->pending == NULL);
- if (priv->child_watch)
- g_source_remove (priv->child_watch);
+ g_signal_handlers_disconnect_by_func (nm_dbus_manager_get (),
+ G_CALLBACK (_name_owner_changed),
+ self);
- clear_quit_timeout (self);
- service_quit (self);
+ G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
+}
- priv->dbus_mgr = NULL;
+static void
+finalize (GObject *object)
+{
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object);
g_free (priv->name);
g_free (priv->dbus_service);
g_free (priv->program);
g_free (priv->namefile);
-out:
- G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
+ G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object);
}
static void
@@ -462,4 +396,5 @@ nm_vpn_service_class_init (NMVPNServiceClass *service_class)
/* virtual methods */
object_class->dispose = dispose;
+ object_class->finalize = finalize;
}
diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h
index 2ad79a0d6b..4545d1f07d 100644
--- a/src/vpn-manager/nm-vpn-service.h
+++ b/src/vpn-manager/nm-vpn-service.h
@@ -34,8 +34,6 @@
#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_SERVICE))
#define NM_VPN_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVPNServiceClass))
-#define VPN_CONNECTION_GROUP "VPN Connection"
-
typedef struct {
GObject parent;
} NMVPNService;
@@ -58,10 +56,8 @@ gboolean nm_vpn_service_activate (NMVPNService *service,
NMVPNConnection *vpn,
GError **error);
-const GSList *nm_vpn_service_get_active_connections (NMVPNService *service);
-
-void nm_vpn_service_connections_stop (NMVPNService *service,
- gboolean fail,
+void nm_vpn_service_stop_connections (NMVPNService *service,
+ gboolean quitting,
NMVPNConnectionStateReason reason);
#endif /* NM_VPN_VPN_SERVICE_H */