mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-04-22 04:40:47 +02:00
vpn: handle VPN service installation/removal dynamically (rh #489114) (lp:458595)
Use inotify to watch /etc/NetworkManager/VPN for new .name files and handle their installation and removal on-the-fly instead of just at startup.
This commit is contained in:
parent
c4db66c7ed
commit
46afadc02c
3 changed files with 399 additions and 263 deletions
|
|
@ -15,11 +15,12 @@
|
|||
* 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 - 2008 Red Hat, Inc.
|
||||
* Copyright (C) 2005 - 2010 Red Hat, Inc.
|
||||
* Copyright (C) 2006 - 2008 Novell, Inc.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "nm-vpn-manager.h"
|
||||
#include "nm-vpn-service.h"
|
||||
|
|
@ -28,13 +29,18 @@
|
|||
#include "nm-dbus-manager.h"
|
||||
#include "NetworkManagerVPN.h"
|
||||
#include "nm-marshal.h"
|
||||
#include "nm-logging.h"
|
||||
|
||||
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 {
|
||||
GSList *services;
|
||||
gboolean disposed;
|
||||
|
||||
GHashTable *services;
|
||||
GFileMonitor *monitor;
|
||||
guint monitor_id;
|
||||
} NMVPNManagerPrivate;
|
||||
|
||||
enum {
|
||||
|
|
@ -81,53 +87,42 @@ nm_vpn_manager_error_get_type (void)
|
|||
}
|
||||
|
||||
|
||||
|
||||
static NMVPNService *
|
||||
nm_vpn_manager_get_service (NMVPNManager *manager, const char *service_name)
|
||||
get_service_by_namefile (NMVPNManager *self, const char *namefile)
|
||||
{
|
||||
GSList *iter;
|
||||
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
|
||||
GHashTableIter iter;
|
||||
gpointer data;
|
||||
|
||||
for (iter = NM_VPN_MANAGER_GET_PRIVATE (manager)->services; iter; iter = iter->next) {
|
||||
NMVPNService *service = NM_VPN_SERVICE (iter->data);
|
||||
g_return_val_if_fail (namefile, NULL);
|
||||
g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
|
||||
|
||||
if (!strcmp (service_name, nm_vpn_service_get_name (service)))
|
||||
return g_object_ref (service);
|
||||
g_hash_table_iter_init (&iter, priv->services);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &data)) {
|
||||
NMVPNService *candidate = NM_VPN_SERVICE (data);
|
||||
const char *service_namefile;
|
||||
|
||||
service_namefile = nm_vpn_service_get_name_file (candidate);
|
||||
if (!strcmp (namefile, service_namefile))
|
||||
return candidate;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
remove_service (gpointer data, GObject *service)
|
||||
{
|
||||
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (data);
|
||||
|
||||
priv->services = g_slist_remove (priv->services, service);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_vpn_manager_add_service (NMVPNManager *manager, NMVPNService *service)
|
||||
{
|
||||
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
|
||||
|
||||
priv->services = g_slist_prepend (priv->services, service);
|
||||
g_object_weak_ref (G_OBJECT (service), remove_service, manager);
|
||||
}
|
||||
|
||||
static NMVPNConnection *
|
||||
find_active_vpn_connection_by_connection (NMVPNManager *manager, NMConnection *connection)
|
||||
find_active_vpn_connection_by_connection (NMVPNManager *self, NMConnection *connection)
|
||||
{
|
||||
NMVPNManagerPrivate *priv;
|
||||
GSList *iter;
|
||||
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
|
||||
GHashTableIter iter;
|
||||
gpointer data;
|
||||
GSList *connections, *elt;
|
||||
|
||||
g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
|
||||
g_return_val_if_fail (connection, NULL);
|
||||
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
||||
|
||||
priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
|
||||
for (iter = priv->services; iter; iter = g_slist_next (iter)) {
|
||||
GSList *connections, *elt;
|
||||
|
||||
connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
|
||||
g_hash_table_iter_init (&iter, priv->services);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &data)) {
|
||||
connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
|
||||
for (elt = connections; elt; elt = g_slist_next (elt)) {
|
||||
NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
|
||||
|
||||
|
|
@ -169,7 +164,7 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
|
|||
NMSettingVPN *vpn_setting;
|
||||
NMVPNService *service;
|
||||
NMVPNConnection *vpn = NULL;
|
||||
const char *service_type;
|
||||
const char *service_name;
|
||||
|
||||
g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
|
||||
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
||||
|
|
@ -199,78 +194,78 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
|
|||
vpn = NULL;
|
||||
}
|
||||
|
||||
service_type = nm_setting_vpn_get_service_type (vpn_setting);
|
||||
service = nm_vpn_manager_get_service (manager, service_type);
|
||||
service_name = nm_setting_vpn_get_service_type (vpn_setting);
|
||||
g_assert (service_name);
|
||||
service = g_hash_table_lookup (NM_VPN_MANAGER_GET_PRIVATE (manager)->services, service_name);
|
||||
if (!service) {
|
||||
service = nm_vpn_service_new (service_type);
|
||||
if (service)
|
||||
nm_vpn_manager_add_service (manager, service);
|
||||
}
|
||||
|
||||
if (service) {
|
||||
vpn = nm_vpn_service_activate (service, connection, act_request, device, error);
|
||||
if (vpn) {
|
||||
g_signal_connect (vpn, "vpn-state-changed",
|
||||
G_CALLBACK (connection_vpn_state_changed),
|
||||
manager);
|
||||
}
|
||||
} else {
|
||||
g_set_error (error,
|
||||
NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID,
|
||||
"%s", "The VPN service was invalid.");
|
||||
"The VPN service '%s' was not installed.",
|
||||
service_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vpn = nm_vpn_service_activate (service, connection, act_request, device, error);
|
||||
if (vpn) {
|
||||
g_signal_connect (vpn, "vpn-state-changed",
|
||||
G_CALLBACK (connection_vpn_state_changed),
|
||||
manager);
|
||||
}
|
||||
|
||||
return vpn;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_vpn_manager_deactivate_connection (NMVPNManager *manager,
|
||||
nm_vpn_manager_deactivate_connection (NMVPNManager *self,
|
||||
const char *path,
|
||||
NMVPNConnectionStateReason reason)
|
||||
{
|
||||
NMVPNManagerPrivate *priv;
|
||||
GSList *iter;
|
||||
gboolean found = FALSE;
|
||||
GHashTableIter iter;
|
||||
gpointer data;
|
||||
GSList *active, *elt;
|
||||
|
||||
g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), FALSE);
|
||||
g_return_val_if_fail (self, FALSE);
|
||||
g_return_val_if_fail (NM_IS_VPN_MANAGER (self), FALSE);
|
||||
g_return_val_if_fail (path != NULL, FALSE);
|
||||
|
||||
priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
|
||||
for (iter = priv->services; iter; iter = g_slist_next (iter)) {
|
||||
GSList *connections, *elt;
|
||||
|
||||
connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
|
||||
for (elt = connections; elt; elt = g_slist_next (elt)) {
|
||||
priv = NM_VPN_MANAGER_GET_PRIVATE (self);
|
||||
g_hash_table_iter_init (&iter, priv->services);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &data)) {
|
||||
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
|
||||
for (elt = active; elt; elt = g_slist_next (elt)) {
|
||||
NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
|
||||
const char *vpn_path;
|
||||
|
||||
vpn_path = nm_vpn_connection_get_active_connection_path (vpn);
|
||||
if (!strcmp (path, vpn_path)) {
|
||||
nm_vpn_connection_disconnect (vpn, reason);
|
||||
found = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found ? TRUE : FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
nm_vpn_manager_add_active_connections (NMVPNManager *manager,
|
||||
nm_vpn_manager_add_active_connections (NMVPNManager *self,
|
||||
NMConnection *filter,
|
||||
GPtrArray *array)
|
||||
{
|
||||
NMVPNManagerPrivate *priv;
|
||||
GSList *iter;
|
||||
GHashTableIter iter;
|
||||
gpointer data;
|
||||
GSList *active, *elt;
|
||||
|
||||
g_return_if_fail (NM_IS_VPN_MANAGER (manager));
|
||||
g_return_if_fail (self);
|
||||
g_return_if_fail (NM_IS_VPN_MANAGER (self));
|
||||
g_return_if_fail (array != NULL);
|
||||
|
||||
priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
|
||||
for (iter = priv->services; iter; iter = g_slist_next (iter)) {
|
||||
GSList *active, *elt;
|
||||
|
||||
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
|
||||
priv = NM_VPN_MANAGER_GET_PRIVATE (self);
|
||||
g_hash_table_iter_init (&iter, priv->services);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &data)) {
|
||||
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
|
||||
for (elt = active; elt; elt = g_slist_next (elt)) {
|
||||
NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
|
||||
const char *path;
|
||||
|
|
@ -284,23 +279,23 @@ nm_vpn_manager_add_active_connections (NMVPNManager *manager,
|
|||
}
|
||||
|
||||
GSList *
|
||||
nm_vpn_manager_get_active_connections (NMVPNManager *manager)
|
||||
nm_vpn_manager_get_active_connections (NMVPNManager *self)
|
||||
{
|
||||
NMVPNManagerPrivate *priv;
|
||||
GSList *iter;
|
||||
GSList *list = NULL;
|
||||
GHashTableIter iter;
|
||||
gpointer data;
|
||||
GSList *list = NULL, *active, *elt;
|
||||
|
||||
g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (NM_IS_VPN_MANAGER (self), NULL);
|
||||
|
||||
priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
|
||||
for (iter = priv->services; iter; iter = g_slist_next (iter)) {
|
||||
GSList *active, *elt;
|
||||
|
||||
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
|
||||
priv = NM_VPN_MANAGER_GET_PRIVATE (self);
|
||||
g_hash_table_iter_init (&iter, priv->services);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &data)) {
|
||||
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
|
||||
for (elt = active; elt; elt = g_slist_next (elt))
|
||||
list = g_slist_append (list, g_object_ref (NM_VPN_CONNECTION (elt->data)));
|
||||
list = g_slist_append (list, g_object_ref (G_OBJECT (elt->data)));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
@ -309,28 +304,130 @@ nm_vpn_manager_get_connection_for_active (NMVPNManager *manager,
|
|||
const char *active_path)
|
||||
{
|
||||
NMVPNManagerPrivate *priv;
|
||||
GSList *iter;
|
||||
GHashTableIter iter;
|
||||
gpointer data;
|
||||
GSList *active, *elt;
|
||||
|
||||
g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
|
||||
|
||||
priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
|
||||
for (iter = priv->services; iter; iter = g_slist_next (iter)) {
|
||||
GSList *active, *elt;
|
||||
|
||||
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
|
||||
g_hash_table_iter_init (&iter, priv->services);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &data)) {
|
||||
active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
|
||||
for (elt = active; elt; elt = g_slist_next (elt)) {
|
||||
NMVPNConnection *candidate = NM_VPN_CONNECTION (elt->data);
|
||||
NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
|
||||
const char *ac_path;
|
||||
|
||||
ac_path = nm_vpn_connection_get_active_connection_path (candidate);
|
||||
ac_path = nm_vpn_connection_get_active_connection_path (vpn);
|
||||
if (ac_path && !strcmp (ac_path, active_path))
|
||||
return nm_vpn_connection_get_connection (candidate);
|
||||
return nm_vpn_connection_get_connection (vpn);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *
|
||||
service_name_from_file (const char *path)
|
||||
{
|
||||
GKeyFile *kf = NULL;
|
||||
char *service_name = NULL;
|
||||
|
||||
g_return_val_if_fail (g_path_is_absolute (path), NULL);
|
||||
|
||||
if (!g_str_has_suffix (path, ".name"))
|
||||
return NULL;
|
||||
|
||||
kf = g_key_file_new ();
|
||||
if (g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
|
||||
service_name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
|
||||
|
||||
g_key_file_free (kf);
|
||||
return service_name;
|
||||
}
|
||||
|
||||
static void
|
||||
try_add_service (NMVPNManager *self, const char *namefile)
|
||||
{
|
||||
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
|
||||
NMVPNService *service = NULL;
|
||||
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;
|
||||
|
||||
/* New service, add it */
|
||||
service = nm_vpn_service_new (namefile, &error);
|
||||
if (!service) {
|
||||
nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s",
|
||||
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, service);
|
||||
}
|
||||
|
||||
static void
|
||||
vpn_dir_changed (GFileMonitor *monitor,
|
||||
GFile *file,
|
||||
GFile *other_file,
|
||||
GFileMonitorEvent event_type,
|
||||
gpointer user_data)
|
||||
{
|
||||
NMVPNManager *self = NM_VPN_MANAGER (user_data);
|
||||
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
|
||||
NMVPNService *service;
|
||||
char *path;
|
||||
|
||||
path = g_file_get_path (file);
|
||||
if (!g_str_has_suffix (path, ".name")) {
|
||||
g_free (path);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event_type) {
|
||||
case G_FILE_MONITOR_EVENT_DELETED:
|
||||
nm_log_dbg (LOGD_VPN, "service file %s deleted", path);
|
||||
|
||||
service = get_service_by_namefile (self, path);
|
||||
if (service) {
|
||||
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_CONNECTION_STATE_REASON_SERVICE_STOPPED);
|
||||
nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name, service);
|
||||
g_hash_table_remove (priv->services, service_name);
|
||||
}
|
||||
break;
|
||||
case G_FILE_MONITOR_EVENT_CREATED:
|
||||
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
|
||||
nm_log_dbg (LOGD_VPN, "service file %s created or modified", path);
|
||||
try_add_service (self, path);
|
||||
break;
|
||||
default:
|
||||
nm_log_dbg (LOGD_VPN, "service file %s change event %d", path, event_type);
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
NMVPNManager *
|
||||
nm_vpn_manager_get (void)
|
||||
{
|
||||
|
|
@ -345,21 +442,61 @@ nm_vpn_manager_get (void)
|
|||
return singleton;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
nm_vpn_manager_init (NMVPNManager *manager)
|
||||
nm_vpn_manager_init (NMVPNManager *self)
|
||||
{
|
||||
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
|
||||
GFile *file;
|
||||
GDir *dir;
|
||||
const char *fn;
|
||||
char *path;
|
||||
|
||||
priv->services = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
NULL, g_object_unref);
|
||||
|
||||
/* Watch the VPN directory for changes */
|
||||
file = g_file_new_for_path (VPN_NAME_FILES_DIR "/");
|
||||
priv->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
if (priv->monitor) {
|
||||
priv->monitor_id = g_signal_connect (priv->monitor, "changed",
|
||||
G_CALLBACK (vpn_dir_changed), self);
|
||||
}
|
||||
|
||||
/* Load VPN service files */
|
||||
dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
|
||||
if (dir) {
|
||||
while ((fn = g_dir_read_name (dir))) {
|
||||
/* only parse filenames that end with .name */
|
||||
if (g_str_has_suffix (fn, ".name")) {
|
||||
path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL);
|
||||
try_add_service (self, path);
|
||||
g_free (path);
|
||||
}
|
||||
}
|
||||
g_dir_close (dir);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
finalize (GObject *object)
|
||||
dispose (GObject *object)
|
||||
{
|
||||
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
|
||||
|
||||
g_slist_foreach (priv->services, (GFunc) g_object_unref, NULL);
|
||||
if (!priv->disposed) {
|
||||
priv->disposed = TRUE;
|
||||
|
||||
G_OBJECT_CLASS (nm_vpn_manager_parent_class)->finalize (object);
|
||||
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);
|
||||
}
|
||||
|
||||
g_hash_table_destroy (priv->services);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -370,7 +507,7 @@ nm_vpn_manager_class_init (NMVPNManagerClass *manager_class)
|
|||
g_type_class_add_private (manager_class, sizeof (NMVPNManagerPrivate));
|
||||
|
||||
/* virtual methods */
|
||||
object_class->finalize = finalize;
|
||||
object_class->dispose = dispose;
|
||||
|
||||
/* signals */
|
||||
signals[CONNECTION_ACTIVATED] =
|
||||
|
|
|
|||
|
|
@ -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 - 2008 Red Hat, Inc.
|
||||
* Copyright (C) 2005 - 2010 Red Hat, Inc.
|
||||
* Copyright (C) 2005 - 2008 Novell, Inc.
|
||||
*/
|
||||
|
||||
|
|
@ -36,125 +36,94 @@
|
|||
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;
|
||||
guint service_start_timeout;
|
||||
guint service_child_watch;
|
||||
guint start_timeout;
|
||||
guint quit_timeout;
|
||||
guint child_watch;
|
||||
gulong name_owner_id;
|
||||
} 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 GKeyFile *
|
||||
find_service_file (const char *name)
|
||||
NMVPNService *
|
||||
nm_vpn_service_new (const char *namefile, GError **error)
|
||||
{
|
||||
GDir *dir;
|
||||
const char *fn;
|
||||
GKeyFile *key_file = NULL;
|
||||
NMVPNService *self = NULL;
|
||||
GKeyFile *kf;
|
||||
char *dbus_service = NULL, *program = NULL, *name = NULL;
|
||||
|
||||
dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
|
||||
if (!dir)
|
||||
g_return_val_if_fail (namefile != NULL, NULL);
|
||||
g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
|
||||
|
||||
kf = g_key_file_new ();
|
||||
if (!g_key_file_load_from_file (kf, namefile, G_KEY_FILE_NONE, error)) {
|
||||
g_key_file_free (kf);
|
||||
return NULL;
|
||||
|
||||
while ((fn = g_dir_read_name (dir))) {
|
||||
char *path;
|
||||
gboolean found = FALSE;
|
||||
|
||||
/* only parse filenames that end with .name */
|
||||
if (!g_str_has_suffix (fn, ".name"))
|
||||
continue;
|
||||
|
||||
key_file = g_key_file_new ();
|
||||
path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL);
|
||||
|
||||
if (g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL)) {
|
||||
gchar *val;
|
||||
|
||||
val = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL);
|
||||
if (val) {
|
||||
if (!strcmp (val, name))
|
||||
found = TRUE;
|
||||
g_free (val);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (path);
|
||||
|
||||
if (found)
|
||||
break;
|
||||
|
||||
g_key_file_free (key_file);
|
||||
key_file = NULL;
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
|
||||
return key_file;
|
||||
}
|
||||
|
||||
NMVPNService *
|
||||
nm_vpn_service_new (const char *name)
|
||||
{
|
||||
GKeyFile *key_file;
|
||||
NMVPNService *service = NULL;
|
||||
NMVPNServicePrivate *priv;
|
||||
char *dbus_service = NULL;
|
||||
char *program = NULL;
|
||||
gboolean success = FALSE;
|
||||
|
||||
g_return_val_if_fail (name != NULL, NULL);
|
||||
|
||||
key_file = find_service_file (name);
|
||||
if (!key_file)
|
||||
return NULL;
|
||||
|
||||
dbus_service = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL);
|
||||
if (!dbus_service)
|
||||
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 (key_file, VPN_CONNECTION_GROUP, "program", NULL);
|
||||
if (!program)
|
||||
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;
|
||||
}
|
||||
|
||||
service = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
|
||||
if (!service)
|
||||
name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", NULL);
|
||||
if (!name) {
|
||||
g_set_error (error, 0, 0, "VPN service file %s had no 'name' key", namefile);
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv = NM_VPN_SERVICE_GET_PRIVATE (service);
|
||||
self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
|
||||
if (!self) {
|
||||
g_set_error (error, 0, 0, "out of memory creating VPN service object");
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->name = g_strdup (name);
|
||||
priv->dbus_service = dbus_service;
|
||||
priv->program = program;
|
||||
|
||||
success = TRUE;
|
||||
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);
|
||||
|
||||
out:
|
||||
g_key_file_free (key_file);
|
||||
|
||||
if (!success) {
|
||||
g_free (dbus_service);
|
||||
g_free (program);
|
||||
}
|
||||
|
||||
return service;
|
||||
g_key_file_free (kf);
|
||||
g_free (dbus_service);
|
||||
g_free (program);
|
||||
g_free (name);
|
||||
return self;
|
||||
}
|
||||
|
||||
const char *
|
||||
nm_vpn_service_get_name (NMVPNService *service)
|
||||
nm_vpn_service_get_dbus_service (NMVPNService *service)
|
||||
{
|
||||
g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
|
||||
|
||||
return NM_VPN_SERVICE_GET_PRIVATE (service)->name;
|
||||
return NM_VPN_SERVICE_GET_PRIVATE (service)->dbus_service;
|
||||
}
|
||||
|
||||
static void
|
||||
const char *
|
||||
nm_vpn_service_get_name_file (NMVPNService *service)
|
||||
{
|
||||
g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
|
||||
|
||||
return NM_VPN_SERVICE_GET_PRIVATE (service)->namefile;
|
||||
}
|
||||
|
||||
void
|
||||
nm_vpn_service_connections_stop (NMVPNService *service,
|
||||
gboolean fail,
|
||||
NMVPNConnectionStateReason reason)
|
||||
|
|
@ -175,6 +144,17 @@ nm_vpn_service_connections_stop (NMVPNService *service,
|
|||
g_slist_free (copy);
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
|
|
@ -200,21 +180,22 @@ vpn_service_watch_cb (GPid pid, gint status, gpointer user_data)
|
|||
|
||||
if (err != 0) {
|
||||
nm_log_warn (LOGD_VPN, "VPN service '%s' exited with error: %d",
|
||||
nm_vpn_service_get_name (service), WSTOPSIG (status));
|
||||
priv->name, WSTOPSIG (status));
|
||||
}
|
||||
} else if (WIFSTOPPED (status)) {
|
||||
nm_log_warn (LOGD_VPN, "VPN service '%s' stopped unexpectedly with signal %d",
|
||||
nm_vpn_service_get_name (service), WSTOPSIG (status));
|
||||
priv->name, WSTOPSIG (status));
|
||||
} else if (WIFSIGNALED (status)) {
|
||||
nm_log_warn (LOGD_VPN, "VPN service '%s' died with signal %d",
|
||||
nm_vpn_service_get_name (service), WTERMSIG (status));
|
||||
priv->name, WTERMSIG (status));
|
||||
} else {
|
||||
nm_log_warn (LOGD_VPN, "VPN service '%s' died from an unknown cause",
|
||||
nm_vpn_service_get_name (service));
|
||||
priv->name);
|
||||
}
|
||||
|
||||
priv->pid = 0;
|
||||
priv->service_child_watch = 0;
|
||||
priv->child_watch = 0;
|
||||
clear_quit_timeout (service);
|
||||
|
||||
nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
|
||||
}
|
||||
|
|
@ -222,14 +203,12 @@ vpn_service_watch_cb (GPid pid, gint status, gpointer user_data)
|
|||
static gboolean
|
||||
nm_vpn_service_timeout (gpointer data)
|
||||
{
|
||||
NMVPNService *service = NM_VPN_SERVICE (data);
|
||||
|
||||
nm_log_warn (LOGD_VPN, "VPN service '%s' did not start in time, cancelling connections",
|
||||
nm_vpn_service_get_name (service));
|
||||
|
||||
NM_VPN_SERVICE_GET_PRIVATE (service)->service_start_timeout = 0;
|
||||
nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -253,13 +232,15 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
|
|||
&spawn_error);
|
||||
if (success) {
|
||||
nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d",
|
||||
nm_vpn_service_get_name (service), priv->dbus_service, priv->pid);
|
||||
priv->name, priv->dbus_service, priv->pid);
|
||||
|
||||
priv->service_child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service);
|
||||
priv->service_start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service);
|
||||
priv->child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service);
|
||||
priv->start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service);
|
||||
} else {
|
||||
nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.",
|
||||
nm_vpn_service_get_name (service), spawn_error->code, spawn_error->message);
|
||||
priv->name,
|
||||
spawn_error ? spawn_error->code : -1,
|
||||
spawn_error && spawn_error->message ? spawn_error->message : "(unknown)");
|
||||
|
||||
g_set_error (error,
|
||||
NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED,
|
||||
|
|
@ -274,9 +255,40 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
destroy_service (gpointer data)
|
||||
ensure_killed (gpointer data)
|
||||
{
|
||||
g_object_unref (data);
|
||||
int pid = GPOINTER_TO_INT (data);
|
||||
|
||||
if (kill (pid, 0) == 0)
|
||||
kill (pid, SIGKILL);
|
||||
|
||||
/* 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);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
service_quit (gpointer user_data)
|
||||
{
|
||||
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data);
|
||||
|
||||
if (priv->pid) {
|
||||
if (kill (priv->pid, SIGTERM) == 0)
|
||||
g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
|
||||
else {
|
||||
kill (priv->pid, SIGKILL);
|
||||
|
||||
/* ensure the child is reaped */
|
||||
nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid);
|
||||
waitpid (priv->pid, NULL, 0);
|
||||
nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid);
|
||||
}
|
||||
priv->pid = 0;
|
||||
}
|
||||
priv->quit_timeout = 0;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
|
@ -297,8 +309,9 @@ connection_vpn_state_changed (NMVPNConnection *connection,
|
|||
g_object_unref (connection);
|
||||
|
||||
if (priv->connections == NULL) {
|
||||
/* schedule a timeout (10 seconds) to destroy the service */
|
||||
g_timeout_add_seconds (10, destroy_service, user_data);
|
||||
/* 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:
|
||||
|
|
@ -325,6 +338,8 @@ nm_vpn_service_activate (NMVPNService *service,
|
|||
|
||||
priv = NM_VPN_SERVICE_GET_PRIVATE (service);
|
||||
|
||||
clear_quit_timeout (service);
|
||||
|
||||
vpn = nm_vpn_connection_new (connection, act_request, device);
|
||||
g_signal_connect (vpn, "vpn-state-changed",
|
||||
G_CALLBACK (connection_vpn_state_changed),
|
||||
|
|
@ -335,9 +350,8 @@ nm_vpn_service_activate (NMVPNService *service,
|
|||
if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) {
|
||||
// FIXME: fill in error when errors happen
|
||||
nm_vpn_connection_activate (vpn);
|
||||
} else if (priv->service_start_timeout == 0) {
|
||||
nm_log_info (LOGD_VPN, "Starting VPN service '%s'...",
|
||||
nm_vpn_service_get_name (service));
|
||||
} 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))
|
||||
vpn = NULL;
|
||||
}
|
||||
|
|
@ -364,14 +378,15 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
|
|||
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
|
||||
gboolean old_owner_good;
|
||||
gboolean new_owner_good;
|
||||
GSList *iter;
|
||||
|
||||
if (strcmp (name, priv->dbus_service))
|
||||
return;
|
||||
|
||||
/* Service changed, no need to wait for the timeout any longer */
|
||||
if (priv->service_start_timeout) {
|
||||
g_source_remove (priv->service_start_timeout);
|
||||
priv->service_start_timeout = 0;
|
||||
if (priv->start_timeout) {
|
||||
g_source_remove (priv->start_timeout);
|
||||
priv->start_timeout = 0;
|
||||
}
|
||||
|
||||
old_owner_good = (old && (strlen (old) > 0));
|
||||
|
|
@ -379,18 +394,14 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
|
|||
|
||||
if (!old_owner_good && new_owner_good) {
|
||||
/* service just appeared */
|
||||
GSList *iter;
|
||||
|
||||
nm_log_info (LOGD_VPN, "VPN service '%s' appeared, activating connections",
|
||||
nm_vpn_service_get_name (service));
|
||||
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));
|
||||
|
||||
} else if (old_owner_good && !new_owner_good) {
|
||||
/* service went away */
|
||||
nm_log_info (LOGD_VPN, "VPN service '%s' disappeared, cancelling connections",
|
||||
nm_vpn_service_get_name (service));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -398,40 +409,28 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
|
|||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
nm_vpn_service_init (NMVPNService *service)
|
||||
nm_vpn_service_init (NMVPNService *self)
|
||||
{
|
||||
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
|
||||
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, "name-owner-changed",
|
||||
G_CALLBACK (nm_vpn_service_name_owner_changed),
|
||||
service);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ensure_killed (gpointer data)
|
||||
{
|
||||
int pid = GPOINTER_TO_INT (data);
|
||||
|
||||
if (kill (pid, 0) == 0)
|
||||
kill (pid, SIGKILL);
|
||||
|
||||
/* 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);
|
||||
|
||||
return FALSE;
|
||||
G_CALLBACK (nm_vpn_service_name_owner_changed),
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
finalize (GObject *object)
|
||||
dispose (GObject *object)
|
||||
{
|
||||
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object);
|
||||
NMVPNService *self = NM_VPN_SERVICE (object);
|
||||
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
|
||||
|
||||
if (priv->service_start_timeout)
|
||||
g_source_remove (priv->service_start_timeout);
|
||||
if (priv->disposed)
|
||||
goto out;
|
||||
priv->disposed = TRUE;
|
||||
|
||||
if (priv->start_timeout)
|
||||
g_source_remove (priv->start_timeout);
|
||||
|
||||
nm_vpn_service_connections_stop (NM_VPN_SERVICE (object),
|
||||
FALSE,
|
||||
|
|
@ -439,31 +438,21 @@ finalize (GObject *object)
|
|||
|
||||
g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
|
||||
|
||||
if (priv->service_child_watch)
|
||||
g_source_remove (priv->service_child_watch);
|
||||
if (priv->child_watch)
|
||||
g_source_remove (priv->child_watch);
|
||||
|
||||
if (priv->pid) {
|
||||
if (kill (priv->pid, SIGTERM) == 0)
|
||||
g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
|
||||
else {
|
||||
kill (priv->pid, SIGKILL);
|
||||
|
||||
/* ensure the child is reaped */
|
||||
nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid);
|
||||
waitpid (priv->pid, NULL, 0);
|
||||
nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid);
|
||||
}
|
||||
|
||||
priv->pid = 0;
|
||||
}
|
||||
clear_quit_timeout (self);
|
||||
service_quit (self);
|
||||
|
||||
g_object_unref (priv->dbus_mgr);
|
||||
|
||||
g_free (priv->name);
|
||||
g_free (priv->dbus_service);
|
||||
g_free (priv->program);
|
||||
g_free (priv->namefile);
|
||||
|
||||
G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object);
|
||||
out:
|
||||
G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -474,5 +463,5 @@ nm_vpn_service_class_init (NMVPNServiceClass *service_class)
|
|||
g_type_class_add_private (service_class, sizeof (NMVPNServicePrivate));
|
||||
|
||||
/* virtual methods */
|
||||
object_class->finalize = finalize;
|
||||
object_class->dispose = dispose;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), 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;
|
||||
|
|
@ -45,9 +47,13 @@ typedef struct {
|
|||
|
||||
GType nm_vpn_service_get_type (void);
|
||||
|
||||
NMVPNService * nm_vpn_service_new (const char *service_name);
|
||||
NMVPNService * nm_vpn_service_new (const char *namefile, GError **error);
|
||||
|
||||
const char * nm_vpn_service_get_name (NMVPNService *service);
|
||||
/* Returns the VPN service's D-Bus service name */
|
||||
const char *nm_vpn_service_get_dbus_service (NMVPNService *service);
|
||||
|
||||
/* Returns the path of the VPN service's .name file */
|
||||
const char *nm_vpn_service_get_name_file (NMVPNService *service);
|
||||
|
||||
NMVPNConnection * nm_vpn_service_activate (NMVPNService *service,
|
||||
NMConnection *connection,
|
||||
|
|
@ -57,4 +63,8 @@ NMVPNConnection * nm_vpn_service_activate (NMVPNService *service,
|
|||
|
||||
GSList * nm_vpn_service_get_active_connections (NMVPNService *service);
|
||||
|
||||
void nm_vpn_service_connections_stop (NMVPNService *service,
|
||||
gboolean fail,
|
||||
NMVPNConnectionStateReason reason);
|
||||
|
||||
#endif /* NM_VPN_VPN_SERVICE_H */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue