/* BTP library - Banana Tree Protocol
* Copyright (C) 1999-2001 The Regents of the University of Michigan
*
* 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 <string.h>
#include "b_conn_io.h"
#include "b_packet.h"
#include "btp_debug.h"
#include "util.h"
static gchar* flags2str (guint flags);
static gboolean demux_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data);
static gboolean read_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data);
static gboolean write_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data);
static gboolean error_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data);
/* ************************************************************ */
static
gchar* flags2str (guint flags)
{
gchar* str = NULL;
str = g_strdup ("");
if (flags & G_IO_IN)
{
gchar* oldstr = str;
str = g_strconcat (oldstr, "IN|", NULL);
g_free (oldstr);
}
if (flags & G_IO_OUT)
{
gchar* oldstr = str;
str = g_strconcat (oldstr, "OUT|", NULL);
g_free (oldstr);
}
if (flags & G_IO_PRI)
{
gchar* oldstr = str;
str = g_strconcat (oldstr, "PRI|", NULL);
g_free (oldstr);
}
if (flags & G_IO_ERR)
{
gchar* oldstr = str;
str = g_strconcat (oldstr, "ERR|", NULL);
g_free (oldstr);
}
if (flags & G_IO_HUP)
{
gchar* oldstr = str;
str = g_strconcat (oldstr, "HUP|", NULL);
g_free (oldstr);
}
if (flags & G_IO_NVAL)
{
gchar* oldstr = str;
str = g_strconcat (oldstr, "NVAL|", NULL);
g_free (oldstr);
}
if (*str != '\0')
str[strlen(str) - 1] = '\0'; /* wipe out last | */
return str;
}
gboolean
b_conn_watch_update (BConn* conn, guint new_flags)
{
g_return_val_if_fail (conn, FALSE);
{
gchar* str = flags2str(conn->watch_flags);
gchar* str2 = flags2str(new_flags);
BTPP (1, "b_conn_watch_update: %s -> %s (%d)\n",
str, str2, conn->watch);
g_free (str);
g_free (str2);
}
if (conn->watch_flags == new_flags)
return FALSE;
conn->watch_changed = 1;
if (conn->watch)
{
g_source_remove (conn->watch);
conn->watch = 0;
}
if (new_flags && conn->conn->iochannel)
conn->watch = g_io_add_watch (conn->conn->iochannel, new_flags,
demux_cb, conn);
conn->watch_flags = new_flags;
return TRUE;
}
/* ************************************************************ */
/**
demux_cb
Called when any IO activity. This is a work around Glib's poll
problem.
*/
static gboolean
demux_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data)
{
BConn* conn = (BConn*) data;
gboolean rv;
g_return_val_if_fail (conn, FALSE);
g_return_val_if_fail (!conn->in_upcall, FALSE);
g_return_val_if_fail (condition & conn->watch_flags, FALSE);
{
gchar* str;
str = flags2str (condition);
BTPP (1, "demux_cb %s\n", str);
g_free (str);
}
conn->in_upcall = 1;
conn->watch_changed = 0;
if (condition & conn->watch_flags & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
{
rv = error_cb (iochannel, condition, data);
if (conn->should_destroy)
goto destroy;
if (!rv)
b_conn_watch_update (conn, conn->watch_flags &
~(G_IO_ERR | G_IO_HUP | G_IO_NVAL));
}
if (condition & conn->watch_flags & G_IO_OUT)
{
rv = write_cb (iochannel, condition, data);
if (conn->should_destroy)
goto destroy;
if (!rv)
b_conn_watch_update (conn, conn->watch_flags & ~G_IO_OUT);
}
if (condition & conn->watch_flags & G_IO_IN)
{
rv = read_cb (iochannel, condition, data);
if (conn->should_destroy)
goto destroy;
if (!rv)
b_conn_watch_update (conn, conn->watch_flags & ~G_IO_IN);
}
conn->in_upcall = 0;
/* Update flags */
if (conn->watch_changed)
{
conn->watch_changed = 0;
BTPP (0, "demux_cb: return FALSE\n");
return FALSE;
}
BTPP (0, "demux_cb: return TRUE\n");
return TRUE; /* otherwise, watch did not change */
destroy:
conn->in_upcall = 0;
b_conn_delete (conn);
return FALSE;
}
/**
read_cb
Called when an iochannel is readable. Read from the iochannel.
Loop parsing the packet and sending the packet up.
Last checked: 2001-4-30 DAH
*/
static gboolean
read_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data)
{
BConn* conn = (BConn*) data;
gchar* buf;
gint len;
GIOError error;
guint bytes_read;
guint bytes_parsed = 0;
BPacket* packet;
g_return_val_if_fail (conn, FALSE);
BTPP (1, "read_cb: buffer_offset = %d\n", conn->buffer_offset);
/* Read stuff into our buffer */
buf = &conn->buffer[conn->buffer_offset];
len = sizeof(conn->buffer) - conn->buffer_offset;
error = g_io_channel_read (iochannel, buf, len, &bytes_read);
/* If we read EOF or got an error, then the connection has failed
(we should have received a BYE) */
if ((error == G_IO_ERROR_NONE && bytes_read == 0) ||
(error != G_IO_ERROR_NONE && error != G_IO_ERROR_AGAIN))
{
BTPP (0, "read_cb: error %d, bytes_read %d\n", error, bytes_read);
goto error;
}
/* Update buffer length */
conn->buffer_offset += bytes_read;
BTPP (0, "read_cb: bytes_read = %d\n", bytes_read);
/* Parse the packets and pass them up to the host */
while (conn->buffer_offset &&
(bytes_parsed = b_packet_read (conn, conn->buffer, conn->buffer_offset, &packet)) > 0)
{
/* Move data over based on how much was read */
g_memmove(conn->buffer, &conn->buffer[bytes_parsed],
conn->buffer_offset - bytes_parsed);
conn->buffer_offset -= bytes_parsed;
BTPP (0, "read_cb: bytes_parsed = %d\n", bytes_parsed);
/* If there is no packet (it may only be part of a packet, break */
if (!packet)
break;
BTPP (2, "READ %s\n", b_packet_type_to_string(packet->type));
/* The packet should have the right ID (unless we just accepted
it) */
if (!b_conn_is_accepted(conn) && packet->id != conn->in_conn_id)
{
g_warning ("Packet has wrong id (is %d, should be %d)\n",
packet->id, conn->in_conn_id);
}
/* Dispatch the packet */
b_conn_handler_dispatch (conn, packet);
b_packet_delete (packet);
/* If the connection has been destroyed, then return FALSE. */
if (conn->should_destroy)
return FALSE;
}
/* Dump the connection if there is no more buffer space */
if (sizeof(conn->buffer) == conn->buffer_offset)
{
g_warning ("Maximum buffer length reached for connection\n");
goto error;
}
/* Dump the connection if there was a parsing error. */
if (bytes_parsed < 0)
{
BTPP (0, "read_cb: parse failed\n");
goto error;
}
return TRUE;
error:
b_conn_func_fail (conn);
return FALSE;
}
/**
write_cb
Called when an iochannel is writable. If there are no packets to
send, do an upcall. If there's a packet to send, send it.
Otherwise, we don't want any more callbacks.
Last checked: 2001-4-30 DAH
*/
gboolean
write_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data)
{
BConn* conn = (BConn*) data;
BPacket* packet;
g_return_val_if_fail (conn, FALSE);
BTPP (1, "write_cb %p\n", conn);
/* If there are no data packets to send, call the write callbacks -
they may give us a data packet. */
if (b_conn_is_connected (conn) &&
!(b_conn_has_packet (conn, B_PACKET_TYPE_MCAST) ||
b_conn_has_packet (conn, B_PACKET_TYPE_UCAST)) )
{
b_conn_func_writeable (conn);
}
/* If we have a packet, send it */
if (conn->pkt_queue && (packet = b_conn_pop_packet (conn)) != NULL)
{
gchar* buffer;
guint length;
guint bytes_writen;
GIOError error;
/* Get the next packet from the conn. We may not be ready to
send a packet because we're awaiting a response. */
if (!packet)
return FALSE;
BTPP (3, "WRITE %s\n", b_packet_type_to_string(packet->type));
/* Convert it to bytes */
length = b_packet_write (packet, &buffer);
g_return_val_if_fail (buffer, FALSE);
g_return_val_if_fail (length > 0, FALSE);
/* Write it */
error = gnet_io_channel_writen (conn->conn->iochannel, buffer, length,
&bytes_writen);
BTPP (1, "write_cb: bytes_writen = %d, length = %d\n", bytes_writen, length);
if (error != G_IO_ERROR_NONE || length != bytes_writen)
{
b_conn_func_fail (conn);
return FALSE;
}
/* Delete the packet and buffer */
b_packet_delete (packet);
g_free (buffer);
/* Return TRUE. If there were no more packets, this would
result in a call to writeable, which may generate more
packets. */
return TRUE;
}
/* Otherwise, if the host is being closed, close it */
else if (b_conn_is_closing (conn))
b_conn_func_close (conn);
/* Otherwise, we just don't have any packets to send */
return FALSE;
}
/*
error_cb
Called when a socket has an ERR or HUP.
Last checked: 2001-4-30 DAH
*/
gboolean
error_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data)
{
BConn* conn = (BConn*) data;
BTPP (4, "ERROR %d\n", condition);
b_conn_func_fail (conn);
return FALSE;
}
syntax highlighted by Code2HTML, v. 0.9.1