diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 2b1f5ed891..4be5d462b3 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -78,7 +78,7 @@ NMPlatformRoutingRule *nm_ip_routing_rule_to_platform (const NMIPRoutingRule *ru * SIGKILL. * * After NM_SHUTDOWN_TIMEOUT_MS, NetworkManager will however not yet terminate right - * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_MS_EXTRA. This + * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG. This * should give time to reap the child process (after SIGKILL). * * So, the maximum time we should wait before sending SIGKILL should be at most diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 89207586e7..38582937e4 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -397,8 +397,7 @@ typedef struct _NMDevicePrivate { /* Proxy Configuration */ NMProxyConfig *proxy_config; - NMPacrunnerManager *pacrunner_manager; - NMPacrunnerCallId *pacrunner_call_id; + NMPacrunnerConfId *pacrunner_conf_id; /* IP configuration info. Combined config from VPN, settings, and device */ union { @@ -11128,21 +11127,17 @@ nm_device_reactivate_ip6_config (NMDevice *self, } static void -_pacrunner_manager_send (NMDevice *self) +_pacrunner_manager_add (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); - if (!priv->pacrunner_manager) - priv->pacrunner_manager = g_object_ref (nm_pacrunner_manager_get ()); - - priv->pacrunner_call_id = nm_pacrunner_manager_send (priv->pacrunner_manager, - nm_device_get_ip_iface (self), - priv->proxy_config, - NULL, - NULL); + priv->pacrunner_conf_id = nm_pacrunner_manager_add (nm_pacrunner_manager_get (), + priv->proxy_config, + nm_device_get_ip_iface (self), + NULL, + NULL); } static void @@ -11150,10 +11145,10 @@ reactivate_proxy_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (!priv->pacrunner_call_id) + if (!priv->pacrunner_conf_id) return; nm_device_set_proxy_config (self, priv->dhcp4.pac_url); - _pacrunner_manager_send (self); + _pacrunner_manager_add (self); } static gboolean @@ -15081,8 +15076,7 @@ _set_state_full (NMDevice *self, } } - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); break; case NM_DEVICE_STATE_DISCONNECTED: if ( priv->queued_act_request @@ -15102,7 +15096,7 @@ _set_state_full (NMDevice *self, NULL, NULL, NULL); if (priv->proxy_config) - _pacrunner_manager_send (self); + _pacrunner_manager_add (self); break; case NM_DEVICE_STATE_FAILED: /* Usually upon failure the activation chain is interrupted in @@ -16345,9 +16339,7 @@ dispose (GObject *object) dispatcher_cleanup (self); - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); - g_clear_object (&priv->pacrunner_manager); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); _cleanup_generic_pre (self, CLEANUP_TYPE_KEEP); diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c index b9881f76f2..1ad9cb0f75 100644 --- a/src/nm-pacrunner-manager.c +++ b/src/nm-pacrunner-manager.c @@ -23,11 +23,14 @@ #include "nm-pacrunner-manager.h" #include "nm-utils.h" +#include "NetworkManagerUtils.h" #include "platform/nm-platform.h" +#include "nm-dbus-manager.h" #include "nm-proxy-config.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" #include "c-list/src/c-list.h" +#include "nm-glib-aux/nm-dbus-aux.h" #define PACRUNNER_DBUS_SERVICE "org.pacrunner" #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager" @@ -35,25 +38,27 @@ /*****************************************************************************/ -struct _NMPacrunnerCallId { - CList lst; +struct _NMPacrunnerConfId { + CList conf_id_lst; - /* this might be a dangling pointer after the async operation - * is cancelled. */ - NMPacrunnerManager *manager_maybe_dangling; + NMPacrunnerManager *self; + + GVariant *parameters; - GVariant *args; char *path; + guint64 log_id; guint refcount; }; -typedef struct _NMPacrunnerCallId Config; - typedef struct { - char *iface; - GDBusProxy *pacrunner; + GDBusConnection *dbus_connection; GCancellable *cancellable; - CList configs; + CList conf_id_lst_head; + guint64 log_id_counter; + guint name_owner_changed_id; + bool dbus_initied:1; + bool has_name_owner:1; + bool try_start_blocked:1; } NMPacrunnerManagerPrivate; struct _NMPacrunnerManager { @@ -79,332 +84,139 @@ NM_DEFINE_SINGLETON_GETTER (NMPacrunnerManager, nm_pacrunner_manager_get, NM_TYP #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "pacrunner", __VA_ARGS__) #define _NMLOG2_PREFIX_NAME "pacrunner" -#define _NMLOG2(level, config, ...) \ +#define _NMLOG2(level, conf_id, ...) \ G_STMT_START { \ nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \ - "%s%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + "%s%"G_GUINT64_FORMAT"]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ _NMLOG2_PREFIX_NAME": call[", \ - (config) \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + (conf_id)->log_id \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } G_STMT_END /*****************************************************************************/ -static void pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data); +static void _call_destroy_proxy_configuration (NMPacrunnerManager *self, + NMPacrunnerConfId *conf_id, + const char *path, + gboolean verbose_log); /*****************************************************************************/ -static Config * -config_new (NMPacrunnerManager *manager, GVariant *args) +static NMPacrunnerConfId * +conf_id_ref (NMPacrunnerConfId *conf_id) { - Config *config; + nm_assert (conf_id); + nm_assert (conf_id->refcount > 0); - config = g_slice_new0 (Config); - config->manager_maybe_dangling = manager; - config->args = g_variant_ref_sink (args); - config->refcount = 1; - c_list_link_tail (&NM_PACRUNNER_MANAGER_GET_PRIVATE (manager)->configs, - &config->lst); - - return config; -} - -static Config * -config_ref (Config *config) -{ - nm_assert (config); - nm_assert (config->refcount > 0); - - config->refcount++; - return config; + conf_id->refcount++; + return conf_id; } static void -config_unref (Config *config) +conf_id_unref (NMPacrunnerConfId *conf_id) { - nm_assert (config); - nm_assert (config->refcount > 0); + nm_assert (conf_id); + nm_assert (conf_id->refcount > 0); - if (config->refcount == 1) { - g_variant_unref (config->args); - g_free (config->path); - c_list_unlink_stale (&config->lst); - g_slice_free (Config, config); + if (conf_id->refcount == 1) { + g_variant_unref (conf_id->parameters); + g_free (conf_id->path); + c_list_unlink_stale (&conf_id->conf_id_lst); + g_object_unref (conf_id->self); + g_slice_free (NMPacrunnerConfId, conf_id); } else - config->refcount--; + conf_id->refcount--; } +NM_AUTO_DEFINE_FCN0 (NMPacrunnerConfId *, _nm_auto_unref_conf_id, conf_id_unref); +#define nm_auto_unref_conf_id nm_auto (_nm_auto_unref_conf_id) + /*****************************************************************************/ static void -add_proxy_config (GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config) -{ - const char *pac_url, *pac_script; - NMProxyConfigMethod method; - - method = nm_proxy_config_get_method (proxy_config); - - if (method == NM_PROXY_CONFIG_METHOD_AUTO) { - pac_url = nm_proxy_config_get_pac_url (proxy_config); - if (pac_url) { - g_variant_builder_add (proxy_data, "{sv}", - "URL", - g_variant_new_string (pac_url)); - } - - pac_script = nm_proxy_config_get_pac_script (proxy_config); - if (pac_script) { - g_variant_builder_add (proxy_data, "{sv}", - "Script", - g_variant_new_string (pac_script)); - } - } - - g_variant_builder_add (proxy_data, "{sv}", - "BrowserOnly", - g_variant_new_boolean (nm_proxy_config_get_browser_only (proxy_config))); -} - -static void -get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) +get_ip_domains (GPtrArray *domains, NMIPConfig *ip_config) { NMDedupMultiIter ipconf_iter; char *cidr; - const NMPlatformIP4Address *address; - const NMPlatformIP4Route *routes; - guint i; + guint i, num; char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + int addr_family; - /* Extract searches */ - for (i = 0; i < nm_ip4_config_get_num_searches (ip4); i++) - g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_search (ip4, i))); - - /* Extract domains */ - for (i = 0; i < nm_ip4_config_get_num_domains (ip4); i++) - g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_domain (ip4, i))); - - /* Add addresses and routes in CIDR form */ - - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) { - cidr = g_strdup_printf ("%s/%u", - nm_utils_inet4_ntop (address->address, sbuf), - address->plen); - g_ptr_array_add (domains, cidr); - } - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &routes) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) - continue; - cidr = g_strdup_printf ("%s/%u", - nm_utils_inet4_ntop (routes->network, sbuf), - routes->plen); - g_ptr_array_add (domains, cidr); - } -} - -static void -get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) -{ - NMDedupMultiIter ipconf_iter; - char *cidr; - const NMPlatformIP6Address *address; - const NMPlatformIP6Route *routes; - guint i; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - /* Extract searches */ - for (i = 0; i < nm_ip6_config_get_num_searches (ip6); i++) - g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_search (ip6, i))); - - /* Extract domains */ - for (i = 0; i < nm_ip6_config_get_num_domains (ip6); i++) - g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_domain (ip6, i))); - - /* Add addresses and routes in CIDR form */ - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) { - cidr = g_strdup_printf ("%s/%u", - nm_utils_inet6_ntop (&address->address, sbuf), - address->plen); - g_ptr_array_add (domains, cidr); - } - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &routes) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) - continue; - cidr = g_strdup_printf ("%s/%u", - nm_utils_inet6_ntop (&routes->network, sbuf), - routes->plen); - g_ptr_array_add (domains, cidr); - } -} - -/*****************************************************************************/ - -static GCancellable * -_ensure_cancellable (NMPacrunnerManagerPrivate *priv) -{ - if (G_UNLIKELY (!priv->cancellable)) - priv->cancellable = g_cancellable_new (); - return priv->cancellable; -} - -static void -pacrunner_send_done (GObject *source, GAsyncResult *res, gpointer user_data) -{ - Config *config = user_data; - NMPacrunnerManager *self; - NMPacrunnerManagerPrivate *priv; - gs_free_error GError *error = NULL; - gs_unref_variant GVariant *variant = NULL; - const char *path = NULL; - - nm_assert (!config->path); - - variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - goto out; - - self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling); - priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - - if (!variant) - _LOG2D (config, "sending failed: %s", error->message); - else { - g_variant_get (variant, "(&o)", &path); - - if (c_list_is_empty (&config->lst)) { - _LOG2D (config, "sent (%s), but destroy it right away", path); - g_dbus_proxy_call (priv->pacrunner, - "DestroyProxyConfiguration", - g_variant_new ("(o)", path), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - _ensure_cancellable (priv), - pacrunner_remove_done, - config_ref (config)); - } else { - _LOG2D (config, "sent (%s)", path); - config->path = g_strdup (path); - } - } - -out: - config_unref (config); -} - -static void -pacrunner_send_config (NMPacrunnerManager *self, Config *config) -{ - NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - - if (priv->pacrunner) { - _LOG2T (config, "sending..."); - - nm_assert (!config->path); - g_dbus_proxy_call (priv->pacrunner, - "CreateProxyConfiguration", - config->args, - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - _ensure_cancellable (priv), - pacrunner_send_done, - config_ref (config)); - } -} - -static void -name_owner_changed (NMPacrunnerManager *self) -{ - NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - gs_free char *owner = NULL; - CList *iter; - - owner = g_dbus_proxy_get_name_owner (priv->pacrunner); - if (owner) { - _LOGD ("name owner appeared (%s)", owner); - c_list_for_each (iter, &priv->configs) - pacrunner_send_config (self, c_list_entry (iter, Config, lst)); - } else { - _LOGD ("name owner disappeared"); - nm_clear_g_cancellable (&priv->cancellable); - c_list_for_each (iter, &priv->configs) - nm_clear_g_free (&c_list_entry (iter, Config, lst)->path); - } -} - -static void -name_owner_changed_cb (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - name_owner_changed (user_data); -} - -static void -pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data) -{ - NMPacrunnerManager *self = user_data; - NMPacrunnerManagerPrivate *priv; - gs_free_error GError *error = NULL; - GDBusProxy *proxy; - - proxy = g_dbus_proxy_new_for_bus_finish (res, &error); - if (!proxy) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - _LOGE ("failed to create D-Bus proxy for pacrunner: %s", error->message); + if (!ip_config) return; + + addr_family = nm_ip_config_get_addr_family (ip_config); + + num = nm_ip_config_get_num_searches (ip_config); + for (i = 0; i < num; i++) + g_ptr_array_add (domains, g_strdup (nm_ip_config_get_search (ip_config, i))); + + num = nm_ip_config_get_num_domains (ip_config); + for (i = 0; i < num; i++) + g_ptr_array_add (domains, g_strdup (nm_ip_config_get_domain (ip_config, i))); + + if (addr_family == AF_INET) { + const NMPlatformIP4Address *address; + + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, (NMIP4Config *) ip_config, &address) { + cidr = g_strdup_printf ("%s/%u", + nm_utils_inet4_ntop (address->address, sbuf), + address->plen); + g_ptr_array_add (domains, cidr); + } + } else { + const NMPlatformIP6Address *address; + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, (NMIP6Config *) ip_config, &address) { + cidr = g_strdup_printf ("%s/%u", + nm_utils_inet6_ntop (&address->address, sbuf), + address->plen); + g_ptr_array_add (domains, cidr); + } } - priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + if (addr_family == AF_INET) { + const NMPlatformIP4Route *routes; - priv->pacrunner = proxy; - g_signal_connect (priv->pacrunner, "notify::g-name-owner", - G_CALLBACK (name_owner_changed_cb), self); - name_owner_changed (self); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, (NMIP4Config *) ip_config, &routes) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) + continue; + cidr = g_strdup_printf ("%s/%u", + nm_utils_inet4_ntop (routes->network, sbuf), + routes->plen); + g_ptr_array_add (domains, cidr); + } + } else { + const NMPlatformIP6Route *routes; + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, (NMIP6Config *) ip_config, &routes) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) + continue; + cidr = g_strdup_printf ("%s/%u", + nm_utils_inet6_ntop (&routes->network, sbuf), + routes->plen); + g_ptr_array_add (domains, cidr); + } + } } -/** - * nm_pacrunner_manager_send: - * @self: the #NMPacrunnerManager - * @iface: the iface for the connection or %NULL - * @proxy_config: proxy config of the connection - * @ip4_config: IP4 config of the connection to extract domain info from - * @ip6_config: IP6 config of the connection to extract domain info from - * - * Returns: a #NMPacrunnerCallId call id. The function cannot - * fail and always returns a non NULL pointer. The call-id may - * be used to remove the configuration later via nm_pacrunner_manager_remove(). - * Note that the call-id does not keep the @self instance alive. - * If you plan to remove the configuration later, you must keep - * the instance alive long enough. You can remove the configuration - * at most once using this call call-id. - */ -NMPacrunnerCallId * -nm_pacrunner_manager_send (NMPacrunnerManager *self, - const char *iface, - NMProxyConfig *proxy_config, - NMIP4Config *ip4_config, - NMIP6Config *ip6_config) +static GVariant * +_make_request_create_proxy_configuration (NMProxyConfig *proxy_config, + const char *iface, + NMIP4Config *ip4_config, + NMIP6Config *ip6_config) { - char **strv = NULL; + GVariantBuilder builder; NMProxyConfigMethod method; - NMPacrunnerManagerPrivate *priv; - GVariantBuilder proxy_data; - GPtrArray *domains; - Config *config; + const char *pac_url; + const char *pac_script; - g_return_val_if_fail (NM_IS_PACRUNNER_MANAGER (self), NULL); - g_return_val_if_fail (proxy_config, NULL); + nm_assert (NM_IS_PROXY_CONFIG (proxy_config)); - priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - - g_free (priv->iface); - priv->iface = g_strdup (iface); - - g_variant_builder_init (&proxy_data, G_VARIANT_TYPE_VARDICT); + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); if (iface) { - g_variant_builder_add (&proxy_data, "{sv}", + g_variant_builder_add (&builder, "{sv}", "Interface", g_variant_new_string (iface)); } @@ -412,167 +224,410 @@ nm_pacrunner_manager_send (NMPacrunnerManager *self, method = nm_proxy_config_get_method (proxy_config); switch (method) { case NM_PROXY_CONFIG_METHOD_AUTO: - g_variant_builder_add (&proxy_data, "{sv}", + g_variant_builder_add (&builder, "{sv}", "Method", g_variant_new_string ("auto")); + pac_url = nm_proxy_config_get_pac_url (proxy_config); + if (pac_url) { + g_variant_builder_add (&builder, "{sv}", + "URL", + g_variant_new_string (pac_url)); + } + + pac_script = nm_proxy_config_get_pac_script (proxy_config); + if (pac_script) { + g_variant_builder_add (&builder, "{sv}", + "Script", + g_variant_new_string (pac_script)); + } break; case NM_PROXY_CONFIG_METHOD_NONE: - g_variant_builder_add (&proxy_data, "{sv}", + g_variant_builder_add (&builder, "{sv}", "Method", g_variant_new_string ("direct")); + break; } - /* Extract stuff from configs */ - add_proxy_config (&proxy_data, proxy_config); + g_variant_builder_add (&builder, "{sv}", + "BrowserOnly", + g_variant_new_boolean (nm_proxy_config_get_browser_only (proxy_config))); if (ip4_config || ip6_config) { + gs_unref_ptrarray GPtrArray *domains = NULL; + domains = g_ptr_array_new_with_free_func (g_free); - if (ip4_config) - get_ip4_domains (domains, ip4_config); - if (ip6_config) - get_ip6_domains (domains, ip6_config); + get_ip_domains (domains, NM_IP_CONFIG_CAST (ip4_config)); + get_ip_domains (domains, NM_IP_CONFIG_CAST (ip6_config)); - g_ptr_array_add (domains, NULL); - strv = (char **) g_ptr_array_free (domains, (domains->len == 1)); - - if (strv) { - g_variant_builder_add (&proxy_data, "{sv}", + if (domains->len > 0) { + g_variant_builder_add (&builder, "{sv}", "Domains", - g_variant_new_strv ((const char *const *) strv, -1)); - g_strfreev (strv); + g_variant_new_strv ((const char *const*) domains->pdata, + domains->len)); } } - config = config_new (self, g_variant_new ("(a{sv})", &proxy_data)); - - { - gs_free char *args_str = NULL; - - _LOG2D (config, "send: new config %s", - (args_str = g_variant_print (config->args, FALSE))); - } - - /* Send if pacrunner is available on bus, otherwise - * config has already been appended above to be - * sent when pacrunner appears. - */ - pacrunner_send_config (self, config); - - return config; + return g_variant_new ("(a{sv})", &builder); } +/*****************************************************************************/ + static void -pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data) +_call_destroy_proxy_configuration_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { - Config *config = user_data; + nm_auto_unref_conf_id NMPacrunnerConfId *conf_id = user_data; gs_free_error GError *error = NULL; gs_unref_variant GVariant *ret = NULL; - ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); if (!ret) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - goto out; - _LOG2D (config, "remove failed: %s", error->message); - goto out; + _LOG2T (conf_id, "destroy proxy configuration: failed with %s", error->message); + else + _LOG2T (conf_id, "destroy proxy configuration: cancelled"); + return; + } + _LOG2T (conf_id, "destroy proxy configuration: success"); +} + +static void +_call_create_proxy_configuration_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + nm_auto_unref_conf_id NMPacrunnerConfId *conf_id = user_data; + NMPacrunnerManager *self = NM_PACRUNNER_MANAGER (conf_id->self); + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *variant = NULL; + const char *path = NULL; + + nm_assert (!conf_id->path); + + variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); + + if (!variant) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + _LOG2T (conf_id, "create proxy configuration failed: %s", error->message); + else + _LOG2T (conf_id, "create proxy configuration cancelled"); + return; } - _LOG2D (config, "removed"); + g_variant_get (variant, "(&o)", &path); -out: - config_unref (config); + if (c_list_is_empty (&conf_id->conf_id_lst)) { + _LOG2T (conf_id, "create proxy configuration succeeded (%s), but destroy it right away", path); + _call_destroy_proxy_configuration (self, + conf_id, + path, + FALSE); + } else { + _LOG2T (conf_id, "create proxy configuration succeeded (%s)", path); + conf_id->path = g_strdup (path); + } +} + +static void +_call_destroy_proxy_configuration (NMPacrunnerManager *self, + NMPacrunnerConfId *conf_id, + const char *path, + gboolean verbose_log) +{ + NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + + if (verbose_log) + _LOG2T (conf_id, "destroy proxy configuration %s...", path); + + g_dbus_connection_call (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + PACRUNNER_DBUS_PATH, + PACRUNNER_DBUS_INTERFACE, + "DestroyProxyConfiguration", + g_variant_new ("(o)", path), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + NM_SHUTDOWN_TIMEOUT_MS, + priv->cancellable, + _call_destroy_proxy_configuration_cb, + conf_id_ref (conf_id)); +} + +static void +_call_create_proxy_configuration (NMPacrunnerManager *self, + NMPacrunnerConfId *conf_id, + gboolean verbose_log) +{ + NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + + if (verbose_log) + _LOG2T (conf_id, "create proxy configuration..."); + + g_dbus_connection_call (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + PACRUNNER_DBUS_PATH, + PACRUNNER_DBUS_INTERFACE, + "CreateProxyConfiguration", + conf_id->parameters, + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + NM_SHUTDOWN_TIMEOUT_MS, + priv->cancellable, + _call_create_proxy_configuration_cb, + conf_id_ref (conf_id)); +} + +static gboolean +_try_start_service_by_name (NMPacrunnerManager *self) +{ + NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + + if ( priv->try_start_blocked + || !priv->dbus_initied) + return FALSE; + + _LOGD ("try D-Bus activating pacrunner..."); + priv->try_start_blocked = TRUE; + nm_dbus_connection_call_start_service_by_name (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + -1, + NULL, + NULL, + NULL); + return TRUE; +} + +/*****************************************************************************/ + +/** + * nm_pacrunner_manager_add: + * @self: the #NMPacrunnerManager + * @proxy_config: proxy config of the connection + * @iface: the iface for the connection or %NULL + * @ip4_config: IP4 config of the connection to extract domain info from + * @ip6_config: IP6 config of the connection to extract domain info from + * + * Returns: a #NMPacrunnerConfId id. The function cannot + * fail and always returns a non NULL pointer. The conf-id may + * be used to remove the configuration later via nm_pacrunner_manager_remove(). + * Note that the conf-id keeps the @self instance alive. + */ +NMPacrunnerConfId * +nm_pacrunner_manager_add (NMPacrunnerManager *self, + NMProxyConfig *proxy_config, + const char *iface, + NMIP4Config *ip4_config, + NMIP6Config *ip6_config) +{ + NMPacrunnerManagerPrivate *priv; + NMPacrunnerConfId *conf_id; + gs_free char *log_msg = NULL; + + g_return_val_if_fail (NM_IS_PACRUNNER_MANAGER (self), NULL); + g_return_val_if_fail (proxy_config, NULL); + + priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + + conf_id = g_slice_new (NMPacrunnerConfId); + *conf_id = (NMPacrunnerConfId) { + .log_id = ++priv->log_id_counter, + .refcount = 1, + .self = g_object_ref (self), + .parameters = g_variant_ref_sink (_make_request_create_proxy_configuration (proxy_config, + iface, + ip4_config, + ip6_config)), + }; + c_list_link_tail (&priv->conf_id_lst_head, + &conf_id->conf_id_lst); + + if (!priv->has_name_owner) { + _LOG2T (conf_id, "add config: %s (%s)", + (log_msg = g_variant_print (conf_id->parameters, FALSE)), + "pacrunner D-Bus service not running"); + _try_start_service_by_name (self); + } else { + _LOG2T (conf_id, "add config: %s (%s)", + (log_msg = g_variant_print (conf_id->parameters, FALSE)), + "create proxy configuration"); + _call_create_proxy_configuration (self, conf_id, FALSE); + } + + return conf_id; } /** * nm_pacrunner_manager_remove: - * @self: the #NMPacrunnerManager - * @call_id: the call-id obtained from nm_pacrunner_manager_send() + * @conf_id: the conf id obtained from nm_pacrunner_manager_add() */ void -nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_id) +nm_pacrunner_manager_remove (NMPacrunnerConfId *conf_id) { + _nm_unused nm_auto_unref_conf_id NMPacrunnerConfId *conf_id_free = conf_id; + NMPacrunnerManager *self; NMPacrunnerManagerPrivate *priv; - Config *config; + + g_return_if_fail (conf_id); + + self = conf_id->self; g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self)); - g_return_if_fail (call_id); - config = call_id; priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - _LOG2T (config, "removing..."); + _LOG2T (conf_id, "removing..."); - nm_assert (c_list_contains (&priv->configs, &config->lst)); + nm_assert (c_list_contains (&priv->conf_id_lst_head, &conf_id->conf_id_lst)); - if (priv->pacrunner) { - if (!config->path) { - /* send() failed or is still pending. The item is unlinked from - * priv->configs, so pacrunner_send_done() knows to call - * DestroyProxyConfiguration right away. - */ - } else { - g_dbus_proxy_call (priv->pacrunner, - "DestroyProxyConfiguration", - g_variant_new ("(o)", config->path), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - _ensure_cancellable (priv), - pacrunner_remove_done, - config_ref (config)); - nm_clear_g_free (&config->path); - } + c_list_unlink (&conf_id->conf_id_lst); + + if (!conf_id->path) { + /* There is no ID to destroy the configuration. + * + * That can happen because: + * + * - pacrunner D-Bus service is not running (no name owner) and we didn't call CreateProxyConfiguration. + * - CreateProxyConfiguration failed. + * - CreateProxyConfiguration is in progress. + * + * In all cases there is nothing to do. Note that if CreateProxyConfiguration is in progress + * it has a reference on the conf-id and it will automatically destroy the configuration + * when it completes. + */ + return; } - c_list_unlink (&config->lst); - config_unref (config); + _call_destroy_proxy_configuration (self, conf_id, conf_id->path, TRUE); } gboolean -nm_pacrunner_manager_remove_clear (NMPacrunnerManager *self, - NMPacrunnerCallId **p_call_id) +nm_pacrunner_manager_remove_clear (NMPacrunnerConfId **p_conf_id) { - g_return_val_if_fail (p_call_id, FALSE); + g_return_val_if_fail (p_conf_id, FALSE); - /* if we have no call-id, allow for %NULL */ - g_return_val_if_fail ((!self && !*p_call_id) || NM_IS_PACRUNNER_MANAGER (self), FALSE); - - if (!*p_call_id) + if (!*p_conf_id) return FALSE; - nm_pacrunner_manager_remove (self, - g_steal_pointer (p_call_id)); + nm_pacrunner_manager_remove (g_steal_pointer (p_conf_id)); return TRUE; } /*****************************************************************************/ +static void +name_owner_changed (NMPacrunnerManager *self, + const char *name_owner) +{ + NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + NMPacrunnerConfId *conf_id; + gboolean has_name_owner; + + has_name_owner = (name_owner && name_owner[0]); + + if ( priv->dbus_initied + && priv->has_name_owner == has_name_owner) + return; + + priv->has_name_owner = has_name_owner; + + nm_clear_g_cancellable (&priv->cancellable); + + if (has_name_owner) { + priv->dbus_initied = TRUE; + priv->try_start_blocked = FALSE; + _LOGD ("pacrunner appeared on D-Bus (%s)", name_owner); + priv->cancellable = g_cancellable_new (); + c_list_for_each_entry (conf_id, &priv->conf_id_lst_head, conf_id_lst) + _call_create_proxy_configuration (self, conf_id, TRUE); + } else { + if (!priv->dbus_initied) { + priv->dbus_initied = TRUE; + nm_assert (!priv->try_start_blocked); + _LOGD ("pacrunner not on D-Bus"); + } else + _LOGD ("pacrunner disappeared from D-Bus"); + if (!c_list_is_empty (&priv->conf_id_lst_head)) { + c_list_for_each_entry (conf_id, &priv->conf_id_lst_head, conf_id_lst) + nm_clear_g_free (&conf_id->path); + _try_start_service_by_name (self); + } + } +} + +static void +name_owner_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + const char *new_owner; + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) + return; + + g_variant_get (parameters, + "(&s&s&s)", + NULL, + NULL, + &new_owner); + + name_owner_changed (user_data, new_owner); +} + +static void +get_name_owner_cb (const char *name_owner, + GError *error, + gpointer user_data) +{ + if ( !name_owner + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + name_owner_changed (user_data, name_owner); +} + +/*****************************************************************************/ + static void nm_pacrunner_manager_init (NMPacrunnerManager *self) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - c_list_init (&priv->configs); - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - PACRUNNER_DBUS_SERVICE, - PACRUNNER_DBUS_PATH, - PACRUNNER_DBUS_INTERFACE, - _ensure_cancellable (priv), - pacrunner_proxy_cb, - self); + c_list_init (&priv->conf_id_lst_head); + + priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET); + + if (!priv->dbus_connection) { + _LOGD ("no D-Bus connection to talk to pacrunner"); + return; + } + + priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + priv->cancellable = g_cancellable_new (); + + nm_dbus_connection_call_get_name_owner (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + -1, + priv->cancellable, + get_name_owner_cb, + self); } static void dispose (GObject *object) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE ((NMPacrunnerManager *) object); - CList *iter, *safe; - c_list_for_each_safe (iter, safe, &priv->configs) { - c_list_unlink (iter); - config_unref (c_list_entry (iter, Config, lst)); - } + nm_assert (c_list_is_empty (&priv->conf_id_lst_head)); /* we cancel all pending operations. Note that pacrunner automatically * removes all configuration once NetworkManager disconnects from @@ -580,8 +635,9 @@ dispose (GObject *object) */ nm_clear_g_cancellable (&priv->cancellable); - g_clear_pointer (&priv->iface, g_free); - g_clear_object (&priv->pacrunner); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->name_owner_changed_id); + g_clear_object (&priv->dbus_connection); G_OBJECT_CLASS (nm_pacrunner_manager_parent_class)->dispose (object); } diff --git a/src/nm-pacrunner-manager.h b/src/nm-pacrunner-manager.h index 3080c4f5fd..35ccb3c351 100644 --- a/src/nm-pacrunner-manager.h +++ b/src/nm-pacrunner-manager.h @@ -31,22 +31,20 @@ typedef struct _NMPacrunnerManagerClass NMPacrunnerManagerClass; -typedef struct _NMPacrunnerCallId NMPacrunnerCallId; +typedef struct _NMPacrunnerConfId NMPacrunnerConfId; GType nm_pacrunner_manager_get_type (void); NMPacrunnerManager *nm_pacrunner_manager_get (void); -NMPacrunnerCallId *nm_pacrunner_manager_send (NMPacrunnerManager *self, - const char *iface, - NMProxyConfig *proxy_config, - NMIP4Config *ip4_config, - NMIP6Config *ip6_config); +NMPacrunnerConfId *nm_pacrunner_manager_add (NMPacrunnerManager *self, + NMProxyConfig *proxy_config, + const char *iface, + NMIP4Config *ip4_config, + NMIP6Config *ip6_config); -void nm_pacrunner_manager_remove (NMPacrunnerManager *self, - NMPacrunnerCallId *call_id); +void nm_pacrunner_manager_remove (NMPacrunnerConfId *conf_id); -gboolean nm_pacrunner_manager_remove_clear (NMPacrunnerManager *self, - NMPacrunnerCallId **p_call_id); +gboolean nm_pacrunner_manager_remove_clear (NMPacrunnerConfId **p_conf_id); #endif /* __NETWORKMANAGER_PACRUNNER_MANAGER_H__ */ diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 5acf491a2c..2c4f33359f 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -124,8 +124,7 @@ typedef struct { GVariant *connect_hash; guint connect_timeout; NMProxyConfig *proxy_config; - NMPacrunnerManager *pacrunner_manager; - NMPacrunnerCallId *pacrunner_call_id; + NMPacrunnerConfId *pacrunner_conf_id; gboolean has_ip4; NMIP4Config *ip4_config; guint32 ip4_internal_gw; @@ -552,18 +551,12 @@ _set_vpn_state (NMVpnConnection *self, NULL); if (priv->proxy_config) { - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); - if (!priv->pacrunner_manager) { - /* the pending call doesn't keep NMPacrunnerManager alive. - * Take a reference to it. */ - priv->pacrunner_manager = g_object_ref (nm_pacrunner_manager_get ()); - } - priv->pacrunner_call_id = nm_pacrunner_manager_send (priv->pacrunner_manager, - priv->ip_iface, - priv->proxy_config, - priv->ip4_config, - priv->ip6_config); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); + priv->pacrunner_conf_id = nm_pacrunner_manager_add (nm_pacrunner_manager_get (), + priv->proxy_config, + priv->ip_iface, + priv->ip4_config, + priv->ip6_config); } break; case STATE_DEACTIVATING: @@ -594,8 +587,7 @@ _set_vpn_state (NMVpnConnection *self, } } - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); break; case STATE_FAILED: case STATE_DISCONNECTED: @@ -2801,9 +2793,7 @@ dispose (GObject *object) fw_call_cleanup (self); - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); - g_clear_object (&priv->pacrunner_manager); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); }