Dan Williams
2014-06-06 13:56:08 -05:00
21 changed files with 3081 additions and 2775 deletions

View File

@@ -26,7 +26,7 @@ dbusservice_DATA = \
nm-avahi-autoipd.conf nm-avahi-autoipd.conf
libexec_PROGRAMS = \ libexec_PROGRAMS = \
nm-dispatcher.action \ nm-dispatcher \
nm-avahi-autoipd.action nm-avahi-autoipd.action
@@ -38,13 +38,13 @@ nm_avahi_autoipd_action_LDADD = \
$(GLIB_LIBS) $(GLIB_LIBS)
nm_dispatcher_action_SOURCES = \ nm_dispatcher_SOURCES = \
nm-dispatcher-action.c \ nm-dispatcher.c \
nm-dispatcher-action.h \ nm-dispatcher-api.h \
nm-dispatcher-utils.c \ nm-dispatcher-utils.c \
nm-dispatcher-utils.h nm-dispatcher-utils.h
nm_dispatcher_action_LDADD = \ nm_dispatcher_LDADD = \
$(top_builddir)/libnm-util/libnm-util.la \ $(top_builddir)/libnm-util/libnm-util.la \
$(DBUS_LIBS) \ $(DBUS_LIBS) \
$(GLIB_LIBS) $(GLIB_LIBS)

View File

@@ -20,6 +20,10 @@
#include <dbus/dbus-glib.h> #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 */ /* 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 (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)) #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_STATE "state"
#define NMD_DEVICE_PROPS_PATH "path" #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 { typedef enum {
DISPATCH_RESULT_UNKNOWN = 0, DISPATCH_RESULT_UNKNOWN = 0,
DISPATCH_RESULT_SUCCESS = 1, DISPATCH_RESULT_SUCCESS = 1,

View File

@@ -30,7 +30,7 @@
#include <nm-setting-ip6-config.h> #include <nm-setting-ip6-config.h>
#include <nm-setting-connection.h> #include <nm-setting-connection.h>
#include "nm-dispatcher-action.h" #include "nm-dispatcher-api.h"
#include "nm-utils.h" #include "nm-utils.h"
#include "nm-dispatcher-utils.h" #include "nm-dispatcher-utils.h"

View File

@@ -37,12 +37,10 @@
#include <dbus/dbus-glib.h> #include <dbus/dbus-glib.h>
#include "nm-dispatcher-action.h" #include "nm-dispatcher-api.h"
#include "nm-dispatcher-utils.h" #include "nm-dispatcher-utils.h"
#include "nm-glib-compat.h" #include "nm-glib-compat.h"
#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d"
static GMainLoop *loop = NULL; static GMainLoop *loop = NULL;
static gboolean debug = FALSE; static gboolean debug = FALSE;
@@ -322,33 +320,33 @@ script_timeout_cb (gpointer user_data)
} }
static inline gboolean 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 (s != 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 (*error == NULL, FALSE); g_return_val_if_fail (*out_error_msg == NULL, FALSE);
/* Only accept regular files */ /* Only accept regular files */
if (!S_ISREG (s->st_mode)) { 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; return FALSE;
} }
/* Only accept files owned by root */ /* Only accept files owned by root */
if (s->st_uid != 0) { if (s->st_uid != 0) {
g_set_error (error, 0, 0, "not owned by root."); *out_error_msg = "not owned by root.";
return FALSE; return FALSE;
} }
/* Only accept files not writable by group or other, and not SUID */ /* Only accept files not writable by group or other, and not SUID */
if (s->st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { 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; return FALSE;
} }
/* Only accept files executable by the owner */ /* Only accept files executable by the owner */
if (!(s->st_mode & S_IXUSR)) { 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; return FALSE;
} }
@@ -385,6 +383,8 @@ child_setup (gpointer user_data G_GNUC_UNUSED)
setpgid (pid, pid); setpgid (pid, pid);
} }
#define SCRIPT_TIMEOUT 600 /* 10 minutes */
static void static void
dispatch_one_script (Request *request) 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)) { 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_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 { } else {
g_warning ("Failed to execute script '%s': (%d) %s", g_warning ("Failed to execute script '%s': (%d) %s",
script->script, error->code, error->message); script->script, error->code, error->message);
@@ -416,16 +416,26 @@ dispatch_one_script (Request *request)
} }
static GSList * static GSList *
find_scripts (void) find_scripts (const char *str_action)
{ {
GDir *dir; GDir *dir;
const char *filename; const char *filename;
GSList *sorted = NULL; GSList *sorted = NULL;
GError *error = NULL; GError *error = NULL;
const char *dirname;
if (!(dir = g_dir_open (NMD_SCRIPT_DIR, 0, &error))) { 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 (dirname, 0, &error))) {
g_warning ("Failed to open dispatcher directory '%s': (%d) %s", 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); g_error_free (error);
return NULL; return NULL;
} }
@@ -434,19 +444,19 @@ find_scripts (void)
char *path; char *path;
struct stat st; struct stat st;
int err; int err;
const char *err_msg = NULL;
if (!check_filename (filename)) if (!check_filename (filename))
continue; continue;
path = g_build_filename (NMD_SCRIPT_DIR, filename, NULL); path = g_build_filename (dirname, filename, NULL);
err = stat (path, &st); err = stat (path, &st);
if (err) if (err)
g_warning ("Failed to stat '%s': %d", path, err); g_warning ("Failed to stat '%s': %d", path, err);
else if (!check_permissions (&st, &error)) { else if (!check_permissions (&st, &err_msg))
g_warning ("Cannot execute '%s': %s", path, error->message); g_warning ("Cannot execute '%s': %s", path, err_msg);
g_clear_error (&error); else {
} else {
/* success */ /* success */
sorted = g_slist_insert_sorted (sorted, path, (GCompareFunc) g_strcmp0); sorted = g_slist_insert_sorted (sorted, path, (GCompareFunc) g_strcmp0);
} }
@@ -478,7 +488,7 @@ impl_dispatch (Handler *h,
char **p; char **p;
char *iface = NULL; char *iface = NULL;
sorted_scripts = find_scripts (); sorted_scripts = find_scripts (str_action);
if (!sorted_scripts) { if (!sorted_scripts) {
dbus_g_method_return (context, g_ptr_array_new ()); dbus_g_method_return (context, g_ptr_array_new ());

View File

@@ -1,6 +1,6 @@
[D-BUS Service] [D-BUS Service]
Name=org.freedesktop.nm_dispatcher Name=org.freedesktop.nm_dispatcher
Exec=@libexecdir@/nm-dispatcher.action Exec=@libexecdir@/nm-dispatcher
User=root User=root
SystemdService=dbus-org.freedesktop.nm-dispatcher.service SystemdService=dbus-org.freedesktop.nm-dispatcher.service

View File

@@ -29,7 +29,7 @@
#include "nm-setting-connection.h" #include "nm-setting-connection.h"
#include "nm-dispatcher-utils.h" #include "nm-dispatcher-utils.h"
#include "nm-dbus-glib-types.h" #include "nm-dbus-glib-types.h"
#include "nm-dispatcher-action.h" #include "nm-dispatcher-api.h"
#include "nm-utils.h" #include "nm-utils.h"
/*******************************************/ /*******************************************/

View File

@@ -441,7 +441,7 @@ fi
%{_bindir}/nm-online %{_bindir}/nm-online
%{_libexecdir}/nm-dhcp-helper %{_libexecdir}/nm-dhcp-helper
%{_libexecdir}/nm-avahi-autoipd.action %{_libexecdir}/nm-avahi-autoipd.action
%{_libexecdir}/nm-dispatcher.action %{_libexecdir}/nm-dispatcher
%dir %{_libdir}/NetworkManager %dir %{_libdir}/NetworkManager
%{_libdir}/NetworkManager/libnm-settings-plugin*.so %{_libdir}/NetworkManager/libnm-settings-plugin*.so
%{_mandir}/man1/* %{_mandir}/man1/*

View File

@@ -4,7 +4,7 @@ Description=Network Manager Script Dispatcher Service
[Service] [Service]
Type=dbus Type=dbus
BusName=org.freedesktop.nm_dispatcher 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 # We want to allow scripts to spawn long-running daemons, so tell
# systemd to not clean up when nm-dispatcher exits # systemd to not clean up when nm-dispatcher exits

View File

@@ -53,9 +53,9 @@
<title>Dispatcher scripts</title> <title>Dispatcher scripts</title>
<para> <para>
NetworkManager will execute scripts in the NetworkManager will execute scripts in the
/etc/NetworkManager/dispatcher.d directory in alphabetical order /etc/NetworkManager/dispatcher.d directory or subdirectories in
in response to network events. Each script should be a regular alphabetical order in response to network events. Each script should
executable file, owned by root. Furthermore, it must not be be a regular executable file owned by root. Furthermore, it must not be
writable by group or other, and not setuid. writable by group or other, and not setuid.
</para> </para>
<para> <para>
@@ -64,22 +64,65 @@
</para> </para>
<para>The actions are:</para> <para>The actions are:</para>
<variablelist class="dispatcher-options"> <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> <varlistentry>
<term><varname>up</varname></term> <term><varname>up</varname></term>
<listitem><para>The interface has been activated.</para></listitem> <listitem><para>The interface has been activated.</para></listitem>
</varlistentry> </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> <varlistentry>
<term><varname>down</varname></term> <term><varname>down</varname></term>
<listitem><para> <listitem><para>
The interface has been deactivated. The interface has been deactivated.
</para></listitem> </para></listitem>
</varlistentry> </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> <varlistentry>
<term><varname>vpn-up</varname></term> <term><varname>vpn-up</varname></term>
<listitem><para> <listitem><para>
A VPN connection has been activated. A VPN connection has been activated.
</para></listitem> </para></listitem>
</varlistentry> </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> <varlistentry>
<term><varname>vpn-down</varname></term> <term><varname>vpn-down</varname></term>
<listitem><para> <listitem><para>

File diff suppressed because it is too large Load Diff

View File

@@ -218,7 +218,7 @@ typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device,
GType nm_device_get_type (void); GType nm_device_get_type (void);
const char * nm_device_get_path (NMDevice *dev); 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_udi (NMDevice *dev);
const char * nm_device_get_iface (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); int nm_device_get_ip_ifindex(NMDevice *dev);
const char * nm_device_get_driver (NMDevice *dev); const char * nm_device_get_driver (NMDevice *dev);
const char * nm_device_get_driver_version (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); const char * nm_device_get_type_desc (NMDevice *dev);
NMDeviceType nm_device_get_device_type (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); void nm_device_capture_initial_config (NMDevice *dev);
/* Master */ /* Master */
gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure);
GSList * nm_device_master_get_slaves (NMDevice *dev); GSList * nm_device_master_get_slaves (NMDevice *dev);
gboolean nm_device_is_master (NMDevice *dev);
/* Slave */ /* Slave */
NMDevice * nm_device_get_master (NMDevice *dev); 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_check_connection_compatible (NMDevice *device, NMConnection *connection);
gboolean nm_device_can_assume_connections (NMDevice *device); gboolean nm_device_can_assume_active_connection (NMDevice *device);
NMConnection * nm_device_find_assumable_connection (NMDevice *device,
const GSList *connections);
gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs); gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs);
@@ -313,19 +307,18 @@ typedef enum {
} NMUnmanagedFlags; } NMUnmanagedFlags;
gboolean nm_device_get_managed (NMDevice *device); 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); gboolean nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag);
void nm_device_set_unmanaged (NMDevice *device, void nm_device_set_unmanaged (NMDevice *device,
NMUnmanagedFlags flag, NMUnmanagedFlags flag,
gboolean unmanaged, gboolean unmanaged,
NMDeviceStateReason reason); NMDeviceStateReason reason);
void nm_device_set_unmanaged_quitting (NMDevice *device);
void nm_device_set_initial_unmanaged_flag (NMDevice *device, void nm_device_set_initial_unmanaged_flag (NMDevice *device,
NMUnmanagedFlags flag, NMUnmanagedFlags flag,
gboolean unmanaged); gboolean unmanaged);
gboolean nm_device_get_is_nm_owned (NMDevice *device); gboolean nm_device_get_is_nm_owned (NMDevice *device);
gboolean nm_device_set_is_nm_owned (NMDevice *device, void nm_device_set_nm_owned (NMDevice *device);
gboolean is_nm_owned);
gboolean nm_device_get_autoconnect (NMDevice *device); gboolean nm_device_get_autoconnect (NMDevice *device);
@@ -341,8 +334,6 @@ void nm_device_queue_state (NMDevice *self,
NMDeviceState state, NMDeviceState state,
NMDeviceStateReason reason); NMDeviceStateReason reason);
void nm_device_queue_ip_config_change (NMDevice *self);
gboolean nm_device_get_firmware_missing (NMDevice *self); gboolean nm_device_get_firmware_missing (NMDevice *self);
void nm_device_queue_activation (NMDevice *device, NMActRequest *req); 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, GPtrArray *nm_device_get_available_connections (NMDevice *device,
const char *specific_object); 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, gboolean nm_device_connection_is_available (NMDevice *device,
NMConnection *connection, NMConnection *connection,
gboolean allow_device_override); gboolean allow_device_override);

View File

