Repository: incubator-guacamole-server
Updated Branches:
  refs/heads/master 8cd98b529 -> a1886f51b


GUACAMOLE-171: Separate broadcast socket from client. Ensure socket remains 
threadsafe even if no users are connected.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/98b92f09
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/98b92f09
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/98b92f09

Branch: refs/heads/master
Commit: 98b92f0900cd579cab24c5640cf55d7b0fc5c5f8
Parents: 0ef87c4
Author: Michael Jumper <[email protected]>
Authored: Tue Dec 20 23:58:53 2016 -0800
Committer: Michael Jumper <[email protected]>
Committed: Mon Jan 23 23:43:32 2017 -0800

----------------------------------------------------------------------
 src/libguac/Makefile.am        |  41 ++--
 src/libguac/client.c           | 273 +-------------------------
 src/libguac/guacamole/socket.h |  27 +++
 src/libguac/socket-broadcast.c | 377 ++++++++++++++++++++++++++++++++++++
 4 files changed, 426 insertions(+), 292 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/98b92f09/src/libguac/Makefile.am
----------------------------------------------------------------------
diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am
index 46d4e58..781f862 100644
--- a/src/libguac/Makefile.am
+++ b/src/libguac/Makefile.am
@@ -70,26 +70,27 @@ noinst_HEADERS =      \
     user-handlers.h   \
     raw_encoder.h
 
