To do so define UDSCS_NO_SERVER. This simplifies reuse in client-only scenarios that don't need peer credential support for instance. The patch also reorders the client and server code so the implementation is not peppered with #ifdefs. Signed-off-by: Francois Gouget <fgouget@xxxxxxxxxxxxxxx> --- src/udscs.c | 486 ++++++++++++++++++++++++++++++------------------------------ src/udscs.h | 128 +++++++++------- 2 files changed, 321 insertions(+), 293 deletions(-) diff --git a/src/udscs.c b/src/udscs.c index 288aca2..334d54a 100644 --- a/src/udscs.c +++ b/src/udscs.c @@ -46,8 +46,10 @@ struct udscs_connection { const char * const *type_to_string; int no_types; int debug; - struct ucred peer_cred; void *user_data; +#ifndef UDSCS_NO_SERVER + struct ucred peer_cred; +#endif /* Read stuff, single buffer, separate header and data buffer */ int header_read; @@ -66,86 +68,6 @@ struct udscs_connection { struct udscs_connection *prev; }; -struct udscs_server { - int fd; - const char * const *type_to_string; - int no_types; - int debug; - struct udscs_connection connections_head; - udscs_connect_callback connect_callback; - udscs_read_callback read_callback; - udscs_disconnect_callback disconnect_callback; -}; - -static void udscs_do_write(struct udscs_connection **connp); -static void udscs_do_read(struct udscs_connection **connp); - - -struct udscs_server *udscs_create_server(const char *socketname, - udscs_connect_callback connect_callback, - udscs_read_callback read_callback, - udscs_disconnect_callback disconnect_callback, - const char * const type_to_string[], int no_types, int debug) -{ - int c; - struct sockaddr_un address; - struct udscs_server *server; - - server = calloc(1, sizeof(*server)); - if (!server) - return NULL; - - server->type_to_string = type_to_string; - server->no_types = no_types; - server->debug = debug; - - server->fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (server->fd == -1) { - syslog(LOG_ERR, "creating unix domain socket: %m"); - free(server); - return NULL; - } - - address.sun_family = AF_UNIX; - snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname); - c = bind(server->fd, (struct sockaddr *)&address, sizeof(address)); - if (c != 0) { - syslog(LOG_ERR, "bind %s: %m", socketname); - free(server); - return NULL; - } - - c = listen(server->fd, 5); - if (c != 0) { - syslog(LOG_ERR, "listen: %m"); - free(server); - return NULL; - } - - server->connect_callback = connect_callback; - server->read_callback = read_callback; - server->disconnect_callback = disconnect_callback; - - return server; -} - -void udscs_destroy_server(struct udscs_server *server) -{ - struct udscs_connection *conn, *next_conn; - - if (!server) - return; - - conn = server->connections_head.next; - while (conn) { - next_conn = conn->next; - udscs_destroy_connection(&conn); - conn = next_conn; - } - close(server->fd); - free(server); -} - struct udscs_connection *udscs_connect(const char *socketname, udscs_read_callback read_callback, udscs_disconnect_callback disconnect_callback, @@ -225,131 +147,17 @@ void udscs_destroy_connection(struct udscs_connection **connp) *connp = NULL; } -struct ucred udscs_get_peer_cred(struct udscs_connection *conn) -{ - return conn->peer_cred; -} - -int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds, - fd_set *writefds) +void udscs_set_user_data(struct udscs_connection *conn, void *data) { - struct udscs_connection *conn; - int nfds = server->fd + 1; - - if (!server) - return -1; - - FD_SET(server->fd, readfds); - - conn = server->connections_head.next; - while (conn) { - int conn_nfds = udscs_client_fill_fds(conn, readfds, writefds); - if (conn_nfds > nfds) - nfds = conn_nfds; - - conn = conn->next; - } - - return nfds; + conn->user_data = data; } -int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds, - fd_set *writefds) +void *udscs_get_user_data(struct udscs_connection *conn) { if (!conn) - return -1; - - FD_SET(conn->fd, readfds); - if (conn->write_buf) - FD_SET(conn->fd, writefds); - - return conn->fd + 1; -} - -static void udscs_server_accept(struct udscs_server *server) { - struct udscs_connection *new_conn, *conn; - struct sockaddr_un address; - socklen_t length = sizeof(address); - int r, fd; - - fd = accept(server->fd, (struct sockaddr *)&address, &length); - if (fd == -1) { - if (errno == EINTR) - return; - syslog(LOG_ERR, "accept: %m"); - return; - } - - new_conn = calloc(1, sizeof(*conn)); - if (!new_conn) { - syslog(LOG_ERR, "out of memory, disconnecting new client"); - close(fd); - return; - } - - new_conn->fd = fd; - new_conn->type_to_string = server->type_to_string; - new_conn->no_types = server->no_types; - new_conn->debug = server->debug; - new_conn->read_callback = server->read_callback; - new_conn->disconnect_callback = server->disconnect_callback; - - length = sizeof(new_conn->peer_cred); - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &new_conn->peer_cred, &length); - if (r != 0) { - syslog(LOG_ERR, "Could not get peercred, disconnecting new client"); - close(fd); - free(new_conn); - return; - } - - conn = &server->connections_head; - while (conn->next) - conn = conn->next; - - new_conn->prev = conn; - conn->next = new_conn; - - if (server->debug) - syslog(LOG_DEBUG, "new client accepted: %p, pid: %d", - new_conn, (int)new_conn->peer_cred.pid); - - if (server->connect_callback) - server->connect_callback(new_conn); -} - -void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds, - fd_set *writefds) -{ - struct udscs_connection *conn, *next_conn; - - if (!server) - return; - - if (FD_ISSET(server->fd, readfds)) - udscs_server_accept(server); - - conn = server->connections_head.next; - while (conn) { - /* conn maybe destroyed by udscs_client_handle_fds (when disconnected), - so get the next connection first. */ - next_conn = conn->next; - udscs_client_handle_fds(&conn, readfds, writefds); - conn = next_conn; - } -} - -void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds, - fd_set *writefds) -{ - if (!*connp) - return; - - if (FD_ISSET((*connp)->fd, readfds)) - udscs_do_read(connp); + return NULL; - if (*connp && FD_ISSET((*connp)->fd, writefds)) - udscs_do_write(connp); + return conn->user_data; } int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1, @@ -404,41 +212,7 @@ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1, return 0; } -int udscs_server_write_all(struct udscs_server *server, - uint32_t type, uint32_t arg1, uint32_t arg2, - const uint8_t *data, uint32_t size) -{ - struct udscs_connection *conn; - - conn = server->connections_head.next; - while (conn) { - if (udscs_write(conn, type, arg1, arg2, data, size)) - return -1; - conn = conn->next; - } - - return 0; -} - -int udscs_server_for_all_clients(struct udscs_server *server, - udscs_for_all_clients_callback func, void *priv) -{ - int r = 0; - struct udscs_connection *conn, *next_conn; - - if (!server) - return 0; - - conn = server->connections_head.next; - while (conn) { - /* Get next conn as func may destroy the current conn */ - next_conn = conn->next; - r += func(&conn, priv); - conn = next_conn; - } - return r; -} - +/* A helper for udscs_do_read() */ static void udscs_read_complete(struct udscs_connection **connp) { struct udscs_connection *conn = *connp; @@ -466,6 +240,7 @@ static void udscs_read_complete(struct udscs_connection **connp) memset(&conn->data, 0, sizeof(conn->data)); } +/* A helper for udscs_client_handle_fds() */ static void udscs_do_read(struct udscs_connection **connp) { ssize_t n; @@ -516,6 +291,7 @@ static void udscs_do_read(struct udscs_connection **connp) } } +/* A helper for udscs_client_handle_fds() */ static void udscs_do_write(struct udscs_connection **connp) { ssize_t n; @@ -549,15 +325,247 @@ static void udscs_do_write(struct udscs_connection **connp) } } -void udscs_set_user_data(struct udscs_connection *conn, void *data) +void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds, + fd_set *writefds) { - conn->user_data = data; + if (!*connp) + return; + + if (FD_ISSET((*connp)->fd, readfds)) + udscs_do_read(connp); + + if (*connp && FD_ISSET((*connp)->fd, writefds)) + udscs_do_write(connp); } -void *udscs_get_user_data(struct udscs_connection *conn) +int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds, + fd_set *writefds) { if (!conn) + return -1; + + FD_SET(conn->fd, readfds); + if (conn->write_buf) + FD_SET(conn->fd, writefds); + + return conn->fd + 1; +} + + +#ifndef UDSCS_NO_SERVER + +/* ---------- Server-side implementation ---------- */ + +struct udscs_server { + int fd; + const char * const *type_to_string; + int no_types; + int debug; + struct udscs_connection connections_head; + udscs_connect_callback connect_callback; + udscs_read_callback read_callback; + udscs_disconnect_callback disconnect_callback; +}; + +struct udscs_server *udscs_create_server(const char *socketname, + udscs_connect_callback connect_callback, + udscs_read_callback read_callback, + udscs_disconnect_callback disconnect_callback, + const char * const type_to_string[], int no_types, int debug) +{ + int c; + struct sockaddr_un address; + struct udscs_server *server; + + server = calloc(1, sizeof(*server)); + if (!server) return NULL; - return conn->user_data; + server->type_to_string = type_to_string; + server->no_types = no_types; + server->debug = debug; + + server->fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (server->fd == -1) { + syslog(LOG_ERR, "creating unix domain socket: %m"); + free(server); + return NULL; + } + + address.sun_family = AF_UNIX; + snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname); + c = bind(server->fd, (struct sockaddr *)&address, sizeof(address)); + if (c != 0) { + syslog(LOG_ERR, "bind %s: %m", socketname); + free(server); + return NULL; + } + + c = listen(server->fd, 5); + if (c != 0) { + syslog(LOG_ERR, "listen: %m"); + free(server); + return NULL; + } + + server->connect_callback = connect_callback; + server->read_callback = read_callback; + server->disconnect_callback = disconnect_callback; + + return server; +} + +void udscs_destroy_server(struct udscs_server *server) +{ + struct udscs_connection *conn, *next_conn; + + if (!server) + return; + + conn = server->connections_head.next; + while (conn) { + next_conn = conn->next; + udscs_destroy_connection(&conn); + conn = next_conn; + } + close(server->fd); + free(server); +} + +struct ucred udscs_get_peer_cred(struct udscs_connection *conn) +{ + return conn->peer_cred; } + +static void udscs_server_accept(struct udscs_server *server) { + struct udscs_connection *new_conn, *conn; + struct sockaddr_un address; + socklen_t length = sizeof(address); + int r, fd; + + fd = accept(server->fd, (struct sockaddr *)&address, &length); + if (fd == -1) { + if (errno == EINTR) + return; + syslog(LOG_ERR, "accept: %m"); + return; + } + + new_conn = calloc(1, sizeof(*conn)); + if (!new_conn) { + syslog(LOG_ERR, "out of memory, disconnecting new client"); + close(fd); + return; + } + + new_conn->fd = fd; + new_conn->type_to_string = server->type_to_string; + new_conn->no_types = server->no_types; + new_conn->debug = server->debug; + new_conn->read_callback = server->read_callback; + new_conn->disconnect_callback = server->disconnect_callback; + + length = sizeof(new_conn->peer_cred); + r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &new_conn->peer_cred, &length); + if (r != 0) { + syslog(LOG_ERR, "Could not get peercred, disconnecting new client"); + close(fd); + free(new_conn); + return; + } + + conn = &server->connections_head; + while (conn->next) + conn = conn->next; + + new_conn->prev = conn; + conn->next = new_conn; + + if (server->debug) + syslog(LOG_DEBUG, "new client accepted: %p, pid: %d", + new_conn, (int)new_conn->peer_cred.pid); + + if (server->connect_callback) + server->connect_callback(new_conn); +} + +int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds, + fd_set *writefds) +{ + struct udscs_connection *conn; + int nfds = server->fd + 1; + + if (!server) + return -1; + + FD_SET(server->fd, readfds); + + conn = server->connections_head.next; + while (conn) { + int conn_nfds = udscs_client_fill_fds(conn, readfds, writefds); + if (conn_nfds > nfds) + nfds = conn_nfds; + + conn = conn->next; + } + + return nfds; +} + +void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds, + fd_set *writefds) +{ + struct udscs_connection *conn, *next_conn; + + if (!server) + return; + + if (FD_ISSET(server->fd, readfds)) + udscs_server_accept(server); + + conn = server->connections_head.next; + while (conn) { + /* conn maybe destroyed by udscs_client_handle_fds (when disconnected), + so get the next connection first. */ + next_conn = conn->next; + udscs_client_handle_fds(&conn, readfds, writefds); + conn = next_conn; + } +} + +int udscs_server_write_all(struct udscs_server *server, + uint32_t type, uint32_t arg1, uint32_t arg2, + const uint8_t *data, uint32_t size) +{ + struct udscs_connection *conn; + + conn = server->connections_head.next; + while (conn) { + if (udscs_write(conn, type, arg1, arg2, data, size)) + return -1; + conn = conn->next; + } + + return 0; +} + +int udscs_server_for_all_clients(struct udscs_server *server, + udscs_for_all_clients_callback func, void *priv) +{ + int r = 0; + struct udscs_connection *conn, *next_conn; + + if (!server) + return 0; + + conn = server->connections_head.next; + while (conn) { + /* Get next conn as func may destroy the current conn */ + next_conn = conn->next; + r += func(&conn, priv); + conn = next_conn; + } + return r; +} + +#endif diff --git a/src/udscs.h b/src/udscs.h index 89ab617..8b0b14e 100644 --- a/src/udscs.h +++ b/src/udscs.h @@ -27,8 +27,10 @@ #include <sys/select.h> #include <sys/socket.h> + +/* ---------- Generic bits and client-side API ---------- */ + struct udscs_connection; -struct udscs_server; struct udscs_message_header { uint32_t type; uint32_t arg1; @@ -36,89 +38,107 @@ struct udscs_message_header { uint32_t size; }; -/* Callbacks with this type will be called when a new connection to a - server is accepted. */ -typedef void (*udscs_connect_callback)(struct udscs_connection *conn); /* Callbacks with this type will be called when a complete message has been - received. The callback may call udscs_destroy_connection, in which case - *connp must be made NULL (which udscs_destroy_connection takes care of) */ + * received. The callback may call udscs_destroy_connection, in which case + * *connp must be made NULL (which udscs_destroy_connection takes care of). + */ typedef void (*udscs_read_callback)(struct udscs_connection **connp, struct udscs_message_header *header, uint8_t *data); -/* Callback type for udscs_server_for_all_clients. Clients can be disconnected - from this callback just like with a read callback. */ -typedef int (*udscs_for_all_clients_callback)(struct udscs_connection **connp, - void *priv); + /* Callbacks with this type will be called when the connection is disconnected. - Note: - 1) udscs will destroy the connection in question itself after - this callback has completed! - 2) This callback is always called, even if the disconnect is initiated - by the udscs user through returning -1 from a read callback, or - by explictly calling udscs_destroy_connection */ + * Note: + * 1) udscs will destroy the connection in question itself after + * this callback has completed! + * 2) This callback is always called, even if the disconnect is initiated + * by the udscs user through returning -1 from a read callback, or + * by explictly calling udscs_destroy_connection. + */ typedef void (*udscs_disconnect_callback)(struct udscs_connection *conn); -/* Create a unix domain socket named name and start listening on it. */ -struct udscs_server *udscs_create_server(const char *socketname, - udscs_connect_callback connect_callback, - udscs_read_callback read_callback, - udscs_disconnect_callback disconnect_callback, - const char * const type_to_string[], int no_types, int debug); - -void udscs_destroy_server(struct udscs_server *server); - /* Connect to a unix domain socket named name. */ struct udscs_connection *udscs_connect(const char *socketname, udscs_read_callback read_callback, udscs_disconnect_callback disconnect_callback, const char * const type_to_string[], int no_types, int debug); -/* The contents of connp will be made NULL */ +/* The contents of connp will be made NULL. */ void udscs_destroy_connection(struct udscs_connection **connp); - -/* Given an udscs server or client fill the fd_sets pointed to by readfds and - writefds for select() usage. - - Return value: value of the highest fd + 1 */ -int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds, - fd_set *writefds); - int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds, - fd_set *writefds); - -/* Handle any events flagged by select for the given udscs server or client. */ -void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds, - fd_set *writefds); + fd_set *writefds); /* Note the connection may be destroyed (when disconnected) by this call - in this case the disconnect calllback will get called before the - destruction and the contents of connp will be made NULL */ + * in this case the disconnect calllback will get called before the + * destruction and the contents of connp will be made NULL. + */ void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds, - fd_set *writefds); - + fd_set *writefds); /* Queue a message for delivery to the client connected through conn. - - Returns 0 on success -1 on error (only happens when malloc fails) */ + * Returns 0 on success -1 on error (only happens when malloc fails). + */ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1, uint32_t arg2, const uint8_t *data, uint32_t size); +/* To associate per connection data with a connection. */ +void udscs_set_user_data(struct udscs_connection *conn, void *data); +void *udscs_get_user_data(struct udscs_connection *conn); + + +#ifndef UDSCS_NO_SERVER + +/* ---------- Server-side API ---------- */ + +struct udscs_server; + +/* Callbacks with this type will be called when a new connection to a + * server is accepted. + */ +typedef void (*udscs_connect_callback)(struct udscs_connection *conn); + +/* Create a unix domain socket named name and start listening on it. */ +struct udscs_server *udscs_create_server(const char *socketname, + udscs_connect_callback connect_callback, + udscs_read_callback read_callback, + udscs_disconnect_callback disconnect_callback, + const char * const type_to_string[], int no_types, int debug); + +void udscs_destroy_server(struct udscs_server *server); + /* Like udscs_write, but then send the message to all clients connected to - the server */ + * the server. + */ int udscs_server_write_all(struct udscs_server *server, - uint32_t type, uint32_t arg1, uint32_t arg2, - const uint8_t *data, uint32_t size); + uint32_t type, uint32_t arg1, uint32_t arg2, + const uint8_t *data, uint32_t size); + +/* Callback type for udscs_server_for_all_clients. Clients can be disconnected + * from this callback just like with a read callback. + */ +typedef int (*udscs_for_all_clients_callback)(struct udscs_connection **connp, + void *priv); + /* Call func for all clients connected to the server, passing through - priv to all func calls. Returns the total of the return values from all - calls to func */ + * priv to all func calls. Returns the total of the return values from all + * calls to func. + */ int udscs_server_for_all_clients(struct udscs_server *server, - udscs_for_all_clients_callback func, void *priv); + udscs_for_all_clients_callback func, void *priv); +/* Given an udscs server or client fill the fd_sets pointed to by readfds and + * writefds for select() usage. + * Return value: value of the highest fd + 1 + */ +int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds, + fd_set *writefds); + +/* Handle any events flagged by select for the given udscs server or client. */ +void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds, + fd_set *writefds); +/* Returns the peer's user credentials. */ struct ucred udscs_get_peer_cred(struct udscs_connection *conn); -/* For server use, to associate per connection data with a connection */ -void udscs_set_user_data(struct udscs_connection *conn, void *data); -void *udscs_get_user_data(struct udscs_connection *conn); +#endif #endif -- 2.6.1 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel