/*
* The Spread Toolkit.
*
* The contents of this file are subject to the Spread Open-Source
* License, Version 1.0 (the ``License''); you may not use
* this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.spread.org/license/
*
* or in the file ``license.txt'' found in this distribution.
*
* Software distributed under the License is distributed on an AS IS basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Creators of Spread are:
* Yair Amir, Michal Miskin-Amir, Jonathan Stanton.
*
* Copyright (C) 1993-2004 Spread Concepts LLC <spread@spreadconcepts.com>
*
* All Rights Reserved.
*
* Major Contributor(s):
* ---------------
* Cristina Nita-Rotaru crisn@cs.purdue.edu - group communication security.
* Theo Schlossnagle jesus@omniti.com - Perl, skiplists, autoconf.
* Dan Schoenblum dansch@cnds.jhu.edu - Java interface.
* John Schultz jschultz@cnds.jhu.edu - contribution to process group membership.
*
*/
#include "sp.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _REENTRANT
#ifndef ARCH_PC_WIN95
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#else /* ARCH_PC_WIN95 */
#include <windows.h>
#endif /* ARCH_PC_WIN95 */
#endif /* _REENTRANT */
#ifdef ENABLE_PASSWORD
#include "auth-pword-client.h"
#endif
static char User[80];
static char Spread_name[80];
/* password variables always defined. Only have effect if ENABLE_PASSWORD is defined */
static int Use_Pword;
static char Pword_username[33];
static char Pword_password[9];
#ifdef ENABLE_PASSWORD
/* variables for Password authentication */
static struct user_password Pword_user;
#endif
static char Private_group[MAX_GROUP_NAME];
static mailbox Mbox;
static int Num_sent;
static unsigned int Previous_len;
static int To_exit = 0;
#define MAX_MESSLEN 102400
#ifdef _REENTRANT
#ifndef ARCH_PC_WIN95
static pthread_t Read_pthread;
static void *Read_thread_routine();
#else /* ARCH_PC_WIN95 */
static HANDLE Read_pthread;
static DWORD WINAPI Read_thread_routine( void *);
#endif /* ARCH_PC_WIN95 */
#endif /* _REENTRANT */
static void Print_menu();
static void User_command();
static void Read_message();
static void Usage( int argc, char *argv[] );
static void Print_help();
static void Bye();
static int ip_authenticate( char * auth_data)
{
return(1);
}
int main( int argc, char *argv[] )
{
int ret;
#ifdef SPREAD_VERSION
int mver, miver, pver;
#endif
sp_time test_timeout;
test_timeout.sec = 5;
test_timeout.usec = 0;
Usage( argc, argv );
#ifdef SPREAD_VERSION
if (!SP_version( &mver, &miver, &pver))
{
printf("main: Illegal variables passed to SP_version()\n");
Bye();
}
printf("Spread library version is %d.%d.%d\n", mver, miver, pver);
#else
printf("Spread library version is %1.2f\n", SP_version() );
#endif
#ifdef ENABLE_PASSWORD
if (Use_Pword)
{
strncpy(Pword_user.username, Pword_username, 32);
Pword_user.username[32] = '\0';
strncpy(Pword_user.password, Pword_password, 8);
Pword_user.password[8] = '\0';
SP_set_auth_method("PWORD", pword_authenticate, &Pword_user);
}
#endif
ret = SP_connect_timeout( Spread_name, User, 0, 1, &Mbox, Private_group, test_timeout );
if( ret != ACCEPT_SESSION )
{
SP_error( ret );
Bye();
}
printf("User: connected to %s with private group %s\n", Spread_name, Private_group );
#ifndef _REENTRANT
E_init();
E_attach_fd( 0, READ_FD, User_command, 0, NULL, LOW_PRIORITY );
E_attach_fd( Mbox, READ_FD, Read_message, 0, NULL, HIGH_PRIORITY );
#endif /* _REENTRANT */
Print_menu();
printf("\nUser> ");
fflush(stdout);
Num_sent = 0;
#ifdef _REENTRANT
#ifndef ARCH_PC_WIN95
ret = pthread_create( &Read_pthread, NULL, Read_thread_routine, 0 );
#else /* ARCH_PC_WIN95 */
Read_pthread = CreateThread( NULL, 0, Read_thread_routine, NULL, 0, &ret );
#endif /* ARCH_PC_WIN95 */
for(;;)
{
User_command();
#ifdef __bsdi__
/* bug in BSDI */
sched_yield();
#endif
}
#else /* _REENTRANT */
#ifndef ARCH_PC_WIN95
E_handle_events();
#else /* ARCH_PC_WIN95 */
for(;;)
User_command();
#endif /* ARCH_PC_WIN95 */
#endif /* _REENTRANT */
return( 0 );
}
#ifdef _REENTRANT
#ifndef ARCH_PC_WIN95
static void *Read_thread_routine()
#else /* ARCH_PC_WIN95 */
static DWORD WINAPI Read_thread_routine( void *dummy)
#endif /* ARCH_PC_WIN95 */
{
for(;;)
{
Read_message();
}
return( 0 );
}
#endif /* _REENTRANT */
static void User_command()
{
char command[130];
char mess[MAX_MESSLEN];
char group[80];
char groups[10][MAX_GROUP_NAME];
int num_groups;
unsigned int mess_len;
int ret;
int i;
for( i=0; i < sizeof(command); i++ ) command[i] = 0;
if( fgets( command, 130, stdin ) == 0 ) Bye();
switch( command[0] )
{
case 'j':
ret = sscanf( &command[2], "%s", group );
if( ret < 1 )
{
printf(" invalid group \n");
break;
}
ret = SP_join( Mbox, group );
if( ret < 0 ) SP_error( ret );
break;
case 'l':
ret = sscanf( &command[2], "%s", group );
if( ret < 1 )
{
printf(" invalid group \n");
break;
}
ret = SP_leave( Mbox, group );
if( ret < 0 ) SP_error( ret );
break;
case 's':
num_groups = sscanf(&command[2], "%s%s%s%s%s%s%s%s%s%s",
groups[0], groups[1], groups[2], groups[3], groups[4],
groups[5], groups[6], groups[7], groups[8], groups[9] );
if( num_groups < 1 )
{
printf(" invalid group \n");
break;
}
printf("enter message: ");
ret = (int) fgets( mess, 200, stdin );
if( ret==0 ) Bye();
mess_len = strlen( mess );
#ifdef _REENTRANT
#ifdef __bsdi__ /* bsdi bug - doing a close when another thread blocks on the socket causes a seg fault */
ret = send( Mbox, mess, 0, 0 );
if( ret < 0 )
{
SP_error( CONNECTION_CLOSED );
Bye();
}
#endif /* __bsdi__ */
#endif /* _REENTRANT */
ret= SP_multigroup_multicast( Mbox, SAFE_MESS, num_groups, (const char (*)[MAX_GROUP_NAME]) groups, 1, mess_len, mess );
if( ret < 0 )
{
SP_error( ret );
Bye();
}
Num_sent++;
break;
case 'b':
ret=sscanf( &command[2], "%s", group );
if( ret != 1 ) strcpy( group, "dummy_group_name" );
printf("enter size of each message: ");
ret = (int) fgets( mess, 200, stdin );
if( ret==0 ) Bye();
ret=sscanf(mess, "%u", &mess_len );
if( ret !=1 ) mess_len = Previous_len;
if( mess_len > MAX_MESSLEN ) mess_len = MAX_MESSLEN;
Previous_len = mess_len;
printf("sending 10 messages of %u bytes\n", mess_len );
for( i=0; i<10; i++ )
{
Num_sent++;
sprintf( mess, "mess num %d ", Num_sent );
#ifdef _REENTRANT
#ifdef __bsdi__ /* bsdi bug - doing a close when another thread blocks on the socket causes a seg fault */
ret = send( Mbox, mess, 0,0 );
if( ret < 0 )
{
SP_error( CONNECTION_CLOSED );
Bye();
}
#endif /* __bsdi__ */
#endif /* _REENTRANT */
ret= SP_multicast( Mbox, FIFO_MESS, group, 2, mess_len, mess );
if( ret < 0 )
{
SP_error( ret );
Bye();
}
printf("sent message %d (total %d)\n", i+1, Num_sent );
}
break;
#ifndef _REENTRANT
case 'r':
Read_message();
break;
case 'p':
ret = SP_poll( Mbox );
printf("Polling sais: %d\n", ret );
break;
case 'e':
E_attach_fd( Mbox, READ_FD, Read_message, 0, NULL, HIGH_PRIORITY );
break;
case 'd':
E_detach_fd( Mbox, READ_FD );
break;
#endif /* _REENTRANT */
case 'q':
Bye();
break;
default:
printf("\nUnknown commnad\n");
Print_menu();
break;
}
printf("\nUser> ");
fflush(stdout);
}
static void Print_menu()
{
printf("\n");
printf("==========\n");
printf("User Menu:\n");
printf("----------\n");
printf("\n");
printf("\tj <group> -- join a group\n");
printf("\tl <group> -- leave a group\n");
printf("\n");
printf("\ts <group> -- send a message\n");
printf("\tb <group> -- send a burst of messages\n");
printf("\n");
#ifndef _REENTRANT
printf("\tr -- receive a message (stuck) \n");
printf("\tp -- poll for a message \n");
printf("\te -- enable asynchonous read (default)\n");
printf("\td -- disable asynchronous read \n");
printf("\n");
#endif /* _REENTRANT */
printf("\tq -- quit\n");
fflush(stdout);
}
static void Read_message()
{
static char mess[MAX_MESSLEN];
char sender[MAX_GROUP_NAME];
char target_groups[100][MAX_GROUP_NAME];
group_id *grp_id;
int32 *num_vs;
char *vs_members;
int num_groups;
int num_bytes;
int service_type;
int16 mess_type;
int endian_mismatch;
int i;
int ret;
service_type = 0;
ret = SP_receive( Mbox, &service_type, sender, 100, &num_groups, target_groups,
&mess_type, &endian_mismatch, sizeof(mess), mess );
printf("\n============================\n");
if( ret < 0 )
{
if ( (ret == GROUPS_TOO_SHORT) || (ret == BUFFER_TOO_SHORT) ) {
service_type = DROP_RECV;
printf("\n========Buffers or Groups too Short=======\n");
ret = SP_receive( Mbox, &service_type, sender, 100, &num_groups, target_groups,
&mess_type, &endian_mismatch, sizeof(mess), mess );
}
}
if (ret < 0 )
{
if( ! To_exit )
{
SP_error( ret );
printf("\n============================\n");
printf("\nBye.\n");
}
exit( 0 );
}
if( Is_regular_mess( service_type ) )
{
mess[ret] = 0;
if ( Is_unreliable_mess( service_type ) ) printf("received UNRELIABLE ");
else if( Is_reliable_mess( service_type ) ) printf("received RELIABLE ");
else if( Is_fifo_mess( service_type ) ) printf("received FIFO ");
else if( Is_causal_mess( service_type ) ) printf("received CAUSAL ");
else if( Is_agreed_mess( service_type ) ) printf("received AGREED ");
else if( Is_safe_mess( service_type ) ) printf("received SAFE ");
printf("message from %s, of type %d, (endian %d) to %d groups \n(%d bytes): %s\n",
sender, mess_type, endian_mismatch, num_groups, ret, mess );
}else if( Is_membership_mess( service_type ) )
{
if ( Is_reg_memb_mess( service_type ) )
{
num_bytes = SP_get_gid_offset_memb_mess();
grp_id = (group_id *)&mess[num_bytes];
num_bytes = SP_get_num_vs_offset_memb_mess();
num_vs = (int32 *)&mess[num_bytes];
num_bytes = SP_get_vs_set_offset_memb_mess();
vs_members = &mess[num_bytes];
printf("Received REGULAR membership for group %s with %d members, where I am member %d:\n",
sender, num_groups, mess_type );
for( i=0; i < num_groups; i++ )
printf("\t%s\n", &target_groups[i][0] );
printf("grp id is %d %d %d\n",grp_id->id[0], grp_id->id[1], grp_id->id[2] );
if( Is_caused_join_mess( service_type ) )
{
printf("Due to the JOIN of %s\n", vs_members );
}else if( Is_caused_leave_mess( service_type ) ){
printf("Due to the LEAVE of %s\n", vs_members);
}else if( Is_caused_disconnect_mess( service_type ) ){
printf("Due to the DISCONNECT of %s\n", vs_members );
}else if( Is_caused_network_mess( service_type ) ){
printf("Due to NETWORK change. ");
printf("VS set has %d members:\n", *num_vs );
for( i=0; i < *num_vs; i++, vs_members+= MAX_GROUP_NAME )
printf("\t%s\n", vs_members );
}
}else if( Is_transition_mess( service_type ) ) {
printf("received TRANSITIONAL membership for group %s\n", sender );
}else if( Is_caused_leave_mess( service_type ) ){
printf("received membership message that left group %s\n", sender );
}else printf("received incorrecty membership message of type 0x%x\n", service_type );
} else if ( Is_reject_mess( service_type ) )
{
printf("REJECTED message from %s, of servicetype 0x%x messtype %d, (endian %d) to %d groups \n(%d bytes): %s\n",
sender, service_type, mess_type, endian_mismatch, num_groups, ret, mess );
}else printf("received message of unknown message type 0x%x with ret %d\n", service_type, ret);
printf("\n");
printf("User> ");
fflush(stdout);
}
static void Usage(int argc, char *argv[])
{
Use_Pword = 0;
sprintf( User, "user" );
sprintf( Spread_name, "4803@localhost");
while( --argc > 0 )
{
argv++;
if( !strncmp( *argv, "-u", 2 ) )
{
if (argc < 2) Print_help();
strcpy( User, argv[1] );
argc--; argv++;
}else if( !strncmp( *argv, "-r", 2 ) )
{
strcpy( User, "" );
}else if( !strncmp( *argv, "-s", 2 ) ){
if (argc < 2) Print_help();
strcpy( Spread_name, argv[1] );
argc--; argv++;
}else if( !strncmp( *argv, "-n", 2 ) ){
if (argc < 2) Print_help();
strcpy( Pword_username, argv[1] );
Use_Pword = 1;
argc--; argv++;
}else if( !strncmp( *argv, "-p", 2 ) ){
if (argc < 2) Print_help();
strcpy( Pword_password, argv[1] );
Use_Pword = 1;
argc--; argv++;
}else{
Print_help();
}
}
}
static void Print_help()
{
printf( "Usage: spuser\n%s\n%s\n%s\n%s\n%s\n",
"\t[-u <user name>] : unique (in this machine) user name",
"\t[-s <address>] : either port or port@machine",
"\t[-n <username>] : username for authentication",
"\t[-p <password>] : users password",
"\t[-r ] : use random user name");
exit( 0 );
}
static void Bye()
{
To_exit = 1;
printf("\nBye.\n");
#ifdef _REENTRANT
#ifdef __bsdi__
/* bug in threaded bsdi that cores in SP_disconnect when closing mbox while another thread is reading*/
exit( 0 );
#endif /* __bsdi__ */
#endif /* _REENTRANT */
SP_disconnect( Mbox );
#ifdef _REENTRANT
#ifndef ARCH_PC_WIN95
pthread_join( Read_pthread, NULL );
#else /* ARCH_PC_WIN95 */
#endif /* ARCH_PC_WIN95 */
#endif /* _REENTRANT */
exit( 0 );
}
syntax highlighted by Code2HTML, v. 0.9.1