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
libexec_PROGRAMS = \
nm-dispatcher.action \
nm-dispatcher \
nm-avahi-autoipd.action
@@ -38,13 +38,13 @@ nm_avahi_autoipd_action_LDADD = \
$(GLIB_LIBS)
nm_dispatcher_action_SOURCES = \
nm-dispatcher-action.c \
nm-dispatcher-action.h \
nm_dispatcher_SOURCES = \
nm-dispatcher.c \
nm-dispatcher-api.h \
nm-dispatcher-utils.c \
nm-dispatcher-utils.h
nm_dispatcher_action_LDADD = \
nm_dispatcher_LDADD = \
$(top_builddir)/libnm-util/libnm-util.la \
$(DBUS_LIBS) \
$(GLIB_LIBS)

View File

@@ -20,6 +20,10 @@
#include <dbus/dbus-glib.h>
#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d"
#define NMD_PRE_UP_DIR NMD_SCRIPT_DIR "/pre-up.d"
#define NMD_PRE_DOWN_DIR NMD_SCRIPT_DIR "/pre-down.d"
/* dbus-glib types for dispatcher call return value */
#define DISPATCHER_TYPE_RESULT (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID))
#define DISPATCHER_TYPE_RESULT_ARRAY (dbus_g_type_get_collection ("GPtrArray", DISPATCHER_TYPE_RESULT))
@@ -36,6 +40,19 @@
#define NMD_DEVICE_PROPS_STATE "state"
#define NMD_DEVICE_PROPS_PATH "path"
/* Actions */
#define NMD_ACTION_HOSTNAME "hostname"
#define NMD_ACTION_PRE_UP "pre-up"
#define NMD_ACTION_UP "up"
#define NMD_ACTION_PRE_DOWN "pre-down"
#define NMD_ACTION_DOWN "down"
#define NMD_ACTION_VPN_PRE_UP "vpn-pre-up"
#define NMD_ACTION_VPN_UP "vpn-up"
#define NMD_ACTION_VPN_PRE_DOWN "vpn-pre-down"
#define NMD_ACTION_VPN_DOWN "vpn-down"
#define NMD_ACTION_DHCP4_CHANGE "dhcp4-change"
#define NMD_ACTION_DHCP6_CHANGE "dhcp6-change"
typedef enum {
DISPATCH_RESULT_UNKNOWN = 0,
DISPATCH_RESULT_SUCCESS = 1,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -53,9 +53,9 @@
<title>Dispatcher scripts</title>
<para>
NetworkManager will execute scripts in the
/etc/NetworkManager/dispatcher.d directory in alphabetical order
in response to network events. Each script should be a regular
executable file, owned by root. Furthermore, it must not be
/etc/NetworkManager/dispatcher.d directory or subdirectories in
alphabetical order in response to network events. Each script should
be a regular executable file owned by root. Furthermore, it must not be
writable by group or other, and not setuid.
</para>
<para>
@@ -64,22 +64,65 @@
</para>
<para>The actions are:</para>
<variablelist class="dispatcher-options">
<varlistentry>
<term><varname>pre-up</varname></term>
<listitem><para>The interface is connected to the network but is not
yet fully activated. Scripts acting on this event must be placed or
symlinked into the /etc/NetworkManager/dispatcher.d/pre-up.d directory,
and NetworkManager will wait for script execution to complete before
indicating to applications that the interface is fully activated.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>up</varname></term>
<listitem><para>The interface has been activated.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>pre-down</varname></term>
<listitem><para>The interface will be deactivated but has not yet been
disconnected from the network. Scripts acting on this event must be
placed or symlinked into the /etc/NetworkManager/dispatcher.d/pre-down.d
directory, and NetworkManager will wait for script execution to complete
before disconnecting the interface from its network. Note that this
event is not emitted for forced disconnections, like when carrier is
lost or a wireless signal fades. It is only emitted when there is
an opportunity to cleanly handle a network disconnection event.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>down</varname></term>
<listitem><para>
The interface has been deactivated.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>vpn-pre-up</varname></term>
<listitem><para>The VPN is connected to the network but is not yet
fully activated. Scripts acting on this event must be placed or
symlinked into the /etc/NetworkManager/dispatcher.d/pre-up.d directory,
and NetworkManager will wait for script execution to complete before
indicating to applications that the VPN is fully activated.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>vpn-up</varname></term>
<listitem><para>
A VPN connection has been activated.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>vpn-pre-down</varname></term>
<listitem><para>The VPN will be deactivated but has not yet been
disconnected from the network. Scripts acting on this event must be
placed or symlinked into the /etc/NetworkManager/dispatcher.d/pre-down.d
directory, and NetworkManager will wait for script execution to complete
before disconnecting the VPN from its network. Note that this
event is not emitted for forced disconnections, like when the VPN
terminates unexpectedly or general connectivity is lost. It is only
emitted when there is an opportunity to cleanly handle a VPN
disconnection event.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>vpn-down</varname></term>
<listitem><para>

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);
const char * nm_device_get_path (NMDevice *dev);
void nm_device_set_path (NMDevice *dev, const char *path);
void nm_device_dbus_export (NMDevice *device);
const char * nm_device_get_udi (NMDevice *dev);
const char * nm_device_get_iface (NMDevice *dev);
@@ -228,7 +228,6 @@ const char * nm_device_get_ip_iface (NMDevice *dev);
int nm_device_get_ip_ifindex(NMDevice *dev);
const char * nm_device_get_driver (NMDevice *dev);
const char * nm_device_get_driver_version (NMDevice *dev);
const char * nm_device_get_firmware_version (NMDevice *dev);
const char * nm_device_get_type_desc (NMDevice *dev);
NMDeviceType nm_device_get_device_type (NMDevice *dev);
@@ -248,9 +247,7 @@ void nm_device_set_vpn6_config (NMDevice *dev, NMIP6Config *config)
void nm_device_capture_initial_config (NMDevice *dev);
/* Master */
gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure);
GSList * nm_device_master_get_slaves (NMDevice *dev);
gboolean nm_device_is_master (NMDevice *dev);
/* Slave */
NMDevice * nm_device_get_master (NMDevice *dev);
@@ -275,10 +272,7 @@ gboolean nm_device_complete_connection (NMDevice *device,
gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection);
gboolean nm_device_can_assume_connections (NMDevice *device);
NMConnection * nm_device_find_assumable_connection (NMDevice *device,
const GSList *connections);
gboolean nm_device_can_assume_active_connection (NMDevice *device);
gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs);
@@ -313,19 +307,18 @@ typedef enum {
} NMUnmanagedFlags;
gboolean nm_device_get_managed (NMDevice *device);
gboolean nm_device_get_default_unmanaged (NMDevice *device);
gboolean nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag);
void nm_device_set_unmanaged (NMDevice *device,
NMUnmanagedFlags flag,
gboolean unmanaged,
NMDeviceStateReason reason);
void nm_device_set_unmanaged_quitting (NMDevice *device);
void nm_device_set_initial_unmanaged_flag (NMDevice *device,
NMUnmanagedFlags flag,
gboolean unmanaged);
gboolean nm_device_get_is_nm_owned (NMDevice *device);
gboolean nm_device_set_is_nm_owned (NMDevice *device,
gboolean is_nm_owned);
void nm_device_set_nm_owned (NMDevice *device);
gboolean nm_device_get_autoconnect (NMDevice *device);
@@ -341,8 +334,6 @@ void nm_device_queue_state (NMDevice *self,
NMDeviceState state,
NMDeviceStateReason reason);
void nm_device_queue_ip_config_change (NMDevice *self);
gboolean nm_device_get_firmware_missing (NMDevice *self);
void nm_device_queue_activation (NMDevice *device, NMActRequest *req);
@@ -356,10 +347,6 @@ gboolean nm_device_has_pending_action (NMDevice *device);
GPtrArray *nm_device_get_available_connections (NMDevice *device,
const char *specific_object);
const char *nm_device_get_physical_port_id (NMDevice *device);
guint32 nm_device_get_mtu (NMDevice *device);
gboolean nm_device_connection_is_available (NMDevice *device,
NMConnection *connection,
gboolean allow_device_override);

View File

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

View File

@@ -23,7 +23,7 @@
#include <string.h>
#include "nm-dispatcher.h"
#include "nm-dispatcher-action.h"
#include "nm-dispatcher-api.h"
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
#include "nm-logging.h"
@@ -31,7 +31,10 @@
#include "nm-dbus-glib-types.h"
#include "nm-glib-compat.h"
static GSList *requests = NULL;
#define CALL_TIMEOUT (1000 * 60 * 10) /* 10 mintues for all scripts */
static gboolean do_dispatch = TRUE;
static GHashTable *requests = NULL;
static void
dump_object_to_props (GObject *object, GHashTable *hash)
@@ -132,17 +135,37 @@ fill_vpn_props (NMIP4Config *ip4_config,
}
typedef struct {
guint request_id;
DispatcherFunc callback;
gpointer user_data;
guint idle_id;
} DispatchInfo;
static void
dispatcher_info_free (DispatchInfo *info)
{
requests = g_slist_remove (requests, info);
if (info->idle_id)
g_source_remove (info->idle_id);
g_free (info);
}
static void
_ensure_requests (void)
{
if (G_UNLIKELY (requests == NULL)) {
requests = g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
(GDestroyNotify) dispatcher_info_free);
}
}
static void
dispatcher_info_cleanup (DispatchInfo *info)
{
g_hash_table_remove (requests, GUINT_TO_POINTER (info->request_id));
}
static const char *
dispatch_result_to_string (DispatchResult result)
{
@@ -161,115 +184,149 @@ dispatch_result_to_string (DispatchResult result)
g_assert_not_reached ();
}
static gboolean
validate_element (guint request_id, GValue *val, GType expected_type, guint idx, guint eltnum)
{
if (G_VALUE_TYPE (val) != expected_type) {
nm_log_dbg (LOGD_DISPATCH, "(%u) result %d element %d invalid type %s",
request_id, idx, eltnum, G_VALUE_TYPE_NAME (val));
return FALSE;
}
return TRUE;
}
static void
dispatcher_results_process (guint request_id, GPtrArray *results)
{
guint i;
g_return_if_fail (results != NULL);
for (i = 0; i < results->len; i++) {
GValueArray *item = g_ptr_array_index (results, i);
GValue *tmp;
const char *script, *err;
DispatchResult result;
if (item->n_values != 3) {
nm_log_dbg (LOGD_DISPATCH, "(%u) unexpected number of items in "
"dispatcher result (got %d, expected 3)",
request_id, item->n_values);
continue;
}
/* Script */
tmp = g_value_array_get_nth (item, 0);
if (!validate_element (request_id, tmp, G_TYPE_STRING, i, 0))
continue;
script = g_value_get_string (tmp);
if (!script || strncmp (script, NMD_SCRIPT_DIR "/", STRLEN (NMD_SCRIPT_DIR "/")))
continue;
/* Result */
tmp = g_value_array_get_nth (item, 1);
if (!validate_element (request_id, tmp, G_TYPE_UINT, i, 1))
continue;
result = g_value_get_uint (tmp);
/* Error */
tmp = g_value_array_get_nth (item, 2);
if (!validate_element (request_id, tmp, G_TYPE_STRING, i, 2))
continue;
err = g_value_get_string (tmp);
if (result == DISPATCH_RESULT_SUCCESS) {
nm_log_dbg (LOGD_DISPATCH, "(%u) %s succeeded",
request_id,
script + STRLEN (NMD_SCRIPT_DIR "/"));
} else {
nm_log_warn (LOGD_DISPATCH, "(%u) %s failed (%s): %s",
request_id,
script + STRLEN (NMD_SCRIPT_DIR "/"),
dispatch_result_to_string (result),
err ? err : "");
}
}
}
static void
free_results (GPtrArray *results)
{
g_return_if_fail (results != NULL);
g_ptr_array_foreach (results, (GFunc) g_value_array_free, NULL);
g_ptr_array_free (results, TRUE);
}
static void
dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
{
DispatchInfo *info = user_data;
GError *error = NULL;
GPtrArray *results = NULL;
guint i;
if (dbus_g_proxy_end_call (proxy, call, &error,
DISPATCHER_TYPE_RESULT_ARRAY, &results,
G_TYPE_INVALID)) {
for (i = 0; results && (i < results->len); i++) {
GValueArray *item = g_ptr_array_index (results, i);
GValue *tmp;
const char *script, *err;
DispatchResult result;
if (item->n_values != 3) {
nm_log_dbg (LOGD_DISPATCH, "Unexpected number of items in "
"dispatcher result (got %d, expectd 3)",
item->n_values);
goto next;
}
/* Script */
tmp = g_value_array_get_nth (item, 0);
if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) {
nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 0 invalid type %s",
i, G_VALUE_TYPE_NAME (tmp));
goto next;
}
script = g_value_get_string (tmp);
/* Result */
tmp = g_value_array_get_nth (item, 1);
if (G_VALUE_TYPE (tmp) != G_TYPE_UINT) {
nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 1 invalid type %s",
i, G_VALUE_TYPE_NAME (tmp));
goto next;
}
result = g_value_get_uint (tmp);
/* Error */
tmp = g_value_array_get_nth (item, 2);
if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) {
nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 2 invalid type %s",
i, G_VALUE_TYPE_NAME (tmp));
goto next;
}
err = g_value_get_string (tmp);
if (result != DISPATCH_RESULT_SUCCESS) {
nm_log_warn (LOGD_DISPATCH, "Dispatcher script \"%s\" failed with %s: %s",
script, dispatch_result_to_string (result), err);
}
next:
g_value_array_free (item);
}
g_ptr_array_free (results, TRUE);
dispatcher_results_process (info->request_id, results);
free_results (results);
} else {
g_assert (error);
nm_log_warn (LOGD_DISPATCH, "Dispatcher failed: (%d) %s", error->code, error->message);
nm_log_warn (LOGD_DISPATCH, "(%u) failed: (%d) %s",
info->request_id, error->code, error->message);
}
if (info->callback)
info->callback (info, info->user_data);
info->callback (info->request_id, info->user_data);
g_clear_error (&error);
g_object_unref (proxy);
}
static const char *action_table[] = {
[DISPATCHER_ACTION_HOSTNAME] = NMD_ACTION_HOSTNAME,
[DISPATCHER_ACTION_PRE_UP] = NMD_ACTION_PRE_UP,
[DISPATCHER_ACTION_UP] = NMD_ACTION_UP,
[DISPATCHER_ACTION_PRE_DOWN] = NMD_ACTION_PRE_DOWN,
[DISPATCHER_ACTION_DOWN] = NMD_ACTION_DOWN,
[DISPATCHER_ACTION_VPN_PRE_UP] = NMD_ACTION_VPN_PRE_UP,
[DISPATCHER_ACTION_VPN_UP] = NMD_ACTION_VPN_UP,
[DISPATCHER_ACTION_VPN_PRE_DOWN] = NMD_ACTION_VPN_PRE_DOWN,
[DISPATCHER_ACTION_VPN_DOWN] = NMD_ACTION_VPN_DOWN,
[DISPATCHER_ACTION_DHCP4_CHANGE] = NMD_ACTION_DHCP4_CHANGE,
[DISPATCHER_ACTION_DHCP6_CHANGE] = NMD_ACTION_DHCP6_CHANGE,
};
static const char *
action_to_string (DispatcherAction action)
{
switch (action) {
case DISPATCHER_ACTION_HOSTNAME:
return "hostname";
case DISPATCHER_ACTION_UP:
return "up";
case DISPATCHER_ACTION_PRE_DOWN:
return "pre-down";
case DISPATCHER_ACTION_DOWN:
return "down";
case DISPATCHER_ACTION_VPN_UP:
return "vpn-up";
case DISPATCHER_ACTION_VPN_PRE_DOWN:
return "vpn-pre-down";
case DISPATCHER_ACTION_VPN_DOWN:
return "vpn-down";
case DISPATCHER_ACTION_DHCP4_CHANGE:
return "dhcp4-change";
case DISPATCHER_ACTION_DHCP6_CHANGE:
return "dhcp6-change";
default:
break;
}
g_assert_not_reached ();
g_assert (action >= 0 && action < G_N_ELEMENTS (action_table));
return action_table[action];
}
static 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,
gboolean blocking,
NMConnection *connection,
NMDevice *device,
const char *vpn_iface,
NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config,
DispatcherFunc callback,
gpointer user_data)
gpointer user_data,
guint *out_call_id)
{
DBusGProxy *proxy;
DBusGConnection *g_connection;
@@ -282,22 +339,49 @@ _dispatcher_call (DispatcherAction action,
GHashTable *device_dhcp6_props;
GHashTable *vpn_ip4_props;
GHashTable *vpn_ip6_props;
DispatchInfo *info;
DispatchInfo *info = NULL;
gboolean success = FALSE;
GError *error = NULL;
static guint request_counter = 0;
guint reqid = ++request_counter;
/* Wrapping protection */
if (G_UNLIKELY (!reqid))
reqid = ++request_counter;
g_assert (!blocking || (!callback && !user_data));
_ensure_requests ();
/* All actions except 'hostname' require a device */
if (action == DISPATCHER_ACTION_HOSTNAME) {
nm_log_dbg (LOGD_DISPATCH, "dispatching action '%s'",
action_to_string (action));
nm_log_dbg (LOGD_DISPATCH, "(%u) dispatching action '%s'",
reqid, action_to_string (action));
} else {
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
nm_log_dbg (LOGD_DISPATCH, "(%s) dispatching action '%s'",
nm_device_get_iface (device), action_to_string (action));
nm_log_dbg (LOGD_DISPATCH, "(%u) (%s) dispatching action '%s'",
reqid,
vpn_iface ? vpn_iface : nm_device_get_iface (device),
action_to_string (action));
}
/* VPN actions require at least an IPv4 config (for now) */
if (action == DISPATCHER_ACTION_VPN_UP)
g_return_val_if_fail (vpn_ip4_config != NULL, NULL);
g_return_val_if_fail (vpn_ip4_config != NULL, FALSE);
if (do_dispatch == FALSE) {
if (blocking == FALSE && (out_call_id || callback)) {
info = g_malloc0 (sizeof (*info));
info->request_id = reqid;
info->callback = callback;
info->user_data = user_data;
info->idle_id = g_idle_add (dispatcher_idle_cb, info);
}
nm_log_dbg (LOGD_DISPATCH, "(%u) ignoring request; no scripts in " NMD_SCRIPT_DIR, reqid);
success = TRUE;
goto done;
}
g_connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
proxy = dbus_g_proxy_new_for_name (g_connection,
@@ -305,8 +389,8 @@ _dispatcher_call (DispatcherAction action,
NM_DISPATCHER_DBUS_PATH,
NM_DISPATCHER_DBUS_IFACE);
if (!proxy) {
nm_log_err (LOGD_DISPATCH, "could not get dispatcher proxy!");
return NULL;
nm_log_err (LOGD_DISPATCH, "(%u) could not get dispatcher proxy!", reqid);
return FALSE;
}
if (connection) {
@@ -341,29 +425,61 @@ _dispatcher_call (DispatcherAction action,
fill_vpn_props (vpn_ip4_config, vpn_ip6_config, vpn_ip4_props, vpn_ip6_props);
}
info = g_malloc0 (sizeof (*info));
info->callback = callback;
info->user_data = user_data;
/* Send the action to the dispatcher */
dbus_g_proxy_begin_call_with_timeout (proxy, "Action",
dispatcher_done_cb,
info,
(GDestroyNotify) dispatcher_info_free,
30000,
G_TYPE_STRING, action_to_string (action),
DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
G_TYPE_STRING, vpn_iface ? vpn_iface : "",
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH),
G_TYPE_INVALID);
if (blocking) {
GPtrArray *results = NULL;
success = dbus_g_proxy_call_with_timeout (proxy, "Action",
CALL_TIMEOUT,
&error,
G_TYPE_STRING, action_to_string (action),
DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
G_TYPE_STRING, vpn_iface ? vpn_iface : "",
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH),
G_TYPE_INVALID,
DISPATCHER_TYPE_RESULT_ARRAY, &results,
G_TYPE_INVALID);
if (success) {
dispatcher_results_process (reqid, results);
free_results (results);
} else {
nm_log_warn (LOGD_DISPATCH, "(%u) failed: (%d) %s", reqid, error->code, error->message);
g_error_free (error);
}
} else {
info = g_malloc0 (sizeof (*info));
info->request_id = reqid;
info->callback = callback;
info->user_data = user_data;
dbus_g_proxy_begin_call_with_timeout (proxy, "Action",
dispatcher_done_cb,
info,
(GDestroyNotify) dispatcher_info_cleanup,
CALL_TIMEOUT,
G_TYPE_STRING, action_to_string (action),
DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
G_TYPE_STRING, vpn_iface ? vpn_iface : "",
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH),
G_TYPE_INVALID);
success = TRUE;
}
g_hash_table_destroy (connection_hash);
g_hash_table_destroy (connection_props);
g_hash_table_destroy (device_props);
@@ -374,45 +490,192 @@ _dispatcher_call (DispatcherAction action,
g_hash_table_destroy (vpn_ip4_props);
g_hash_table_destroy (vpn_ip6_props);
/* Track the request in case of cancelation */
requests = g_slist_append (requests, info);
done:
if (success && info) {
/* Track the request in case of cancelation */
g_hash_table_insert (requests, GUINT_TO_POINTER (info->request_id), info);
if (out_call_id)
*out_call_id = info->request_id;
} else if (out_call_id)
*out_call_id = 0;
return info;
return success;
}
gconstpointer
/**
* nm_dispatcher_call:
* @action: the %DispatcherAction
* @connection: the #NMConnection the action applies to
* @device: the #NMDevice the action applies to
* @callback: a caller-supplied callback to execute when done
* @user_data: caller-supplied pointer passed to @callback
* @out_call_id: on success, a call identifier which can be passed to
* nm_dispatcher_call_cancel()
*
* This method always invokes the dispatcher action asynchronously. To ignore
* the result, pass %NULL to @callback.
*
* Returns: %TRUE if the action was dispatched, %FALSE on failure
*/
gboolean
nm_dispatcher_call (DispatcherAction action,
NMConnection *connection,
NMDevice *device,
DispatcherFunc callback,
gpointer user_data)
gpointer user_data,
guint *out_call_id)
{
return _dispatcher_call (action, 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,
NMConnection *connection,
NMDevice *device,
NMDevice *parent_device,
const char *vpn_iface,
NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config,
DispatcherFunc callback,
gpointer user_data)
gpointer user_data,
guint *out_call_id)
{
return _dispatcher_call (action, 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
nm_dispatcher_call_cancel (gconstpointer call)
nm_dispatcher_call_cancel (guint call_id)
{
/* 'call' is really a DispatchInfo pointer, just opaque to callers.
* Look it up in our requests list, but don't access it directly before
* we've made sure it's a valid request,since it may have long since been
* freed. Canceling just means the callback doesn't get called, so set
* the DispatcherInfo's callback to NULL.
DispatchInfo *info;
_ensure_requests ();
/* Canceling just means the callback doesn't get called, so set the
* DispatcherInfo's callback to NULL.
*/
if (g_slist_find (requests, call))
((DispatchInfo *) call)->callback = NULL;
info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id));
if (info)
info->callback = NULL;
else
g_return_if_reached ();
}
typedef struct {
const char *dir;
GFileMonitor *monitor;
gboolean has_scripts;
} Monitor;
static Monitor monitors[3] = {
{ NMD_SCRIPT_DIR, NULL, TRUE },
{ NMD_PRE_UP_DIR, NULL, TRUE },
{ NMD_PRE_DOWN_DIR, NULL, TRUE }
};
static void
dispatcher_dir_changed (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
Monitor *item)
{
GDir *dir;
guint i;
/* Default to dispatching on any errors */
item->has_scripts = TRUE;
dir = g_dir_open (item->dir, 0, NULL);
if (dir) {
item->has_scripts = !!g_dir_read_name (dir);
g_dir_close (dir);
}
/* Recheck all dirs for scripts and update global variable */
do_dispatch = FALSE;
for (i = 0; i < G_N_ELEMENTS (monitors); i++)
do_dispatch |= monitors[i].has_scripts;
}
void
nm_dispatcher_init (void)
{
GFile *file;
guint i;
for (i = 0; i < G_N_ELEMENTS (monitors); i++) {
file = g_file_new_for_path (monitors[i].dir);
monitors[i].monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
if (monitors[i].monitor) {
g_signal_connect (monitors[i].monitor, "changed", G_CALLBACK (dispatcher_dir_changed), &monitors[i]);
dispatcher_dir_changed (monitors[i].monitor, file, NULL, 0, &monitors[i]);
}
g_object_unref (file);
}
}

