about summary refs log tree commit diff
path: root/src/nm-firewall-manager.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-05-11 14:55:55 +0200
committerMichael Biebl <biebl@debian.org>2017-05-11 14:55:55 +0200
commitc333f062ddcba9b35330647bf6cbd0a07f2d786e (patch)
tree257c3a0c74c09f4ad2328eab5b932806405f0c1c /src/nm-firewall-manager.c
parenta222e56e103f949b148a6942e385ccca2c26d9f3 (diff)
New upstream version 1.8.0 upstream/1.8.0
Diffstat (limited to 'src/nm-firewall-manager.c')
-rw-r--r--src/nm-firewall-manager.c314
1 files changed, 192 insertions, 122 deletions
diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c
index 5ba3d23e..4a887b79 100644
--- a/src/nm-firewall-manager.c
+++ b/src/nm-firewall-manager.c
@@ -28,22 +28,19 @@
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE (NMFirewallManager,
-	PROP_AVAILABLE,
-);
-
 enum {
-	STARTED,
+	STATE_CHANGED,
 	LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
-	GDBusProxy *    proxy;
-	gboolean        running;
+	GDBusProxy     *proxy;
+	GCancellable   *proxy_cancellable;
 
 	GHashTable     *pending_calls;
+	bool            running;
 } NMFirewallManagerPrivate;
 
 struct _NMFirewallManager {
@@ -73,6 +70,7 @@ typedef enum {
 
 typedef enum {
 	CB_INFO_MODE_IDLE = 1,
+	CB_INFO_MODE_DBUS_WAITING,
 	CB_INFO_MODE_DBUS,
 	CB_INFO_MODE_DBUS_COMPLETED,
 } CBInfoMode;
@@ -80,7 +78,10 @@ typedef enum {
 struct _NMFirewallManagerCallId {
 	NMFirewallManager *self;
 	CBInfoOpsType ops_type;
-	CBInfoMode mode;
+	union {
+		const CBInfoMode mode;
+		CBInfoMode mode_mutable;
+	};
 	char *iface;
 	NMFirewallManagerAddRemoveCallback callback;
 	gpointer user_data;
@@ -88,6 +89,7 @@ struct _NMFirewallManagerCallId {
 	union {
 		struct {
 			GCancellable *cancellable;
+			GVariant *arg;
 		} dbus;
 		struct {
 			guint id;
@@ -118,7 +120,7 @@ _ops_type_to_string (CBInfoOpsType ops_type)
             char __prefix_name[30]; \
             char __prefix_info[64]; \
             \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s: %s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      (self) != singleton_instance \
                         ? ({ \
@@ -129,7 +131,7 @@ _ops_type_to_string (CBInfoOpsType ops_type)
                      __info \
                         ? ({ \
                                 g_snprintf (__prefix_info, sizeof (__prefix_info), "[%p,%s%s:%s%s%s]: ", __info, \
-                                            _ops_type_to_string (__info->ops_type), _cb_info_is_idle (__info) ? "*" : "", \
+                                            _ops_type_to_string (__info->ops_type), __info->mode == CB_INFO_MODE_IDLE ? "*" : "", \
                                             NM_PRINT_FMT_QUOTE_STRING (__info->iface)); \
                                 __prefix_info; \
                            }) \
@@ -140,16 +142,21 @@ _ops_type_to_string (CBInfoOpsType ops_type)
 
 /*****************************************************************************/
 
-static gboolean
-_cb_info_is_idle (CBInfo *info)
+gboolean
+nm_firewall_manager_get_running (NMFirewallManager *self)
 {
-	return info->mode == CB_INFO_MODE_IDLE;
+	g_return_val_if_fail (NM_IS_FIREWALL_MANAGER (self), FALSE);
+
+	return NM_FIREWALL_MANAGER_GET_PRIVATE (self)->running;
 }
 
+/*****************************************************************************/
+
 static CBInfo *
 _cb_info_create (NMFirewallManager *self,
                  CBInfoOpsType ops_type,
                  const char *iface,
+                 const char *zone,
                  NMFirewallManagerAddRemoveCallback callback,
                  gpointer user_data)
 {
@@ -163,14 +170,14 @@ _cb_info_create (NMFirewallManager *self,
 	info->callback = callback;
 	info->user_data = user_data;
 
-	if (priv->running) {
-		info->mode = CB_INFO_MODE_DBUS;
-		info->dbus.cancellable = g_cancellable_new ();
+	if (priv->running || priv->proxy_cancellable) {
+		info->mode_mutable = CB_INFO_MODE_DBUS_WAITING;
+		info->dbus.arg = g_variant_new ("(ss)", zone ? zone : "", iface);
 	} else
-		info->mode = CB_INFO_MODE_IDLE;
+		info->mode_mutable = CB_INFO_MODE_IDLE;
 
 	if (!nm_g_hash_table_add (priv->pending_calls, info))
-		g_return_val_if_reached (NULL);
+		nm_assert_not_reached ();
 
 	return info;
 }
@@ -178,8 +185,11 @@ _cb_info_create (NMFirewallManager *self,
 static void
 _cb_info_free (CBInfo *info)
 {
-	if (!_cb_info_is_idle (info))
-		g_object_unref (info->dbus.cancellable);
+	if (info->mode != CB_INFO_MODE_IDLE) {
+		if (info->dbus.arg)
+			g_variant_unref (info->dbus.arg);
+		g_clear_object (&info->dbus.cancellable);
+	}
 	g_free (info->iface);
 	if (info->self)
 		g_object_unref (info->self);
@@ -253,14 +263,16 @@ _handle_dbus (GObject *proxy, GAsyncResult *result, gpointer user_data)
 			non_error = "UNKNOWN_INTERFACE";
 			break;
 		}
-		if (!g_strcmp0 (error->message, non_error)) {
+		if (   error->message
+		    && non_error
+		    && g_str_has_prefix (error->message, non_error)
+		    && NM_IN_SET (error->message[strlen (non_error)], '\0', ':')) {
 			_LOGD (info, "complete: request failed with a non-error (%s)", error->message);
 
 			/* The operation failed with an error reason that we don't want
 			 * to propagate. Instead, signal success. */
 			g_clear_error (&error);
-		}
-		else
+		} else
 			_LOGW (info, "complete: request failed (%s)", error->message);
 	} else
 		_LOGD (info, "complete: success");
@@ -268,6 +280,48 @@ _handle_dbus (GObject *proxy, GAsyncResult *result, gpointer user_data)
 	_cb_info_complete_normal (info, error);
 }
 
+static void
+_handle_dbus_start (NMFirewallManager *self,
+                    CBInfo *info)
+{
+	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
+	const char *dbus_method = NULL;
+	GVariant *arg;
+
+	nm_assert (info);
+	nm_assert (priv->running);
+	nm_assert (info->mode == CB_INFO_MODE_DBUS_WAITING);
+
+	switch (info->ops_type) {
+	case CB_INFO_OPS_ADD:
+		dbus_method = "addInterface";
+		break;
+	case CB_INFO_OPS_CHANGE:
+		dbus_method = "changeZone";
+		break;
+	case CB_INFO_OPS_REMOVE:
+		dbus_method = "removeInterface";
+		break;
+	}
+	nm_assert (dbus_method);
+
+	arg = info->dbus.arg;
+	info->dbus.arg = NULL;
+
+	nm_assert (arg && g_variant_is_floating (arg));
+
+	info->mode_mutable = CB_INFO_MODE_DBUS;
+	info->dbus.cancellable = g_cancellable_new ();
+
+	g_dbus_proxy_call (priv->proxy,
+	                   dbus_method,
+	                   arg,
+	                   G_DBUS_CALL_FLAGS_NONE, 10000,
+	                   info->dbus.cancellable,
+	                   _handle_dbus,
+	                   info);
+}
+
 static NMFirewallManagerCallId
 _start_request (NMFirewallManager *self,
                 CBInfoOpsType ops_type,
@@ -278,45 +332,27 @@ _start_request (NMFirewallManager *self,
 {
 	NMFirewallManagerPrivate *priv;
 	CBInfo *info;
-	const char *dbus_method;
 
 	g_return_val_if_fail (NM_IS_FIREWALL_MANAGER (self), NULL);
 	g_return_val_if_fail (iface && *iface, NULL);
 
 	priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
 
-	info = _cb_info_create (self, ops_type, iface, callback, user_data);
+	info = _cb_info_create (self, ops_type, iface, zone, callback, user_data);
 
 	_LOGD (info, "firewall zone %s %s:%s%s%s%s",
 	       _ops_type_to_string (info->ops_type),
 	       iface,
 	       NM_PRINT_FMT_QUOTED (zone, "\"", zone, "\"", "default"),
-	       _cb_info_is_idle (info) ? " (not running, simulate success)" : "");
-
-	if (!_cb_info_is_idle (info)) {
-
-		switch (ops_type) {
-		case CB_INFO_OPS_ADD:
-			dbus_method = "addInterface";
-			break;
-		case CB_INFO_OPS_CHANGE:
-			dbus_method = "changeZone";
-			break;
-		case CB_INFO_OPS_REMOVE:
-			dbus_method = "removeInterface";
-			break;
-		default:
-			g_assert_not_reached ();
-		}
-
-		g_dbus_proxy_call (priv->proxy,
-		                   dbus_method,
-		                   g_variant_new ("(ss)", zone ? zone : "", iface),
-		                   G_DBUS_CALL_FLAGS_NONE, 10000,
-		                   info->dbus.cancellable,
-		                   _handle_dbus,
-		                   info);
-
+	       info->mode == CB_INFO_MODE_IDLE
+	         ? " (not running, simulate success)"
+	         : (!priv->running
+	              ? " (waiting to initialize)"
+	              : ""));
+
+	if (info->mode == CB_INFO_MODE_DBUS_WAITING) {
+		if (priv->running)
+			_handle_dbus_start (self, info);
 		if (!info->callback) {
 			/* if the user did not provide a callback, the call_id is useless.
 			 * Especially, the user cannot use the call-id to cancel the request,
@@ -326,15 +362,18 @@ _start_request (NMFirewallManager *self,
 			 * (the request will always be started). */
 			return NULL;
 		}
-	} else if (!info->callback) {
-		/* if the user did not provide a callback and firewalld is not running,
-		 * there is no point in scheduling an idle-request to fake success. Just
-		 * return right away. */
-		_LOGD (info, "complete: drop request simulating success");
-		_cb_info_complete_normal (info, NULL);
-		return NULL;
+	} else if (info->mode == CB_INFO_MODE_IDLE) {
+		if (!info->callback) {
+			/* if the user did not provide a callback and firewalld is not running,
+			 * there is no point in scheduling an idle-request to fake success. Just
+			 * return right away. */
+			_LOGD (info, "complete: drop request simulating success");
+			_cb_info_complete_normal (info, NULL);
+			return NULL;
+		} else
+			info->idle.id = g_idle_add (_handle_idle, info);
 	} else
-		info->idle.id = g_idle_add (_handle_idle, info);
+		nm_assert_not_reached ();
 
 	return info;
 }
@@ -393,11 +432,13 @@ nm_firewall_manager_cancel_call (NMFirewallManagerCallId call)
 
 	_cb_info_callback (info, error);
 
-	if (_cb_info_is_idle (info)) {
+	if (info->mode == CB_INFO_MODE_DBUS_WAITING)
+		_cb_info_free (info);
+	else if (info->mode == CB_INFO_MODE_IDLE) {
 		g_source_remove (info->idle.id);
 		_cb_info_free (info);
 	} else {
-		info->mode = CB_INFO_MODE_DBUS_COMPLETED;
+		info->mode_mutable = CB_INFO_MODE_DBUS_COMPLETED;
 		g_cancellable_cancel (info->dbus.cancellable);
 		g_clear_object (&info->self);
 	}
@@ -405,49 +446,92 @@ nm_firewall_manager_cancel_call (NMFirewallManagerCallId call)
 
 /*****************************************************************************/
 
-static void
-set_running (NMFirewallManager *self, gboolean now_running)
+static gboolean
+name_owner_changed (NMFirewallManager *self)
 {
 	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
-	gboolean old_running = priv->running;
+	gs_free char *owner = NULL;
+	gboolean now_running;
+
+	owner = g_dbus_proxy_get_name_owner (priv->proxy);
+	now_running = !!owner;
+
+	if (now_running == priv->running)
+		return FALSE;
 
 	priv->running = now_running;
-	if (old_running != priv->running)
-		_notify (self, PROP_AVAILABLE);
+	_LOGD (NULL, "firewall %s", now_running ? "started" : "stopped");
+	return TRUE;
 }
 
 static void
-name_owner_changed (GObject    *object,
-                    GParamSpec *pspec,
-                    gpointer    user_data)
+name_owner_changed_cb (GObject    *object,
+                       GParamSpec *pspec,
+                       gpointer    user_data)
 {
-	NMFirewallManager *self = NM_FIREWALL_MANAGER (user_data);
-	gs_free char *owner = NULL;
+	NMFirewallManager *self = user_data;
 
-	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
-	if (owner) {
-		_LOGD (NULL, "firewall started");
-		set_running (self, TRUE);
-		g_signal_emit (self, signals[STARTED], 0);
-	} else {
-		_LOGD (NULL, "firewall stopped");
-		set_running (self, FALSE);
-	}
-}
+	nm_assert (NM_IS_FIREWALL_MANAGER (self));
+	nm_assert (G_IS_DBUS_PROXY (object));
+	nm_assert (NM_FIREWALL_MANAGER_GET_PRIVATE (self)->proxy == G_DBUS_PROXY (object));
 
-/*****************************************************************************/
+	if (name_owner_changed (self))
+		g_signal_emit (self, signals[STATE_CHANGED], 0, FALSE);
+}
 
 static void
-get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+_proxy_new_cb (GObject *source_object,
+               GAsyncResult *result,
+               gpointer user_data)
 {
-	switch (prop_id) {
-	case PROP_AVAILABLE:
-		g_value_set_boolean (value, NM_FIREWALL_MANAGER_GET_PRIVATE ((NMFirewallManager *) object)->running);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
+	NMFirewallManager *self;
+	NMFirewallManagerPrivate *priv;
+	GDBusProxy *proxy;
+	gs_free_error GError *error = NULL;
+	GHashTableIter iter;
+	CBInfo *info;
+
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
+	g_clear_object (&priv->proxy_cancellable);
+
+	if (!proxy) {
+		_LOGW (NULL, "could not connect to system D-Bus (%s)", error->message);
+		return;
+	}
+
+	priv->proxy = proxy;
+	g_signal_connect (priv->proxy, "notify::g-name-owner",
+	                  G_CALLBACK (name_owner_changed_cb), self);
+
+	if (!name_owner_changed (self))
+		_LOGD (NULL, "firewall %s", "initialized (not running)");
+
+again:
+	g_hash_table_iter_init (&iter, priv->pending_calls);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &info, NULL)) {
+		if (info->mode != CB_INFO_MODE_DBUS_WAITING)
+			continue;
+		if (priv->running) {
+			_LOGD (info, "make D-Bus call");
+			_handle_dbus_start (self, info);
+		} else {
+			_LOGD (info, "complete: fake success");
+			g_hash_table_iter_remove (&iter);
+			_cb_info_callback (info, NULL);
+			_cb_info_free (info);
+			goto again;
+		}
 	}
+
+	/* we always emit a state-changed signal, even if the
+	 * "running" property is still false. */
+	g_signal_emit (self, signals[STATE_CHANGED], 0, TRUE);
 }
 
 /*****************************************************************************/
@@ -465,28 +549,21 @@ constructed (GObject *object)
 {
 	NMFirewallManager *self = (NMFirewallManager *) object;
 	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
-	gs_free char *owner = NULL;
-	gs_free_error GError *error = NULL;
 
-	G_OBJECT_CLASS (nm_firewall_manager_parent_class)->constructed (object);
+	priv->proxy_cancellable = g_cancellable_new ();
 
-	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,
-	                                             NULL,
-	                                             FIREWALL_DBUS_SERVICE,
-	                                             FIREWALL_DBUS_PATH,
-	                                             FIREWALL_DBUS_INTERFACE_ZONE,
-	                                             NULL, &error);
-	if (priv->proxy) {
-		g_signal_connect (priv->proxy, "notify::g-name-owner",
-		                  G_CALLBACK (name_owner_changed), self);
-		owner = g_dbus_proxy_get_name_owner (priv->proxy);
-		priv->running = (owner != NULL);
-	} else
-		_LOGW (NULL, "could not connect to system D-Bus (%s)", error->message);
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+	                          | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+	                          NULL,
+	                          FIREWALL_DBUS_SERVICE,
+	                          FIREWALL_DBUS_PATH,
+	                          FIREWALL_DBUS_INTERFACE_ZONE,
+	                          priv->proxy_cancellable,
+	                          _proxy_new_cb,
+	                          self);
 
-	_LOGD (NULL, "firewall constructed (%srunning)", priv->running ? "" : "not");
+	G_OBJECT_CLASS (nm_firewall_manager_parent_class)->constructed (object);
 }
 
 static void
@@ -503,6 +580,7 @@ dispose (GObject *object)
 		priv->pending_calls = NULL;
 	}
 
+	nm_clear_g_cancellable (&priv->proxy_cancellable);
 	g_clear_object (&priv->proxy);
 
 	G_OBJECT_CLASS (nm_firewall_manager_parent_class)->dispose (object);
@@ -514,23 +592,15 @@ nm_firewall_manager_class_init (NMFirewallManagerClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
 	object_class->constructed = constructed;
-	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 
-	obj_properties[PROP_AVAILABLE] =
-	     g_param_spec_boolean (NM_FIREWALL_MANAGER_AVAILABLE, "", "",
-	                           FALSE,
-	                           G_PARAM_READABLE |
-	                           G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
-	signals[STARTED] =
-	    g_signal_new (NM_FIREWALL_MANAGER_STARTED,
+	signals[STATE_CHANGED] =
+	    g_signal_new (NM_FIREWALL_MANAGER_STATE_CHANGED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0,
 	                  NULL, NULL,
-	                  g_cclosure_marshal_VOID__VOID,
-	                  G_TYPE_NONE, 0);
+	                  g_cclosure_marshal_VOID__BOOLEAN,
+	                  G_TYPE_NONE, 1,
+	                  G_TYPE_BOOLEAN /* initialized_now */);
 }