-libguac_la_SOURCES =  \
-    audio.c           \
-    client.c          \
-    encode-jpeg.c     \
-    encode-png.c      \
-    error.c           \
-    hash.c            \
-    id.c              \
-    palette.c         \
-    parser.c          \
-    pool.c            \
-    protocol.c        \
-    raw_encoder.c     \
-    socket.c          \
-    socket-fd.c       \
-    socket-nest.c     \
-    socket-tee.c      \
-    timestamp.c       \
-    unicode.c         \
-    user.c            \
+libguac_la_SOURCES =   \
+    audio.c            \
+    client.c           \
+    encode-jpeg.c      \
+    encode-png.c       \
+    error.c            \
+    hash.c             \
+    id.c               \
+    palette.c          \
+    parser.c           \
+    pool.c             \
+    protocol.c         \
+    raw_encoder.c      \
+    socket.c           \
+    socket-broadcast.c \
+    socket-fd.c        \
+    socket-nest.c      \
+    socket-tee.c       \
+    timestamp.c        \
+    unicode.c          \
+    user.c             \
     user-handlers.c
 
 # Compile WebP support if available

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/98b92f09/src/libguac/client.c
----------------------------------------------------------------------
diff --git a/src/libguac/client.c b/src/libguac/client.c
index b9862a1..a3c3733 100644
--- a/src/libguac/client.c
+++ b/src/libguac/client.c
@@ -47,23 +47,6 @@ guac_layer __GUAC_DEFAULT_LAYER = {
 
 const guac_layer* GUAC_DEFAULT_LAYER = &__GUAC_DEFAULT_LAYER;
 
-/**
- * Single chunk of data, to be broadcast to all users.
- */
-typedef struct __write_chunk {
-
-    /**
-     * The buffer to write.
-     */
-    const void* buffer;
-
-    /**
-     * The number of bytes in the buffer.
-     */
-    size_t length;
-
-} __write_chunk;
-
 guac_layer* guac_client_alloc_layer(guac_client* client) {
 
     /* Init new layer */
@@ -138,251 +121,6 @@ void guac_client_free_stream(guac_client* client, 
guac_stream* stream) {
 
 }
 
-/**
- * Callback which handles read requests on the broadcast socket. This callback
- * always fails, as the broadcast socket is write-only; it cannot be read.
- *
- * @param socket
- *     The broadcast socket to read from.
- *
- * @param buf
- *     The buffer into which data should be read.
- *
- * @param count
- *     The number of bytes to attempt to read.
- *
- * @return
- *     The number of bytes read, or -1 if an error occurs. This implementation
- *     always returns -1, as the broadcast socket is write-only and cannot be
- *     read.
- */
-static ssize_t __guac_socket_broadcast_read_handler(guac_socket* socket,
-        void* buf, size_t count) {
-
-    /* Broadcast socket reads are not allowed */
-    return -1;
-
-}
-
-/**
- * Callback invoked by guac_client_foreach_user() which write a given chunk of
- * data to that user's socket. If the write attempt fails, the user is
- * signalled to stop with guac_user_stop().
- *
- * @param user
- *     The user that the chunk of data should be written to.
- *
- * @param data
- *     A pointer to a __write_chunk which describes the data to be written.
- *
- * @return
- *     Always NULL.
- */
-static void* __write_chunk_callback(guac_user* user, void* data) {
-
-    __write_chunk* chunk = (__write_chunk*) data;
-
-    /* Attempt write, disconnect on failure */
-    if (guac_socket_write(user->socket, chunk->buffer, chunk->length))
-        guac_user_stop(user);
-
-    return NULL;
-
-}
-
-/**
- * Socket write handler which operates on each of the sockets of all connected
- * users. This write handler will always succeed, but any failing user-specific
- * writes will invoke guac_user_stop() on the failing user.
- *
- * @param socket
- *     The socket to which the given data must be written.
- *
- * @param buf
- *     The buffer containing the data to write.
- *
- * @param count
- *     The number of bytes to attempt to write from the given buffer.
- *
- * @return
- *     The number of bytes written, or -1 if an error occurs. This handler will
- *     always succeed, and thus will always return the exact number of bytes
- *     specified by count.
- */
-static ssize_t __guac_socket_broadcast_write_handler(guac_socket* socket,
-        const void* buf, size_t count) {
-
-    guac_client* client = (guac_client*) socket->data;
-
-    /* Build chunk */
-    __write_chunk chunk;
-    chunk.buffer = buf;
-    chunk.length = count;
-
-    /* Broadcast chunk to all users */
-    guac_client_foreach_user(client, __write_chunk_callback, &chunk);
-
-    return count;
-
-}
-
-/**
- * Callback which is invoked by guac_client_foreach_user() to flush all
- * pending data on the given user's socket. If an error occurs while flushing
- * a user's socket, that user is signalled to stop with guac_user_stop().
- *
- * @param user
- *     The user whose socket should be flushed.
- *
- * @param data
- *     Arbitrary data passed to guac_client_foreach_user(). This is not needed
- *     by this callback, and should be left as NULL.
- *
- * @return
- *     Always NULL.
- */
-static void* __flush_callback(guac_user* user, void* data) {
-
-    /* Attempt flush, disconnect on failure */
-    if (guac_socket_flush(user->socket))
-        guac_user_stop(user);
-
-    return NULL;
-
-}
-
-/**
- * Socket flush handler which operates on each of the sockets of all connected
- * users. This flush handler will always succeed, but any failing user-specific
- * flush will invoke guac_user_stop() on the failing user.
- *
- * @param socket
- *     The broadcast socket to flush.
- *
- * @return
- *     Zero if the flush operation succeeds, non-zero if the operation fails.
- *     This handler will always succeed, and thus will always return zero.
- */
-static ssize_t __guac_socket_broadcast_flush_handler(guac_socket* socket) {
-
-    guac_client* client = (guac_client*) socket->data;
-
-    /* Flush all users */
-    guac_client_foreach_user(client, __flush_callback, NULL);
-
-    return 0;
-
-}
-
-/**
- * Callback which is invoked by guac_client_foreach_user() to lock the given
- * user's socket in preparation for the beginning of a Guacamole protocol
- * instruction.
- *
- * @param user
- *     The user whose socket should be locked.
- *
- * @param data
- *     Arbitrary data passed to guac_client_foreach_user(). This is not needed
- *     by this callback, and should be left as NULL.
- *
- * @return
- *     Always NULL.
- */
-static void* __lock_callback(guac_user* user, void* data) {
-
-    /* Lock socket */
-    guac_socket_instruction_begin(user->socket);
-
-    return NULL;
-
-}
-
-/**
- * Socket lock handler which acquires the socket locks of all connected users.
- * Socket-level locks are acquired in preparation for the beginning of a new
- * Guacamole instruction to ensure that parallel writes are only interleaved at
- * instruction boundaries.
- *
- * @param socket
- *     The broadcast socket to lock.
- */
-static void __guac_socket_broadcast_lock_handler(guac_socket* socket) {
-
-    guac_client* client = (guac_client*) socket->data;
-
-    /* Lock sockets of all users */
-    guac_client_foreach_user(client, __lock_callback, NULL);
-
-}
-
-/**
- * Callback which is invoked by guac_client_foreach_user() to unlock the given
- * user's socket at the end of a Guacamole protocol instruction.
- *
- * @param user
- *     The user whose socket should be unlocked.
- *
- * @param data
- *     Arbitrary data passed to guac_client_foreach_user(). This is not needed
- *     by this callback, and should be left as NULL.
- *
- * @return
- *     Always NULL.
- */
-static void* __unlock_callback(guac_user* user, void* data) {
-
-    /* Unlock socket */
-    guac_socket_instruction_end(user->socket);
-
-    return NULL;
-
-}
-
-/**
- * Socket unlock handler which releases the socket locks of all connected 
users.
- * Socket-level locks are released after a Guacamole instruction has finished
- * being written.
- *
- * @param socket
- *     The broadcast socket to unlock.
- */
-static void __guac_socket_broadcast_unlock_handler(guac_socket* socket) {
-
-    guac_client* client = (guac_client*) socket->data;
-
-    /* Unlock sockets of all users */
-    guac_client_foreach_user(client, __unlock_callback, NULL);
-
-}
-
-/**
- * Callback which handles select operations on the broadcast socket, waiting
- * for data to become available such that the next read operation will not
- * block. This callback always fails, as the broadcast socket is write-only; it
- * cannot be read.
- *
- * @param socket
- *     The broadcast socket to wait for.
- *
- * @param usec_timeout
- *     The maximum amount of time to wait for data, in microseconds, or -1 to
- *     potentially wait forever.
- *
- * @return
- *     A positive value on success, zero if the timeout elapsed and no data is
- *     available, or a negative value if an error occurs. This implementation
- *     always returns -1, as the broadcast socket is write-only and cannot be
- *     read.
- */
-static int __guac_socket_broadcast_select_handler(guac_socket* socket,
-        int usec_timeout) {
-
-    /* Selecting the broadcast socket is not possible */
-    return -1;
-
-}
-
 guac_client* guac_client_alloc() {
 
     int i;
@@ -431,16 +169,7 @@ guac_client* guac_client_alloc() {
     pthread_rwlock_init(&(client->__users_lock), &lock_attributes);
 
     /* Set up socket to broadcast to all users */
-    guac_socket* socket = guac_socket_alloc();
-    client->socket = socket;
-    socket->data   = client;
-
-    socket->read_handler   = __guac_socket_broadcast_read_handler;
-    socket->write_handler  = __guac_socket_broadcast_write_handler;
-    socket->select_handler = __guac_socket_broadcast_select_handler;
-    socket->flush_handler  = __guac_socket_broadcast_flush_handler;
-    socket->lock_handler   = __guac_socket_broadcast_lock_handler;
-    socket->unlock_handler = __guac_socket_broadcast_unlock_handler;
+    client->socket = guac_socket_broadcast(client);
 
     return client;
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/98b92f09/src/libguac/guacamole/socket.h
----------------------------------------------------------------------
diff --git a/src/libguac/guacamole/socket.h b/src/libguac/guacamole/socket.h
index 1290c34..e83a4eb 100644
--- a/src/libguac/guacamole/socket.h
+++ b/src/libguac/guacamole/socket.h
@@ -26,6 +26,7 @@
  * @file socket.h
  */
 
+#include "client-types.h"
 #include "socket-constants.h"
 #include "socket-fntypes.h"
 #include "socket-types.h"
@@ -221,6 +222,32 @@ guac_socket* guac_socket_nest(guac_socket* parent, int 
index);
 guac_socket* guac_socket_tee(guac_socket* primary, guac_socket* secondary);
 
 /**
+ * Allocates and initializes a new guac_socket which duplicates all
+ * instructions written across the sockets of each connected user of the given
+ * guac_client. The returned socket is a write-only socket. Attempts to read
+ * from the socket will fail.  If a write occurs while no users are connected,
+ * that write will simply be dropped.
+ *
+ * Return values (error codes) from each user's socket will not affect the
+ * in-progress write, but each failing user will be forcibly stopped with
+ * guac_user_stop().
+ *
+ * If an error occurs while allocating the guac_socket object, NULL is 
returned,
+ * and guac_error is set appropriately.
+ *
+ * @param client
+ *     The client associated with the group of connected users across which
+ *     duplicates of all instructions should be written.
+ *
+ * @return
+ *     A write-only guac_socket object which broadcasts copies of all
+ *     instructions written across all connected users of the given
+ *     guac_client, or NULL if an error occurs while allocating the guac_socket
+ *     object.
+ */
+guac_socket* guac_socket_broadcast(guac_client* client);
+
+/**
  * Writes the given unsigned int to the given guac_socket object. The data
  * written may be buffered until the buffer is flushed automatically or
  * manually.

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/98b92f09/src/libguac/socket-broadcast.c
----------------------------------------------------------------------
diff --git a/src/libguac/socket-broadcast.c b/src/libguac/socket-broadcast.c
new file mode 100644
index 0000000..a3f17fd
--- /dev/null
+++ b/src/libguac/socket-broadcast.c
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "error.h"
+#include "socket.h"
+#include "user.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+
+/**
+ * Data associated with an open socket which writes to all connected users of
+ * a particular guac_client.
+ */
+typedef struct guac_socket_broadcast_data {
+
+    /**
+     * The guac_client whose connected users should receive all instructions
+     * written to this socket.
+     */
+    guac_client* client;
+
+    /**
+     * Lock which is acquired when an instruction is being written, and
+     * released when the instruction is finished being written.
+     */
+    pthread_mutex_t socket_lock;
+
+} guac_socket_broadcast_data;
+
+/**
+ * Single chunk of data, to be broadcast to all users.
+ */
+typedef struct __write_chunk {
+
+    /**
+     * The buffer to write.
+     */
+    const void* buffer;
+
+    /**
+     * The number of bytes in the buffer.
+     */
+    size_t length;
+
+} __write_chunk;
+
+/**
+ * Callback which handles read requests on the broadcast socket. This callback
+ * always fails, as the broadcast socket is write-only; it cannot be read.
+ *
+ * @param socket
+ *     The broadcast socket to read from.
+ *
+ * @param buf
+ *     The buffer into which data should be read.
+ *
+ * @param count
+ *     The number of bytes to attempt to read.
+ *
+ * @return
+ *     The number of bytes read, or -1 if an error occurs. This implementation
+ *     always returns -1, as the broadcast socket is write-only and cannot be
+ *     read.
+ */
+static ssize_t __guac_socket_broadcast_read_handler(guac_socket* socket,
+        void* buf, size_t count) {
+
+    /* Broadcast socket reads are not allowed */
+    return -1;
+
+}
+
+/**
+ * Callback invoked by guac_client_foreach_user() which write a given chunk of
+ * data to that user's socket. If the write attempt fails, the user is
+ * signalled to stop with guac_user_stop().
+ *
+ * @param user
+ *     The user that the chunk of data should be written to.
+ *
+ * @param data
+ *     A pointer to a __write_chunk which describes the data to be written.
+ *
+ * @return
+ *     Always NULL.
+ */
+static void* __write_chunk_callback(guac_user* user, void* data) {
+
+    __write_chunk* chunk = (__write_chunk*) data;
+
+    /* Attempt write, disconnect on failure */
+    if (guac_socket_write(user->socket, chunk->buffer, chunk->length))
+        guac_user_stop(user);
+
+    return NULL;
+
+}
+
+/**
+ * Socket write handler which operates on each of the sockets of all connected
+ * users. This write handler will always succeed, but any failing user-specific
+ * writes will invoke guac_user_stop() on the failing user.
+ *
+ * @param socket
+ *     The socket to which the given data must be written.
+ *
+ * @param buf
+ *     The buffer containing the data to write.
+ *
+ * @param count
+ *     The number of bytes to attempt to write from the given buffer.
+ *
+ * @return
+ *     The number of bytes written, or -1 if an error occurs. This handler will
+ *     always succeed, and thus will always return the exact number of bytes
+ *     specified by count.
+ */
+static ssize_t __guac_socket_broadcast_write_handler(guac_socket* socket,
+        const void* buf, size_t count) {
+
+    guac_socket_broadcast_data* data =
+        (guac_socket_broadcast_data*) socket->data;
+
+    /* Build chunk */
+    __write_chunk chunk;
+    chunk.buffer = buf;
+    chunk.length = count;
+
+    /* Broadcast chunk to all users */
+    guac_client_foreach_user(data->client, __write_chunk_callback, &chunk);
+
+    return count;
+
+}
+
+/**
+ * Callback which is invoked by guac_client_foreach_user() to flush all
+ * pending data on the given user's socket. If an error occurs while flushing
+ * a user's socket, that user is signalled to stop with guac_user_stop().
+ *
+ * @param user
+ *     The user whose socket should be flushed.
+ *
+ * @param data
+ *     Arbitrary data passed to guac_client_foreach_user(). This is not needed
+ *     by this callback, and should be left as NULL.
+ *
+ * @return
+ *     Always NULL.
+ */
+static void* __flush_callback(guac_user* user, void* data) {
+
+    /* Attempt flush, disconnect on failure */
+    if (guac_socket_flush(user->socket))
+        guac_user_stop(user);
+
+    return NULL;
+
+}
+
+/**
+ * Socket flush handler which operates on each of the sockets of all connected
+ * users. This flush handler will always succeed, but any failing user-specific
+ * flush will invoke guac_user_stop() on the failing user.
+ *
+ * @param socket
+ *     The broadcast socket to flush.
+ *
+ * @return
+ *     Zero if the flush operation succeeds, non-zero if the operation fails.
+ *     This handler will always succeed, and thus will always return zero.
+ */
+static ssize_t __guac_socket_broadcast_flush_handler(guac_socket* socket) {
+
+    guac_socket_broadcast_data* data =
+        (guac_socket_broadcast_data*) socket->data;
+
+    /* Flush all users */
+    guac_client_foreach_user(data->client, __flush_callback, NULL);
+
+    return 0;
+
+}
+
+/**
+ * Callback which is invoked by guac_client_foreach_user() to lock the given
+ * user's socket in preparation for the beginning of a Guacamole protocol
+ * instruction.
+ *
+ * @param user
+ *     The user whose socket should be locked.
+ *
+ * @param data
+ *     Arbitrary data passed to guac_client_foreach_user(). This is not needed
+ *     by this callback, and should be left as NULL.
+ *
+ * @return
+ *     Always NULL.
+ */
+static void* __lock_callback(guac_user* user, void* data) {
+
+    /* Lock socket */
+    guac_socket_instruction_begin(user->socket);
+
+    return NULL;
+
+}
+
+/**
+ * Socket lock handler which acquires the socket locks of all connected users.
+ * Socket-level locks are acquired in preparation for the beginning of a new
+ * Guacamole instruction to ensure that parallel writes are only interleaved at
+ * instruction boundaries.
+ *
+ * @param socket
+ *     The broadcast socket to lock.
+ */
+static void __guac_socket_broadcast_lock_handler(guac_socket* socket) {
+
+    guac_socket_broadcast_data* data =
+        (guac_socket_broadcast_data*) socket->data;
+
+    /* Acquire exclusive access to socket */
+    pthread_mutex_lock(&(data->socket_lock));
+
+    /* Lock sockets of all users */
+    guac_client_foreach_user(data->client, __lock_callback, NULL);
+
+}
+
+/**
+ * Callback which is invoked by guac_client_foreach_user() to unlock the given
+ * user's socket at the end of a Guacamole protocol instruction.
+ *
+ * @param user
+ *     The user whose socket should be unlocked.
+ *
+ * @param data
+ *     Arbitrary data passed to guac_client_foreach_user(). This is not needed
+ *     by this callback, and should be left as NULL.
+ *
+ * @return
+ *     Always NULL.
+ */
+static void* __unlock_callback(guac_user* user, void* data) {
+
+    /* Unlock socket */
+    guac_socket_instruction_end(user->socket);
+
+    return NULL;
+
+}
+
+/**
+ * Socket unlock handler which releases the socket locks of all connected 
users.
+ * Socket-level locks are released after a Guacamole instruction has finished
+ * being written.
+ *
+ * @param socket
+ *     The broadcast socket to unlock.
+ */
+static void __guac_socket_broadcast_unlock_handler(guac_socket* socket) {
+
+    guac_socket_broadcast_data* data =
+        (guac_socket_broadcast_data*) socket->data;
+
+    /* Unlock sockets of all users */
+    guac_client_foreach_user(data->client, __unlock_callback, NULL);
+
+    /* Relinquish exclusive access to socket */
+    pthread_mutex_unlock(&(data->socket_lock));
+
+}
+
+/**
+ * Callback which handles select operations on the broadcast socket, waiting
+ * for data to become available such that the next read operation will not
+ * block. This callback always fails, as the broadcast socket is write-only; it
+ * cannot be read.
+ *
+ * @param socket
+ *     The broadcast socket to wait for.
+ *
+ * @param usec_timeout
+ *     The maximum amount of time to wait for data, in microseconds, or -1 to
+ *     potentially wait forever.
+ *
+ * @return
+ *     A positive value on success, zero if the timeout elapsed and no data is
+ *     available, or a negative value if an error occurs. This implementation
+ *     always returns -1, as the broadcast socket is write-only and cannot be
+ *     read.
+ */
+static int __guac_socket_broadcast_select_handler(guac_socket* socket,
+        int usec_timeout) {
+
+    /* Selecting the broadcast socket is not possible */
+    return -1;
+
+}
+
+/**
+ * Frees all implementation-specific data associated with the given socket, but
+ * not the socket object itself.
+ *
+ * @param socket
+ *     The guac_socket whose associated data should be freed.
+ *
+ * @return
+ *     Zero if the data was successfully freed, non-zero otherwise. This
+ *     implementation always succeeds, and will always return zero.
+ */
+static int __guac_socket_broadcast_free_handler(guac_socket* socket) {
+
+    guac_socket_broadcast_data* data =
+        (guac_socket_broadcast_data*) socket->data;
+
+    /* Destroy locks */
+    pthread_mutex_destroy(&(data->socket_lock));
+
+    free(data);
+    return 0;
+
+}
+
+guac_socket* guac_socket_broadcast(guac_client* client) {
+
+    pthread_mutexattr_t lock_attributes;
+
+    /* Allocate socket and associated data */
+    guac_socket* socket = guac_socket_alloc();
+    guac_socket_broadcast_data* data =
+        malloc(sizeof(guac_socket_broadcast_data));
+
+    /* Store client as socket data */
+    data->client = client;
+    socket->data = data;
+
+    pthread_mutexattr_init(&lock_attributes);
+    pthread_mutexattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED);
+
+    /* Init lock */
+    pthread_mutex_init(&(data->socket_lock), &lock_attributes);
+    
+    /* Set read/write handlers */
+    socket->read_handler   = __guac_socket_broadcast_read_handler;
+    socket->write_handler  = __guac_socket_broadcast_write_handler;
+    socket->select_handler = __guac_socket_broadcast_select_handler;
+    socket->flush_handler  = __guac_socket_broadcast_flush_handler;
+    socket->lock_handler   = __guac_socket_broadcast_lock_handler;
+    socket->unlock_handler = __guac_socket_broadcast_unlock_handler;
+    socket->free_handler   = __guac_socket_broadcast_free_handler;
+
+    return socket;
+
+}
+

Reply via email to