View File

@@ -32,9 +32,11 @@
typedef enum {
DISPATCHER_ACTION_HOSTNAME,
DISPATCHER_ACTION_PRE_UP,
DISPATCHER_ACTION_UP,
DISPATCHER_ACTION_PRE_DOWN,
DISPATCHER_ACTION_DOWN,
DISPATCHER_ACTION_VPN_PRE_UP,
DISPATCHER_ACTION_VPN_UP,
DISPATCHER_ACTION_VPN_PRE_DOWN,
DISPATCHER_ACTION_VPN_DOWN,
@@ -42,23 +44,38 @@ typedef enum {
DISPATCHER_ACTION_DHCP6_CHANGE
} DispatcherAction;
typedef void (*DispatcherFunc) (gconstpointer call, gpointer user_data);
typedef void (*DispatcherFunc) (guint call_id, gpointer user_data);
gconstpointer nm_dispatcher_call (DispatcherAction action,
gboolean nm_dispatcher_call (DispatcherAction action,
NMConnection *connection,
NMDevice *device,
DispatcherFunc callback,
gpointer user_data,
guint *out_call_id);
gboolean nm_dispatcher_call_sync (DispatcherAction action,
NMConnection *connection,
NMDevice *device,
DispatcherFunc callback,
gpointer user_data);
NMDevice *device);
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,
NMDevice *device,
NMDevice *parent_device,
const char *vpn_iface,
NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config,
DispatcherFunc callback,
gpointer user_data);
NMIP6Config *vpn_ip6_config);
void nm_dispatcher_call_cancel (gconstpointer call);
void nm_dispatcher_call_cancel (guint call_id);
void nm_dispatcher_init (void);
#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);
if (nm_device_get_managed (device)) {
/* Leave configured interfaces up when quitting so they can be
* taken over again if NM starts up, and to ensure connectivity while
* NM is gone. Assumed connections don't get taken down even if they
* haven't been fully activated.
NMActRequest *req = nm_device_get_act_request (device);
gboolean unmanage = FALSE;
/* 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)
|| (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED)
|| !quitting) {
NMActRequest *req = nm_device_get_act_request (device);
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);
}
if (unmanage) {
if (quitting)
nm_device_set_unmanaged_quitting (device);
else
nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
}
}
g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager);
@@ -754,8 +761,7 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting)
nm_dbus_manager_unregister_object (priv->dbus_mgr, device);
g_object_unref (device);
if (priv->startup)
check_if_startup_complete (manager);
check_if_startup_complete (manager);
}
static void
@@ -1087,7 +1093,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
}
if (device) {
nm_device_set_is_nm_owned (device, TRUE);
nm_device_set_nm_owned (device);
add_device (self, device, FALSE);
g_object_unref (device);
}
@@ -1683,8 +1689,6 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
const char *iface, *driver, *type_desc;
char *path;
static guint32 devcount = 0;
const GSList *unmanaged_specs;
gboolean user_unmanaged, sleeping;
NMConnection *connection = NULL;
@@ -1761,11 +1765,7 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con)
sleeping = manager_sleeping (self);
nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping);
path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++);
nm_device_set_path (device, path);
nm_dbus_manager_register_object (priv->dbus_mgr, path, device);
nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path);
g_free (path);
nm_device_dbus_export (device);
/* Don't generate a connection e.g. for devices NM just created, or
* for the loopback, or when we're sleeping. */

View File

@@ -326,7 +326,7 @@ _set_hostname (NMPolicy *policy,
nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
if (set_system_hostname (priv->cur_hostname, msg))
nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL);
nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL);
}
static void

