pacrunner: refactor pacrunner to use GDBusConnection

- use GDBusConnection instead of GDBusProxy.

- rename "call-id" to "conf-id". It's really not a "call" but
  configuration that gets added and NMPacrunnerManager ensures that
  the configuration is send to pacrunner.

- let "conf-id" keep a reference to NMPacrunnerManager. For one,
  when we remove configurations we need to call DestroyProxyConfiguration
  to remove it again. We cannot just abort the requests but must linger
  around until our configuration is properly cleaned up. Hence, we
  anyway cannot destroy the NMPacrunnerManager earlier.
  With respect to fixing shutdown not to leak anything, this merely
  means that we must wait (and iterate the main loop) as long as
  NMPacrunnerManager singleton still exits (that is anyway the plan
  how to fix shutdown).
  With these considerations it's also clear that our D-Bus calls must
  have a stricter timeout: NM_SHUTDOWN_TIMEOUT_MS.
  This is also nice because nm_pacrunner_manager_remove() no longer
  needs a manager parameter, it can just rely on having a reference
  to the manager.

- for logging the configuration IDs, don't log pointer values.
  Logging pointer values should be avoided as it defeats ASLR.
  Instead, give them a "log_id" number.

- pacrunner is a D-Bus activatable service. D-Bus activatable services
  needs special care. We don't want to start it over and over again.
  Instead, we only try to "StartServiceByName" if

    - we have any configuration to add

    - if pacrunner is currently confirmed not to be running (by watching
      name owner changes)

    - we didn't try to start it already. That means, only start it
      at the beginning and afterwards set a flag to block it. When
      we see pacrunner appear on D-Bus we always clear that flag,
      that means if pacrunner drops of, we will try to restart it
      (once).
This commit is contained in:
Thomas Haller 2019-05-05 09:51:09 +02:00
parent cbdb498197
commit 83476a3fb6
5 changed files with 477 additions and 441 deletions

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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);
}