merge: add blocking dispatcher pre-up and pre-down events (bgo #387832)
https://bugzilla.gnome.org/show_bug.cgi?id=387832 https://bugzilla.redhat.com/show_bug.cgi?id=1048345 https://bugzilla.redhat.com/show_bug.cgi?id=982734
This commit is contained in:
@@ -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)
|
||||
|
@@ -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,
|
@@ -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"
|
||||
|
@@ -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 ());
|
@@ -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
|
||||
|
||||
|
@@ -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"
|
||||
|
||||
/*******************************************/
|
||||
|
@@ -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/*
|
||||
|
@@ -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
|
||||
|
@@ -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
@@ -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);
|
||||
|
@@ -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",
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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. */
|
||||
|
@@ -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
|
||||
|
@@ -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, "/");
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
Reference in New Issue
Block a user