/*
 * Helper program to view internal contents of web server view peek
 * mailbox.  Format:
 *
 *    server_peek mailbox [name[->subname]|@address]
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iodef.h>
#include <descrip.h>
#include <pthread.h>
#include <time.h>

static $DESCRIPTOR(mbx_name,"HTTP_PEEK");

static struct { unsigned short status, count; long pid; } iosb;
static struct { int code; 		/* 1-return, 2-evaluate */
	int size;
	void *ptr;
	char data[600];
} message;

static int mbx_chan, ef, header_size;
/*
 * Peek request places answer in global message structure.
 */
int peek_request (int code, int size, void *addr, int *ret_length )
{
    int status, SYS$QIOW(), length;

    message.code = code;
    message.size = size;
    message.ptr = addr;
    if ( code == 2 ) {
	length = strlen ( (char *) addr ) + header_size;
	strcpy ( message.data, (char *) addr );
    } else length = header_size;
    status = SYS$QIOW ( ef, mbx_chan, IO$_WRITEVBLK, &iosb, 0, 0,
	&message, length, 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) return status;

    status = SYS$QIOW ( ef, mbx_chan, IO$_READVBLK, &iosb, 0, 0,
	&message, sizeof(message), 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) return status;
    *ret_length = iosb.count - header_size;
    return status;
}

int peek_copy ( int size, void *remote_addr, void *buffer )
{
    int status, ret_length;
    status = peek_request ( 1, size, remote_addr, &ret_length );
    if ( (status&1) == 1 ) memcpy ( buffer, message.data, ret_length );
    return status;
}

/***************************************************************************/
/*
 * Display symbol table at given address.
 */
static int show_table ( void *tbl_addr, char *sub_name )
{
    int status, ndx, ret_length;
    struct tentry { char *name; void *addr; } tbl;
    char *sub_sub_name;

    sub_sub_name = strstr ( sub_name, "->" );
    if ( sub_sub_name ) {
	*sub_sub_name++ = '\0'; sub_sub_name++;
    } else sub_sub_name = "";

    for ( ndx = 0; ; ndx++ ) {
	status = peek_copy ( sizeof(tbl), tbl_addr, &tbl );
	if ( (status&1) == 0 ) break;
	printf("  [%d].name=%x, .value=%x", ndx, tbl.name, tbl.addr );
	if ( !tbl.addr ) break;
	status = peek_request ( 3, 100, tbl.name, &ret_length );
	if ( (status&1) == 0 ) break;
	message.data[ret_length] = '\0';
	printf(" (%s)\n", message.data );
	tbl_addr = ((struct tentry *) tbl_addr) + 1;

	if ( *sub_name ) {
	    if ( strcmp ( message.data, sub_name ) == 0 ) {
		printf("Found it\n");
	    }
	}
    }
    return status;
}
/*************************************************************************/
/*
 */
static int display_service_list ( void *list_addr, char *sub_name )
{
    int status, ndx, ret_length;
    char *sub_sub_name, info[256];
    struct service_def {
        struct service_def *next;
        char *info;
        char name[64];
        pthread_attr_t thread_attr;
        void *start;
        int pool_id;
    };
    struct service_def srvc, *srvc_addr;

    sub_sub_name = strstr ( sub_name, "->" );
    if ( sub_sub_name ) {
	*sub_sub_name++ = '\0'; sub_sub_name++;
    } else sub_sub_name = "";
    /*
     * list_addr is address of pointer to first block in linked list
     */
    status = peek_copy ( sizeof(void *), list_addr, &srvc.next );
    if ( (status&1) == 0 ) return status;
    /*
     * Scan list of pool data structures.
     */
    while ( srvc.next ) {
	/*
	 * Make local copy of next pool entry pointed to by list_addr and
	 * redownload name string it points to.
	 */
	status = peek_copy ( sizeof(struct service_def), srvc.next, &srvc );
	if ( (status&1) == 0 ) break;
	if ( *sub_name ) {
	    /* check for match */
	    if ( strncmp(sub_name, srvc.name, sizeof(srvc.name)) != 0 )
		continue;
	}
	if ( srvc.info ) {
	    int ret_len;
	    status = peek_request ( 3, sizeof(info)-1, srvc.info, &ret_len );
	    message.data[ret_len] = '\0';
	} else message.data[0] = '\0';
	printf ( "    service %s: pool=%d, start=%x, info = '%s'\n",
		srvc.name, srvc.pool_id, srvc.start, message.data );
    }
    return 1;
}
/***************************************************************************/
/*
 * Display MST pool structures.
 */
