/*
* linc-server.c: This file is part of the linc library.
*
* Authors:
* Elliot Lee (sopwith@redhat.com)
* Michael Meeks (michael@ximian.com)
* Mark McLouglin (mark@skynet.ie) & others
*
* Copyright 2001, Red Hat, Inc., Ximian, Inc.,
* Sun Microsystems, Inc.
*/
#include <config.h>
#include <stdio.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <linc/linc.h>
#include <linc/linc-server.h>
#include <linc/linc-connection.h>
#include "linc-debug.h"
#include "linc-private.h"
#include "linc-compat.h"
enum {
NEW_CONNECTION,
LAST_SIGNAL
};
static guint server_signals [LAST_SIGNAL] = { 0 };
static GObjectClass *parent_class = NULL;
static void
my_cclosure_marshal_VOID__OBJECT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
typedef void (*GSignalFunc_VOID__OBJECT) (gpointer data1,
GObject *arg_1,
gpointer data2);
register GSignalFunc_VOID__OBJECT callback;
register GCClosure *cc = (GCClosure*) closure;
register gpointer data1, data2;
g_return_if_fail (n_param_values >= 2);
if (G_CCLOSURE_SWAP_DATA (closure)) {
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
} else {
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}
callback = (GSignalFunc_VOID__OBJECT) (
marshal_data ? marshal_data : cc->callback);
callback (data1,
g_value_peek_pointer (param_values + 1),
data2);
}
static void
linc_server_init (LINCServer *cnx)
{
cnx->priv = g_new0 (LINCServerPrivate, 1);
cnx->priv->mutex = linc_mutex_new ();
cnx->priv->fd = -1;
}
static void
linc_server_dispose (GObject *obj)
{
GSList *l;
LINCServer *cnx = (LINCServer *) obj;
d_printf ("Dispose / close server fd %d\n", cnx->priv->fd);
#ifdef G_THREADS_ENABLED
if (cnx->priv->mutex) {
g_mutex_free (cnx->priv->mutex);
cnx->priv->mutex = NULL;
}
#endif
if (cnx->priv->tag) {
LincWatch *thewatch = cnx->priv->tag;
cnx->priv->tag = NULL;
linc_io_remove_watch (thewatch);
}
linc_protocol_destroy_cnx (cnx->proto,
cnx->priv->fd,
cnx->local_host_info,
cnx->local_serv_info);
cnx->priv->fd = -1;
while ((l = cnx->priv->connections)) {
GObject *o = l->data;
cnx->priv->connections = l->next;
g_slist_free_1 (l);
g_object_unref (o);
}
parent_class->dispose (obj);
}
static void
linc_server_finalize (GObject *obj)
{
LINCServer *cnx = (LINCServer *)obj;
g_free (cnx->local_host_info);
g_free (cnx->local_serv_info);
g_free (cnx->priv);
parent_class->finalize (obj);
}
static LINCConnection *
linc_server_create_connection (LINCServer *cnx)
{
return g_object_new (linc_connection_get_type (), NULL);
}
static gboolean
linc_server_accept_connection (LINCServer *server,
LINCConnection **connection)
{
LINCServerClass *klass;
struct sockaddr *saddr;
int addrlen, fd;
g_return_val_if_fail (connection != NULL, FALSE);
*connection = NULL;
addrlen = server->proto->addr_len;
saddr = g_alloca (addrlen);
fd = accept (server->priv->fd, saddr, &addrlen);
if (fd < 0) {
d_printf ("accept on %d failed %d", server->priv->fd, errno);
return FALSE; /* error */
}
if (server->create_options & LINC_CONNECTION_LOCAL_ONLY &&
!linc_protocol_is_local (server->proto, saddr, addrlen)) {
LINC_CLOSE (fd);
return FALSE;
}
if (server->create_options & LINC_CONNECTION_NONBLOCKING)
if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0) {
d_printf ("failed to set O_NONBLOCK on %d", fd);
LINC_CLOSE (fd);
return FALSE;
}
if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0) {
d_printf ("failed to set cloexec on %d", fd);
LINC_CLOSE (fd);
return FALSE;
}
klass = (LINCServerClass *) G_OBJECT_GET_CLASS (server);
g_assert (klass->create_connection);
*connection = klass->create_connection (server);
g_return_val_if_fail (*connection != NULL, FALSE);
d_printf ("accepted a new connection (%d) on server %d\n",
fd, server->priv->fd);
if (!linc_connection_from_fd (
*connection, fd, server->proto, NULL, NULL,
FALSE, LINC_CONNECTED, server->create_options)) {
g_object_unref (G_OBJECT (*connection));
*connection = NULL;
LINC_CLOSE (fd);
return FALSE;
}
server->priv->connections = g_slist_prepend (
server->priv->connections, *connection);
return TRUE;
}
static gboolean
linc_server_handle_io (GIOChannel *gioc,
GIOCondition condition,
gpointer data)
{
gboolean accepted;
LINCServer *server = data;
LINCConnection *connection = NULL;
if (!(condition & LINC_IN_CONDS))
g_error ("error condition on server fd is %#x", condition);
LINC_MUTEX_LOCK (server->priv->mutex);
accepted = linc_server_accept_connection (server, &connection);
LINC_MUTEX_UNLOCK (server->priv->mutex);
if (!accepted) {
GValue parms[2];
memset (parms, 0, sizeof (parms));
g_value_init (parms, G_OBJECT_TYPE (server));
g_value_set_object (parms, G_OBJECT (server));
g_value_init (parms + 1, G_TYPE_OBJECT);
/* FIXME: this connection is always NULL */
g_value_set_object (parms + 1, (GObject *) connection);
d_printf ("p %d, Non-accepted input on fd %d",
getpid (), server->priv->fd);
g_signal_emitv (parms, server_signals [NEW_CONNECTION], 0, NULL);
g_value_unset (parms);
g_value_unset (parms + 1);
}
return TRUE;
}
/**
* linc_server_setup:
* @cnx: the connection to setup
* @proto_name: the protocol to use
* @local_host_info: the local hsot
* @local_serv_info: remote server info
* @create_options: various create options
*
* Setup the server object. You should create a server object
* via g_object_new and then set it up, using this method.
*
* Return value: the initialized server
**/
gboolean
linc_server_setup (LINCServer *cnx,
const char *proto_name,
const char *local_host_info,
const char *local_serv_info,
LINCConnectionOptions create_options)
{
const LINCProtocolInfo *proto;
int fd, n;
struct sockaddr *saddr;
LincSockLen saddr_len;
const char *local_host;
char *service, *hostname;
#if !LINC_SSL_SUPPORT
if (create_options & LINC_CONNECTION_SSL)
return FALSE;
#endif
proto = linc_protocol_find (proto_name);
if (!proto) {
d_printf ("Can't find proto '%s'\n", proto_name);
return FALSE;
}
if (local_host_info)
local_host = local_host_info;
else
local_host = linc_get_local_hostname ();
address_in_use:
saddr = linc_protocol_get_sockaddr (
proto, local_host, local_serv_info, &saddr_len);
if (!saddr) {
d_printf ("Can't get_sockaddr proto '%s' '%s'\n",
local_host, local_serv_info ? local_serv_info : "(null)");
return FALSE;
}
fd = socket (proto->family, SOCK_STREAM,
proto->stream_proto_num);
if (fd < 0) {
g_free (saddr);
d_printf ("socket (%d, %d, %d) failed\n",
proto->family, SOCK_STREAM,
proto->stream_proto_num);
return FALSE;
}
{
static const int oneval = 1;
setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &oneval, sizeof (oneval));
}
n = 0;
errno = 0;
if ((proto->flags & LINC_PROTOCOL_NEEDS_BIND) || local_serv_info)
n = bind (fd, saddr, saddr_len);
if (n && errno == EADDRINUSE) {
d_printf ("bind failed; retrying");
goto address_in_use;
}
if (!n)
n = listen (fd, 10);
else
d_printf ("bind really failed errno: %d\n", errno);
if (!n &&
create_options & LINC_CONNECTION_NONBLOCKING)
n = fcntl (fd, F_SETFL, O_NONBLOCK);
else
d_printf ("listen failed errno: %d\n", errno);
if (!n)
n = fcntl (fd, F_SETFD, FD_CLOEXEC);
else
d_printf ("failed to set nonblock on %d", fd);
if (!n)
n = getsockname (fd, saddr, &saddr_len);
else
d_printf ("failed to set cloexec on %d", fd);
if (n) {
linc_protocol_destroy_addr (proto, fd, saddr);
d_printf ("get_sockname failed errno: %d\n", errno);
return FALSE;
}
if (!linc_protocol_get_sockinfo (proto, saddr, &hostname, &service)) {
linc_protocol_destroy_addr (proto, fd, saddr);
d_printf ("linc_getsockinfo failed.\n");
return FALSE;
}
g_free (saddr);
cnx->proto = proto;
cnx->priv->fd = fd;
if (create_options & LINC_CONNECTION_NONBLOCKING) {
g_assert (cnx->priv->tag == NULL);
cnx->priv->tag = linc_io_add_watch_fd (
fd, LINC_IN_CONDS | LINC_ERR_CONDS,
linc_server_handle_io, cnx);
}
cnx->create_options = create_options;
if (local_host_info) {
g_free (hostname);
cnx->local_host_info = g_strdup (local_host_info);
} else
cnx->local_host_info = hostname;
cnx->local_serv_info = service;
d_printf ("Created a new server fd (%d) '%s', '%s', '%s'\n",
fd, proto->name,
hostname ? hostname : "<Null>",
service ? service : "<Null>");
return TRUE;
}
static void
linc_server_class_init (LINCServerClass *klass)
{
GType ptype;
GClosure *closure;
GObjectClass *object_class = (GObjectClass *) klass;
object_class->dispose = linc_server_dispose;
object_class->finalize = linc_server_finalize;
klass->create_connection = linc_server_create_connection;
parent_class = g_type_class_peek_parent (klass);
closure = g_signal_type_cclosure_new (
G_OBJECT_CLASS_TYPE (klass),
G_STRUCT_OFFSET (LINCServerClass, new_connection));
ptype = G_TYPE_OBJECT;
server_signals [NEW_CONNECTION] = g_signal_newv (
"new_connection",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST, closure,
NULL, NULL,
my_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1, &ptype);
}
GType
linc_server_get_type (void)
{
static GType object_type = 0;
if (!object_type) {
static const GTypeInfo object_info = {
sizeof (LINCServerClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) linc_server_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (LINCServer),
0, /* n_preallocs */
(GInstanceInitFunc) linc_server_init,
};
object_type = g_type_register_static (
G_TYPE_OBJECT, "LINCServer",
&object_info, 0);
}
return object_type;
}
syntax highlighted by Code2HTML, v. 0.9.1