core: add a root-only private D-Bus socket

For cases where NM may run without a bus daemon in root-only
environments, like an initramfs.  For disconnection, since private
connection just get a disconnect message instead of NameOwnerChanged
signals broadcast by a bus daemon, just synthesize the NameOwnerChanged
signals using our fake owner name.  It's just easier to do this rather
than modify any code that cares about disconnects.

Note that the new private socket is only enabled if built with
dbus-glib >= 0.100 as there are bugs in previous versions in the
implementation of dbus_g_proxy_new_for_peer() which clients must
use to talk to the private socket.
This commit is contained in:
Dan Williams
2012-10-04 22:26:18 -05:00
parent 496534b9f4
commit c9335a497b
5 changed files with 340 additions and 18 deletions

View File

@@ -215,6 +215,16 @@ AC_SUBST(DBUS_LIBS)
AC_CHECK_LIB([dbus-glib-1], [dbus_g_method_invocation_get_g_connection], ac_have_gmi_get_con="1", ac_have_gmi_get_con="0")
AC_DEFINE_UNQUOTED(HAVE_DBUS_GLIB_GMI_GET_CONNECTION, $ac_have_gmi_get_con, [Define if you have a dbus-glib with dbus_g_method_invocation_get_g_connection()])
dnl
dnl Only dbus-glib >= 0.100 can use private dbus connections
dnl
PKG_CHECK_MODULES(DBUS_GLIB_100, [dbus-glib-1 >= 0.100], [have_dbus_glib_100=yes],[have_dbus_glib_100=no])
if (test "${have_dbus_glib_100}" = "yes"); then
AC_DEFINE(HAVE_DBUS_GLIB_100, 1, [Define if you have dbus-glib >= 0.100])
else
AC_DEFINE(HAVE_DBUS_GLIB_100, 0, [Define if you have dbus-glib >= 0.100])
fi
PKG_CHECK_MODULES(GLIB, gio-unix-2.0 >= 2.32 gmodule-2.0)
dnl GLIB_VERSION_MIN_REQUIRED should match the version above.

View File

@@ -69,7 +69,8 @@ libtest_dhcp_la_SOURCES = \
libtest_dhcp_la_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(DBUS_CFLAGS) \
$(LIBNL_CFLAGS)
$(LIBNL_CFLAGS) \
-DNMRUNDIR=\"$(nmrundir)\"
libtest_dhcp_la_LIBADD = \
$(top_builddir)/src/generated/libnm-generated.la \

View File