struct service_pool {
    void *free;                          /* List of free connection structs */
    int id;                             /* self address */
    int remaining;                      /* Number on free list */
    int allocated;                      /* Number of connections allocated */
    int limit;                          /* Max number we can allocate */
    int resource_wait;
    int pending_connect;                /* predicate for pool waits */
    pthread_cond_t thread_available;
    pthread_attr_t thread_attr;         /* Attributes for pool's threads */
};
static int display_pool_list ( void *list_addr, char *sub_name )
{
    int status, ndx, ret_length;
    struct service_pool pool, *pool_addr;
    char *sub_sub_name;

    sub_sub_name = strstr ( sub_name, "->" );
    if ( sub_sub_name ) {
	*sub_sub_name++ = '\0'; sub_sub_name++;
    } else sub_sub_name = "";
    /*
     * list_addr is address of table of pointers.
     */
    status = peek_copy ( sizeof(void *), list_addr, &list_addr );
    if ( (status&1) == 0 ) return status;
    /*
     * Scan list of pool data structures.
     */
    for ( ndx = 0; ; ndx++ ) {
	/*
	 * Make local copy of next pool entry pointed to by list_addr and
	 * redownload name string it points to.
	 */
	status = peek_copy ( sizeof(pool_addr), list_addr, &pool_addr );
	if ( (status&1) == 0 ) break;
	list_addr = ((struct service_pool **) list_addr) + 1;
	status = peek_copy ( sizeof(pool), pool_addr, &pool );
	if ( (status&1) == 0 ) break;
	if ( pool.id != ndx ) {
	    printf("    --end of list detected (%x)--", pool.id );
	    return 1;
	}
	printf("    pool[%d]: free_link=%x, remaining=%d, alloc=%d, limit=%d\n",
		pool.id, pool.free, pool.remaining, pool.allocated,pool.limit);
	printf("          resource wait flag: %d, pending: %d\n",
		pool.resource_wait, pool.pending_connect );
    }
    return 1;
}
/***************************************************************************/
/*
 * Display symbol table at given address.
 */
int show_mst_data ( void *tbl_addr, char *sub_name )
{
    int status, ndx, ret_length;
    struct tentry { char *name; void *addr; } tbl;
    char *sub_sub_name;

    sub_sub_name = strstr ( sub_name, "->" );
    if ( sub_sub_name ) {
	*sub_sub_name++ = '\0'; sub_sub_name++;
    } else sub_sub_name = "";
    /*
     * Scan for matching subname.
     */
    for ( ndx = 0; ; ndx++ ) {
	/*
	 * Make local copy of next table entry pointed to by tbl_addr and
	 * redownload name string it points to.
	 */
	status = peek_copy ( sizeof(tbl), tbl_addr, &tbl );
	if ( (status&1) == 0 ) break;
	tbl_addr = ((struct tentry *) tbl_addr) + 1;
	if ( !tbl.addr ) break;
	status = peek_request ( 3, 100, tbl.name, &ret_length );
	if ( (status&1) == 0 ) break;
	message.data[ret_length] = '\0';
	/*
	 * display data based upon name.
	 */
	if ( strcmp ( message.data, sub_name ) == 0 ) {
	    if ( strcmp ( sub_name, "pool_list" ) == 0 ) {
		status = display_pool_list ( tbl.addr, sub_sub_name );
	    } else if ( strcmp ( sub_name, "registered_services" ) == 0 ) {
		status = display_service_list ( tbl.addr, sub_sub_name );
	    } else {
		long *long_data;
		if ( tbl.addr ) {
		    status = peek_request ( 1, 4, tbl.addr, &ret_length );
		    long_data = (long *) message.data;
		    printf ( "  member '%s' data: %x -> %x\n", sub_name,
			tbl.addr, *long_data );
		} else {
		    printf("  member '%s' has no data\n", sub_name );
		}
	    }
	    return status;
	}
    }
    printf("  member '%s' not found\n", sub_name );
    return 1;
}
/***************************************************************************/
/* Sho tserver_tcp port list data structure.
 */
