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; } }
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; }
const char * nm_vpn_service_get_dbus_service (NMVpnService *service) { g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); return nm_vpn_plugin_info_get_service (NM_VPN_SERVICE_GET_PRIVATE (service)->plugin_info); }
static void _name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { NMVpnService *service = NM_VPN_SERVICE (user_data); NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); gboolean success; char *owner; owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object)); /* Service changed, no need to wait for the timeout any longer */ if (priv->start_timeout) { g_source_remove (priv->start_timeout); priv->start_timeout = 0; } if (owner && !priv->service_running) { /* service appeared */ priv->service_running = TRUE; nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", nm_vpn_plugin_info_get_name (priv->plugin_info)); /* Expect success because the VPN service has already appeared */ success = start_active_vpn (service, NULL); g_warn_if_fail (success); } else if (!owner && priv->service_running) { /* service went away */ priv->service_running = FALSE; nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", nm_vpn_plugin_info_get_name (priv->plugin_info)); nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); } g_free (owner); }
gboolean nm_vpn_service_activate (NMVPNService *service, NMVPNConnection *vpn, GError **error) { NMVPNServicePrivate *priv; g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), FALSE); g_return_val_if_fail (error != NULL, FALSE); g_return_val_if_fail (*error == NULL, FALSE); 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)); 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; } return TRUE; }
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); }
gboolean nm_vpn_service_activate (NMVpnService *service, NMVpnConnection *vpn, GError **error) { NMVpnServicePrivate *priv; g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), FALSE); g_return_val_if_fail (error != NULL, FALSE); g_return_val_if_fail (*error == NULL, FALSE); priv = NM_VPN_SERVICE_GET_PRIVATE (service); g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, G_CALLBACK (connection_vpn_state_changed), service); /* Queue up the new VPN connection */ priv->pending = g_slist_append (priv->pending, g_object_ref (vpn)); /* 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; } /* Otherwise start the next VPN */ return start_pending_vpn (service, error); }
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; }
void nm_vpn_service_stop_connections (NMVpnService *service, gboolean quitting, NMVpnConnectionStateReason reason) { NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); GSList *iter; /* Just add priv->active to the beginning of priv->pending, * since we're going to clear priv->pending immediately anyway. */ if (priv->active) { priv->pending = g_slist_prepend (priv->pending, priv->active); priv->active = NULL; } 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); }
NMVPNService * nm_vpn_service_new (const char *namefile, GError **error) { NMVPNService *self = NULL; 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); 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; } 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); out: g_key_file_free (kf); g_free (dbus_service); g_free (program); g_free (name); return self; }
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; } }
static gboolean _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", nm_vpn_plugin_info_get_name (priv->plugin_info)); priv->start_timeout = 0; nm_vpn_service_stop_connections (self, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT); return G_SOURCE_REMOVE; }
static gboolean nm_vpn_service_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; }
static gboolean start_pending_vpn (NMVpnService *self, GError **error) { NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); g_assert (priv->active == NULL); if (!priv->pending) return TRUE; /* Make next VPN active */ priv->active = g_slist_nth_data (priv->pending, 0); priv->pending = g_slist_remove (priv->pending, priv->active); return start_active_vpn (self, error); }
void nm_vpn_service_connections_stop (NMVPNService *service, gboolean fail, NMVPNConnectionStateReason reason) { NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); GSList *iter, *copy; /* Copy because stopping the connection may remove it from the list * in the NMVPNService objects' VPN connection state handler. */ 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); } g_slist_free (copy); }
NMVpnService * nm_vpn_service_new (NMVpnPluginInfo *plugin_info, GError **error) { NMVpnService *self; NMVpnServicePrivate *priv; g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), NULL); g_return_val_if_fail (nm_vpn_plugin_info_get_filename (plugin_info), NULL); if (!nm_vpn_plugin_info_get_program (plugin_info)) { g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED, "missing \"program\" entry"); return NULL; } self = (NMVpnService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL); priv = NM_VPN_SERVICE_GET_PRIVATE (self); priv->plugin_info = g_object_ref (plugin_info); priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, nm_vpn_plugin_info_get_service (plugin_info), NM_VPN_DBUS_PLUGIN_PATH, NM_VPN_DBUS_PLUGIN_INTERFACE, NULL, error); if (!priv->proxy) { g_object_unref (self); return NULL; } g_signal_connect (priv->proxy, "notify::g-name-owner", G_CALLBACK (_name_owner_changed), self); _name_owner_changed (G_OBJECT (priv->proxy), NULL, self); return self; }
static gboolean nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) { NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); 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); 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); } else { nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.", 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, "%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); if (spawn_error) g_error_free (spawn_error); } return success; }
static gboolean start_active_vpn (NMVpnService *self, GError **error) { NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); if (!priv->active) return TRUE; 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'...", nm_vpn_plugin_info_get_name (priv->plugin_info)); return nm_vpn_service_daemon_exec (self, error); } /* Already started VPN service, waiting for it to appear on D-Bus */ return TRUE; }
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, NULL); } else priv->pending = g_slist_remove (priv->pending, connection); g_object_unref (connection); } }
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); vpn_argv[0] = (char *) nm_vpn_plugin_info_get_program (priv->plugin_info); vpn_argv[1] = NULL; g_assert (vpn_argv[0]); success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error); if (success) { nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %ld", nm_vpn_plugin_info_get_name (priv->plugin_info), nm_vpn_service_get_dbus_service (service), (long int) 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.", nm_vpn_plugin_info_get_name (priv->plugin_info), spawn_error ? spawn_error->code : -1, spawn_error && spawn_error->message ? spawn_error->message : "(unknown)"); g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); if (spawn_error) g_error_free (spawn_error); } return success; }
NMVPNConnection * nm_vpn_service_activate (NMVPNService *service, NMConnection *connection, NMDevice *device, const char *specific_object, gboolean user_requested, gulong user_uid, GError **error) { NMVPNConnection *vpn; NMVPNServicePrivate *priv; g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); g_return_val_if_fail (NM_IS_DEVICE (device), NULL); g_return_val_if_fail (error != NULL, NULL); g_return_val_if_fail (*error == NULL, NULL); priv = NM_VPN_SERVICE_GET_PRIVATE (service); clear_quit_timeout (service); vpn = nm_vpn_connection_new (connection, device, specific_object, user_requested, user_uid); 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)); 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->start_timeout == 0) { nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name); if (!nm_vpn_service_daemon_exec (service, error)) vpn = NULL; } return vpn; }
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; }
static void dispose (GObject *object) { NMVpnService *self = NM_VPN_SERVICE (object); NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); nm_clear_g_source (&priv->start_timeout); g_clear_object (&priv->plugin_info); /* VPNService owner is required to stop connections before releasing */ g_assert (priv->active == NULL); g_assert (priv->pending == NULL); if (priv->proxy) { g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (_name_owner_changed), self); g_clear_object (&priv->proxy); } G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object); }
nm_vpn_service_get_active_connections (NMVPNService *service) { g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); return NM_VPN_SERVICE_GET_PRIVATE (service)->connections; } static void nm_vpn_service_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; if (strcmp (name, priv->dbus_service)) return; /* Service changed, no need to wait for the timeout any longer */ if (priv->start_timeout) { g_source_remove (priv->start_timeout); priv->start_timeout = 0; } old_owner_good = (old && (strlen (old) > 0)); new_owner_good = (new && (strlen (new) > 0));