@@ -54,6 +54,7 @@
#include "nm-config.h" #include "nm-config.h"
#include "nm-posix-signals.h" #include "nm-posix-signals.h"
#include "nm-session-monitor.h" #include "nm-session-monitor.h"
#include "nm-dispatcher.h"
#if !defined(NM_DIST_VERSION) #if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION # define NM_DIST_VERSION VERSION
@@ -604,6 +605,8 @@ main (int argc, char *argv[])
dhcp_mgr = nm_dhcp_manager_get (); dhcp_mgr = nm_dhcp_manager_get ();
g_assert (dhcp_mgr != NULL); g_assert (dhcp_mgr != NULL);
nm_dispatcher_init ();
settings = nm_settings_new (&error); settings = nm_settings_new (&error);
if (!settings) { if (!settings) {
nm_log_err (LOGD_CORE, "failed to initialize settings storage: %s", nm_log_err (LOGD_CORE, "failed to initialize settings storage: %s",

View File

@@ -23,7 +23,7 @@
#include <string.h> #include <string.h>
#include "nm-dispatcher.h" #include "nm-dispatcher.h"
#include "nm-dispatcher-action.h" #include "nm-dispatcher-api.h"
#include "NetworkManagerUtils.h" #include "NetworkManagerUtils.h"
#include "nm-utils.h" #include "nm-utils.h"
#include "nm-logging.h" #include "nm-logging.h"
@@ -31,7 +31,10 @@
#include "nm-dbus-glib-types.h" #include "nm-dbus-glib-types.h"
#include "nm-glib-compat.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 static void
dump_object_to_props (GObject *object, GHashTable *hash) dump_object_to_props (GObject *object, GHashTable *hash)
@@ -132,17 +135,37 @@ fill_vpn_props (NMIP4Config *ip4_config,
} }
typedef struct { typedef struct {
guint request_id;
DispatcherFunc callback; DispatcherFunc callback;
gpointer user_data; gpointer user_data;
guint idle_id;
} DispatchInfo; } DispatchInfo;
static void static void
dispatcher_info_free (DispatchInfo *info) dispatcher_info_free (DispatchInfo *info)
{ {
requests = g_slist_remove (requests, info); if (info->idle_id)
g_source_remove (info->idle_id);
g_free (info); 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 * static const char *
dispatch_result_to_string (DispatchResult result) dispatch_result_to_string (DispatchResult result)
{ {
@@ -161,115 +184,149 @@ dispatch_result_to_string (DispatchResult result)
g_assert_not_reached (); 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 static void
dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
{ {
DispatchInfo *info = user_data; DispatchInfo *info = user_data;
GError *error = NULL; GError *error = NULL;
GPtrArray *results = NULL; GPtrArray *results = NULL;
guint i;
if (dbus_g_proxy_end_call (proxy, call, &error, if (dbus_g_proxy_end_call (proxy, call, &error,
DISPATCHER_TYPE_RESULT_ARRAY, &results, DISPATCHER_TYPE_RESULT_ARRAY, &results,
G_TYPE_INVALID)) { G_TYPE_INVALID)) {
for (i = 0; results && (i < results->len); i++) { dispatcher_results_process (info->request_id, results);
GValueArray *item = g_ptr_array_index (results, i); free_results (results);
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);
} else { } else {
g_assert (error); 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) if (info->callback)
info->callback (info, info->user_data); info->callback (info->request_id, info->user_data);
g_clear_error (&error); g_clear_error (&error);
g_object_unref (proxy); 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 * static const char *
action_to_string (DispatcherAction action) action_to_string (DispatcherAction action)
{ {
switch (action) { g_assert (action >= 0 && action < G_N_ELEMENTS (action_table));
case DISPATCHER_ACTION_HOSTNAME: return action_table[action];
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 ();
} }
static gconstpointer 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 gboolean
_dispatcher_call (DispatcherAction action, _dispatcher_call (DispatcherAction action,
gboolean blocking,
NMConnection *connection, NMConnection *connection,
NMDevice *device, NMDevice *device,
const char *vpn_iface, const char *vpn_iface,
NMIP4Config *vpn_ip4_config, NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config, NMIP6Config *vpn_ip6_config,
DispatcherFunc callback, DispatcherFunc callback,
gpointer user_data) gpointer user_data,
guint *out_call_id)
{ {
DBusGProxy *proxy; DBusGProxy *proxy;
DBusGConnection *g_connection; DBusGConnection *g_connection;
@@ -282,22 +339,49 @@ _dispatcher_call (DispatcherAction action,
GHashTable *device_dhcp6_props; GHashTable *device_dhcp6_props;
GHashTable *vpn_ip4_props; GHashTable *vpn_ip4_props;
GHashTable *vpn_ip6_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 */ /* All actions except 'hostname' require a device */
if (action == DISPATCHER_ACTION_HOSTNAME) { if (action == DISPATCHER_ACTION_HOSTNAME) {
nm_log_dbg (LOGD_DISPATCH, "dispatching action '%s'", nm_log_dbg (LOGD_DISPATCH, "(%u) dispatching action '%s'",
action_to_string (action)); reqid, action_to_string (action));
} else { } 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_log_dbg (LOGD_DISPATCH, "(%u) (%s) dispatching action '%s'",
nm_device_get_iface (device), action_to_string (action)); reqid,
vpn_iface ? vpn_iface : nm_device_get_iface (device),
action_to_string (action));
} }
/* VPN actions require at least an IPv4 config (for now) */ /* VPN actions require at least an IPv4 config (for now) */
if (action == DISPATCHER_ACTION_VPN_UP) 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 ()); g_connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
proxy = dbus_g_proxy_new_for_name (g_connection, proxy = dbus_g_proxy_new_for_name (g_connection,
@@ -305,8 +389,8 @@ _dispatcher_call (DispatcherAction action,
NM_DISPATCHER_DBUS_PATH, NM_DISPATCHER_DBUS_PATH,
NM_DISPATCHER_DBUS_IFACE); NM_DISPATCHER_DBUS_IFACE);
if (!proxy) { if (!proxy) {
nm_log_err (LOGD_DISPATCH, "could not get dispatcher proxy!"); nm_log_err (LOGD_DISPATCH, "(%u) could not get dispatcher proxy!", reqid);
return NULL; return FALSE;
} }
if (connection) { 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); 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 */ /* Send the action to the dispatcher */
dbus_g_proxy_begin_call_with_timeout (proxy, "Action", if (blocking) {
dispatcher_done_cb, GPtrArray *results = NULL;
info,
(GDestroyNotify) dispatcher_info_free, success = dbus_g_proxy_call_with_timeout (proxy, "Action",
30000, CALL_TIMEOUT,
G_TYPE_STRING, action_to_string (action), &error,
DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash, G_TYPE_STRING, action_to_string (action),
DBUS_TYPE_G_MAP_OF_VARIANT, connection_props, DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
DBUS_TYPE_G_MAP_OF_VARIANT, device_props, DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props, DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props, DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props, DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props, DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
G_TYPE_STRING, vpn_iface ? vpn_iface : "", DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props, G_TYPE_STRING, vpn_iface ? vpn_iface : "",
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props, DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH), DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
G_TYPE_INVALID); 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_hash);
g_hash_table_destroy (connection_props); g_hash_table_destroy (connection_props);
g_hash_table_destroy (device_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_ip4_props);
g_hash_table_destroy (vpn_ip6_props); g_hash_table_destroy (vpn_ip6_props);
/* Track the request in case of cancelation */ done:
requests = g_slist_append (requests, info); 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, nm_dispatcher_call (DispatcherAction action,
NMConnection *connection, NMConnection *connection,
NMDevice *device, NMDevice *device,
DispatcherFunc callback, DispatcherFunc callback,
gpointer user_data) gpointer user_data,
guint *out_call_id)
{ {
return _dispatcher_call (action, connection, device, NULL, NULL, NULL, callback, user_data); return _dispatcher_call (action, FALSE, connection, device, NULL, NULL,
NULL, callback, user_data, out_call_id);
} }
gconstpointer /**
* 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, TRUE, connection, device, NULL, NULL,
NULL, NULL, NULL, NULL);
}
/**
* 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, nm_dispatcher_call_vpn (DispatcherAction action,
NMConnection *connection, NMConnection *connection,
NMDevice *device, NMDevice *parent_device,
const char *vpn_iface, const char *vpn_iface,
NMIP4Config *vpn_ip4_config, NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config, NMIP6Config *vpn_ip6_config,
DispatcherFunc callback, DispatcherFunc callback,
gpointer user_data) gpointer user_data,
guint *out_call_id)
{ {
return _dispatcher_call (action, connection, device, vpn_iface, vpn_ip4_config, vpn_ip6_config, callback, user_data); 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, TRUE, connection, parent_device, vpn_iface,
vpn_ip4_config, vpn_ip6_config, NULL, NULL, NULL);
} }
void void
nm_dispatcher_call_cancel (gconstpointer call) nm_dispatcher_call_cancel (guint call_id)
{ {
/* 'call' is really a DispatchInfo pointer, just opaque to callers. DispatchInfo *info;
* 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 _ensure_requests ();
* freed. Canceling just means the callback doesn't get called, so set
* the DispatcherInfo's callback to NULL. /* Canceling just means the callback doesn't get called, so set the
* DispatcherInfo's callback to NULL.
*/ */
if (g_slist_find (requests, call)) info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id));
((DispatchInfo *) call)->callback = NULL; 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);
}
} }

View File

@@ -32,9 +32,11 @@
typedef enum { typedef enum {
DISPATCHER_ACTION_HOSTNAME, DISPATCHER_ACTION_HOSTNAME,
DISPATCHER_ACTION_PRE_UP,
DISPATCHER_ACTION_UP, DISPATCHER_ACTION_UP,
DISPATCHER_ACTION_PRE_DOWN, DISPATCHER_ACTION_PRE_DOWN,
DISPATCHER_ACTION_DOWN, DISPATCHER_ACTION_DOWN,
DISPATCHER_ACTION_VPN_PRE_UP,
DISPATCHER_ACTION_VPN_UP, DISPATCHER_ACTION_VPN_UP,
DISPATCHER_ACTION_VPN_PRE_DOWN, DISPATCHER_ACTION_VPN_PRE_DOWN,
DISPATCHER_ACTION_VPN_DOWN, DISPATCHER_ACTION_VPN_DOWN,
@@ -42,23 +44,38 @@ typedef enum {
DISPATCHER_ACTION_DHCP6_CHANGE DISPATCHER_ACTION_DHCP6_CHANGE
} DispatcherAction; } 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, NMConnection *connection,
NMDevice *device, NMDevice *device);
DispatcherFunc callback,
gpointer user_data);
gconstpointer nm_dispatcher_call_vpn (DispatcherAction action, 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);
gboolean nm_dispatcher_call_vpn_sync (DispatcherAction action,
NMConnection *connection, NMConnection *connection,
NMDevice *device, NMDevice *parent_device,
const char *vpn_iface, const char *vpn_iface,
NMIP4Config *vpn_ip4_config, NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config, NMIP6Config *vpn_ip6_config);
DispatcherFunc callback,
gpointer user_data);
void nm_dispatcher_call_cancel (gconstpointer call); void nm_dispatcher_call_cancel (guint call_id);
void nm_dispatcher_init (void);
#endif /* NM_DISPATCHER_H */ #endif /* NM_DISPATCHER_H */

View File

@@ -727,20 +727,27 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting)
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
if (nm_device_get_managed (device)) { if (nm_device_get_managed (device)) {
/* Leave configured interfaces up when quitting so they can be NMActRequest *req = nm_device_get_act_request (device);
* taken over again if NM starts up, and to ensure connectivity while gboolean unmanage = FALSE;
* NM is gone. Assumed connections don't get taken down even if they
* haven't been fully activated. /* 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 ( !nm_device_can_assume_connections (device) if (unmanage) {
|| (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) if (quitting)
|| !quitting) { nm_device_set_unmanaged_quitting (device);
NMActRequest *req = nm_device_get_act_request (device); else
nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
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);
}
} }
g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager); 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); nm_dbus_manager_unregister_object (priv->dbus_mgr, device);
g_object_unref (device); g_object_unref (device);
if (priv->startup) check_if_startup_complete (manager);
check_if_startup_complete (manager);
} }
static void static void
@@ -1087,7 +1093,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
} }
if (device) { if (device) {
nm_device_set_is_nm_owned (device, TRUE); nm_device_set_nm_owned (device);
add_device (self, device, FALSE); add_device (self, device, FALSE);
g_object_unref (device); g_object_unref (device);
} }
@@ -1683,8 +1689,6 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con)
{ {
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
const char *iface, *driver, *type_desc; const char *iface, *driver, *type_desc;
char *path;
static guint32 devcount = 0;
const GSList *unmanaged_specs; const GSList *unmanaged_specs;
gboolean user_unmanaged, sleeping; gboolean user_unmanaged, sleeping;
NMConnection *connection = NULL; NMConnection *connection = NULL;
@@ -1761,11 +1765,7 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con)
sleeping = manager_sleeping (self); sleeping = manager_sleeping (self);
nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping); nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping);
path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); nm_device_dbus_export (device);
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);
/* Don't generate a connection e.g. for devices NM just created, or /* Don't generate a connection e.g. for devices NM just created, or
* for the loopback, or when we're sleeping. */ * for the loopback, or when we're sleeping. */

View File

@@ -326,7 +326,7 @@ _set_hostname (NMPolicy *policy,
nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname); nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
if (set_system_hostname (priv->cur_hostname, msg)) 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 static void

View File

@@ -62,17 +62,32 @@ typedef enum {
SECRETS_REQ_LAST SECRETS_REQ_LAST
} SecretsReq; } SecretsReq;
typedef struct { /* Internal VPN states, private to NMVPNConnection */
gboolean disposed; 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; NMConnection *connection;
guint32 secrets_id; guint32 secrets_id;
SecretsReq secrets_idx; SecretsReq secrets_idx;
char *username; char *username;
NMVPNConnectionState vpn_state; VpnState vpn_state;
guint dispatcher_id;
NMVPNConnectionStateReason failure_reason; NMVPNConnectionStateReason failure_reason;
DBusGProxy *proxy; DBusGProxy *proxy;
GHashTable *connect_hash; GHashTable *connect_hash;
guint connect_timeout; guint connect_timeout;
@@ -121,20 +136,65 @@ static void plugin_interactive_secrets_required (DBusGProxy *proxy,
const char **secrets, const char **secrets,
gpointer user_data); 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 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 */ /* Set the NMActiveConnection state based on VPN state */
switch (vpn_state) { switch (vpn_state) {
case NM_VPN_CONNECTION_STATE_PREPARE: case STATE_WAITING:
case NM_VPN_CONNECTION_STATE_NEED_AUTH: case STATE_PREPARE:
case NM_VPN_CONNECTION_STATE_CONNECT: case STATE_NEED_AUTH:
case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: case STATE_CONNECT:
case STATE_IP_CONFIG_GET:
case STATE_PRE_UP:
return NM_ACTIVE_CONNECTION_STATE_ACTIVATING; return NM_ACTIVE_CONNECTION_STATE_ACTIVATING;
case NM_VPN_CONNECTION_STATE_ACTIVATED: case STATE_ACTIVATED:
return NM_ACTIVE_CONNECTION_STATE_ACTIVATED; return NM_ACTIVE_CONNECTION_STATE_ACTIVATED;
case NM_VPN_CONNECTION_STATE_FAILED: case STATE_DEACTIVATING:
case NM_VPN_CONNECTION_STATE_DISCONNECTED: return NM_ACTIVE_CONNECTION_STATE_DEACTIVATING;
case STATE_DISCONNECTED:
case STATE_FAILED:
return NM_ACTIVE_CONNECTION_STATE_DEACTIVATED; return NM_ACTIVE_CONNECTION_STATE_DEACTIVATED;
default: default:
break; break;
@@ -190,12 +250,45 @@ vpn_cleanup (NMVPNConnection *connection, NMDevice *parent_dev)
} }
static void static void
nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, dispatcher_pre_down_done (guint call_id, gpointer user_data)
NMVPNConnectionState vpn_state, {
NMVPNConnectionStateReason reason) 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; 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)); NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
g_return_if_fail (NM_IS_VPN_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 */ /* Update active connection base class state */
nm_active_connection_set_state (NM_ACTIVE_CONNECTION (connection), 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 */ /* Clear any in-progress secrets request */
if (priv->secrets_id) { if (priv->secrets_id) {
@@ -224,24 +317,47 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
priv->secrets_id = 0; priv->secrets_id = 0;
} }
dispatcher_cleanup (connection);
/* The connection gets destroyed by the VPN manager when it enters the /* 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 * disconnected/failed state, but we need to keep it around for a bit
* to send out signals and handle the dispatcher. So ref it. * to send out signals and handle the dispatcher. So ref it.
*/ */
g_object_ref (connection); g_object_ref (connection);
g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason); old_external_state = _state_to_nm_vpn_state (old_vpn_state);
g_signal_emit (connection, signals[INTERNAL_STATE_CHANGED], 0, vpn_state, old_vpn_state, reason); new_external_state = _state_to_nm_vpn_state (priv->vpn_state);
g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_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) { 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 /* Do nothing; not part of 'default' because we don't want to touch
* priv->secrets_req as NEED_AUTH is re-entered during interactive * priv->secrets_req as NEED_AUTH is re-entered during interactive
* secrets. * secrets.
*/ */
break; 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 */ /* Secrets no longer needed now that we're connected */
nm_connection_clear_secrets (priv->connection); nm_connection_clear_secrets (priv->connection);
@@ -253,20 +369,55 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
priv->ip4_config, priv->ip4_config,
priv->ip6_config, priv->ip6_config,
NULL, NULL,
NULL,
NULL); NULL);
break; break;
case NM_VPN_CONNECTION_STATE_FAILED: case STATE_DEACTIVATING:
case NM_VPN_CONNECTION_STATE_DISCONNECTED: if (quitting) {
if (old_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) { 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 */ /* Let dispatcher scripts know we're about to go down */
nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN, if (quitting) {
priv->connection, nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_DOWN,
parent_dev, priv->connection,
priv->ip_iface, parent_dev,
NULL, priv->ip_iface,
NULL, NULL,
NULL, 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 */ /* Tear down and clean up the connection */
@@ -290,14 +441,20 @@ device_state_changed (NMActiveConnection *active,
NMDeviceState old_state) NMDeviceState old_state)
{ {
if (new_state <= NM_DEVICE_STATE_DISCONNECTED) { if (new_state <= NM_DEVICE_STATE_DISCONNECTED) {
nm_vpn_connection_set_vpn_state (NM_VPN_CONNECTION (active), _set_vpn_state (NM_VPN_CONNECTION (active),
NM_VPN_CONNECTION_STATE_DISCONNECTED, STATE_DISCONNECTED,
NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
FALSE);
} else if (new_state == NM_DEVICE_STATE_FAILED) { } else if (new_state == NM_DEVICE_STATE_FAILED) {
nm_vpn_connection_set_vpn_state (NM_VPN_CONNECTION (active), _set_vpn_state (NM_VPN_CONNECTION (active),
NM_VPN_CONNECTION_STATE_FAILED, STATE_FAILED,
NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); 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 static void
@@ -488,27 +645,25 @@ vpn_service_state_to_string (NMVPNServiceState state)
return "unknown"; 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 * static const char *
vpn_state_to_string (NMVPNConnectionState state) vpn_state_to_string (VpnState state)
{ {
switch (state) { if (state >= 0 && state < G_N_ELEMENTS (state_table))
case NM_VPN_CONNECTION_STATE_PREPARE: return state_table[state];
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;
}
return "unknown"; return "unknown";
} }
@@ -561,23 +716,13 @@ plugin_state_changed (DBusGProxy *proxy,
*/ */
nm_connection_clear_secrets (priv->connection); nm_connection_clear_secrets (priv->connection);
switch (nm_vpn_connection_get_vpn_state (connection)) { if ((priv->vpn_state >= STATE_WAITING) && (priv->vpn_state <= STATE_ACTIVATED)) {
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:
nm_log_info (LOGD_VPN, "VPN plugin state change reason: %s (%d)", nm_log_info (LOGD_VPN, "VPN plugin state change reason: %s (%d)",
vpn_reason_to_string (priv->failure_reason), priv->failure_reason); vpn_reason_to_string (priv->failure_reason), priv->failure_reason);
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_FAILED, priv->failure_reason, FALSE);
NM_VPN_CONNECTION_STATE_FAILED,
priv->failure_reason);
/* Reset the failure reason */ /* Reset the failure reason */
priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; 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_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
nm_connection_get_id (priv->connection)); nm_connection_get_id (priv->connection));
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_PRE_UP, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
NM_VPN_CONNECTION_STATE_ACTIVATED,
NM_VPN_CONNECTION_STATE_REASON_NONE);
return TRUE; 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_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.",
nm_connection_get_id (priv->connection)); nm_connection_get_id (priv->connection));
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE);
NM_VPN_CONNECTION_STATE_FAILED,
NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID);
} }
#define LOG_INVALID_ARG(property) \ #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_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.",
nm_connection_get_id (priv->connection)); nm_connection_get_id (priv->connection));
if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { if (priv->vpn_state == STATE_CONNECT)
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
NM_VPN_CONNECTION_STATE_REASON_NONE);
}
if (!process_generic_config (connection, config_hash)) if (!process_generic_config (connection, config_hash))
return; return;
@@ -937,11 +1075,8 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
GValue *val; GValue *val;
int i; int i;
if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { if (priv->vpn_state == STATE_CONNECT)
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
NM_VPN_CONNECTION_STATE_REASON_NONE);
}
if (priv->has_ip4) { if (priv->has_ip4) {
nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.", 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_log_info (LOGD_VPN, "VPN connection '%s' (IP6 Config Get) reply received.",
nm_connection_get_id (priv->connection)); nm_connection_get_id (priv->connection));
if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { if (priv->vpn_state == STATE_CONNECT)
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
NM_VPN_CONNECTION_STATE_REASON_NONE);
}
if (g_hash_table_size (config_hash) == 0) { if (g_hash_table_size (config_hash) == 0) {
priv->has_ip6 = FALSE; priv->has_ip6 = FALSE;
@@ -1233,19 +1365,15 @@ connect_timeout_cb (gpointer user_data)
{ {
NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
NMVPNConnectionState state;
priv->connect_timeout = 0; priv->connect_timeout = 0;
/* Cancel activation if it's taken too long */ /* Cancel activation if it's taken too long */
state = nm_vpn_connection_get_vpn_state (connection); if (priv->vpn_state == STATE_CONNECT ||
if (state == NM_VPN_CONNECTION_STATE_CONNECT || priv->vpn_state == STATE_IP_CONFIG_GET) {
state == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) {
nm_log_warn (LOGD_VPN, "VPN connection '%s' connect timeout exceeded.", nm_log_warn (LOGD_VPN, "VPN connection '%s' connect timeout exceeded.",
nm_connection_get_id (priv->connection)); nm_connection_get_id (priv->connection));
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
NM_VPN_CONNECTION_STATE_FAILED,
NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT);
} }
return 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_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.",
nm_connection_get_id (priv->connection), err->message); nm_connection_get_id (priv->connection), err->message);
g_error_free (err); g_error_free (err);
nm_vpn_connection_set_vpn_state (self, _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
NM_VPN_CONNECTION_STATE_FAILED,
NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
} }
static void 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_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect interactively: '%s'.",
nm_connection_get_id (priv->connection), err->message); nm_connection_get_id (priv->connection), err->message);
g_error_free (err); g_error_free (err);
nm_vpn_connection_set_vpn_state (self, _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
NM_VPN_CONNECTION_STATE_FAILED,
NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
} }
} }
@@ -1355,9 +1479,9 @@ really_activate (NMVPNConnection *connection, const char *username)
GHashTable *details; GHashTable *details;
g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); 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); 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, dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID); 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_object_unref (agent_mgr);
g_hash_table_destroy (details); g_hash_table_destroy (details);
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
NM_VPN_CONNECTION_STATE_CONNECT,
NM_VPN_CONNECTION_STATE_REASON_NONE);
} }
void void
@@ -1422,10 +1544,11 @@ nm_vpn_connection_activate (NMVPNConnection *connection)
DBusGConnection *bus; DBusGConnection *bus;
g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); 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); 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 ()); bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
priv->proxy = dbus_g_proxy_new_for_name (bus, priv->proxy = dbus_g_proxy_new_for_name (bus,
nm_vpn_connection_get_service (connection), nm_vpn_connection_get_service (connection),
@@ -1450,9 +1573,7 @@ nm_vpn_connection_activate (NMVPNConnection *connection)
G_CALLBACK (plugin_interactive_secrets_required), G_CALLBACK (plugin_interactive_secrets_required),
connection, NULL); connection, NULL);
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
NM_VPN_CONNECTION_STATE_NEED_AUTH,
NM_VPN_CONNECTION_STATE_REASON_NONE);
/* Kick off the secrets requests; first we get existing system secrets /* Kick off the secrets requests; first we get existing system secrets
* and ask the plugin if these are sufficient, next we get all existing * 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); 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 * const char *
@@ -1535,25 +1656,31 @@ nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection)
} }
void void
nm_vpn_connection_fail (NMVPNConnection *connection, nm_vpn_connection_disconnect (NMVPNConnection *connection,
NMVPNConnectionStateReason reason) NMVPNConnectionStateReason reason,
gboolean quitting)
{ {
g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_DISCONNECTED, reason, quitting);
NM_VPN_CONNECTION_STATE_FAILED,
reason);
} }
void gboolean
nm_vpn_connection_disconnect (NMVPNConnection *connection, nm_vpn_connection_deactivate (NMVPNConnection *connection,
NMVPNConnectionStateReason reason) 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, g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), FALSE);
NM_VPN_CONNECTION_STATE_DISCONNECTED,
reason); 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, priv->secrets_idx + 1,
g_quark_to_string (error->domain), g_quark_to_string (error->domain),
error->message); 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); g_error_free (error);
return; 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_log_err (LOGD_VPN, "(%s/%s) final secrets request failed to provide sufficient secrets",
nm_connection_get_uuid (priv->connection), nm_connection_get_uuid (priv->connection),
nm_connection_get_id (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 { } else {
nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required", nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required",
nm_connection_get_uuid (priv->connection), 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), nm_connection_get_id (priv->connection),
g_quark_to_string (error->domain), g_quark_to_string (error->domain),
error->message); 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); g_error_free (error);
} }
} }
@@ -1645,7 +1772,7 @@ get_secrets_cb (NMSettingsConnection *connection,
if (error) { if (error) {
nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s", nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s",
priv->secrets_idx + 1, error->code, error->message); 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 { } else {
/* Cache the username for later */ /* Cache the username for later */
if (agent_username) { if (agent_username) {
@@ -1729,7 +1856,7 @@ get_secrets (NMVPNConnection *self,
nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s", nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s",
priv->secrets_idx + 1, error->code, error->message); 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); 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)", nm_log_info (LOGD_VPN, "VPN plugin requested secrets; state %s (%d)",
vpn_state_to_string (priv->vpn_state), priv->vpn_state); vpn_state_to_string (priv->vpn_state), priv->vpn_state);
g_return_if_fail (priv->vpn_state == NM_VPN_CONNECTION_STATE_CONNECT || g_return_if_fail (priv->vpn_state == STATE_CONNECT ||
priv->vpn_state == NM_VPN_CONNECTION_STATE_NEED_AUTH); priv->vpn_state == STATE_NEED_AUTH);
priv->secrets_idx = SECRETS_REQ_INTERACTIVE; priv->secrets_idx = SECRETS_REQ_INTERACTIVE;
nm_vpn_connection_set_vpn_state (connection, _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
NM_VPN_CONNECTION_STATE_NEED_AUTH,
NM_VPN_CONNECTION_STATE_REASON_NONE);
/* Copy hints and add message to the end */ /* Copy hints and add message to the end */
hints = g_malloc0 (sizeof (char *) * (secrets_len + 2)); 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); 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; priv->secrets_idx = SECRETS_REQ_SYSTEM;
} }
@@ -1795,38 +1920,28 @@ dispose (GObject *object)
{ {
NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
if (priv->disposed) { if (priv->connect_hash) {
G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
return;
}
priv->disposed = TRUE;
if (priv->connect_hash)
g_hash_table_destroy (priv->connect_hash); g_hash_table_destroy (priv->connect_hash);
priv->connect_hash = NULL;
}
if (priv->ip6_internal_gw) if (priv->connect_timeout) {
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)
g_source_remove (priv->connect_timeout); g_source_remove (priv->connect_timeout);
priv->connect_timeout = 0;
}
if (priv->proxy) dispatcher_cleanup (NM_VPN_CONNECTION (object));
g_object_unref (priv->proxy);
if (priv->secrets_id) { if (priv->secrets_id) {
nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection),
priv->secrets_id); 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_clear_object (&priv->connection);
g_free (priv->username);
G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
} }
@@ -1838,10 +1953,19 @@ finalize (GObject *object)
g_free (priv->banner); g_free (priv->banner);
g_free (priv->ip_iface); 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); 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 static void
get_property (GObject *object, guint prop_id, get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec) GValue *value, GParamSpec *pspec)
@@ -1851,19 +1975,19 @@ get_property (GObject *object, guint prop_id,
switch (prop_id) { switch (prop_id) {
case PROP_VPN_STATE: 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; break;
case PROP_BANNER: case PROP_BANNER:
g_value_set_string (value, priv->banner ? priv->banner : ""); g_value_set_string (value, priv->banner ? priv->banner : "");
break; break;
case PROP_IP4_CONFIG: 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)); g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config));
else else
g_value_set_boxed (value, "/"); g_value_set_boxed (value, "/");
break; break;
case PROP_IP6_CONFIG: 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)); g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config));
else else
g_value_set_boxed (value, "/"); g_value_set_boxed (value, "/");