int show_port_list ( void *plist_addr, char *sub_name )
{
    int status, ndx, ret_length, j;
    struct tentry { char *name; void *addr; } tbl;
    char *sub_sub_name;
    void *listhead_addr, *listptr, *clientptr;
static char *fmt[] = {
 "\n        %08x: ", "index=%d, port=%d, status=%d, tcount=%d, stack: %x\n",
    "                 tlimit=%d, scavenge=%d, preserve=%d exp: %s",
    "                 chan=%d, iosb={%d,%d,%d,%d}, lcl: %x\n" };

    struct port_context {
        void *flink, *blink;		/* circular list of port contexts */
        int port_num;
        int status;
        pthread_t thread;		/* Listen thread ID */
        unsigned long local_ip_addr;
        short chan, fill;
	void *start;			/* Start routine address */
        int client_count;
        int delete_pending;
        void *pool;			/* client pool structure */
        void *shutdown_monitor;		/* shutdown synchronization structure */
	struct client_context {
	    void *flink, *blink, *parent;
	    void *slink;		/* scavenge list */
	    int index, port_num;
	    pthread_t thread;		/* client thread ID */
	    int status, trans_count;
	    void *stack_top;		/* Client thread stack top */
	    pthread_cond_t io_done;
	    void *start;		/* Start routine (redundant) */
	    struct timespec expiration;
	    int time_limit, scavenge_eligible, preserve;
	    short chan, dns_chan;
	    unsigned long local_ip_addr;
	    int clients_remaining;
	    short iosb[4];
	    unsigned char remote_addr[16];
	    int hn_status;		/* hostname status: 0, 1, -1 */
	    char hostname[256];
	} client_list;			/* listhead for active clients */
    } ctx, *ctxptr;
    /*
     * plist_addr initially is address of longword containing address of
     * port list listhead.
     */
    status = peek_copy ( sizeof(void *), plist_addr, &listhead_addr );
    if ( (status&1) == 0 ) return status;
    printf("Port listhead: %x, sizeof port_ctx: %d\n", listhead_addr,
	sizeof(ctx) );
    /*
     * Traverse list.
     */
    listptr = listhead_addr;
    do {
	status = peek_copy ( sizeof(ctx), listptr, &ctx );
	if ( (status&1) == 0 ) break;
	printf("\n  %08x: flink=%x, blink=%x, status=%d, port=%d\n", listptr,
		ctx.flink, ctx.blink, ctx.status, ctx.port_num );
	printf("            IP addr=%x, chan=%d, client count=%d, del.pend.=%d\n",
		ctx.local_ip_addr, ctx.chan, ctx.client_count,
		ctx.delete_pending );
	printf("            client pool=%x, client listhead: %08x %08x\n",
		ctx.pool, ctx.client_list.flink, ctx.client_list.blink );

	ctxptr = (struct port_context *) listptr;

	clientptr = &ctxptr->client_list;
	if ( ctx.pool ) while ( ctx.client_list.flink != clientptr ) {
	     printf ( fmt[0], ctx.client_list.flink );
	    status = peek_copy ( sizeof(ctx.client_list),
		ctx.client_list.flink, &ctx.client_list );
	    printf(fmt[1], ctx.client_list.index, ctx.client_list.port_num,
		ctx.client_list.status, ctx.client_list.trans_count,
		ctx.client_list.stack_top );
	    printf(fmt[2], ctx.client_list.time_limit, 
		ctx.client_list.scavenge_eligible, ctx.client_list.preserve,
		ctime((const time_t *) &ctx.client_list.expiration.tv_sec) );
	    printf(fmt[3], ctx.client_list.chan, ctx.client_list.iosb[0],
		ctx.client_list.iosb[1], ctx.client_list.iosb[2],
		ctx.client_list.iosb[3], ctx.client_list.local_ip_addr );
	    printf( "                     Remote addr:");
	    for ( j=0; j<16; j++ ) printf(" %d",ctx.client_list.remote_addr[j]);
	    printf("\n");
	}
	
	listptr = ctx.flink;
    } while ( listptr && (listhead_addr != listptr) );
    return status;
}

int main ( int argc, char **argv )
{
    int status, SYS$ASSIGN(), SYS$QIOW(), LIB$GET_EF(), ret_length;
    char *sub_name, sym_name[1024];
    /*
     * Open channel to mailbox.
     */
    mbx_chan = 0;
    if ( argc > 1 ) {
	mbx_name.dsc$w_length = strlen ( argv[1] );
	mbx_name.dsc$a_pointer = argv[1];
    }
    header_size = sizeof(message) - sizeof(message.data);
    printf("header size: %d\n", header_size);
    status = SYS$ASSIGN ( &mbx_name, &mbx_chan, sizeof(message), 0, 0, 0 );
    if ( (status&1) == 0 ) return status;
    LIB$GET_EF ( &ef );
    /*
     * Get table name to display.
     */
    if ( argc < 3 ) {
	strcpy ( sym_name, "peek_symbol_table" );	/* 'root' table */
    } else if ( strlen(argv[2]) < sizeof(sym_name) ) {
	strcpy ( sym_name, argv[2] );
    } else exit(20);

    sub_name = strstr ( sym_name, "->" );
    if ( sub_name ) {
	*sub_name++ = '\0';	/* terminate string */
	sub_name++;		/* skip '>' */
    } else sub_name = "";

   if ( *sym_name != '@' ) {
	status = peek_request ( 2, 0, sym_name, &ret_length );
	printf ( "Symbol '%s' status: %d value: %x\n", sym_name, status,
		message.ptr );

	if ( strcmp("peek_symbol_table",sym_name) == 0 ) 
		show_table ( message.ptr, sub_name );
	else if ( strcmp("http_mst_data",sym_name) == 0 ) 
	    if ( *sub_name ) show_mst_data ( message.ptr, sub_name );
	    else show_table ( message.ptr, sub_name );
	else if ( strcmp("tserver_tcp_port_list",sym_name) == 0 ) 
		show_port_list ( message.ptr, sub_name );
   }

    return 1;
}
