diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index a2e62e0db..e014938a5 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -145,6 +145,12 @@ + + High-level named pipes functionality + + + + TLS (SSL) support diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 6010a0f2b..d3e1a3918 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2336,6 +2336,60 @@ GThreadedSocketServicePrivate g_threaded_socket_service_get_type +
+gwin32namedpipeclient +GWin32NamedPipeClient +GWin32NamedPipeClient +g_win32_named_pipe_client_new +g_win32_named_pipe_client_connect +g_win32_named_pipe_client_connect_async +g_win32_named_pipe_client_connect_finish + +GWin32NamedPipeClientClass +G_IS_WIN32_NAMED_PIPE_CLIENT +G_IS_WIN32_NAMED_PIPE_CLIENT_CLASS +G_WIN32_NAMED_PIPE_CLIENT +G_WIN32_NAMED_PIPE_CLIENT_CLASS +G_WIN32_NAMED_PIPE_CLIENT_GET_CLASS +G_TYPE_WIN32_NAMED_PIPE_CLIENT + +g_win32_named_pipe_client_get_type +
+ +
+gwin32namedpipelistener +GWin32NamedPipeListener +GWin32NamedPipeListener +g_win32_named_pipe_listener_new +g_win32_named_pipe_listener_add_named_pipe +g_win32_named_pipe_listener_accept +g_win32_named_pipe_listener_accept_async +g_win32_named_pipe_listener_accept_finish + +GWin32NamedPipeListenerClass +G_IS_WIN32_NAMED_PIPE_LISTENER +G_IS_WIN32_NAMED_PIPE_LISTENER_CLASS +G_WIN32_NAMED_PIPE_LISTENER +G_WIN32_NAMED_PIPE_LISTENER_CLASS +G_WIN32_NAMED_PIPE_LISTENER_GET_CLASS +G_TYPE_WIN32_NAMED_PIPE_LISTENER + +g_win32_named_pipe_listener_get_type +
+ +
+gwin32namedpipeconnection +GWin32NamedPipeConnection +GWin32NamedPipeConnection +g_win32_named_pipe_connection_new + +G_IS_WIN32_NAMED_PIPE_CONNECTION +G_WIN32_NAMED_PIPE_CONNECTION +G_TYPE_WIN32_NAMED_PIPE_CONNECTION + +g_win32_named_pipe_connection_get_type +
+
gunixfdmessage GUnixFDMessage diff --git a/gio/Makefile.am b/gio/Makefile.am index e911d9145..161a65471 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -313,6 +313,12 @@ win32_actual_sources = \ gwin32outputstream.c \ gwin32outputstream.h \ gwin32networking.h \ + gwin32namedpipelistener.c \ + gwin32namedpipelistener.h \ + gwin32namedpipeclient.c \ + gwin32namedpipeclient.h \ + gwin32namedpipeconnection.c \ + gwin32namedpipeconnection.h \ $(NULL) win32_more_sources_for_vcproj = \ @@ -334,6 +340,9 @@ giowin32includedir=$(includedir)/gio-win32-2.0/gio giowin32include_HEADERS = \ gwin32inputstream.h \ gwin32outputstream.h \ + gwin32namedpipelistener.h \ + gwin32namedpipeclient.h \ + gwin32namedpipeconnection.h \ $(NULL) endif diff --git a/gio/gasynchelper.c b/gio/gasynchelper.c index 7cf86f1b7..76aaf6c3d 100644 --- a/gio/gasynchelper.c +++ b/gio/gasynchelper.c @@ -80,4 +80,102 @@ end: return result; } + +typedef struct { + GSource source; + GPollFD pollfd; +} GWin32HandleSource; + +static gboolean +g_win32_handle_source_prepare (GSource *source, + gint *timeout) +{ + *timeout = -1; + return FALSE; +} + +static gboolean +g_win32_handle_source_check (GSource *source) +{ + GWin32HandleSource *hsource = (GWin32HandleSource *)source; + + return hsource->pollfd.revents; +} + +static gboolean +g_win32_handle_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GWin32HandleSourceFunc func = (GWin32HandleSourceFunc)callback; + GWin32HandleSource *hsource = (GWin32HandleSource *)source; + + return func (hsource->pollfd.fd, user_data); +} + +static void +g_win32_handle_source_finalize (GSource *source) +{ +} + +static gboolean +g_win32_handle_source_closure_callback (HANDLE handle, + gpointer data) +{ + GClosure *closure = data; + + GValue param = G_VALUE_INIT; + GValue result_value = G_VALUE_INIT; + gboolean result; + + g_value_init (&result_value, G_TYPE_BOOLEAN); + + g_value_init (¶m, G_TYPE_POINTER); + g_value_set_pointer (¶m, handle); + + g_closure_invoke (closure, &result_value, 1, ¶m, NULL); + + result = g_value_get_boolean (&result_value); + g_value_unset (&result_value); + g_value_unset (¶m); + + return result; +} + +GSourceFuncs g_win32_handle_source_funcs = { + g_win32_handle_source_prepare, + g_win32_handle_source_check, + g_win32_handle_source_dispatch, + g_win32_handle_source_finalize, + (GSourceFunc)g_win32_handle_source_closure_callback, +}; + +GSource * +_g_win32_handle_create_source (HANDLE handle, + GCancellable *cancellable) +{ + GWin32HandleSource *hsource; + GSource *source; + + source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource)); + hsource = (GWin32HandleSource *)source; + g_source_set_name (source, "GWin32Handle"); + + if (cancellable) + { + GSource *cancellable_source; + + cancellable_source = g_cancellable_source_new (cancellable); + g_source_add_child_source (source, cancellable_source); + g_source_set_dummy_callback (cancellable_source); + g_source_unref (cancellable_source); + } + + hsource->pollfd.fd = (gint)handle; + hsource->pollfd.events = G_IO_IN; + hsource->pollfd.revents = 0; + g_source_add_poll (source, &hsource->pollfd); + + return source; +} #endif diff --git a/gio/gasynchelper.h b/gio/gasynchelper.h index 5f7f5a3c0..1cbd12a85 100644 --- a/gio/gasynchelper.h +++ b/gio/gasynchelper.h @@ -30,10 +30,17 @@ G_BEGIN_DECLS #ifdef G_OS_WIN32 +typedef gboolean (* GWin32HandleSourceFunc) (HANDLE handle, + gpointer user_data); + gboolean _g_win32_overlap_wait_result (HANDLE hfile, OVERLAPPED *overlap, DWORD *transferred, GCancellable *cancellable); + +GSource *_g_win32_handle_create_source (HANDLE handle, + GCancellable *cancellable); + #endif G_END_DECLS diff --git a/gio/gio.h b/gio/gio.h index 5c8b38459..d5b13db23 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -168,6 +168,12 @@ #include #include +#ifdef G_OS_WIN32 +#include +#include +#include +#endif + #include #undef __GIO_GIO_H_INSIDE__ diff --git a/gio/giotypes.h b/gio/giotypes.h index d98ec4a28..0b2ad4daf 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -257,6 +257,10 @@ typedef struct _GProxyAddressEnumerator GProxyAddressEnumerator; typedef struct _GVolume GVolume; /* Dummy typedef */ typedef struct _GVolumeMonitor GVolumeMonitor; +#ifdef G_OS_WIN32 +typedef struct _GWin32NamedPipeConnection GWin32NamedPipeConnection; +#endif + /** * GAsyncReadyCallback: * @source_object: the object the asynchronous operation was started with. diff --git a/gio/gwin32namedpipeclient.c b/gio/gwin32namedpipeclient.c new file mode 100644 index 000000000..c45e1a9cd --- /dev/null +++ b/gio/gwin32namedpipeclient.c @@ -0,0 +1,304 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2016 NICE s.r.l. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "config.h" + +#include "gwin32namedpipeclient.h" +#include "gwin32namedpipeconnection.h" + +#include "gtask.h" +#include "glibintl.h" + +#include + +/** + * SECTION:gwin32namedpipeclient + * @short_description: Helper for connecting to a named pipe + * @include: gio/gio.h + * @see_also: #GWin32NamedPipeListener + * + * #GWin32NamedPipeClient is a lightweight high-level utility class for + * connecting to a named pipe. + * + * You create a #GWin32NamedPipeClient object, set any options you want, and then + * call a sync or async connect operation, which returns a #GWin32NamedPipeConnection + * on success. + * + * As #GWin32NamedPipeClient is a lightweight object, you don't need to + * cache it. You can just create a new one any time you need one. + * + * Since: 2.48 + */ + +typedef struct +{ + guint timeout; +} GWin32NamedPipeClientPrivate; + +enum +{ + PROP_0, + PROP_TIMEOUT, + LAST_PROP +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GWin32NamedPipeClient, g_win32_named_pipe_client, G_TYPE_OBJECT) + +static GParamSpec *props[LAST_PROP]; + +static void +g_win32_named_pipe_client_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GWin32NamedPipeClient *client = G_WIN32_NAMED_PIPE_CLIENT (object); + GWin32NamedPipeClientPrivate *priv; + + priv = g_win32_named_pipe_client_get_instance_private (client); + + switch (prop_id) + { + case PROP_TIMEOUT: + g_value_set_uint (value, priv->timeout); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_win32_named_pipe_client_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GWin32NamedPipeClient *client = G_WIN32_NAMED_PIPE_CLIENT (object); + GWin32NamedPipeClientPrivate *priv; + + priv = g_win32_named_pipe_client_get_instance_private (client); + + switch (prop_id) + { + case PROP_TIMEOUT: + priv->timeout = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_win32_named_pipe_client_class_init (GWin32NamedPipeClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = g_win32_named_pipe_client_get_property; + object_class->set_property = g_win32_named_pipe_client_set_property; + + props[PROP_TIMEOUT] = + g_param_spec_uint ("timeout", + P_("Timeout"), + P_("The timeout in milliseconds to wait"), + 0, + NMPWAIT_WAIT_FOREVER, + NMPWAIT_WAIT_FOREVER, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROP, props); +} + +static void +g_win32_named_pipe_client_init (GWin32NamedPipeClient *self) +{ +} + +/** + * g_win32_named_pipe_client_new: + * + * Creates a new #GWin32NamedPipeClient. + * + * Returns: a #GWin32NamedPipeClient. + * Free the returned object with g_object_unref(). + * + * Since: 2.48 + */ +GWin32NamedPipeClient * +g_win32_named_pipe_client_new (void) +{ + return g_object_new (G_TYPE_WIN32_NAMED_PIPE_CLIENT, NULL); +} + +/** + * g_win32_named_pipe_client_connect: + * @client: a #GWin32NamedPipeClient. + * @pipe_name: a pipe name. + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Waits until the pipe is available or the default timeout experies. + * + * When the pipe is available, a new #GWin32NamedPipeConnection is constructed + * and returned. The caller owns this new object and must drop their + * reference to it when finished with it. + * + * Returns: (transfer full): a #GWin32NamedPipeConnection on success, %NULL on error. + * + * Since: 2.48 + */ +GWin32NamedPipeConnection * +g_win32_named_pipe_client_connect (GWin32NamedPipeClient *client, + const gchar *pipe_name, + GCancellable *cancellable, + GError **error) +{ + GWin32NamedPipeClientPrivate *priv; + HANDLE handle = INVALID_HANDLE_VALUE; + GWin32NamedPipeConnection *connection = NULL; + gunichar2 *pipe_namew; + + g_return_val_if_fail (G_IS_WIN32_NAMED_PIPE_CLIENT (client), NULL); + g_return_val_if_fail (pipe_name != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + priv = g_win32_named_pipe_client_get_instance_private (client); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; + + pipe_namew = g_utf8_to_utf16 (pipe_name, -1, NULL, NULL, NULL); + + if (WaitNamedPipeW (pipe_namew, priv->timeout)) + { + handle = CreateFileW (pipe_namew, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto end; + + connection = g_object_new (G_TYPE_WIN32_NAMED_PIPE_CONNECTION, + "handle", handle, + "close-handle", TRUE, + NULL); + } + else + { + int errsv; + gchar *err; + + errsv = GetLastError (); + err = g_win32_error_message (errsv); + g_set_error_literal (error, G_IO_ERROR, + g_io_error_from_win32_error (errsv), + err); + g_free (err); + } + +end: + g_free (pipe_namew); + + return connection; +} + +static void +client_connect_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GWin32NamedPipeClient *client = G_WIN32_NAMED_PIPE_CLIENT (source_object); + const gchar *pipe_name = (const gchar *)task_data; + GWin32NamedPipeConnection *connection; + GError *error = NULL; + + connection = g_win32_named_pipe_client_connect (client, pipe_name, + cancellable, &error); + + if (connection == NULL) + g_task_return_error (task, error); + else + g_task_return_pointer (task, connection, (GDestroyNotify)g_object_unref); + + g_object_unref(task); +} + +/** + * g_win32_named_pipe_client_connect_async: + * @client: a #GWin32NamedPipeClient + * @pipe_name: a pipe name. + * @cancellable: (allow-none): a #GCancellable, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback + * @user_data: (closure): user data for the callback + * + * This is the asynchronous version of g_win32_named_pipe_client_connect(). + * + * When the operation is finished @callback will be + * called. You can then call g_win32_named_pipe_client_connect_finish() to get + * the result of the operation. + * + * Since: 2.48 + */ +void +g_win32_named_pipe_client_connect_async (GWin32NamedPipeClient *client, + const gchar *pipe_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_return_if_fail (G_IS_WIN32_NAMED_PIPE_CLIENT (client)); + g_return_if_fail (pipe_name != NULL); + + task = g_task_new (client, cancellable, callback, user_data); + g_task_set_task_data (task, g_strdup (pipe_name), g_free); + + g_task_run_in_thread (task, client_connect_thread); +} + +/** + * g_win32_named_pipe_client_connect_finish: + * @client: a #GWin32NamedPipeClient. + * @result: a #GAsyncResult. + * @error: a #GError location to store the error occurring, or %NULL to + * ignore. + * + * Finishes an async connect operation. See g_win32_named_pipe_client_connect_async() + * + * Returns: (transfer full): a #GWin32NamedPipeConnection on success, %NULL on error. + * + * Since: 2.48 + */ +GWin32NamedPipeConnection * +g_win32_named_pipe_client_connect_finish (GWin32NamedPipeClient *client, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (G_IS_WIN32_NAMED_PIPE_CLIENT (client), NULL); + g_return_val_if_fail (g_task_is_valid (result, client), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/gio/gwin32namedpipeclient.h b/gio/gwin32namedpipeclient.h new file mode 100644 index 000000000..160fc8e5d --- /dev/null +++ b/gio/gwin32namedpipeclient.h @@ -0,0 +1,80 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2016 NICE s.r.l. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef __G_WIN32_NAMED_PIPE_CLIENT_H__ +#define __G_WIN32_NAMED_PIPE_CLIENT_H__ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define G_TYPE_WIN32_NAMED_PIPE_CLIENT (g_win32_named_pipe_client_get_type ()) +#define G_WIN32_NAMED_PIPE_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_NAMED_PIPE_CLIENT, GWin32NamedPipeClient)) +#define G_WIN32_NAMED_PIPE_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_WIN32_NAMED_PIPE_CLIENT, GWin32NamedPipeClientClass)) +#define G_IS_WIN32_NAMED_PIPE_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_NAMED_PIPE_CLIENT)) +#define G_IS_WIN32_NAMED_PIPE_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_NAMED_PIPE_CLIENT)) +#define G_WIN32_NAMED_PIPE_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_NAMED_PIPE_CLIENT, GWin32NamedPipeClientClass)) + +typedef struct _GWin32NamedPipeClient GWin32NamedPipeClient; +typedef struct _GWin32NamedPipeClientClass GWin32NamedPipeClientClass; + +struct _GWin32NamedPipeClient +{ + /*< private >*/ + GObject parent_instance; +}; + +struct _GWin32NamedPipeClientClass +{ + GObjectClass parent_class; + + /*< private >*/ + gpointer padding[10]; +}; + +GLIB_AVAILABLE_IN_2_48 +GType g_win32_named_pipe_client_get_type (void) G_GNUC_CONST; + +GLIB_AVAILABLE_IN_2_48 +GWin32NamedPipeClient *g_win32_named_pipe_client_new (void); + +GLIB_AVAILABLE_IN_2_48 +GWin32NamedPipeConnection *g_win32_named_pipe_client_connect (GWin32NamedPipeClient *client, + const gchar *pipe_name, + GCancellable *cancellable, + GError **error); + +GLIB_AVAILABLE_IN_2_48 +void g_win32_named_pipe_client_connect_async (GWin32NamedPipeClient *client, + const gchar *pipe_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GLIB_AVAILABLE_IN_2_48 +GWin32NamedPipeConnection *g_win32_named_pipe_client_connect_finish (GWin32NamedPipeClient *client, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* __G_WIN32_NAMED_PIPE_CLIENT_H__ */ diff --git a/gio/gwin32namedpipeconnection.c b/gio/gwin32namedpipeconnection.c new file mode 100644 index 000000000..f68a79eb1 --- /dev/null +++ b/gio/gwin32namedpipeconnection.c @@ -0,0 +1,213 @@ +/* + * Copyright © 2016 NICE s.r.l. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Authors: Ignacio Casal Quinteiro + */ + + +#include "config.h" + +#include "gwin32namedpipeconnection.h" +#include "gwin32inputstream.h" +#include "gwin32outputstream.h" + +#include "glibintl.h" + +#include + +/** + * SECTION:gwin32namedpipeconnection + * @short_description: A wrapper around a Windows pipe handle. + * @include: gio/gio.h + * @see_also: #GIOStream + * + * GWin32NamedPipeConnection creates a #GIOStream from an arbitrary handle. + * + * Since: 2.48 + */ + +/** + * GWin32NamedPipeConnection: + * + * A wrapper around a Windows pipe handle. + * + * Since: 2.48 + */ +struct _GWin32NamedPipeConnection +{ + GIOStream parent; + + void *handle; + gboolean close_handle; + + GInputStream *input_stream; + GOutputStream *output_stream; +}; + +struct _GWin32NamedPipeConnectionClass +{ + GIOStreamClass parent; +}; + +typedef struct _GWin32NamedPipeConnectionClass GWin32NamedPipeConnectionClass; + +enum +{ + PROP_0, + PROP_HANDLE, + PROP_CLOSE_HANDLE +}; + +G_DEFINE_TYPE (GWin32NamedPipeConnection, g_win32_named_pipe_connection, G_TYPE_IO_STREAM) + +static void +g_win32_named_pipe_connection_finalize (GObject *object) +{ + GWin32NamedPipeConnection *connection = G_WIN32_NAMED_PIPE_CONNECTION (object); + + if (connection->input_stream) + g_object_unref (connection->input_stream); + + if (connection->output_stream) + g_object_unref (connection->output_stream); + + if (connection->close_handle && connection->handle != INVALID_HANDLE_VALUE) + CloseHandle (connection->handle); + + G_OBJECT_CLASS (g_win32_named_pipe_connection_parent_class)->finalize (object); +} + +static void +g_win32_named_pipe_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GWin32NamedPipeConnection *connection = G_WIN32_NAMED_PIPE_CONNECTION (object); + + switch (prop_id) + { + case PROP_HANDLE: + connection->handle = g_value_get_pointer (value); + if (connection->handle != NULL && connection->handle != INVALID_HANDLE_VALUE) + { + connection->input_stream = g_win32_input_stream_new (connection->handle, FALSE); + connection->output_stream = g_win32_output_stream_new (connection->handle, FALSE); + } + break; + + case PROP_CLOSE_HANDLE: + connection->close_handle = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_win32_named_pipe_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GWin32NamedPipeConnection *connection = G_WIN32_NAMED_PIPE_CONNECTION (object); + + switch (prop_id) + { + case PROP_HANDLE: + g_value_set_pointer (value, connection->handle); + break; + + case PROP_CLOSE_HANDLE: + g_value_set_boolean (value, connection->close_handle); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GInputStream * +g_win32_named_pipe_connection_get_input_stream (GIOStream *stream) +{ + GWin32NamedPipeConnection *connection = G_WIN32_NAMED_PIPE_CONNECTION (stream); + + return connection->input_stream; +} + +static GOutputStream * +g_win32_named_pipe_connection_get_output_stream (GIOStream *stream) +{ + GWin32NamedPipeConnection *connection = G_WIN32_NAMED_PIPE_CONNECTION (stream); + + return connection->output_stream; +} + +static void +g_win32_named_pipe_connection_class_init (GWin32NamedPipeConnectionClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + GIOStreamClass *io_class = G_IO_STREAM_CLASS (class); + + gobject_class->finalize = g_win32_named_pipe_connection_finalize; + gobject_class->get_property = g_win32_named_pipe_connection_get_property; + gobject_class->set_property = g_win32_named_pipe_connection_set_property; + + io_class->get_input_stream = g_win32_named_pipe_connection_get_input_stream; + io_class->get_output_stream = g_win32_named_pipe_connection_get_output_stream; + + /** + * GWin32NamedPipeConnection:handle: + * + * The handle for the connection. + * + * Since: 2.48 + */ + g_object_class_install_property (gobject_class, + PROP_HANDLE, + g_param_spec_pointer ("handle", + P_("File handle"), + P_("The file handle to read from"), + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GWin32NamedPipeConnection:close-handle: + * + * Whether to close the file handle when the pipe connection is disposed. + * + * Since: 2.48 + */ + g_object_class_install_property (gobject_class, + PROP_CLOSE_HANDLE, + g_param_spec_boolean ("close-handle", + P_("Close file handle"), + P_("Whether to close the file handle when the stream is closed"), + TRUE, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS)); +} + +static void +g_win32_named_pipe_connection_init (GWin32NamedPipeConnection *stream) +{ +} diff --git a/gio/gwin32namedpipeconnection.h b/gio/gwin32namedpipeconnection.h new file mode 100644 index 000000000..dc2102448 --- /dev/null +++ b/gio/gwin32namedpipeconnection.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2016 NICE s.r.l. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Authors: Ignacio Casal Quinteiro + */ + +#ifndef __G_WIN32_NAMED_PIPE_CONNECTION_H__ +#define __G_WIN32_NAMED_PIPE_CONNECTION_H__ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_WIN32_NAMED_PIPE_CONNECTION (g_win32_named_pipe_connection_get_type ()) +#define G_WIN32_NAMED_PIPE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_NAMED_PIPE_CONNECTION, GWin32NamedPipeConnection)) +#define G_IS_WIN32_NAMED_PIPE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_WIN32_NAMED_PIPE_CONNECTION)) + +GLIB_AVAILABLE_IN_2_48 +GType g_win32_named_pipe_connection_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __G_WIN32_NAMED_PIPE_CONNECTION_H__ */ diff --git a/gio/gwin32namedpipelistener.c b/gio/gwin32namedpipelistener.c new file mode 100644 index 000000000..a96065b31 --- /dev/null +++ b/gio/gwin32namedpipelistener.c @@ -0,0 +1,566 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2016 NICE s.r.l. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "config.h" + +#include "gwin32namedpipelistener.h" +#include "gwin32namedpipeconnection.h" +#include "gasynchelper.h" + +#include "glibintl.h" + +#include + +#define DEFAULT_PIPE_BUF_SIZE 4096 + +typedef struct +{ + gchar *pipe_name; + HANDLE handle; + OVERLAPPED overlapped; + GObject *source_object; + gboolean already_connected; +} PipeData; + +typedef struct +{ + GPtrArray *named_pipes; + GMainContext *main_context; +} GWin32NamedPipeListenerPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (GWin32NamedPipeListener, g_win32_named_pipe_listener, G_TYPE_OBJECT) + +static GQuark source_quark = 0; + +static PipeData * +pipe_data_new (const gchar *pipe_name, + HANDLE handle, + GObject *source_object) +{ + PipeData *data; + + data = g_slice_new0 (PipeData); + data->pipe_name = g_strdup (pipe_name); + data->handle = handle; + data->overlapped.hEvent = CreateEvent (NULL, /* default security attribute */ + TRUE, /* manual-reset event */ + TRUE, /* initial state = signaled */ + NULL); /* unnamed event object */ + if (source_object) + data->source_object = g_object_ref (source_object); + + return data; +} + +static void +pipe_data_free (PipeData *data) +{ + g_free (data->pipe_name); + CloseHandle (data->handle); + CloseHandle (data->overlapped.hEvent); + g_clear_object (&data->source_object); + g_slice_free (PipeData, data); +} + +static void +g_win32_named_pipe_listener_finalize (GObject *object) +{ + GWin32NamedPipeListener *listener = G_WIN32_NAMED_PIPE_LISTENER (object); + GWin32NamedPipeListenerPrivate *priv; + + priv = g_win32_named_pipe_listener_get_instance_private (listener); + + if (priv->main_context) + g_main_context_unref (priv->main_context); + + g_ptr_array_free (priv->named_pipes, TRUE); + + G_OBJECT_CLASS (g_win32_named_pipe_listener_parent_class)->finalize (object); +} + +static void +g_win32_named_pipe_listener_class_init (GWin32NamedPipeListenerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_win32_named_pipe_listener_finalize; + + source_quark = g_quark_from_static_string ("g-win32-named-pipe-listener-source"); +} + +static void +g_win32_named_pipe_listener_init (GWin32NamedPipeListener *listener) +{ + GWin32NamedPipeListenerPrivate *priv; + + priv = g_win32_named_pipe_listener_get_instance_private (listener); + + priv->named_pipes = g_ptr_array_new_with_free_func ((GDestroyNotify) pipe_data_free); +} + +/** + * g_win32_named_pipe_listener_new: + * + * Creates a new #GWin32NamedPipeListener. + * + * Returns: (transfer full): a new #GWin32NamedPipeListener. + * + * Since: 2.48 + */ +GWin32NamedPipeListener * +g_win32_named_pipe_listener_new (void) +{ + return g_object_new (G_TYPE_WIN32_NAMED_PIPE_LISTENER, NULL); +} + +/** + * g_win32_named_pipe_listener_add_named_pipe: + * @listener: a #GWin32NamedPipeListener. + * @pipe_name: a name for the pipe. + * @source_object: (allow-none): Optional #GObject identifying this source + * @error: #GError for error reporting, or %NULL to ignore. + * + * Adds @named_pipe to the set of named pipes that we try to accept clients + * from. + * + * @source_object will be passed out in the various calls + * to accept to identify this particular source, which is + * useful if you're listening on multiple pipes and do + * different things depending on what pipe is connected to. + * + * Returns: %TRUE on success, %FALSE on error. + * + * Since: 2.48 + */ +gboolean +g_win32_named_pipe_listener_add_named_pipe (GWin32NamedPipeListener *listener, + const gchar *pipe_name, + GObject *source_object, + GError **error) +{ + GWin32NamedPipeListenerPrivate *priv; + gunichar2 *pipe_namew; + PipeData *pipe_data; + HANDLE handle; + + g_return_val_if_fail (G_IS_WIN32_NAMED_PIPE_LISTENER (listener), FALSE); + g_return_val_if_fail (pipe_name != NULL, FALSE); + + priv = g_win32_named_pipe_listener_get_instance_private (listener); + + pipe_namew = g_utf8_to_utf16 (pipe_name, -1, NULL, NULL, NULL); + + handle = CreateNamedPipeW (pipe_namew, + PIPE_ACCESS_DUPLEX | + FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + DEFAULT_PIPE_BUF_SIZE, + DEFAULT_PIPE_BUF_SIZE, + 0, NULL); + g_free (pipe_namew); + + if (handle == INVALID_HANDLE_VALUE) + { + int errsv = GetLastError (); + gchar *emsg = g_win32_error_message (errsv); + + g_set_error (error, + G_IO_ERROR, + g_io_error_from_win32_error (errsv), + _("Error creating named pipe '%s': %s"), + pipe_name, emsg); + + g_free (emsg); + return FALSE; + } + + pipe_data = pipe_data_new (pipe_name, handle, source_object); + + if (!ConnectNamedPipe (handle, &pipe_data->overlapped)) + { + switch (GetLastError ()) + { + case ERROR_IO_PENDING: + break; + case ERROR_PIPE_CONNECTED: + pipe_data->already_connected = TRUE; + break; + default: + { + int errsv = GetLastError (); + gchar *emsg = g_win32_error_message (errsv); + + g_set_error (error, + G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + _("Failed to connect named pipe '%s': %s"), + pipe_name, emsg); + g_free (emsg); + pipe_data_free (pipe_data); + + return FALSE; + } + } + } + + g_ptr_array_add (priv->named_pipes, pipe_data); + + return TRUE; +} + +static gboolean +connect_ready (HANDLE handle, + gpointer user_data) +{ + GTask *task = user_data; + GWin32NamedPipeListener *listener = g_task_get_source_object (task); + GWin32NamedPipeListenerPrivate *priv; + PipeData *pipe_data = NULL; + gulong cbret; + int i; + + priv = g_win32_named_pipe_listener_get_instance_private (listener); + + for (i = 0; i < priv->named_pipes->len; i++) + { + PipeData *pdata; + + pdata = priv->named_pipes->pdata[i]; + if (pdata->overlapped.hEvent == handle) + { + pipe_data = pdata; + break; + } + } + + g_return_val_if_fail (pipe_data != NULL, FALSE); + + if (!GetOverlappedResult (pipe_data->handle, &pipe_data->overlapped, &cbret, FALSE)) + { + int errsv = GetLastError (); + gchar *emsg = g_win32_error_message (errsv); + + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("There was an error querying the named pipe: %s"), + emsg); + g_free (emsg); + } + else + { + GWin32NamedPipeConnection *connection; + + if (pipe_data->source_object != NULL) + g_object_set_qdata_full (G_OBJECT (task), + source_quark, + g_object_ref (pipe_data->source_object), + g_object_unref); + + connection = g_object_new (G_TYPE_WIN32_NAMED_PIPE_CONNECTION, + "handle", pipe_data->handle, + "close-handle", FALSE, + NULL); + g_task_return_pointer (task, connection, g_object_unref); + } + + g_object_unref (task); + + return FALSE; +} + +static GList * +add_sources (GWin32NamedPipeListener *listener, + GWin32HandleSourceFunc callback, + gpointer callback_data, + GCancellable *cancellable, + GMainContext *context) +{ + GWin32NamedPipeListenerPrivate *priv; + PipeData *data; + GSource *source; + GList *sources; + int i; + + priv = g_win32_named_pipe_listener_get_instance_private (listener); + + sources = NULL; + for (i = 0; i < priv->named_pipes->len; i++) + { + data = priv->named_pipes->pdata[i]; + + source = _g_win32_handle_create_source (data->overlapped.hEvent, + cancellable); + g_source_set_callback (source, + (GSourceFunc) callback, + callback_data, NULL); + g_source_attach (source, context); + + sources = g_list_prepend (sources, source); + } + + return sources; +} + +static void +free_sources (GList *sources) +{ + GSource *source; + while (sources != NULL) + { + source = sources->data; + sources = g_list_delete_link (sources, sources); + g_source_destroy (source); + g_source_unref (source); + } +} + +struct AcceptData { + GWin32NamedPipeListener *listener; + GMainLoop *loop; + PipeData *pipe_data; +}; + +static gboolean +accept_callback (HANDLE handle, + gpointer user_data) +{ + struct AcceptData *data = user_data; + GWin32NamedPipeListenerPrivate *priv; + PipeData *pipe_data = NULL; + int i; + + priv = g_win32_named_pipe_listener_get_instance_private (data->listener); + + for (i = 0; i < priv->named_pipes->len; i++) + { + PipeData *pdata; + + pdata = priv->named_pipes->pdata[i]; + if (pdata->overlapped.hEvent == handle) + { + pipe_data = pdata; + break; + } + } + + data->pipe_data = pipe_data; + g_main_loop_quit (data->loop); + + return TRUE; +} + +/** + * g_win32_named_pipe_listener_accept: + * @listener: a #GWin32NamedPipeListener + * @source_object: (out) (transfer none) (allow-none): location where #GObject pointer will be stored, or %NULL. + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Blocks waiting for a client to connect to any of the named pipes added + * to the listener. Returns the #GWin32NamedPipeConnection that was accepted. + * + * If @source_object is not %NULL it will be filled out with the source + * object specified when the corresponding named pipe was added + * to the listener. + * + * If @cancellable is not %NULL, then the operation can be cancelled by + * triggering the cancellable object from another thread. If the operation + * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. + * + * Returns: (transfer full): a #GWin32NamedPipeConnection on success, %NULL on error. + * + * Since: 2.48 + */ +GWin32NamedPipeConnection * +g_win32_named_pipe_listener_accept (GWin32NamedPipeListener *listener, + GObject **source_object, + GCancellable *cancellable, + GError **error) +{ + GWin32NamedPipeListenerPrivate *priv; + PipeData *pipe_data = NULL; + GWin32NamedPipeConnection *connection = NULL; + + g_return_val_if_fail (G_IS_WIN32_NAMED_PIPE_LISTENER (listener), NULL); + + priv = g_win32_named_pipe_listener_get_instance_private (listener); + + if (priv->named_pipes->len == 1) + { + gboolean success; + + pipe_data = priv->named_pipes->pdata[0]; + success = pipe_data->already_connected; + + if (!success) + success = WaitForSingleObject (pipe_data->overlapped.hEvent, INFINITE) == WAIT_OBJECT_0; + + if (!success) + pipe_data = NULL; + } + else + { + int i; + + /* First we check if any of the named pipes is already connected and + * pick the the first one. + */ + for (i = 0; i < priv->named_pipes->len; i++) + { + PipeData *pdata = priv->named_pipes->pdata[i]; + + if (pdata->already_connected) + pipe_data = pdata; + } + + if (pipe_data == NULL) + { + GList *sources; + struct AcceptData data; + GMainLoop *loop; + + if (priv->main_context == NULL) + priv->main_context = g_main_context_new (); + + loop = g_main_loop_new (priv->main_context, FALSE); + data.loop = loop; + data.listener = listener; + + sources = add_sources (listener, + accept_callback, + &data, + cancellable, + priv->main_context); + g_main_loop_run (loop); + pipe_data = data.pipe_data; + free_sources (sources); + g_main_loop_unref (loop); + } + } + + if (pipe_data != NULL) + { + connection = g_object_new (G_TYPE_WIN32_NAMED_PIPE_CONNECTION, + "handle", pipe_data->handle, + "close-handle", FALSE, + NULL); + + if (source_object) + *source_object = pipe_data->source_object; + } + + return connection; +} + +/** + * g_win32_named_pipe_listener_accept_async: + * @listener: a #GWin32NamedPipeListener + * @cancellable: (allow-none): a #GCancellable, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback + * @user_data: (closure): user data for the callback + * + * This is the asynchronous version of g_win32_named_pipe_listener_accept(). + * + * When the operation is finished @callback will be + * called. You can then call g_win32_named_pipe_listener_accept_finish() + * to get the result of the operation. + * + * Since: 2.48 + */ +void +g_win32_named_pipe_listener_accept_async (GWin32NamedPipeListener *listener, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GWin32NamedPipeListenerPrivate *priv; + PipeData *pipe_data; + GTask *task; + GList *sources; + int i; + + task = g_task_new (listener, cancellable, callback, user_data); + + priv = g_win32_named_pipe_listener_get_instance_private (listener); + + /* First we check if any of the named pipes is already connected and pick the + * the first one. + */ + for (i = 0; i < priv->named_pipes->len; i++) + { + pipe_data = priv->named_pipes->pdata[i]; + + if (pipe_data->already_connected) + { + GWin32NamedPipeConnection *connection; + + if (pipe_data->source_object) + g_object_set_qdata_full (G_OBJECT (task), + source_quark, + g_object_ref (pipe_data->source_object), + g_object_unref); + + connection = g_object_new (G_TYPE_WIN32_NAMED_PIPE_CONNECTION, + "handle", pipe_data->handle, + "close-handle", FALSE, + NULL); + g_task_return_pointer (task, connection, g_object_unref); + + return; + } + } + + sources = add_sources (listener, + connect_ready, + task, + cancellable, + g_main_context_get_thread_default ()); + g_task_set_task_data (task, sources, (GDestroyNotify) free_sources); +} + +/** + * g_win32_named_pipe_listener_accept_finish: + * @listener: a #GWin32NamedPipeListener. + * @result: a #GAsyncResult. + * @source_object: (out) (transfer none) (allow-none): Optional #GObject identifying this source + * @error: a #GError location to store the error occurring, or %NULL to ignore. + * + * Finishes an async accept operation. See g_win32_named_pipe_listener_accept_async() + * + * Returns: (transfer full): a #GWin32NamedPipeConnection on success, %NULL on error. + * + * Since: 2.48 + */ +GWin32NamedPipeConnection * +g_win32_named_pipe_listener_accept_finish (GWin32NamedPipeListener *listener, + GAsyncResult *result, + GObject **source_object, + GError **error) +{ + g_return_val_if_fail (G_IS_WIN32_NAMED_PIPE_LISTENER (listener), NULL); + g_return_val_if_fail (g_task_is_valid (result, listener), NULL); + + if (source_object) + *source_object = g_object_get_qdata (G_OBJECT (result), source_quark); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/gio/gwin32namedpipelistener.h b/gio/gwin32namedpipelistener.h new file mode 100644 index 000000000..c8d324c20 --- /dev/null +++ b/gio/gwin32namedpipelistener.h @@ -0,0 +1,87 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2016 NICE s.r.l. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef __G_WIN32_NAMED_PIPE_LISTENER_H__ +#define __G_WIN32_NAMED_PIPE_LISTENER_H__ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define G_TYPE_WIN32_NAMED_PIPE_LISTENER (g_win32_named_pipe_listener_get_type ()) +#define G_WIN32_NAMED_PIPE_LISTENER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_NAMED_PIPE_LISTENER, GWin32NamedPipeListener)) +#define G_WIN32_NAMED_PIPE_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_WIN32_NAMED_PIPE_LISTENER, GWin32NamedPipeListenerClass)) +#define G_IS_WIN32_NAMED_PIPE_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_NAMED_PIPE_LISTENER)) +#define G_IS_WIN32_NAMED_PIPE_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_NAMED_PIPE_LISTENER)) +#define G_WIN32_NAMED_PIPE_LISTENER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_NAMED_PIPE_LISTENER, GWin32NamedPipeListenerClass)) + +typedef struct _GWin32NamedPipeListener GWin32NamedPipeListener; +typedef struct _GWin32NamedPipeListenerClass GWin32NamedPipeListenerClass; + +struct _GWin32NamedPipeListener +{ + /*< private >*/ + GObject parent_instance; +}; + +struct _GWin32NamedPipeListenerClass +{ + GObjectClass parent_class; + + /*< private >*/ + gpointer padding[10]; +}; + +GLIB_AVAILABLE_IN_2_48 +GType g_win32_named_pipe_listener_get_type (void) G_GNUC_CONST; + +GLIB_AVAILABLE_IN_2_48 +GWin32NamedPipeListener *g_win32_named_pipe_listener_new (void); + +GLIB_AVAILABLE_IN_2_48 +gboolean g_win32_named_pipe_listener_add_named_pipe (GWin32NamedPipeListener *listener, + const gchar *pipe_name, + GObject *source_object, + GError **error); + +GLIB_AVAILABLE_IN_2_48 +GWin32NamedPipeConnection *g_win32_named_pipe_listener_accept (GWin32NamedPipeListener *listener, + GObject **source_object, + GCancellable *cancellable, + GError **error); + +GLIB_AVAILABLE_IN_2_48 +void g_win32_named_pipe_listener_accept_async (GWin32NamedPipeListener *listener, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GLIB_AVAILABLE_IN_2_48 +GWin32NamedPipeConnection *g_win32_named_pipe_listener_accept_finish (GWin32NamedPipeListener *listener, + GAsyncResult *result, + GObject **source_object, + GError **error); + +G_END_DECLS + +#endif /* __G_WIN32_NAMED_PIPE_LISTENER_H__ */ diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 0553fdfc6..363fe92fe 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -481,7 +481,7 @@ tls_interaction_SOURCES = tls-interaction.c gtesttlsbackend.c gtesttlsbackend.h # ----------------------------------------------------------------------------- if OS_WIN32 -test_programs += win32-streams +test_programs += win32-streams win32-named-pipe endif if PLATFORM_WIN32 diff --git a/gio/tests/win32-named-pipe.c b/gio/tests/win32-named-pipe.c new file mode 100644 index 000000000..13409c4d2 --- /dev/null +++ b/gio/tests/win32-named-pipe.c @@ -0,0 +1,215 @@ +/* + * Copyright © 2016 NICE s.r.l. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Authors: Ignacio Casal Quinteiro + */ + +#include + +static void +test_add_named_pipe (void) +{ + GWin32NamedPipeListener *listener; + GError *error = NULL; + + listener = g_win32_named_pipe_listener_new (); + + g_win32_named_pipe_listener_add_named_pipe (listener, + "\\\\.\\pipe\\gtest-good-named-pipe-name", + NULL, + &error); + g_assert_no_error (error); + + g_win32_named_pipe_listener_add_named_pipe (listener, + "\\\\.\\gtest-bad-named-pipe-name", + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + + g_object_unref (listener); +} + +static void +accepted_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GWin32NamedPipeListener *listener = G_WIN32_NAMED_PIPE_LISTENER (source); + GWin32NamedPipeConnection *conn; + gboolean *sucess = user_data; + GError *error = NULL; + + conn = g_win32_named_pipe_listener_accept_finish (listener, result, NULL, &error); + g_assert_no_error (error); + g_object_unref (conn); + + *sucess = TRUE; +} + +static void +connected_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GWin32NamedPipeClient *client = G_WIN32_NAMED_PIPE_CLIENT (source); + GWin32NamedPipeConnection *conn; + gboolean *sucess = user_data; + GError *error = NULL; + + conn = g_win32_named_pipe_client_connect_finish (client, result, &error); + g_assert_no_error (error); + g_object_unref (conn); + + *sucess = TRUE; +} + +static void +test_connect_basic (void) +{ + GWin32NamedPipeListener *listener; + GWin32NamedPipeClient *client; + gboolean success_accepted = FALSE; + gboolean success_connected = FALSE; + GError *error = NULL; + + listener = g_win32_named_pipe_listener_new (); + + g_win32_named_pipe_listener_add_named_pipe (listener, + "\\\\.\\pipe\\gtest-named-pipe-name", + NULL, + &error); + g_assert_no_error (error); + + g_win32_named_pipe_listener_accept_async (listener, + NULL, + accepted_cb, + &success_accepted); + + client = g_win32_named_pipe_client_new (); + g_win32_named_pipe_client_connect_async (client, + "\\\\.\\pipe\\gtest-named-pipe-name", + NULL, + connected_cb, + &success_connected); + + do + g_main_context_iteration (NULL, TRUE); + while (!success_accepted || !success_connected); + + g_object_unref (client); + g_object_unref (listener); +} + +static void +test_connect_before_accept (void) +{ + GWin32NamedPipeListener *listener; + GWin32NamedPipeClient *client; + gboolean success_accepted = FALSE; + gboolean success_connected = FALSE; + GError *error = NULL; + + listener = g_win32_named_pipe_listener_new (); + + g_win32_named_pipe_listener_add_named_pipe (listener, + "\\\\.\\pipe\\gtest-named-pipe-name", + NULL, + &error); + g_assert_no_error (error); + + client = g_win32_named_pipe_client_new (); + g_win32_named_pipe_client_connect_async (client, + "\\\\.\\pipe\\gtest-named-pipe-name", + NULL, + connected_cb, + &success_connected); + + g_win32_named_pipe_listener_accept_async (listener, + NULL, + accepted_cb, + &success_accepted); + + do + g_main_context_iteration (NULL, TRUE); + while (!success_accepted || !success_connected); + + g_object_unref (client); + g_object_unref (listener); +} + +static void +test_connect_sync (void) +{ + GWin32NamedPipeListener *listener; + GWin32NamedPipeClient *client; + GWin32NamedPipeConnection *connection; + GError *error = NULL; + + listener = g_win32_named_pipe_listener_new (); + + g_win32_named_pipe_listener_add_named_pipe (listener, + "\\\\.\\pipe\\gtest-connect-sync", + NULL, + &error); + g_assert_no_error (error); + + client = g_win32_named_pipe_client_new (); + connection = g_win32_named_pipe_client_connect (client, + "\\\\.\\pipe\\gtest-connect-sync", + NULL, + &error); + + g_assert_no_error (error); + + g_object_unref (client); + g_object_unref (listener); +} + +static void +test_connect_sync_fails (void) +{ + GWin32NamedPipeClient *client; + GWin32NamedPipeConnection *connection; + GError *error = NULL; + + client = g_win32_named_pipe_client_new (); + connection = g_win32_named_pipe_client_connect (client, + "\\\\.\\pipe\\gtest-connect-sync-fails", + NULL, + &error); + + g_assert (connection == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + + g_object_unref (client); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_bug_base ("http://bugzilla.gnome.org/"); + + g_test_add_func ("/named-pipes/add-named-pipe", test_add_named_pipe); + g_test_add_func ("/named-pipes/connect-basic", test_connect_basic); + g_test_add_func ("/named-pipes/connect-before-accept", test_connect_before_accept); + g_test_add_func ("/named-pipes/connect-sync", test_connect_sync); + g_test_add_func ("/named-pipes/connect-sync-fails", test_connect_sync_fails); + + return g_test_run (); +}