View File

@@ -74,10 +74,14 @@ void nm_vpn_connection_activate (NMVPNConnection *connect
NMConnection * nm_vpn_connection_get_connection (NMVPNConnection *connection); NMConnection * nm_vpn_connection_get_connection (NMVPNConnection *connection);
NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *connection); NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *connection);
const char * nm_vpn_connection_get_banner (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, void nm_vpn_connection_disconnect (NMVPNConnection *connection,
NMVPNConnectionStateReason reason); NMVPNConnectionStateReason reason,
gboolean quitting);
NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection); NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection);
NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection); NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection);
const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection); const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection);

View File

@@ -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)) #define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVPNManagerPrivate))
typedef struct { typedef struct {
gboolean disposed;
GHashTable *services; GHashTable *services;
GFileMonitor *monitor; GFileMonitor *monitor;
guint monitor_id; guint monitor_id;
@@ -77,39 +75,11 @@ get_service_by_namefile (NMVPNManager *self, const char *namefile)
return NULL; 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 gboolean
nm_vpn_manager_activate_connection (NMVPNManager *manager, nm_vpn_manager_activate_connection (NMVPNManager *manager,
NMVPNConnection *vpn, NMVPNConnection *vpn,
GError **error) GError **error)
{ {
NMVPNConnection *existing = NULL;
NMConnection *connection; NMConnection *connection;
NMSettingVPN *s_vpn; NMSettingVPN *s_vpn;
NMVPNService *service; NMVPNService *service;
@@ -145,11 +115,6 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
return FALSE; 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); return nm_vpn_service_activate (service, vpn, error);
} }
@@ -158,51 +123,7 @@ nm_vpn_manager_deactivate_connection (NMVPNManager *self,
NMVPNConnection *connection, NMVPNConnection *connection,
NMVPNConnectionStateReason reason) NMVPNConnectionStateReason reason)
{ {
NMVPNManagerPrivate *priv; return nm_vpn_connection_deactivate (connection, reason, FALSE);
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;
} }
static void static void
@@ -210,34 +131,32 @@ try_add_service (NMVPNManager *self, const char *namefile)
{ {
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self); NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
NMVPNService *service = NULL; NMVPNService *service = NULL;
GHashTableIter iter;
GError *error = NULL; GError *error = NULL;
const char *service_name; const char *service_name;
char *tmp;
g_return_if_fail (g_path_is_absolute (namefile)); g_return_if_fail (g_path_is_absolute (namefile));
/* Make sure we don't add dupes */ /* Make sure we don't add dupes */
tmp = service_name_from_file (namefile); g_hash_table_iter_init (&iter, priv->services);
if (tmp) while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) {
service = g_hash_table_lookup (priv->services, tmp); if (g_strcmp0 (namefile, nm_vpn_service_get_name_file (service)) == 0)
g_free (tmp); return;
if (service) }
return;
/* New service, add it */ /* New service */
service = nm_vpn_service_new (namefile, &error); 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", nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s",
namefile, namefile,
error ? error->code : -1, error ? error->code : -1,
error && error->message ? error->message : "(unknown)"); error && error->message ? error->message : "(unknown)");
g_clear_error (&error); 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 static void
@@ -267,7 +186,7 @@ vpn_dir_changed (GFileMonitor *monitor,
const char *service_name = nm_vpn_service_get_dbus_service (service); const char *service_name = nm_vpn_service_get_dbus_service (service);
/* Stop active VPN connections and destroy the 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_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name); nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name);
g_hash_table_remove (priv->services, service_name); g_hash_table_remove (priv->services, service_name);
@@ -338,22 +257,37 @@ 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 static void
dispose (GObject *object) dispose (GObject *object)
{ {
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object); NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
if (!priv->disposed) { if (priv->monitor) {
priv->disposed = TRUE; if (priv->monitor_id)
g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
if (priv->monitor) { g_file_monitor_cancel (priv->monitor);
if (priv->monitor_id) g_clear_object (&priv->monitor);
g_signal_handler_disconnect (priv->monitor, priv->monitor_id); }
g_file_monitor_cancel (priv->monitor);
g_object_unref (priv->monitor);
}
if (priv->services) {
stop_all_services (NM_VPN_MANAGER (object));
g_hash_table_destroy (priv->services); g_hash_table_destroy (priv->services);
priv->services = NULL;
} }
G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object); G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object);

View File

@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 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. * Copyright (C) 2005 - 2008 Novell, Inc.
*/ */
@@ -38,30 +38,30 @@
G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT) G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT)
typedef struct { typedef struct {
gboolean disposed;
NMDBusManager *dbus_mgr;
char *name; char *name;
char *dbus_service; char *dbus_service;
char *program; char *program;
char *namefile; char *namefile;
GPid pid; NMVPNConnection *active;
GSList *connections; GSList *pending;
guint start_timeout; guint start_timeout;
guint quit_timeout; gboolean service_running;
guint child_watch;
gulong name_owner_id;
} NMVPNServicePrivate; } NMVPNServicePrivate;
#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, 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 * NMVPNService *
nm_vpn_service_new (const char *namefile, GError **error) nm_vpn_service_new (const char *namefile, GError **error)
{ {
NMVPNService *self = NULL; NMVPNService *self;
NMVPNServicePrivate *priv;
GKeyFile *kf; GKeyFile *kf;
char *dbus_service = NULL, *program = NULL, *name = NULL;
g_return_val_if_fail (namefile != NULL, NULL); g_return_val_if_fail (namefile != NULL, NULL);
g_return_val_if_fail (g_path_is_absolute (namefile), 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; 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;
}
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;
}
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;
}
self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL); self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
NM_VPN_SERVICE_GET_PRIVATE (self)->name = g_strdup (name); priv = NM_VPN_SERVICE_GET_PRIVATE (self);
NM_VPN_SERVICE_GET_PRIVATE (self)->dbus_service = g_strdup (dbus_service); priv->namefile = g_strdup (namefile);
NM_VPN_SERVICE_GET_PRIVATE (self)->program = g_strdup (program);
NM_VPN_SERVICE_GET_PRIVATE (self)->namefile = g_strdup (namefile); priv->dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", error);
if (!priv->dbus_service)
goto error;
priv->program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", error);
if (!priv->program)
goto error;
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_key_file_free (kf);
g_free (dbus_service);
g_free (program);
g_free (name);
return self; return self;
error:
g_object_unref (self);
g_key_file_free (kf);
return NULL;
} }
const char * const char *
@@ -120,46 +115,60 @@ nm_vpn_service_get_name_file (NMVPNService *service)
return NM_VPN_SERVICE_GET_PRIVATE (service)->namefile; 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 void
nm_vpn_service_connections_stop (NMVPNService *service, nm_vpn_service_stop_connections (NMVPNService *service,
gboolean fail, gboolean quitting,
NMVPNConnectionStateReason reason) NMVPNConnectionStateReason reason)
{ {
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
GSList *iter, *copy; GSList *iter;
/* Copy because stopping the connection may remove it from the list /* Just add priv->active to the beginning of priv->pending,
* in the NMVPNService objects' VPN connection state handler. * since we're going to clear priv->pending immediately anyway.
*/ */
copy = g_slist_copy (priv->connections); if (priv->active) {
for (iter = copy; iter; iter = iter->next) { priv->pending = g_slist_prepend (priv->pending, priv->active);
if (fail) priv->active = NULL;
nm_vpn_connection_fail (NM_VPN_CONNECTION (iter->data), reason);
else
nm_vpn_connection_disconnect (NM_VPN_CONNECTION (iter->data), reason);
} }
g_slist_free (copy);
for (iter = priv->pending; iter; iter = iter->next) {
NMVPNConnection *vpn = NM_VPN_CONNECTION (iter->data);
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);
} }
static void static void
clear_quit_timeout (NMVPNService *self) _daemon_setup (gpointer user_data G_GNUC_UNUSED)
{
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
if (priv->quit_timeout) {
g_source_remove (priv->quit_timeout);
priv->quit_timeout = 0;
}
}
/*
* 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)
{ {
/* We are in the child process at this point */ /* We are in the child process at this point */
pid_t pid = getpid (); pid_t pid = getpid ();
@@ -172,73 +181,37 @@ nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED)
nm_unblock_posix_signals (NULL); 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 static gboolean
nm_vpn_service_timeout (gpointer data) _daemon_exec_timeout (gpointer data)
{ {
NMVPNService *self = NM_VPN_SERVICE (data); NMVPNService *self = NM_VPN_SERVICE (data);
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", priv->name); nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", priv->name);
priv->start_timeout = 0; priv->start_timeout = 0;
nm_vpn_service_connections_stop (self, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT); nm_vpn_service_stop_connections (self, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
return FALSE; return G_SOURCE_REMOVE;
} }
static gboolean static gboolean
nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
{ {
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
GPid pid;
char *vpn_argv[2]; char *vpn_argv[2];
gboolean success = FALSE; gboolean success = FALSE;
GError *spawn_error = NULL; GError *spawn_error = NULL;
g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); 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[0] = priv->program;
vpn_argv[1] = NULL; vpn_argv[1] = NULL;
success = g_spawn_async (NULL, vpn_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, success = g_spawn_async (NULL, vpn_argv, NULL, 0, _daemon_setup, NULL, &pid, &spawn_error);
nm_vpn_service_child_setup, NULL, &priv->pid,
&spawn_error);
if (success) { if (success) {
nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d", nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d",
priv->name, priv->dbus_service, priv->pid); priv->name, priv->dbus_service, pid);
priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, service);
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);
} else { } else {
nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.", nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.",
priv->name, 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, NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED,
"%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); "%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) if (spawn_error)
g_error_free (spawn_error); g_error_free (spawn_error);
} }
@@ -258,69 +231,42 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
} }
static gboolean 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) if (!priv->active)
kill (pid, SIGKILL); return TRUE;
/* ensure the child is reaped */ if (priv->service_running) {
nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid); /* Just activate the VPN */
waitpid (pid, NULL, 0); nm_vpn_connection_activate (priv->active);
nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid); 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 static gboolean
service_quit (gpointer user_data) start_pending_vpn (NMVPNService *self)
{ {
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
if (priv->pid) { g_assert (priv->active == NULL);
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 */ if (!priv->pending)
nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid); return TRUE;
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;
return FALSE; /* Make next VPN active */
} priv->active = g_slist_nth_data (priv->pending, 0);
priv->pending = g_slist_remove (priv->pending, priv->active);
static void return start_active_vpn (self, NULL);
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);
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);
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;
}
} }
gboolean gboolean
@@ -337,45 +283,36 @@ nm_vpn_service_activate (NMVPNService *service,
priv = NM_VPN_SERVICE_GET_PRIVATE (service); priv = NM_VPN_SERVICE_GET_PRIVATE (service);
clear_quit_timeout (service);
g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
G_CALLBACK (connection_vpn_state_changed), G_CALLBACK (connection_vpn_state_changed),
service); 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)) /* Tell the active VPN to deactivate and wait for it to quit before we
nm_vpn_connection_activate (vpn); * start the next VPN. The just-queued VPN will then be started from
else if (priv->start_timeout == 0) { * connection_vpn_state_changed().
nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name); */
if (!nm_vpn_service_daemon_exec (service, error)) if (priv->active) {
return FALSE; nm_vpn_connection_deactivate (priv->active, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE);
return TRUE;
} }
return TRUE; /* Otherwise start the next VPN */
} return start_pending_vpn (service);
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;
} }
static void static void
nm_vpn_service_name_owner_changed (NMDBusManager *mgr, _name_owner_changed (NMDBusManager *mgr,
const char *name, const char *name,
const char *old, const char *old,
const char *new, const char *new,
gpointer user_data) gpointer user_data)
{ {
NMVPNService *service = NM_VPN_SERVICE (user_data); NMVPNService *service = NM_VPN_SERVICE (user_data);
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
gboolean old_owner_good; gboolean old_owner_good, new_owner_good, success;
gboolean new_owner_good;
GSList *iter;
if (strcmp (name, priv->dbus_service)) if (strcmp (name, priv->dbus_service))
return; return;
@@ -386,20 +323,21 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
priv->start_timeout = 0; priv->start_timeout = 0;
} }
old_owner_good = (old && (strlen (old) > 0)); old_owner_good = (old && old[0]);
new_owner_good = (new && (strlen (new) > 0)); new_owner_good = (new && new[0]);
if (!old_owner_good && new_owner_good) { 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); nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name);
clear_quit_timeout (service); /* Expect success because the VPN service has already appeared */
success = start_active_vpn (service, NULL);
for (iter = priv->connections; iter; iter = iter->next) g_warn_if_fail (success);
nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data));
} else if (old_owner_good && !new_owner_good) { } else if (old_owner_good && !new_owner_good) {
/* service went away */ /* service went away */
priv->service_running = FALSE;
nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", priv->name); 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 static void
nm_vpn_service_init (NMVPNService *self) nm_vpn_service_init (NMVPNService *self)
{ {
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); g_signal_connect (nm_dbus_manager_get (),
NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
priv->dbus_mgr = nm_dbus_manager_get (); G_CALLBACK (_name_owner_changed),
priv->name_owner_id = g_signal_connect (priv->dbus_mgr, self);
NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
G_CALLBACK (nm_vpn_service_name_owner_changed),
self);
} }
static void static void
@@ -423,34 +358,33 @@ dispose (GObject *object)
NMVPNService *self = NM_VPN_SERVICE (object); NMVPNService *self = NM_VPN_SERVICE (object);
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
if (priv->disposed) if (priv->start_timeout) {
goto out;
priv->disposed = TRUE;
if (priv->start_timeout)
g_source_remove (priv->start_timeout); g_source_remove (priv->start_timeout);
priv->start_timeout = 0;
}
nm_vpn_service_connections_stop (NM_VPN_SERVICE (object), /* VPNService owner is required to stop connections before releasing */
FALSE, g_assert (priv->active == NULL);
NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); g_assert (priv->pending == NULL);
g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id); g_signal_handlers_disconnect_by_func (nm_dbus_manager_get (),
G_CALLBACK (_name_owner_changed),
self);
if (priv->child_watch) G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
g_source_remove (priv->child_watch); }
clear_quit_timeout (self); static void
service_quit (self); finalize (GObject *object)
{
priv->dbus_mgr = NULL; NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object);
g_free (priv->name); g_free (priv->name);
g_free (priv->dbus_service); g_free (priv->dbus_service);
g_free (priv->program); g_free (priv->program);
g_free (priv->namefile); g_free (priv->namefile);
out: G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object);
G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
} }
static void static void
@@ -462,4 +396,5 @@ nm_vpn_service_class_init (NMVPNServiceClass *service_class)
/* virtual methods */ /* virtual methods */
object_class->dispose = dispose; object_class->dispose = dispose;
object_class->finalize = finalize;
} }

View File

@@ -34,8 +34,6 @@
#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_SERVICE)) #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 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 { typedef struct {
GObject parent; GObject parent;
} NMVPNService; } NMVPNService;
@@ -58,10 +56,8 @@ gboolean nm_vpn_service_activate (NMVPNService *service,
NMVPNConnection *vpn, NMVPNConnection *vpn,
GError **error); GError **error);
const GSList *nm_vpn_service_get_active_connections (NMVPNService *service); void nm_vpn_service_stop_connections (NMVPNService *service,
gboolean quitting,
void nm_vpn_service_connections_stop (NMVPNService *service,
gboolean fail,
NMVPNConnectionStateReason reason); NMVPNConnectionStateReason reason);
#endif /* NM_VPN_VPN_SERVICE_H */ #endif /* NM_VPN_VPN_SERVICE_H */