@@ -530,10 +530,19 @@ main (int argc, char *argv[])
fw_mgr = nm_firewall_manager_get ();
g_assert (fw_mgr != NULL);
/* Start our DBus service */
if (!nm_dbus_manager_start_service (dbus_mgr)) {
nm_log_err (LOGD_CORE, "failed to start the dbus service.");
if (!nm_dbus_manager_get_connection (dbus_mgr)) {
#if HAVE_DBUS_GLIB_100
nm_log_warn (LOGD_CORE, "Failed to connect to D-Bus; only private bus is available");
#else
nm_log_err (LOGD_CORE, "Failed to connect to D-Bus, exiting...");
goto done;
#endif
} else {
/* Start our DBus service */
if (!nm_dbus_manager_start_service (dbus_mgr)) {
nm_log_err (LOGD_CORE, "failed to start the dbus service.");
goto done;
}
}
/* Clean leftover "# Added by NetworkManager" entries from /etc/hosts */

View File

@@ -15,11 +15,15 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2006 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2013 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
#include "config.h"
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "NetworkManager.h"
#include "nm-dbus-manager.h"
#include "nm-marshal.h"
@@ -31,9 +35,14 @@
#include <string.h>
#include "nm-logging.h"
#define PRIV_SOCK_PATH NMRUNDIR "/private"
#define PRIV_SOCK_TAG "private"
enum {
DBUS_CONNECTION_CHANGED = 0,
NAME_OWNER_CHANGED,
PRIVATE_CONNECTION_NEW,
PRIVATE_CONNECTION_DISCONNECTED,
NUMBER_OF_SIGNALS
};
@@ -45,12 +54,17 @@ G_DEFINE_TYPE(NMDBusManager, nm_dbus_manager, G_TYPE_OBJECT)
NM_TYPE_DBUS_MANAGER, \
NMDBusManagerPrivate))
typedef struct _PrivateServer PrivateServer;
typedef struct {
DBusConnection *connection;
DBusGConnection *g_connection;
GHashTable *exported;
gboolean started;
GSList *private_servers;
PrivateServer *priv_server;
DBusGProxy *proxy;
guint proxy_destroy_id;
@@ -79,12 +93,231 @@ nm_dbus_manager_get (void)
return singleton;
}
/**************************************************************/
struct _PrivateServer {
char *tag;
GQuark detail;
char *address;
DBusServer *server;
GHashTable *connections;
NMDBusManager *manager;
};
static DBusHandlerResult
private_server_message_filter (DBusConnection *conn,
DBusMessage *message,
void *data)
{
PrivateServer *s = data;
/* Clean up after the connection */
if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
nm_log_dbg (LOGD_CORE, "(%s) closed connection %p on private socket.",
s->tag, conn);
/* Emit this for the manager */
g_signal_emit (s->manager,
signals[PRIVATE_CONNECTION_DISCONNECTED],
s->detail,
dbus_connection_get_g_connection (conn));
dbus_connection_close (conn);
g_hash_table_remove (s->connections, conn);
/* Let dbus-glib process the message too */
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static dbus_bool_t
allow_only_root (DBusConnection *connection, unsigned long uid, void *data)
{
return uid == 0;
}
static void
private_server_new_connection (DBusServer *server,
DBusConnection *conn,
gpointer user_data)
{
PrivateServer *s = user_data;
static guint32 counter = 0;
char *sender;
if (!dbus_connection_add_filter (conn, private_server_message_filter, s, NULL)) {
dbus_connection_close (conn);
return;
}
dbus_connection_set_unix_user_function (conn, allow_only_root, NULL, NULL);
dbus_connection_setup_with_g_main (conn, NULL);
/* Fake a sender since private connections don't have one */
sender = g_strdup_printf ("x:y:%d", counter++);
g_hash_table_insert (s->connections, dbus_connection_ref (conn), sender);
nm_log_dbg (LOGD_CORE, "(%s) accepted connection %p on private socket.", s->tag, conn);
/* Emit this for the manager */
g_signal_emit (s->manager,
signals[PRIVATE_CONNECTION_NEW],
s->detail,
dbus_connection_get_g_connection (conn));
}
static PrivateServer *
private_server_new (const char *path,
const char *tag,
NMDBusManager *manager)
{
PrivateServer *s;
DBusServer *server;
DBusError error;
char *address;
unlink (path);
address = g_strdup_printf ("unix:path=%s", path);
nm_log_dbg (LOGD_CORE, "(%s) creating private socket %s.", tag, address);
dbus_error_init (&error);
server = dbus_server_listen (address, &error);
if (!server) {
nm_log_warn (LOGD_CORE, "(%s) failed to set up private socket %s: %s",
tag, address, error.message);
dbus_error_free (&error);
return NULL;
}
s = g_malloc0 (sizeof (*s));
s->address = address;
s->server = server;
dbus_server_setup_with_g_main (s->server, NULL);
dbus_server_set_new_connection_function (s->server, private_server_new_connection, s, NULL);
s->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
(GDestroyNotify) dbus_connection_unref,
g_free);
s->manager = manager;
s->tag = g_strdup (tag);
s->detail = g_quark_from_string (s->tag);
return s;
}
static void
private_server_free (PrivateServer *s)
{
unlink (s->address);
g_free (s->address);
g_free (s->tag);
g_hash_table_destroy (s->connections);
dbus_server_unref (s->server);
memset (s, 0, sizeof (*s));
g_free (s);
}
void
nm_dbus_manager_private_server_register (NMDBusManager *self,
const char *path,
const char *tag)
{
NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
PrivateServer *s;
GSList *iter;
#if !HAVE_DBUS_GLIB_100
g_assert_not_reached ();
#endif
g_return_if_fail (self != NULL);
g_return_if_fail (path != NULL);
g_return_if_fail (tag != NULL);
/* Only one instance per tag; but don't warn */
for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) {
s = iter->data;
if (g_strcmp0 (tag, s->tag) == 0)
return;
}
s = private_server_new (path, tag, self);
if (s)
priv->private_servers = g_slist_append (priv->private_servers, s);
}
#if HAVE_DBUS_GLIB_100
static const char *
private_server_get_connection_owner (PrivateServer *s, DBusGConnection *connection)
{
g_return_val_if_fail (s != NULL, NULL);
g_return_val_if_fail (connection != NULL, NULL);
return g_hash_table_lookup (s->connections, dbus_g_connection_get_connection (connection));
}
#endif
/**************************************************************/
#if HAVE_DBUS_GLIB_100
static void
private_connection_new (NMDBusManager *self, DBusGConnection *connection)
{
NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
GHashTableIter iter;
GObject *object;
const char *path;
/* Register all exported objects on this private connection */
g_hash_table_iter_init (&iter, priv->exported);
while (g_hash_table_iter_next (&iter, (gpointer) &object, (gpointer) &path)) {
dbus_g_connection_register_g_object (connection, path, object);
nm_log_dbg (LOGD_CORE, "(%s) registered %p (%s) at '%s' on private socket.",
PRIV_SOCK_TAG, object, G_OBJECT_TYPE_NAME (object), path);
}
}
static void
private_connection_disconnected (NMDBusManager *self, DBusGConnection *connection)
{
NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
const char *owner;
owner = private_server_get_connection_owner (priv->priv_server, connection);
g_assert (owner);
/* Fake a NameOwnerChanged to let listerners know this owner has quit */
g_signal_emit (G_OBJECT (self), signals[NAME_OWNER_CHANGED],
0, owner, owner, NULL);
}
#endif /* HAVE_DBUS_GLIB_100 */
static void
nm_dbus_manager_init (NMDBusManager *self)
{
NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
priv->exported = g_hash_table_new (g_direct_hash, g_direct_equal);
priv->exported = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
#if HAVE_DBUS_GLIB_100
/* Set up our main private DBus socket */
mkdir (NMRUNDIR, 0700);
priv->priv_server = private_server_new (PRIV_SOCK_PATH, PRIV_SOCK_TAG, self);
if (priv->priv_server) {
priv->private_servers = g_slist_append (priv->private_servers, priv->priv_server);
g_signal_connect (self,
NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
(GCallback) private_connection_new,
NULL);
g_signal_connect (self,
NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
(GCallback) private_connection_disconnected,
NULL);
}
#endif
}
static void
@@ -104,6 +337,11 @@ nm_dbus_manager_dispose (GObject *object)
priv->exported = NULL;
}
g_slist_foreach (priv->private_servers, (GFunc) private_server_free, NULL);
g_slist_free (priv->private_servers);
priv->private_servers = NULL;
priv->priv_server = NULL;
nm_dbus_manager_cleanup (self, TRUE);
if (priv->reconnect_id) {
@@ -119,6 +357,8 @@ nm_dbus_manager_class_init (NMDBusManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (NMDBusManagerPrivate));
object_class->dispose = nm_dbus_manager_dispose;
signals[DBUS_CONNECTION_CHANGED] =
@@ -137,7 +377,21 @@ nm_dbus_manager_class_init (NMDBusManagerClass *klass)
NULL, NULL, _nm_marshal_VOID__STRING_STRING_STRING,
G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
g_type_class_add_private (klass, sizeof (NMDBusManagerPrivate));
signals[PRIVATE_CONNECTION_NEW] =
g_signal_new (NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
G_STRUCT_OFFSET (NMDBusManagerClass, private_connection_new),
NULL, NULL, _nm_marshal_VOID__STRING_POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
signals[PRIVATE_CONNECTION_DISCONNECTED] =
g_signal_new (NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
G_STRUCT_OFFSET (NMDBusManagerClass, private_connection_disconnected),
NULL, NULL, _nm_marshal_VOID__STRING_POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
}
@@ -212,6 +466,9 @@ nm_dbus_manager_get_name_owner (NMDBusManager *self,
if (error)
g_return_val_if_fail (*error == NULL, NULL);
if (!NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy)
return NULL;
if (!dbus_g_proxy_call_with_timeout (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy,
"GetNameOwner", 2000, error,
G_TYPE_STRING, name,
@@ -234,6 +491,9 @@ nm_dbus_manager_name_has_owner (NMDBusManager *self,
g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (!NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy)
return FALSE;
if (!dbus_g_proxy_call (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy,
"NameHasOwner", &err,
G_TYPE_STRING, name,
@@ -279,7 +539,6 @@ static gboolean
nm_dbus_manager_init_bus (NMDBusManager *self)
{
NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
GError *err = NULL;
if (priv->connection) {
nm_log_warn (LOGD_CORE, "DBus Manager already has a valid connection.");
@@ -288,12 +547,14 @@ nm_dbus_manager_init_bus (NMDBusManager *self)
dbus_connection_set_change_sigpipe (TRUE);
priv->g_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
priv->g_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
if (!priv->g_connection) {
nm_log_err (LOGD_CORE, "Could not get the system bus. Make sure "
"the message bus daemon is running! Message: %s",
err->message);
g_error_free (err);
/* Log with 'info' severity; there won't be a bus daemon in minimal
* environments (eg, initrd) where we only want to use the private
* socket.
*/
nm_log_info (LOGD_CORE, "Could not connect to the system bus; only the "
"private D-Bus socket will be available.");
return FALSE;
}
@@ -338,6 +599,10 @@ nm_dbus_manager_start_service (NMDBusManager *self)
return FALSE;
}
/* Pointless to request a name when we aren't connected to the bus */
if (!priv->proxy)
return FALSE;
if (!dbus_g_proxy_call (priv->proxy, "RequestName", &err,
G_TYPE_STRING, NM_DBUS_SERVICE,
G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
@@ -388,13 +653,26 @@ nm_dbus_manager_register_object (NMDBusManager *self,
gpointer object)
{
NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
GHashTableIter iter;
DBusConnection *connection;
g_assert (G_IS_OBJECT (object));
g_warn_if_fail (g_hash_table_lookup (priv->exported, object) == NULL);
g_hash_table_insert (priv->exported, G_OBJECT (object), GUINT_TO_POINTER (1));
g_hash_table_insert (priv->exported, G_OBJECT (object), g_strdup (path));
if (priv->g_connection)
dbus_g_connection_register_g_object (priv->g_connection, path, G_OBJECT (object));
if (priv->priv_server) {
g_hash_table_iter_init (&iter, priv->priv_server->connections);
while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) {
dbus_g_connection_register_g_object (dbus_connection_get_g_connection (connection),
path,
G_OBJECT (object));
}
}
dbus_g_connection_register_g_object (priv->g_connection, path, G_OBJECT (object));
g_object_weak_ref (G_OBJECT (object), (GWeakNotify) object_destroyed, self);
}
@@ -402,12 +680,24 @@ void
nm_dbus_manager_unregister_object (NMDBusManager *self, gpointer object)
{
NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
GHashTableIter iter;
DBusConnection *connection;
g_assert (G_IS_OBJECT (object));
g_hash_table_remove (NM_DBUS_MANAGER_GET_PRIVATE (self)->exported, G_OBJECT (object));
g_object_weak_unref (G_OBJECT (object), (GWeakNotify) object_destroyed, self);
dbus_g_connection_unregister_g_object (priv->g_connection, G_OBJECT (object));
if (priv->g_connection)
dbus_g_connection_unregister_g_object (priv->g_connection, G_OBJECT (object));
if (priv->priv_server) {
g_hash_table_iter_init (&iter, priv->priv_server->connections);
while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) {
dbus_g_connection_unregister_g_object (dbus_connection_get_g_connection (connection),
G_OBJECT (object));
}
}
}
#if !HAVE_DBUS_GLIB_GMI_GET_CONNECTION

View File

@@ -40,8 +40,10 @@ typedef gboolean (* NMDBusSignalHandlerFunc) (DBusConnection * connection,
#define NM_IS_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_DBUS_MANAGER))
#define NM_DBUS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass))
#define NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED "dbus-connection-changed"
#define NM_DBUS_MANAGER_NAME_OWNER_CHANGED "name-owner-changed"
#define NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED "dbus-connection-changed"
#define NM_DBUS_MANAGER_NAME_OWNER_CHANGED "name-owner-changed"
#define NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "private-connection-new"
#define NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "private-connection-disconnected"
typedef struct {
GObject parent;
@@ -58,6 +60,12 @@ typedef struct {
const char *name,
const char *old_owner,
const char *new_owner);
void (*private_connection_new) (NMDBusManager *mgr,
DBusGConnection *connection);
void (*private_connection_disconnected) (NMDBusManager *mgr,
DBusGConnection *connection);
} NMDBusManagerClass;
GType nm_dbus_manager_get_type (void);
@@ -82,6 +90,10 @@ void nm_dbus_manager_register_object (NMDBusManager *self,
void nm_dbus_manager_unregister_object (NMDBusManager *self, gpointer object);
void nm_dbus_manager_private_server_register (NMDBusManager *self,
const char *path,
const char *tag);
#if !HAVE_DBUS_GLIB_GMI_GET_CONNECTION
DBusGConnection *dbus_g_method_invocation_get_g_connection (DBusGMethodInvocation *context);
#endif