/* 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 <gnet/gnet.h>
#include <stdlib.h>
#include "util.h"
#include "btp.h"
#include "btp_node.h"
#include "btp_proto.h"
#include "b_packet.h"
#include "btp_debug.h"
static void mcast_read_cb (BtpTree* tree, BtpNode* from_node,
guint8* buffer, guint length, gpointer user_data);
static void ucast_read_cb (BtpTree* tree, BtpNode* from_node,
guint8* buffer, guint length, gpointer user_data);
static void error_cb (BtpTree* tree, gpointer user_data);
static guint btp_max_degree = BTP_MAX_DEGREE;
static gboolean btp_follow_nodes = TRUE;
static gboolean btp_use_shortcuts = FALSE;
/* ************************************************************ */
/**
Create a new BTP tree.
Last checked: 2001-5-7 DAH
*/
Btp*
btp_create (BPeer* peer, const gchar* name)
{
GURL* url;
Btp* btp = NULL;
BtpTree* tree = NULL;
g_return_val_if_fail (peer, NULL);
g_return_val_if_fail (name, NULL);
BTPP (1, "btp_create %s\n", name);
/* ******************** */
/* Check if tree already exists */
if (btp_has (peer, name))
return NULL;
/* Create URL */
if (*name == '/')
url = gnet_url_new_fields ("btp", peer->hostname, peer->port, name);
else
{
url = gnet_url_new_fields ("btp", peer->hostname, peer->port, NULL);
url->resource = g_strconcat ("/", name, NULL);
}
/* Create tree */
tree = btp_tree_new (peer, url);
/* Set initial sequence number */
tree->group.seq_num = rand() % G_MAXUINT16;
tree->group.source_id = rand() % G_MAXUINT32;
/* Set default policies */
tree->max_degree = btp_max_degree;
tree->follow_nodes = btp_follow_nodes;
tree->use_shortcuts = btp_use_shortcuts;
/* Add me to tree */
tree->me = btp_node_new (tree, peer->hostname, peer->port, NULL);
/* Make me root */
tree->root = tree->me;
tree->parent = NULL;
/* Add peer handler */
b_peer_add_handler (peer, url->resource, btp_peer_handler, tree);
/* Create BTP */
btp = g_new0 (Btp, 1);
btp->tree = tree;
/* Set callbacks */
tree->mcast_read_func = mcast_read_cb;
tree->ucast_read_func = ucast_read_cb;
tree->error_func = error_cb;
tree->mcast_read_user_data = btp;
tree->ucast_read_user_data = btp;
tree->error_user_data = btp;
/* Initialize */
btp_proto_init (tree);
return btp;
}
/**
Join an existing BtpTree.
Last checked: 2001-5-7 DAH
*/
Btp*
btp_join (BPeer* peer, const GURL* url)
{
Btp* btp;
BtpTree* tree;
GURL* gurl;
g_return_val_if_fail (peer, NULL);
g_return_val_if_fail (url, NULL);
/* ******************** */
/* Check if we have this tree. b_peer would get confused because it
doesn't know if an incoming connection is going to us, or to the
root. TODO: Allow this. */
if (btp_has (peer, url->resource?url->resource:"/"))
return NULL;
gurl = gnet_url_clone (url); /* do not delete */
if (gurl->port == 0)
gurl->port = BTP_PORT;
/* Create tree */
tree = btp_tree_new (peer, gurl);
/* Set initial sequence number */
tree->group.seq_num = rand() % G_MAXUINT16;
tree->group.source_id = rand() % G_MAXUINT32;
/* Set default policies */
tree->max_degree = btp_max_degree;
tree->follow_nodes = btp_follow_nodes;
tree->use_shortcuts = btp_use_shortcuts;
/* Create me */
tree->me = btp_node_new (tree, peer->hostname, peer->port, NULL);
/* Create root and connect */
tree->root = btp_node_new (tree, gurl->hostname, gurl->port, NULL);
tree->parent = tree->root;
b_conn_connect (tree->root->conn);
/* Add peer handler */
b_peer_add_handler (peer, url->resource?url->resource:"/",
btp_peer_handler, tree);
/* Create BTP */
btp = g_new0 (Btp, 1);
btp->tree = tree;
/* Set callbacks */
tree->mcast_read_func = mcast_read_cb;
tree->ucast_read_func = ucast_read_cb;
tree->error_func = error_cb;
tree->mcast_read_user_data = btp;
tree->ucast_read_user_data = btp;
tree->error_user_data = btp;
/* Initialize */
btp_proto_init (tree);
return btp;
}
/**
*/
Btp*
btp_join_passive (BPeer* peer, const GURL* url)
{
Btp* btp;
g_return_val_if_fail (peer, NULL);
g_return_val_if_fail (url, NULL);
/* ******************** */
btp = btp_join (peer, url);
if (!btp) return NULL;
btp->tree->passive = TRUE;
/* We have no parent in passive mode */
btp->tree->parent = NULL;
return btp;
}
void
btp_leave (Btp* btp)
{
BGroup* group;
g_return_if_fail (btp);
group = &btp->tree->group;
b_peer_remove_handler (group->peer, group->url->resource);
gnet_url_delete (group->url);
btp_tree_delete (btp->tree);
memset (btp, 0, sizeof(*btp));
g_free (btp);
}
gboolean
btp_has (BPeer* peer, const gchar* name)
{
gchar* tname;
gboolean rv;
g_return_val_if_fail (peer, FALSE);
g_return_val_if_fail (name, FALSE);
/* Clean up name so it starts with / */
if (*name == '/')
tname = g_strdup (name);
else
tname = g_strconcat ("/", name, NULL);
rv = b_peer_has_handler (peer, tname);
g_free (tname);
return rv;
}
guint
btp_get_max_degree (Btp* btp)
{
if (btp)
return btp->tree->max_degree;
else
return btp_max_degree;
}
void
btp_set_max_degree (Btp* btp, guint max_degree)
{
if (btp)
btp->tree->max_degree = max_degree;
else
btp_max_degree = max_degree;
}
gboolean
btp_get_follow_nodes (Btp* btp)
{
if (btp)
return btp->tree->follow_nodes;
else
return btp_follow_nodes;
}
void
btp_set_follow_nodes (Btp* btp, gboolean follow_nodes)
{
if (btp)
btp->tree->follow_nodes = follow_nodes;
else
btp_follow_nodes = follow_nodes;
}
gboolean
btp_get_use_shortcuts (Btp* btp)
{
if (btp)
return btp->tree->use_shortcuts;
else
return btp_use_shortcuts;
}
void
btp_set_use_shortcuts (Btp* btp, gboolean use_shortcuts)
{
if (btp)
btp->tree->use_shortcuts = use_shortcuts;
else
btp_use_shortcuts = use_shortcuts;
}
GURL*
btp_get_url (Btp* btp)
{
g_return_val_if_fail (btp, NULL);
return btp->tree->group.url;
}
gboolean
btp_is_up (const Btp* btp)
{
if (btp && btp->tree)
{
if (btp_tree_is_root (btp->tree->me))
return TRUE;
if (btp->tree->parent &&
btp->tree->parent->conn &&
b_conn_is_connected (btp->tree->parent->conn))
return TRUE;
}
return FALSE;
}
void
btp_print (FILE* file, BPeer* peer)
{
GSList* trees;
GSList* i;
g_return_if_fail (file);
g_return_if_fail (peer);
trees = b_peer_get_handler_data (peer);
fprintf (file, "BtpPeer\n");
for (i = trees; i != NULL; i = i->next)
btp_tree_print (file, (BtpTree*) i->data);
g_slist_free (trees);
}
void
btp_send (Btp* btp, const guint8* buffer, guint16 length)
{
g_return_if_fail (btp);
btp_tree_send_mcast (btp->tree, buffer, length);
}
/* **************************************** */
/* Callbacks */
static void
mcast_read_cb (BtpTree* tree, BtpNode* from_node,
guint8* buffer, guint length, gpointer user_data)
{
Btp* btp = (Btp*) user_data;
/* Pass packet up */
if (btp->packet_func)
(btp->packet_func)(btp, buffer, length, btp->packet_user_data);
}
static void
ucast_read_cb (BtpTree* tree, BtpNode* from_node,
guint8* buffer, guint length, gpointer user_data)
{
Btp* btp = (Btp*) user_data;
/* Pass packet up */
if (btp->packet_func)
(btp->packet_func)(btp, buffer, length, btp->packet_user_data);
}
static void
error_cb (BtpTree* tree, gpointer user_data)
{
Btp* btp = (Btp*) user_data;
/* Pass error up */
if (btp->error_func)
(btp->error_func)(btp, btp->error_user_data);
}
syntax highlighted by Code2HTML, v. 0.9.1