NetworkManager/libnm-glib/nm-client.c
Dan Williams 9c9ae6abff 2007-08-16 Dan Williams <dcbw@redhat.com>
* libnm-glib/nm-client.c
		- (nm_client_init): create VPN connections hash table with key free
			function
		- (proxy_vpn_connection_added): VPN connections hash table key should
			be a duplicated value, not the same memory address as the VPN
			connection name.  This is because the VPN connection name could
			potentially be freed and set to something else during the lifetime
			of the NMVPNConnection object.



git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@2706 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
2007-08-16 22:54:22 +00:00

785 lines
21 KiB
C

#include <dbus/dbus-glib.h>
#include <string.h>
#include "nm-client.h"
#include "nm-device-802-3-ethernet.h"
#include "nm-device-802-11-wireless.h"
#include "nm-device-private.h"
#include "nm-marshal.h"
#include "nm-client-bindings.h"
G_DEFINE_TYPE (NMClient, nm_client, NM_TYPE_OBJECT)
#define NM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CLIENT, NMClientPrivate))
typedef struct {
DBusGProxy *client_proxy;
DBusGProxy *bus_proxy;
gboolean manager_running;
NMState state;
gboolean have_device_list;
GHashTable *devices;
DBusGProxy *vpn_proxy;
NMVPNConnectionState vpn_state;
gboolean have_vpn_connections;
GHashTable *vpn_connections;
} NMClientPrivate;
enum {
MANAGER_RUNNING,
DEVICE_ADDED,
DEVICE_REMOVED,
STATE_CHANGE,
VPN_CONNECTION_ADDED,
VPN_CONNECTION_REMOVED,
VPN_STATE_CHANGE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
static void proxy_name_owner_changed (DBusGProxy *proxy,
const char *name,
const char *old_owner,
const char *new_owner,
gpointer user_data);
static void client_state_change_proxy (DBusGProxy *proxy, guint state, gpointer user_data);
static void client_device_added_proxy (DBusGProxy *proxy, char *path, gpointer user_data);
static void client_device_removed_proxy (DBusGProxy *proxy, char *path, gpointer user_data);
static void setup_vpn_proxy (NMClient *client, DBusGConnection *connection);
static void clear_vpn_connections (NMClient * client);
static void
nm_client_init (NMClient *client)
{
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
priv->state = NM_STATE_UNKNOWN;
priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_object_unref);
priv->vpn_connections = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
NULL);
priv->vpn_state = NM_VPN_CONNECTION_STATE_UNKNOWN;
}
static GObject*
constructor (GType type,
guint n_construct_params,
GObjectConstructParam *construct_params)
{
NMObject *object;
DBusGConnection *connection;
NMClientPrivate *priv;
GError *err = NULL;
object = (NMObject *) G_OBJECT_CLASS (nm_client_parent_class)->constructor (type,
n_construct_params,
construct_params);
if (!object)
return NULL;
priv = NM_CLIENT_GET_PRIVATE (object);
connection = nm_object_get_connection (object);
priv->client_proxy = dbus_g_proxy_new_for_name (connection,
NM_DBUS_SERVICE,
nm_object_get_path (object),
NM_DBUS_INTERFACE);
dbus_g_proxy_add_signal (priv->client_proxy, "StateChange", G_TYPE_UINT, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->client_proxy,
"StateChange",
G_CALLBACK (client_state_change_proxy),
object,
NULL);
dbus_g_proxy_add_signal (priv->client_proxy, "DeviceAdded", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->client_proxy,
"DeviceAdded",
G_CALLBACK (client_device_added_proxy),
object,
NULL);
dbus_g_proxy_add_signal (priv->client_proxy, "DeviceRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->client_proxy,
"DeviceRemoved",
G_CALLBACK (client_device_removed_proxy),
object,
NULL);
setup_vpn_proxy (NM_CLIENT (object), connection);
priv->bus_proxy = dbus_g_proxy_new_for_name (connection,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus");
dbus_g_proxy_add_signal (priv->bus_proxy, "NameOwnerChanged",
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->bus_proxy,
"NameOwnerChanged",
G_CALLBACK (proxy_name_owner_changed),
object, NULL);
if (!dbus_g_proxy_call (priv->bus_proxy,
"NameHasOwner", &err,
G_TYPE_STRING, NM_DBUS_SERVICE,
G_TYPE_INVALID,
G_TYPE_BOOLEAN, &priv->manager_running,
G_TYPE_INVALID)) {
g_warning ("Error on NameHasOwner DBUS call: %s", err->message);
g_error_free (err);
}
return G_OBJECT (object);
}
static void
finalize (GObject *object)
{
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object);
g_object_unref (priv->vpn_proxy);
g_object_unref (priv->client_proxy);
g_object_unref (priv->bus_proxy);
g_hash_table_destroy (priv->devices);
g_hash_table_destroy (priv->vpn_connections);
}
static void
manager_running (NMClient *client, gboolean running)
{
if (!running) {
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
priv->state = NM_STATE_UNKNOWN;
g_hash_table_remove_all (priv->devices);
priv->have_device_list = FALSE;
clear_vpn_connections (client);
priv->have_vpn_connections = FALSE;
}
}
static void
nm_client_class_init (NMClientClass *client_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (client_class);
g_type_class_add_private (client_class, sizeof (NMClientPrivate));
/* virtual methods */
object_class->constructor = constructor;
object_class->finalize = finalize;
client_class->manager_running = manager_running;
/* signals */
signals[MANAGER_RUNNING] =
g_signal_new ("manager-running",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMClientClass, manager_running),
NULL, NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1,
G_TYPE_BOOLEAN);
signals[DEVICE_ADDED] =
g_signal_new ("device-added",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMClientClass, device_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
G_TYPE_OBJECT);
signals[DEVICE_REMOVED] =
g_signal_new ("device-removed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMClientClass, device_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
G_TYPE_OBJECT);
signals[STATE_CHANGE] =
g_signal_new ("state-change",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMClientClass, state_change),
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
signals[VPN_CONNECTION_ADDED] =
g_signal_new ("vpn-connection-added",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMClientClass, vpn_connection_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
G_TYPE_OBJECT);
signals[VPN_CONNECTION_REMOVED] =
g_signal_new ("vpn-connection-removed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMClientClass, vpn_connection_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
G_TYPE_OBJECT);
signals[VPN_STATE_CHANGE] =
g_signal_new ("vpn-state-change",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMClientClass, state_change),
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
}
NMClient *
nm_client_new (void)
{
DBusGConnection *connection;
GError *err = NULL;
connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
if (!connection) {
g_warning ("Couldn't connect to system bus: %s", err->message);
g_error_free (err);
return NULL;
}
return (NMClient *) g_object_new (NM_TYPE_CLIENT,
NM_OBJECT_CONNECTION, connection,
NM_OBJECT_PATH, NM_DBUS_PATH,
NULL);
}
static void
proxy_name_owner_changed (DBusGProxy *proxy,
const char *name,
const char *old_owner,
const char *new_owner,
gpointer user_data)
{
if (name && !strcmp (name, NM_DBUS_SERVICE)) {
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (user_data);
gboolean old_good = (old_owner && strlen (old_owner));
gboolean new_good = (new_owner && strlen (new_owner));
gboolean new_running = FALSE;
if (!old_good && new_good)
new_running = TRUE;
else if (old_good && !new_good)
new_running = FALSE;
if (new_running != priv->manager_running) {
priv->manager_running = new_running;
g_signal_emit (NM_CLIENT (user_data),
signals[MANAGER_RUNNING],
0,
priv->manager_running);
}
}
}
static void
client_state_change_proxy (DBusGProxy *proxy, guint state, gpointer user_data)
{
NMClient *client = NM_CLIENT (user_data);
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
if (priv->state != state) {
priv->state = state;
g_signal_emit (client, signals[STATE_CHANGE], 0, state);
}
}
static NMDevice *
get_device (NMClient *client, const char *path, gboolean create_if_not_found)
{
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
NMDevice *device;
device = g_hash_table_lookup (priv->devices, path);
if (!device && create_if_not_found) {
DBusGConnection *connection;
NMDeviceType type;
connection = nm_object_get_connection (NM_OBJECT (client));
type = nm_device_type_for_path (connection, path);
switch (type) {
case DEVICE_TYPE_802_3_ETHERNET:
device = NM_DEVICE (nm_device_802_3_ethernet_new (connection, path));
break;
case DEVICE_TYPE_802_11_WIRELESS:
device = NM_DEVICE (nm_device_802_11_wireless_new (connection, path));
break;
default:
device = nm_device_new (connection, path);
}
if (device)
g_hash_table_insert (priv->devices, g_strdup (path), device);
}
return device;
}
static void
client_device_added_proxy (DBusGProxy *proxy, char *path, gpointer user_data)
{
NMClient *client = NM_CLIENT (user_data);
NMDevice *device;
device = get_device (client, path, TRUE);
if (device)
g_signal_emit (client, signals[DEVICE_ADDED], 0, device);
}
static void
client_device_removed_proxy (DBusGProxy *proxy, char *path, gpointer user_data)
{
NMClient *client = NM_CLIENT (user_data);
NMDevice *device;
device = get_device (client, path, FALSE);
if (device) {
g_signal_emit (client, signals[DEVICE_REMOVED], 0, device);
g_hash_table_remove (NM_CLIENT_GET_PRIVATE (client)->devices, path);
}
}
gboolean
nm_client_manager_is_running (NMClient *client)
{
g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
return NM_CLIENT_GET_PRIVATE (client)->manager_running;
}
static void
devices_to_slist (gpointer key, gpointer value, gpointer user_data)
{
GSList **list = (GSList **) user_data;
*list = g_slist_prepend (*list, value);
}
GSList *
nm_client_get_devices (NMClient *client)
{
NMClientPrivate *priv;
GSList *list = NULL;
GPtrArray *array = NULL;
GError *err = NULL;
g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
priv = NM_CLIENT_GET_PRIVATE (client);
if (priv->have_device_list) {
g_hash_table_foreach (priv->devices, devices_to_slist, &list);
return list;
}
if (!org_freedesktop_NetworkManager_get_devices (priv->client_proxy, &array, &err)) {
g_warning ("Error in get_devices: %s", err->message);
g_error_free (err);
} else {
int i;
for (i = 0; i < array->len; i++) {
NMDevice *device;
device = get_device (client, (const char *) g_ptr_array_index (array, i), TRUE);
if (device)
list = g_slist_append (list, device);
}
g_ptr_array_free (array, TRUE);
priv->have_device_list = TRUE;
}
return list;
}
NMDevice *
nm_client_get_device_by_path (NMClient *client, const char *object_path)
{
GSList *devices;
GSList *iter;
NMDevice *device = NULL;
g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
g_return_val_if_fail (object_path, NULL);
devices = nm_client_get_devices (client);
for (iter = devices; iter; iter = iter->next) {
if (!strcmp (nm_object_get_path (NM_OBJECT (iter->data)), object_path)) {
device = NM_DEVICE (iter->data);
break;
}
}
g_slist_free (devices);
return device;
}
gboolean
nm_client_wireless_get_enabled (NMClient *client)
{
g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
return nm_object_get_boolean_property (NM_OBJECT (client), NM_DBUS_INTERFACE, "WirelessEnabled");
}
void
nm_client_wireless_set_enabled (NMClient *client, gboolean enabled)
{
GValue value = {0,};
g_return_if_fail (NM_IS_CLIENT (client));
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, enabled);
nm_object_set_property (NM_OBJECT (client),
NM_DBUS_INTERFACE,
"WirelessEnabled",
&value);
}
NMState
nm_client_get_state (NMClient *client)
{
NMClientPrivate *priv;
g_return_val_if_fail (NM_IS_CLIENT (client), NM_STATE_UNKNOWN);
priv = NM_CLIENT_GET_PRIVATE (client);
if (priv->state == NM_STATE_UNKNOWN)
priv->state = nm_object_get_uint_property (NM_OBJECT (client), NM_DBUS_INTERFACE, "State");
return priv->state;
}
void
nm_client_sleep (NMClient *client, gboolean sleep)
{
GError *err = NULL;
g_return_if_fail (NM_IS_CLIENT (client));
if (!org_freedesktop_NetworkManager_sleep (NM_CLIENT_GET_PRIVATE (client)->client_proxy, sleep, &err)) {
g_warning ("Error in sleep: %s", err->message);
g_error_free (err);
}
}
/* VPN */
/*
* This "best" state is the summary of all states from all connections and
* available for convenience.
* For the exact state, each connection has it's own state which' changes
* are also signalled.
*/
static NMVPNConnectionState
nm_client_get_best_vpn_state (NMClient *client)
{
GSList *iter, *list;
NMVPNConnectionState state;
NMVPNConnectionState best_state = NM_VPN_CONNECTION_STATE_UNKNOWN;
list = nm_client_get_vpn_connections (client);
for (iter = list; iter; iter = iter->next) {
state = nm_vpn_connection_get_state (NM_VPN_CONNECTION (iter->data));
if (state > best_state && state < NM_VPN_CONNECTION_STATE_FAILED)
best_state = state;
}
g_slist_free (list);
return best_state;
}
static void
proxy_vpn_state_change (DBusGProxy *proxy,
char *connection_name,
NMVPNConnectionState state,
gpointer user_data)
{
NMClient *client = NM_CLIENT (user_data);
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
NMVPNConnection *connection;
NMVPNConnectionState best_state;
connection = nm_client_get_vpn_connection_by_name (client, connection_name);
if (connection)
nm_vpn_connection_set_state (connection, state);
best_state = nm_client_get_best_vpn_state (client);
if (best_state != priv->vpn_state) {
priv->vpn_state = state;
g_signal_emit (client, signals[VPN_STATE_CHANGE], 0, best_state);
}
}
static void
proxy_vpn_connection_added (DBusGProxy *proxy, char *name, gpointer user_data)
{
NMClient *client = NM_CLIENT (user_data);
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
NMVPNConnection *connection;
if (g_hash_table_lookup (priv->vpn_connections, name))
return;
connection = nm_vpn_connection_new (proxy, name);
if (connection == NULL) {
g_log (G_LOG_DOMAIN,
G_LOG_LEVEL_WARNING,
"Warning: out of memory creating NMVPNConnection for '%s'\n",
name);
return;
}
g_hash_table_insert (priv->vpn_connections, g_strdup (name), connection);
g_signal_emit (client, signals[VPN_CONNECTION_ADDED], 0, connection);
}
static void
proxy_vpn_connection_removed (DBusGProxy *proxy, char *name, gpointer user_data)
{
NMClient *client = NM_CLIENT (user_data);
NMVPNConnection *connection;
connection = nm_client_get_vpn_connection_by_name (client, name);
if (connection)
nm_client_remove_vpn_connection (client, connection);
}
static void
proxy_vpn_connection_update (DBusGProxy *proxy, char *name, gpointer user_data)
{
NMClient *client = NM_CLIENT (user_data);
NMVPNConnection *connection;
connection = nm_client_get_vpn_connection_by_name (client, name);
if (connection)
nm_vpn_connection_update (connection);
}
static void
setup_vpn_proxy (NMClient *client, DBusGConnection *connection)
{
DBusGProxy *proxy;
proxy = dbus_g_proxy_new_for_name (connection,
NM_DBUS_SERVICE,
NM_DBUS_PATH_VPN,
NM_DBUS_INTERFACE_VPN);
dbus_g_object_register_marshaller (nm_marshal_VOID__STRING_INT,
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID);
dbus_g_proxy_add_signal (proxy, "VPNConnectionStateChange", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (proxy, "VPNConnectionStateChange",
G_CALLBACK (proxy_vpn_state_change),
client, NULL);
dbus_g_proxy_add_signal (proxy, "VPNConnectionAdded", G_TYPE_STRING, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (proxy, "VPNConnectionAdded",
G_CALLBACK (proxy_vpn_connection_added),
client, NULL);
dbus_g_proxy_add_signal (proxy, "VPNConnectionRemoved", G_TYPE_STRING, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (proxy, "VPNConnectionRemoved",
G_CALLBACK (proxy_vpn_connection_removed),
client, NULL);
dbus_g_proxy_add_signal (proxy, "VPNConnectionUpdate", G_TYPE_STRING, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (proxy, "VPNConnectionUpdate",
G_CALLBACK (proxy_vpn_connection_update),
client, NULL);
NM_CLIENT_GET_PRIVATE (client)->vpn_proxy = proxy;
}
static void
get_connections (NMClient *client)
{
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
char **name;
char **vpn_names = NULL;
GError *err = NULL;
if (!dbus_g_proxy_call (priv->vpn_proxy, "getVPNConnections", &err,
G_TYPE_INVALID,
G_TYPE_STRV, &vpn_names,
G_TYPE_INVALID)) {
g_warning ("Error while getting VPN connections: %s", err->message);
g_error_free (err);
return;
}
for (name = vpn_names; *name; name++)
proxy_vpn_connection_added (priv->vpn_proxy, *name, client);
g_strfreev (vpn_names);
}
static void
signal_one_vpn_connection_removed (gpointer data,
gpointer user_data)
{
NMClient * client = NM_CLIENT (user_data);
NMVPNConnection * connection = NM_VPN_CONNECTION (data);
g_signal_emit (client, signals[VPN_CONNECTION_REMOVED], 0, connection);
}
static void
clear_vpn_connections (NMClient * client)
{
NMClientPrivate * priv;
GSList * list;
g_return_if_fail (NM_IS_CLIENT (client));
priv = NM_CLIENT_GET_PRIVATE (client);
list = nm_client_get_vpn_connections (client);
g_hash_table_remove_all (priv->vpn_connections);
g_slist_foreach (list, signal_one_vpn_connection_removed, client);
g_slist_foreach (list, (GFunc) g_object_unref, NULL);
g_slist_free (list);
}
static void
vpn_connections_to_slist (gpointer key, gpointer value, gpointer user_data)
{
GSList **list = (GSList **) user_data;
*list = g_slist_prepend (*list, value);
}
GSList *
nm_client_get_vpn_connections (NMClient *client)
{
NMClientPrivate *priv;
GSList * list = NULL;
g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
priv = NM_CLIENT_GET_PRIVATE (client);
if (!priv->have_vpn_connections) {
get_connections (client);
priv->have_vpn_connections = TRUE;
}
g_hash_table_foreach (priv->vpn_connections,
vpn_connections_to_slist,
&list);
return list;
}
NMVPNConnection *
nm_client_get_vpn_connection_by_name (NMClient *client, const char *name)
{
NMClientPrivate *priv;
GSList * list;
g_return_if_fail (NM_IS_CLIENT (client));
priv = NM_CLIENT_GET_PRIVATE (client);
/* Ensure list of VPN connections is current */
list = nm_client_get_vpn_connections (client);
g_slist_free (list);
return g_hash_table_lookup (priv->vpn_connections, name);
}
struct find_info {
char * found_key;
NMVPNConnection * connection;
};
static void
find_connection (gpointer key,
gpointer value,
gpointer user_data)
{
struct find_info * info = (struct find_info *) user_data;
if (info->connection == value)
info->found_key = key;
}
void
nm_client_remove_vpn_connection (NMClient *client, NMVPNConnection *connection)
{
NMClientPrivate *priv;
struct find_info info = { NULL, NULL };
g_return_if_fail (NM_IS_CLIENT (client));
g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
/* Note that the connection isn't removed from NetworkManager, it's
because it doesn't have DBUS API for that right now. */
priv = NM_CLIENT_GET_PRIVATE (client);
info.connection = connection;
g_hash_table_foreach (priv->vpn_connections, find_connection, &info);
if (!info.found_key) {
g_log (G_LOG_DOMAIN,
G_LOG_LEVEL_WARNING,
"Warning: tried to remove unknown NMVPNConnection object %p\n",
connection);
return;
}
g_hash_table_remove (priv->vpn_connections, info.found_key);
g_signal_emit (client, signals[VPN_CONNECTION_REMOVED], 0, connection);
g_object_unref (connection);
}
NMVPNConnectionState
nm_client_get_vpn_state (NMClient *client)
{
NMClientPrivate *priv;
g_return_val_if_fail (NM_IS_CLIENT (client), NM_VPN_CONNECTION_STATE_UNKNOWN);
priv = NM_CLIENT_GET_PRIVATE (client);
if (priv->vpn_state == NM_VPN_CONNECTION_STATE_UNKNOWN)
priv->vpn_state = nm_client_get_best_vpn_state (client);
return priv->vpn_state;
}