/* 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 <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include "b_conn.h"
#include "b_packet.h"
#include "btp_debug.h"
#include "util.h"
guint b_conn_num_conns = 0;
void (*b_conn_num_conns_zero)(void) = NULL;
static void b_conn_reset (BConn* conn);
/* ************************************************************ */
BConn*
b_conn_new (gchar* hostname, gint port, BGroup* group,
BConnFuncs* funcs, gpointer user_data)
{
BConn* conn;
g_return_val_if_fail (hostname, NULL);
g_return_val_if_fail (port != 0, NULL);
g_return_val_if_fail (group, NULL);
BTPP (1, "b_conn_new\n");
conn = g_new0 (BConn, 1);
conn->conn = gnet_conn_new (hostname, port, NULL, 0);
conn->group = group;
conn->funcs = funcs;
conn->user_data = user_data;
conn->status = B_CONN_STATUS_UNCONNECTED;
++b_conn_num_conns;
return conn;
}
BConn*
b_conn_new_accepted (GConn* gconn)
{
BConn* conn;
conn = g_new0 (BConn, 1);
conn->conn = gconn;
conn->status = B_CONN_STATUS_ACCEPTED;
++b_conn_num_conns;
return conn;
}
void
b_conn_delete (BConn* conn)
{
g_return_if_fail (conn != NULL);
BTPP (1, "b_conn_delete\n");
/* If we're in an upcall, don't delete yet */
if (conn->in_upcall)
{
conn->should_destroy = 1;
return;
}
b_conn_reset (conn);
gnet_conn_delete (conn->conn, FALSE);
memset (conn, 0, sizeof(*conn));
g_free (conn);
/* Call callback */
if (b_conn_num_conns)
{
--b_conn_num_conns;
if (!b_conn_num_conns && b_conn_num_conns_zero)
(b_conn_num_conns_zero)();
}
else
g_warning ("b_conn_num_conns invalid\n");
}
void
b_conn_reset (BConn* conn)
{
GSList* i;
g_return_if_fail (conn);
BTPP (1, "b_conn_reset\n");
conn->status = B_CONN_STATUS_UNCONNECTED;
/* Stop pinging it */
b_conn_ping_stop (conn);
/* keep the distance estimate */
/* Reset BYE timer */
rand_timer_cancel (&conn->bye_to_timer);
/* Remove all IO handlers */
b_conn_ignore_all (conn);
BTPP (1, "b_conn_reset: disconnect %p\n", conn->conn);
/* Reset connection */
gnet_conn_disconnect (conn->conn, TRUE);
BTPP (1, "b_conn_reset: disconnect done\n");
/* Remove all handlers */
b_conn_handler_remove_all (conn);
/* Reset packet management stuff */
conn->in_conn_id = 0;
conn->out_conn_id = 0;
BTPP (1, "b_conn_reset: delete packets\n");
for (i = conn->pkt_queue; i != NULL; i = i->next)
b_packet_delete ((BPacket*) i->data);
g_slist_free (conn->pkt_queue);
conn->pkt_queue = NULL;
conn->pkt_queue_size = 0;
conn->buffer_offset = 0;
conn->cur_read = 0;
b_packet_delete (conn->cur_pkt); conn->cur_pkt = NULL;
BTPP (1, "b_conn_reset: done\n");
}
void
b_conn_print (FILE* file, BConn* conn)
{
if (conn)
{
gchar* status = "none";
switch (conn->status)
{
case B_CONN_STATUS_UNCONNECTED: status = "unconnected"; break;
case B_CONN_STATUS_CLOSING: status = "closing"; break;
case B_CONN_STATUS_CONNECTED: status = "connected"; break;
case B_CONN_STATUS_CONNECTING: status = "connecting"; break;
case B_CONN_STATUS_ACCEPTED: status = "accepted"; break;
}
fprintf (file,
"%s:%d [status = %s, distance = %d, "
"pktq length = %d, pktq size = %d, handlers = ",
conn->conn->hostname, conn->conn->port, status, conn->distance,
g_slist_length (conn->pkt_queue), conn->pkt_queue_size);
b_conn_handler_print (file, conn);
fprintf (file, "]\n");
}
else
{
fprintf (file, "<null>");
}
}
/* **************************************** */
void
b_conn_func_connect (BConn* conn)
{
g_return_if_fail (conn);
BTPP (1, "b_conn_func_connect\n");
if (conn->funcs && conn->funcs->connect)
conn->funcs->connect (conn, conn->user_data);
}
void
b_conn_func_ping (BConn* conn)
{
g_return_if_fail (conn);
BTPP (1, "b_conn_func_ping\n");
if (conn->funcs && conn->funcs->ping)
conn->funcs->ping (conn, conn->user_data);
}
void
b_conn_func_writeable (BConn* conn)
{
g_return_if_fail (conn);
BTPP (1, "b_conn_func_writeable\n");
if (conn->funcs && conn->funcs->writeable)
conn->funcs->writeable (conn, conn->user_data);
}
void
b_conn_func_close (BConn* conn)
{
g_return_if_fail (conn);
BTPP (1, "b_conn_func_close\n");
b_conn_reset (conn);
if (conn->funcs && conn->funcs->close)
conn->funcs->close (conn, conn->user_data);
}
void
b_conn_func_fail (BConn* conn)
{
g_return_if_fail (conn);
BTPP (1, "b_conn_func_fail\n");
b_conn_reset (conn);
if (conn->funcs && conn->funcs->fail)
conn->funcs->fail (conn, conn->user_data);
}
/* **************************************** */
gboolean
b_conn_has_packet (BConn* conn, guint8 type)
{
GSList* i;
g_return_val_if_fail (conn != NULL, FALSE);
for (i = conn->pkt_queue; i != NULL; i = i->next)
{
BPacket* packet = (BPacket*) i->data;
if (packet->type == type)
return TRUE;
}
return FALSE;
}
gboolean
b_conn_has_packet_by_subtype (BConn* conn, guint8 type, guint8 subtype)
{
GSList* i;
g_return_val_if_fail (conn != NULL, FALSE);
for (i = conn->pkt_queue; i != NULL; i = i->next)
{
BPacket* packet = (BPacket*) i->data;
if (packet->type == type && packet->subtype == subtype)
return TRUE;
}
return FALSE;
}
/* **************************************** */
void
b_conn_send_packet (BConn* bconn, BPacket* packet)
{
g_return_if_fail (bconn);
g_return_if_fail (packet);
BTPP (1, "b_conn_send_packet: proto = %d, version = %d, type = %d, %d, length = %d, id = %d\n",
packet->protocol, packet->version, packet->type,
packet->subtype, g_ntohs(packet->length), packet->id);
BTPP (3, "SEND %s (%d)\n", b_packet_type_to_string(packet->type),
packet->subtype);
/* Connect if unconnected */
if (b_conn_is_unconnected(bconn))
{
b_conn_connect (bconn);
}
/* Make sure we're watching for a write */
else if (gnet_conn_is_connected(bconn->conn))
{
b_conn_watch_write (bconn);
}
/* If the packet is a HELLO, put it at the front of the packet list */
if (packet->type == B_PACKET_TYPE_HELLO)
{
/* Remove any HELLOs that are in the queue now */
b_conn_remove_packets (bconn, B_PACKET_TYPE_HELLO);
/* Put at the front of the queue */
b_conn_prepend_packet (bconn, packet);
}
/* Put any reply immediately after the last reply or at the front,
which ever is first */
else if (packet->type == B_PACKET_TYPE_OK ||
packet->type == B_PACKET_TYPE_ERROR)
{
GSList* i;
GSList* last_reply = NULL;
for (i = bconn->pkt_queue; i != NULL; i = i->next)
{
gint t = ((BPacket*) i->data)->type;
if (t == B_PACKET_TYPE_OK ||
t == B_PACKET_TYPE_ERROR)
last_reply = i;
}
if (last_reply)
last_reply = g_slist_insert (last_reply, packet, 1);
else
bconn->pkt_queue = g_slist_prepend (bconn->pkt_queue, packet);
}
/* If the packet is MCAST data, make sure we have enough room. */
else if (packet->type == B_PACKET_TYPE_MCAST)
{
if ((bconn->pkt_queue_size + g_ntohs(packet->length)) <
B_CONN_MAX_MCAST_SIZE)
{
b_conn_append_packet (bconn, packet);
bconn->pkt_queue_size += g_ntohs(packet->length);
}
else
{
BTPP (15, "Dropping MCAST packet due to space\n");
}
}
/* Otherwise, its just a regular packet, so add it to the back of
the list */
else
b_conn_append_packet (bconn, packet);
}
void
b_conn_prepend_packet (BConn* conn, BPacket* packet)
{
g_return_if_fail (conn);
g_return_if_fail (packet);
conn->pkt_queue = g_slist_prepend (conn->pkt_queue, packet);
}
void
b_conn_append_packet (BConn* conn, BPacket* packet)
{
g_return_if_fail (conn);
g_return_if_fail (packet);
conn->pkt_queue = g_slist_append (conn->pkt_queue, packet);
}
/* **************************************** */
BPacket*
b_conn_pop_packet (BConn* conn)
{
BPacket* packet = NULL;
BPacket* data_packet = NULL;
GSList* i;
g_return_val_if_fail (conn != NULL, NULL);
BTPP (1, "b_conn_pop_packet\n");
if (conn->pkt_queue == NULL)
return NULL;
for (i = conn->pkt_queue; i != NULL; i = i->next)
{
BPacket* pkt = (BPacket*) i->data;
BTPP (1, "b_conn_pop_packet: QUEUE type = %d, %d, length = %d\n",
pkt->type, pkt->subtype, g_ntohs(pkt->length));
}
/* Search for a control packet (other than BYE). Also keep
track of the first data packet we see. */
for (i = conn->pkt_queue; i != NULL; i = i->next)
{
BPacket* p = (BPacket*) i->data;
if (p->type == B_PACKET_TYPE_MCAST ||
p->type == B_PACKET_TYPE_UCAST)
{
if (data_packet == NULL)
data_packet = p;
}
else if (p->type != B_PACKET_TYPE_BYE)
{
packet = p;
break;
}
/* otherwise it's a BYE, ignore it for now. This effectively
pushes the BYE to the back of the queue. */
}
/* If we didn't find a control packet, use a data packet. If we
didn't find a data packet, use the top packet, which should be a
BYE. */
if (packet == NULL)
{
if (data_packet != NULL)
packet = data_packet;
else
{
packet = (BPacket*) conn->pkt_queue->data;
g_return_val_if_fail (packet->type == B_PACKET_TYPE_BYE, NULL);
}
}
g_return_val_if_fail (packet != NULL, NULL);
/* If we aren't connected and this is a non-connect packet, return
NULL. We don't want to send anything special yet. */
if (conn->status == B_CONN_STATUS_CONNECTING &&
packet->type != B_PACKET_TYPE_HELLO)
return NULL;
/* Remove the packet from the list */
conn->pkt_queue = g_slist_remove (conn->pkt_queue, packet);
BTPP (1, "b_conn_pop_packet: remove type = %d, %d, lenght = %d\n",
packet->type, packet->subtype, g_ntohs(packet->length));
/* If it's a ping, set the time now. (If we set it earlier, it
might sit around in our buffers before we actually sent it,
which could cause a bad distance estimate.) */
if (packet->type == B_PACKET_TYPE_INFO_REQUEST &&
packet->subtype == B_PACKET_INFO_SUBTYPE_PING)
{
BTPP (1, "b_conn_pop_packet: update ping\n");
gettimeofday((struct timeval*) packet->data, NULL);
}
/* Otherwise, if it's a MCAST packet, deduct it's length from
pkt_queue_size. */
else if (packet->type == B_PACKET_TYPE_MCAST)
{
conn->pkt_queue_size -= g_ntohs(packet->length);
}
return packet;
}
void
b_conn_remove_packets (BConn* conn, guint8 type)
{
GSList* i;
BTPP (1, "b_conn_remove_packets\n");
for (i = conn->pkt_queue; i != NULL; )
{
GSList* old_i = i;
BPacket* packet = (BPacket*) i->data;
i = i->next;
if (packet->type == type)
{
if (packet->type == B_PACKET_TYPE_MCAST)
conn->pkt_queue_size -= g_ntohs (packet->length);
b_packet_delete (packet);
conn->pkt_queue = g_slist_remove_link (conn->pkt_queue, old_i);
}
}
}
void
b_conn_remove_packets_by_subtype (BConn* conn, guint8 type, guint8 subtype)
{
GSList* i;
BTPP (1, "b_conn_remove_packets_by_subtype\n");
for (i = conn->pkt_queue; i != NULL; )
{
GSList* old_i = i;
BPacket* packet = (BPacket*) i->data;
i = i->next;
if (packet->type == type && packet->subtype == subtype)
{
if (packet->type == B_PACKET_TYPE_MCAST)
conn->pkt_queue_size -= g_ntohs (packet->length);
b_packet_delete (packet);
conn->pkt_queue = g_slist_remove_link (conn->pkt_queue, old_i);
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1