View File

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

View File

@@ -74,10 +74,14 @@ void nm_vpn_connection_activate (NMVPNConnection *connect
NMConnection * nm_vpn_connection_get_connection (NMVPNConnection *connection);
NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *connection);
const char * nm_vpn_connection_get_banner (NMVPNConnection *connection);
void nm_vpn_connection_fail (NMVPNConnection *connection,
NMVPNConnectionStateReason reason);
gboolean nm_vpn_connection_deactivate (NMVPNConnection *connection,
NMVPNConnectionStateReason reason,
gboolean quitting);
void nm_vpn_connection_disconnect (NMVPNConnection *connection,
NMVPNConnectionStateReason reason);
NMVPNConnectionStateReason reason,
gboolean quitting);
NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection);
NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection);
const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection);

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))
typedef struct {
gboolean disposed;
GHashTable *services;
GFileMonitor *monitor;
guint monitor_id;
@@ -77,39 +75,11 @@ get_service_by_namefile (NMVPNManager *self, const char *namefile)
return NULL;
}
static NMVPNConnection *
find_active_vpn_connection (NMVPNManager *self, NMConnection *connection)
{
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
GHashTableIter iter;
gpointer data;
const GSList *active, *aiter;
NMVPNConnection *found = NULL;
g_return_val_if_fail (connection, NULL);
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
g_hash_table_iter_init (&iter, priv->services);
while (g_hash_table_iter_next (&iter, NULL, &data) && (found == NULL)) {
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
for (aiter = active; aiter; aiter = g_slist_next (aiter)) {
NMVPNConnection *vpn = NM_VPN_CONNECTION (aiter->data);
if (nm_vpn_connection_get_connection (vpn) == connection) {
found = vpn;
break;
}
}
}
return found;
}
gboolean
nm_vpn_manager_activate_connection (NMVPNManager *manager,
NMVPNConnection *vpn,
GError **error)
{
NMVPNConnection *existing = NULL;
NMConnection *connection;
NMSettingVPN *s_vpn;
NMVPNService *service;
@@ -145,11 +115,6 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
return FALSE;
}
existing = find_active_vpn_connection (manager,
nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (vpn)));
if (existing)
nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
return nm_vpn_service_activate (service, vpn, error);
}
@@ -158,51 +123,7 @@ nm_vpn_manager_deactivate_connection (NMVPNManager *self,
NMVPNConnection *connection,
NMVPNConnectionStateReason reason)
{
NMVPNManagerPrivate *priv;
GHashTableIter iter;
gpointer data;
const GSList *active, *aiter;
gboolean success = FALSE;
g_return_val_if_fail (self, FALSE);
g_return_val_if_fail (NM_IS_VPN_MANAGER (self), FALSE);
g_return_val_if_fail (connection != NULL, FALSE);
priv = NM_VPN_MANAGER_GET_PRIVATE (self);
g_hash_table_iter_init (&iter, priv->services);
while (g_hash_table_iter_next (&iter, NULL, &data) && (success == FALSE)) {
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
for (aiter = active; aiter; aiter = g_slist_next (aiter)) {
NMVPNConnection *candidate = aiter->data;
if (connection == candidate) {
nm_vpn_connection_disconnect (connection, reason);
success = TRUE;
break;
}
}
}
return success;
}
static char *
service_name_from_file (const char *path)
{
GKeyFile *kf = NULL;
char *service_name = NULL;
g_return_val_if_fail (g_path_is_absolute (path), NULL);
if (!g_str_has_suffix (path, ".name"))
return NULL;
kf = g_key_file_new ();
if (g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
service_name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
g_key_file_free (kf);
return service_name;
return nm_vpn_connection_deactivate (connection, reason, FALSE);
}
static void
@@ -210,34 +131,32 @@ try_add_service (NMVPNManager *self, const char *namefile)
{
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
NMVPNService *service = NULL;
GHashTableIter iter;
GError *error = NULL;
const char *service_name;
char *tmp;
g_return_if_fail (g_path_is_absolute (namefile));
/* Make sure we don't add dupes */
tmp = service_name_from_file (namefile);
if (tmp)
service = g_hash_table_lookup (priv->services, tmp);
g_free (tmp);
if (service)
return;
g_hash_table_iter_init (&iter, priv->services);
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) {
if (g_strcmp0 (namefile, nm_vpn_service_get_name_file (service)) == 0)
return;
}
/* New service, add it */
/* New service */
service = nm_vpn_service_new (namefile, &error);
if (!service) {
if (service) {
service_name = nm_vpn_service_get_dbus_service (service);
g_hash_table_insert (priv->services, (char *) service_name, service);
nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name);
} else {
nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s",
namefile,
error ? error->code : -1,
error && error->message ? error->message : "(unknown)");
g_clear_error (&error);
return;
}
service_name = nm_vpn_service_get_dbus_service (service);
g_hash_table_insert (priv->services, (char *) service_name, service);
nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name);
}
static void
@@ -267,7 +186,7 @@ vpn_dir_changed (GFileMonitor *monitor,
const char *service_name = nm_vpn_service_get_dbus_service (service);
/* Stop active VPN connections and destroy the service */
nm_vpn_service_connections_stop (service, TRUE,
nm_vpn_service_stop_connections (service, FALSE,
NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name);
g_hash_table_remove (priv->services, service_name);
@@ -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
dispose (GObject *object)
{
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
if (!priv->disposed) {
priv->disposed = TRUE;
if (priv->monitor) {
if (priv->monitor_id)
g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
g_file_monitor_cancel (priv->monitor);
g_object_unref (priv->monitor);
}
if (priv->monitor) {
if (priv->monitor_id)
g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
g_file_monitor_cancel (priv->monitor);
g_clear_object (&priv->monitor);
}
if (priv->services) {
stop_all_services (NM_VPN_MANAGER (object));
g_hash_table_destroy (priv->services);
priv->services = NULL;
}
G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object);

View File

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

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_VPN_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVPNServiceClass))
#define VPN_CONNECTION_GROUP "VPN Connection"
typedef struct {
GObject parent;
} NMVPNService;
@@ -58,10 +56,8 @@ gboolean nm_vpn_service_activate (NMVPNService *service,
NMVPNConnection *vpn,
GError **error);
const GSList *nm_vpn_service_get_active_connections (NMVPNService *service);
void nm_vpn_service_connections_stop (NMVPNService *service,
gboolean fail,
void nm_vpn_service_stop_connections (NMVPNService *service,
gboolean quitting,
NMVPNConnectionStateReason reason);
#endif /* NM_VPN_VPN_SERVICE_H */