From f24c7fa9cbf2e0caa08a48ef64141d7ea50105aa Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 18 May 2009 21:30:33 +0200 Subject: [PATCH] Add support for abstract unix socket addresses --- docs/reference/gio/gio-sections.txt | 5 + gio/gsocketaddress.c | 3 + gio/gunixsocketaddress.c | 300 +++++++++++++++++++++++++++++------- gio/gunixsocketaddress.h | 9 +- 4 files changed, 262 insertions(+), 55 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 8b52e83..d4c8962 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -1486,6 +1486,11 @@ g_inet_socket_address_get_type GUnixSocketAddress GUnixSocketAddress g_unix_socket_address_new +g_unix_socket_address_new_abstract +g_unix_socket_address_get_is_abstract +g_unix_socket_address_get_path +g_unix_socket_address_get_path_len +g_unix_socket_address_abstract_names_supported GUnixSocketAddressClass GUnixSocketAddressPrivate diff --git a/gio/gsocketaddress.c b/gio/gsocketaddress.c index 582dd1f..3ca793f 100644 --- a/gio/gsocketaddress.c +++ b/gio/gsocketaddress.c @@ -240,6 +240,9 @@ g_socket_address_new_from_native (gpointer native, { struct sockaddr_un *addr = (struct sockaddr_un *) native; + if (addr->sun_path[0] == 0) + return g_unix_socket_address_new_abstract (addr->sun_path+1, + sizeof (addr->sun_path) - 1); return g_unix_socket_address_new (addr->sun_path); } #endif diff --git a/gio/gunixsocketaddress.c b/gio/gunixsocketaddress.c index 6efa814..d1a2e03 100644 --- a/gio/gunixsocketaddress.c +++ b/gio/gunixsocketaddress.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "gunixsocketaddress.h" #include "glibintl.h" @@ -50,49 +52,98 @@ enum { PROP_0, PROP_PATH, + PROP_PATH_AS_ARRAY, + PROP_ABSTRACT, }; +#define UNIX_PATH_MAX sizeof (((struct sockaddr_un *) 0)->sun_path) + struct _GUnixSocketAddressPrivate { - char *path; + char path[UNIX_PATH_MAX]; /* Not including the initial zero in abstract case, so + we can guarantee zero termination of abstract + pathnames in the get_path() API */ + gsize path_len; /* Not including any terminating zeros */ + gboolean abstract; }; static void -g_unix_socket_address_finalize (GObject *object) -{ - GUnixSocketAddress *address G_GNUC_UNUSED = G_UNIX_SOCKET_ADDRESS (object); - - g_free (address->priv->path); - - if (G_OBJECT_CLASS (g_unix_socket_address_parent_class)->finalize) - (G_OBJECT_CLASS (g_unix_socket_address_parent_class)->finalize) (object); -} - -static void -g_unix_socket_address_dispose (GObject *object) +g_unix_socket_address_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - GUnixSocketAddress *address G_GNUC_UNUSED = G_UNIX_SOCKET_ADDRESS (object); + GUnixSocketAddress *address = G_UNIX_SOCKET_ADDRESS (object); + const char *str; + GByteArray *array; + gsize len; - if (G_OBJECT_CLASS (g_unix_socket_address_parent_class)->dispose) - (*G_OBJECT_CLASS (g_unix_socket_address_parent_class)->dispose) (object); + switch (prop_id) + { + case PROP_PATH: + str = g_value_get_string (value); + if (str) + { + g_strlcpy (address->priv->path, str, + sizeof (address->priv->path)); + address->priv->path_len = strlen (address->priv->path); + } + break; + + case PROP_PATH_AS_ARRAY: + array = g_value_get_boxed (value); + + if (array) + { + /* Clip to fit in UNIX_PATH_MAX with zero termination or first byte */ + len = MIN (array->len, UNIX_PATH_MAX-1); + + /* Remove any trailing zeros from path_len */ + while (len > 0 && array->data[len-1] == 0) + len--; + + memcpy (address->priv->path, array->data, len); + address->priv->path[len] = 0; /* Ensure null-terminated */ + address->priv->path_len = len; + } + break; + + case PROP_ABSTRACT: + address->priv->abstract = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } } static void g_unix_socket_address_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) + guint prop_id, + GValue *value, + GParamSpec *pspec) { GUnixSocketAddress *address = G_UNIX_SOCKET_ADDRESS (object); + GByteArray *array; switch (prop_id) { case PROP_PATH: - g_value_set_string (value, address->priv->path); - break; + g_value_set_string (value, address->priv->path); + break; + + case PROP_PATH_AS_ARRAY: + array = g_byte_array_sized_new (address->priv->path_len); + g_byte_array_append (array, (guint8 *)address->priv->path, address->priv->path_len); + g_value_take_boxed (value, array); + break; + + case PROP_ABSTRACT: + g_value_set_boolean (value, address->priv->abstract); + break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } @@ -104,26 +155,6 @@ g_unix_socket_address_get_family (GSocketAddress *address) return G_SOCKET_FAMILY_UNIX; } -static void -g_unix_socket_address_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GUnixSocketAddress *address = G_UNIX_SOCKET_ADDRESS (object); - - switch (prop_id) - { - case PROP_PATH: - g_free (address->priv->path); - address->priv->path = g_value_dup_string (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - static gssize g_unix_socket_address_get_native_size (GSocketAddress *address) { @@ -146,9 +177,24 @@ g_unix_socket_address_to_native (GSocketAddress *address, return FALSE; } + if (addr->priv->abstract && + !g_unix_socket_address_abstract_names_supported ()) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Abstract unix domain socket addresses not supported on this system")); + return FALSE; + } + sock = (struct sockaddr_un *) dest; sock->sun_family = AF_UNIX; - g_strlcpy (sock->sun_path, addr->priv->path, sizeof (sock->sun_path)); + memset (sock->sun_path, 0, sizeof (sock->sun_path)); + if (addr->priv->abstract) + { + sock->sun_path[0] = 0; + memcpy (sock->sun_path+1, addr->priv->path, addr->priv->path_len); + } + else + strcpy (sock->sun_path, addr->priv->path); return TRUE; } @@ -161,8 +207,6 @@ g_unix_socket_address_class_init (GUnixSocketAddressClass *klass) g_type_class_add_private (klass, sizeof (GUnixSocketAddressPrivate)); - gobject_class->finalize = g_unix_socket_address_finalize; - gobject_class->dispose = g_unix_socket_address_dispose; gobject_class->set_property = g_unix_socket_address_set_property; gobject_class->get_property = g_unix_socket_address_get_property; @@ -171,22 +215,41 @@ g_unix_socket_address_class_init (GUnixSocketAddressClass *klass) gsocketaddress_class->get_native_size = g_unix_socket_address_get_native_size; g_object_class_install_property (gobject_class, - PROP_PATH, - g_param_spec_string ("path", - P_("Path"), - P_("UNIX socket path"), - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + PROP_PATH, + g_param_spec_string ("path", + P_("Path"), + P_("UNIX socket path"), + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PATH_AS_ARRAY, + g_param_spec_boxed ("path-as-array", + P_("Path array"), + P_("UNIX socket path, as byte array"), + G_TYPE_BYTE_ARRAY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ABSTRACT, + g_param_spec_boolean ("abstract", + P_("Abstract"), + P_("Whether or not this is an abstract address"), + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); } static void g_unix_socket_address_init (GUnixSocketAddress *address) { address->priv = G_TYPE_INSTANCE_GET_PRIVATE (address, - G_TYPE_UNIX_SOCKET_ADDRESS, - GUnixSocketAddressPrivate); + G_TYPE_UNIX_SOCKET_ADDRESS, + GUnixSocketAddressPrivate); - address->priv->path = NULL; + memset (address->priv->path, 0, sizeof (address->priv->path)); + address->priv->path_len = -1; } /** @@ -195,6 +258,9 @@ g_unix_socket_address_init (GUnixSocketAddress *address) * * Creates a new #GUnixSocketAddress for @path. * + * To create abstract socket addresses, on systems that support that, + * use g_unix_socket_address_new_abstract(). + * * Returns: a new #GUnixSocketAddress * * Since: 2.22 @@ -204,8 +270,134 @@ g_unix_socket_address_new (const gchar *path) { return g_object_new (G_TYPE_UNIX_SOCKET_ADDRESS, "path", path, + "abstract", FALSE, NULL); } +/** + * g_unix_socket_address_new_abstract: + * @path: the abstract name + * @path_len: the length of @path, or -1 + * + * Creates a new abstract #GUnixSocketAddress for @path. + * + * Unix domain sockets are generally visible in the filesystem. However, some + * systems support abstract socket name which are not visible in the + * filesystem and not affected by the filesystem permissions, visibility, etc. + * + * Note that not all systems (really only Linux) support abstract + * socket names, so if you use them on other systems function calls may + * return %G_IO_ERROR_NOT_SUPPORTED errors. You can use + * g_unix_socket_address_abstract_names_supported() to see if abstract + * names are supported. + * + * If @path_len is -1 then @path is assumed to be a zero terminated + * string (although in general abstract names need not be zero terminated + * and can have embedded nuls). All bytes after @path_len up to the max size + * of an abstract unix domain name is filled with zero bytes. + * + * Returns: a new #GUnixSocketAddress + * + * Since: 2.22 + */ +GSocketAddress * +g_unix_socket_address_new_abstract (const gchar *path, + int path_len) +{ + GSocketAddress *address; + GByteArray *array; + + if (path_len == -1) + path_len = strlen (path); + + array = g_byte_array_sized_new (path_len); + + g_byte_array_append (array, (guint8 *)path, path_len); + + address = g_object_new (G_TYPE_UNIX_SOCKET_ADDRESS, + "path-as-array", array, + "abstract", TRUE, + NULL); + + g_byte_array_unref (array); + + return address; +} + +/** + * g_unix_socket_address_get_path: + * @address: a #GInetSocketAddress + * + * Gets @address's path, or for abstract sockets the "name". + * + * Guaranteed to be zero-terminated, but an abstract socket + * may contain embedded zeros, and thus you should use + * g_unix_socket_address_get_path_len() to get the true length + * of this string. + * + * Returns: the path for @address + * + * Since: 2.22 + */ +const char * +g_unix_socket_address_get_path (GUnixSocketAddress *address) +{ + return address->priv->path; +} + +/** + * g_unix_socket_address_get_path_len: + * @address: a #GInetSocketAddress + * + * Gets the length of @address's path. + * + * For details, see g_unix_socket_address_get_path(). + * + * Returns: the length of the path + * + * Since: 2.22 + */ +gsize +g_unix_socket_address_get_path_len (GUnixSocketAddress *address) +{ + return address->priv->path_len; +} + +/** + * g_unix_socket_address_get_is_abstract: + * @address: a #GInetSocketAddress + * + * Gets @address's path. + * + * Returns: %TRUE if the address is abstract, %FALSE otherwise + * + * Since: 2.22 + */ +gboolean +g_unix_socket_address_get_is_abstract (GUnixSocketAddress *address) +{ + return address->priv->abstract; +} + +/** + * g_unix_socket_address_abstract_names_supported: + * @address: a #GInetSocketAddress + * + * Gets @address's path. + * + * Returns: %TRUE if the address is abstract, %FALSE otherwise + * + * Since: 2.22 + */ +gboolean +g_unix_socket_address_abstract_names_supported (void) +{ +#ifdef __linux__ + return TRUE; +#else + return FALSE; +#endif +} + #define __G_UNIX_SOCKET_ADDRESS_C__ #include "gioaliasdef.c" diff --git a/gio/gunixsocketaddress.h b/gio/gunixsocketaddress.h index 527937f..fabddd8 100644 --- a/gio/gunixsocketaddress.h +++ b/gio/gunixsocketaddress.h @@ -54,7 +54,14 @@ struct _GUnixSocketAddressClass GType g_unix_socket_address_get_type (void) G_GNUC_CONST; -GSocketAddress *g_unix_socket_address_new (const gchar *path); +GSocketAddress *g_unix_socket_address_new (const gchar *path); +GSocketAddress *g_unix_socket_address_new_abstract (const gchar *path, + int path_len); +const char * g_unix_socket_address_get_path (GUnixSocketAddress *address); +gsize g_unix_socket_address_get_path_len (GUnixSocketAddress *address); +gboolean g_unix_socket_address_get_is_abstract (GUnixSocketAddress *address); + +gboolean g_unix_socket_address_abstract_names_supported (void); G_END_DECLS -- 2.7.4