/* GNet - Networking library * Copyright (C) 2000 David Helder * Copyright (C) 2001 Mark Ferlatte * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "gnet-private.h" #ifndef GNET_WIN32 #include "unix.h" #define PATH(S) (((struct sockaddr_un *) (&(S)->sa))->sun_path) gboolean gnet_unix_socket_unlink (const gchar *path); /** * gnet_unix_socket_new * @path: path * * Creates a #GUnixSocket and connects to @path. This function will * block to connect. Use this constructor to create a #GUnixSocket * for a client. * * Returns: a new #GUnixSocket; NULL on failure. * **/ GUnixSocket* gnet_unix_socket_new (const gchar* path) { GUnixSocket *s = g_new0(GUnixSocket, 1); struct sockaddr_un *sa_un; g_return_val_if_fail(path != NULL, NULL); sa_un = (struct sockaddr_un *) &s->sa; /* Create socket */ s->ref_count = 1; s->server = FALSE; s->sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (!GNET_IS_SOCKET_VALID(s->sockfd)) { g_warning ("socket() failed"); g_free(s); return NULL; } memcpy(sa_un->sun_path, path, strlen(path)); sa_un->sun_family = AF_UNIX; if (connect(s->sockfd, (struct sockaddr*) &s->sa, sizeof(s->sa)) != 0) { g_free(s); return NULL; } return s; } /** * gnet_unix_socket_delete * @socket: a #GUnixSocket * * Deletes a #GUnixSocket. * **/ void gnet_unix_socket_delete (GUnixSocket* socket) { if (socket != NULL) gnet_unix_socket_unref (socket); } /** * gnet_unix_socket_ref * @socket: a #GUnixSocket * * Adds a reference to a #GUnixSocket. * **/ void gnet_unix_socket_ref (GUnixSocket* socket) { g_return_if_fail (socket != NULL); socket->ref_count++; } /** * gnet_unix_socket_unref * @socket: a #GUnixSocket * * Removes a reference from a #GUnixSocket. A #GUnixSocket is * deleted when the reference count reaches 0. * **/ void gnet_unix_socket_unref (GUnixSocket* socket) { g_return_if_fail (socket != NULL); socket->ref_count--; if (socket->ref_count == 0) { GNET_CLOSE_SOCKET(socket->sockfd); /* Don't care if this fails. */ if (socket->iochannel) g_io_channel_unref(socket->iochannel); if (socket->server) gnet_unix_socket_unlink(PATH(socket)); g_free(socket); } } /** * gnet_unix_socket_get_io_channel * @socket: a #GUnixSocket * * Gets the #GIOChannel of a #GUnixSocket. * * For a client socket, the #GIOChannel represents the data stream. * Use it like you would any other #GIOChannel. * * For a server socket, however, the #GIOChannel represents the * listening socket. When it's readable, there's a connection * waiting to be accepted. * * Every #GUnixSocket has one and only one #GIOChannel. If you ref * the channel, then you must unref it eventually. Do not close the * channel. The channel is closed by GNet when the socket is * deleted. * * Returns: a #GIOChannel. * **/ GIOChannel* gnet_unix_socket_get_io_channel (GUnixSocket* socket) { g_return_val_if_fail(socket != NULL, NULL); if (socket->iochannel == NULL) socket->iochannel = gnet_private_io_channel_new(socket->sockfd); return socket->iochannel; } /** * gnet_unix_socket_get_path * @socket: a #GUnixSocket * * Gets the path of a #GUnixSocket. * * Returns: the path. * **/ gchar* gnet_unix_socket_get_path(const GUnixSocket* socket) { g_return_val_if_fail(socket != NULL, NULL); return g_strdup (PATH(socket)); } /** * gnet_unix_socket_server_new * @path: path * * Creates a #GUnixSocket bound to @path. Use this constructor to * create a #GUnixSocket for a server. * * Returns: a new #GUnixSocket; NULL on error. * **/ GUnixSocket* gnet_unix_socket_server_new (const gchar *path) { struct sockaddr_un *sa_un; GUnixSocket *s; gint flags; socklen_t n; g_return_val_if_fail(path != NULL, NULL); s = g_new0(GUnixSocket, 1); sa_un = (struct sockaddr_un *) &s->sa; sa_un->sun_family = AF_UNIX; memcpy(sa_un->sun_path, path, strlen(path)); s->ref_count = 1; s->server = TRUE; if (! gnet_unix_socket_unlink(PATH(s))) goto error; s->sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (!GNET_IS_SOCKET_VALID(s->sockfd)) { g_warning ("socket() failed"); goto error; } flags = fcntl(s->sockfd, F_GETFL, 0); if (flags == -1) { g_warning ("fcntl() failed"); goto error; } /* Make the socket non-blocking */ if (fcntl(s->sockfd, F_SETFL, flags | O_NONBLOCK) == -1) { g_warning ("fcntl() failed"); goto error; } if (bind(s->sockfd, (struct sockaddr*) &s->sa, sizeof(s->sa)) != 0) goto error; n = sizeof(s->sa); /* Get the socket name FIXME (why? -DAH) */ if (getsockname(s->sockfd, (struct sockaddr*) &s->sa, &n) != 0) goto error; if (listen(s->sockfd, 10) != 0) goto error; return s; error: if (s) gnet_unix_socket_delete(s); return NULL; } /** * gnet_unix_socket_server_accept * @socket: a #GUnixSocket * * Accepts a connection from a #GUnixSocket. The socket must have * been created using gnet_unix_socket_server_new(). This function * will block. Even if the socket's #GIOChannel is readable, the * function may still block. * * Returns: a new #GUnixSocket representing a new connection; NULL on * error. * **/ GUnixSocket* gnet_unix_socket_server_accept (const GUnixSocket *socket) { gint sockfd; struct sockaddr sa; fd_set fdset; socklen_t n; GUnixSocket *s; g_return_val_if_fail(socket != NULL, NULL); try_again: FD_ZERO(&fdset); FD_SET(socket->sockfd, &fdset); if (select(socket->sockfd + 1, &fdset, NULL, NULL, NULL) == -1) { if (errno == EINTR) goto try_again; return NULL; } n = sizeof(s->sa); if ((sockfd = accept(socket->sockfd, &sa, &n)) == -1) { if (errno == EWOULDBLOCK || errno == ECONNABORTED || #ifdef EPROTO /* OpenBSD does not have EPROTO */ errno == EPROTO || #endif /* EPROTO */ errno == EINTR) goto try_again; return NULL; } s = g_new0(GUnixSocket, 1); s->ref_count = 1; s->sockfd = sockfd; memcpy(&s->sa, &sa, sizeof(s->sa)); return s; } /** * gnet_unix_socket_server_accept_nonblock * @socket: a #GUnixSocket * * Accepts a connection from a #GUnixSocket without blocking. The * socket must have been created using gnet_unix_socket_server_new(). * * Note that if the socket's #GIOChannel is readable, then there is * PROBABLY a new connection. It is possible for the connection * to close by the time this function is called, so it may return * NULL. * * Returns: a new #GUnixSocket representing a new connection; NULL * otherwise. * **/ GUnixSocket* gnet_unix_socket_server_accept_nonblock (const GUnixSocket *socket) { gint sockfd; struct sockaddr sa; fd_set fdset; socklen_t n; GUnixSocket *s; struct timeval tv = {0, 0}; g_return_val_if_fail (socket != NULL, NULL); try_again: FD_ZERO(&fdset); FD_SET(socket->sockfd, &fdset); if(select(socket->sockfd + 1, &fdset, NULL, NULL, &tv) == -1) { if (errno == EINTR) goto try_again; return NULL; } n = sizeof(sa); if ((sockfd = accept(socket->sockfd, &sa, &n)) == -1) { /* If we get an error, return. We don't want to try again as we do in gnet_unix_socket_server_accept() - it might cause a block. */ return NULL; } s = g_new0(GUnixSocket, 1); s->ref_count = 1; s->sockfd = sockfd; memcpy(&s->sa, &sa, sizeof(s->sa)); return s; } /** * gnet_unix_socket_unlink * @path: path * * Unlink a Unix socket named @path. * * Returns: TRUE on success, FALSE on failure. **/ gboolean gnet_unix_socket_unlink(const gchar *path) { struct stat stbuf; int r; g_return_val_if_fail(path != NULL, FALSE); r = stat(path, &stbuf); if (r == 0) { if (S_ISSOCK(stbuf.st_mode)) { if (unlink(path) == 0) { return TRUE; } else { /* Can't unlink */ return FALSE; } } else { /* path is not a socket */ return FALSE; } } else if (errno == ENOENT) { /* File doesn't exist, so we're okay */ return TRUE; } return FALSE; } #endif /* !GNET_WIN32 */