/*
   $Header: /netrcs/RCS/oracle/network/tns/tnsapi/RCS/tnsapifd.c,v 1.2 1995/09/11 21:49:37 mhill Exp $
*/

/* Copyright (c) 1995 by Oracle Corporation.  All rights reserved. */

/*
NAME
  tnsapifd - SQL*Net Open - finger server ("daemon") demo
*/

/* This demo implements a simple "finger" server to work with the finger
   client program in tnsapifc.c, using the SQL*Net Open API.

   This server is implemented for Unix and was tested on Solaris - it 
   simply invokes the finger program.   If you're not on Unix, you may want
   to implement something analogous to this using this program as a
   template. */

#include <tnsapi.h>

/* For non-Solaris operating systems you may need to change this pathname */
#define TNSAPIFD_FINGER_PROGRAM "/bin/finger"

static short get_string_length();
char **build_arg_array();
static void free_arg_array();

int main(argc, argv)
int argc;
char **argv;
{
    void *handle;		/* connection handle I get from tnslsnr */
    char reply[256];		/* work buffer */
    char *work;			/* pointer into work buffer */
    int rc = 0;
    int p[2];
    int length, rcvd, sent = 0;
    char **args;
    
    /* pick up connection from tnslsnr (service name is null because we're
       on server side */
    rc = tnsopen(&handle, (const char *)0);
    if (rc) goto error;

    if (pipe(p)) goto error;
    
    /* read and process the argument string.

       we get this in two pieces.  first read four bytes, which gives
       us enough information to get the length of the argument list - then
       read the argument list */
    length = 4;
    work = reply;
    
    while(length)
    {
	rcvd = length;
	rc = tnsrecv(handle, work, &rcvd);
	if (rc) goto error;
	length -= rcvd;
	work += rcvd;
    }

    length = (int)get_string_length(reply);

    *reply = '\0';
    work = reply;
    
    /* now read the argument string into the reply buffer */
    while(length)
    {
	rcvd = length;
	rc = tnsrecv(handle, work, &rcvd);
	if (rc) goto error;
	length -= rcvd;
	work += rcvd;
    }

    switch(fork())
    {
      case 0:
	/* this is the forked off process (actual finger program) */
	
	/* dup my stdout onto the pipe so that the parent process can 
	   read it */
	(void) close(p[0]);
	(void) dup2(p[1], 1);
	(void) close(p[1]);
	
	/* build an array of arguments out of the string we read */
	args = build_arg_array(reply);

	args[0] = TNSAPIFD_FINGER_PROGRAM;

	if (execv(TNSAPIFD_FINGER_PROGRAM, args) < 0) perror("execv");
	
	free_arg_array(args);
	
	exit(0);

      case -1:
	goto error;

      default:
	/* this is the parent process, read output from finger and send it */
	(void) close(p[1]);

	while ((rcvd = read(p[0], reply, sizeof(reply)) ) > 0 && rcvd)
	{
	    while (rcvd)
	    {
		sent = rcvd;
		rc = tnssend(handle, reply, &sent);
 		if (rc) goto error;
		rcvd -= sent;
	    }
	}

 	rc = tnsclose(&handle);
	if (rc) goto error;

	exit(0);
    }

  error:
    if (rc)
    {
	if (rc != RECVFAIL_TNSAPIE) (void) printf("error code %d\n", rc);
    }
    else
    {
	perror("sfingerd");
    }
    exit(1);
}

/* get length of the argument list to be read from first four bytes of 
   the data that was read.  This is the counterpart to build_arg_string in 
   tnsapifc.c */
static short get_string_length(buf)
char *buf;
{
    char *work = buf;
    short one;
    short result;
    char *resultb = (char *)&result;
    
    
    memcpy(&one, buf, 2);
    if (one == 1) 
    {
	/* same data representation */
	memcpy(&result, buf + 2, 2);
	return(result);
    }
    else
    {
	/* swap bytes */
	*(resultb) = *(buf + 3);
	*(resultb + 1) = *(buf + 2);
	return(result);
    }
}

char **build_arg_array(buf)
char *buf;
{
    char **array;
    char *work = buf;
    int entry = 0;
    
    /* allocate "enough" space, not actually sure how many entries we have */
#define max_arguments 10    
    array = (char **)malloc(4 * max_arguments);

    entry++;			/* reserve first slot for program name */
    if (*work)
    {
	array[entry++] = work++;	/* set up first pointer */
    }
    else
    {
	array[entry] = (char *)0;
	return(array);
    }
    
    for (; *work; )
    {
	if (*work == ' ')
	{
	    *work++ = '\0';
	    array[entry++] = work;
	}
	else
	{
	    work++;
	}
    }

    array[entry] = (char *)0;
    return(array);
}

void free_arg_array(args)
char **args;
{
    free(args);
    return;
}
