/* 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 */
syntax highlighted by Code2HTML, v. 0.9.1