 /*
  * Khoros: $Id: negotiator.c,v 1.2 1992/03/20 23:37:20 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: negotiator.c,v 1.2 1992/03/20 23:37:20 dkhoros Exp $";
#endif

 /*
  * $Log: negotiator.c,v $
 * Revision 1.2  1992/03/20  23:37:20  dkhoros
 * VirtualPatch5
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "transport.h"	


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name: negotiator.c				<<<<
   >>>>								<<<<
   >>>>   description: negotiator support utility		<<<<
   >>>>								<<<<
   >>>>	     public routines:	   kstart_negotiator		<<<<
   >>>>      			   kget_negotiator		<<<<
   >>>>      			   kset_negotiator		<<<<
   >>>>				   kstop_negotiator		<<<<
   >>>>								<<<<
   >>>>      private routines:	   negotiate_open		<<<<
   >>>>      			   negotiate_close		<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



static kfile  *negotiator_reply = NULL,
	      *negotiator_request = NULL;

#define NEGOTIATOR_REPLY   "KHOROS_NEGOTIATION_REPLY"
#define NEGOTIATOR_REQUEST "KHOROS_NEGOTIATION_REQUEST"



/*
 *  The following are private routines that the user should not call but
 *  should be private to the phantom library that enables a negotiation
 *  method between processes thru some user defined negotiator.
 *
 *	routines:	kstart_negotiator
 *			kget_negotiator
 *			kset_negotiator
 *			kstop_negotiator
 */


/************************************************************
*
* Routine Name:  kstart_negotiator
*
*      Purpose:  This routine is used to initialize the negotiation
*		 transport so that we will be able to negotiate sub-
*		 processes.
*
*        Input:  none
*
*       Output:  return the result from kset_negotiator() or -1 upon failure
*
*
*   Written By:  Mark Young
*
*************************************************************/

static char *transport_list[] =
{
	"stream",
	"pipe",
	"socket"
};
#define NumList ((int) sizeof(transport_list)/sizeof(transport_list[0]))


int kstart_negotiator()
{
	int	i;
	TransportInformation *routines;
	char	template[LENGTH], *itemp = NULL, *otemp = NULL;


	/*
	 *  This strange bit of code simply generates two unique transports
	 *  one for the input (or reply) negotiation process and one for the
	 *  output (or request) negotiation process.
	 */
	for (i = 0; i < NumList; i++)
	{
	   if (!(routines = transport_routines(transport_list[i])) ||
	        routines->tempnam == NULL)
	   {
	      continue;
	   }
	   else
	   {
	      (void) sprintf(template,"%s=iPhantom", transport_list[i]);
	      itemp = ktempnam(NULL, template);
	      (void) sprintf(template,"%s=oPhantom", transport_list[i]);
	      otemp = ktempnam(NULL, template);
	   }

	   if (itemp != NULL && otemp != NULL)
	      break;
	   else if (itemp != NULL)
	      kunlink(itemp);
	   else if (otemp != NULL)
	      kunlink(otemp);
	   else
	      break;
	}

	if (itemp == NULL || otemp == NULL)
	   return(-1);

	return(kset_negotiator(itemp, otemp));
}



/**************************************************************
*
* MODULE NAME: kget_negotiator()
*
*     PURPOSE: This function gets the kfile structure associated
*	       with the transport used in negotiating with some
*	       other process.  This used by more complex routines
*	       such as cantata to communicate with sub-process
*	       so that special considerations can be made when
*	       a process needs to open, close, or a special
*	       nofication.
*
*       INPUT: none - this should really be the environment variable
*		      in which the negotiation transport is stored,
*		      but for simplisticty we will hardwire the
*		      environment variable to be:
*
*				 KHOROS_NEGOTIATION_REQUEST
*				 KHOROS_NEGOTIATION_REPLY
*
*      OUTPUT: returns the opened kfile structure or NULL if the
*	       environment variables don't exist
*
* CALLED FROM: various kfile utilities (public)
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int kget_negotiator(ifile, ofile)

kfile **ifile, **ofile;
{
	static int initialized = FALSE;
	char   *path, temp[LENGTH], *identifier, *getenv();


	if (initialized == FALSE)
	{
	   initialized = TRUE;
	   if ((path = getenv(NEGOTIATOR_REPLY)) != NULL)
	   {
	      /*
	       *  Make sure that the transport is supported before
	       *  attempting to make a connection.
	       */
	      identifier = transport_identifier(path, temp);
	      if (transport_routines(identifier) != NULL)
	         negotiator_reply = kfopen(path, "r+");
	   }

	   if ((path = getenv(NEGOTIATOR_REQUEST)) != NULL)
	   {
	      /*
	       *  Make sure that the transport is supported before
	       *  attempting to make a connection.
	       */
	      identifier = transport_identifier(path, temp);
	      if (transport_routines(identifier) != NULL)
	         negotiator_request = kfopen(path, "r+");
	   }
	}
	if (ifile != NULL) *ifile = negotiator_reply;
	if (ofile != NULL) *ofile = negotiator_request;
	return(0);
}



/**************************************************************
*
* MODULE NAME: kset_negotiator()
*
*     PURPOSE: This function takes a new transport and stores it
*	       as the global.
*
*       INPUT: path - this is the negotiator transport stored as
*		      the transport identifier.
*
*      OUTPUT: returns true or false depending on whether we were
*	       able
*
* CALLED FROM: various kfile utilities (public)
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int kset_negotiator(ipath, opath)

char	*ipath, *opath;
{
	kfile   *itemp, *otemp;
	char	buffer[LENGTH];



	/*
	 *  Open the new input negotiation transport.  If we suceed then go
	 *  ahead and close the old transport and update this transport
	 *  as the new one. 
	 */
	if (!(itemp = kfopen(ipath, "R+")) || !(otemp = kfopen(opath, "R+")))
	{
	   if (itemp != NULL) kfclose(itemp);
	   else if (otemp != NULL) kfclose(otemp);
	   return(-1);
	}

	/*
	 *  Stop the old negotiation transport and add our new ones.
	 */
	kstop_negotiator();

	/*
	 *  Now set the environment variables so that sub-processes
	 *  will talk with us, but first remember to switch the
	 *  input and output so that we write output to the sub-processes
	 *  input and read input from the sub-processes output.
	 */
	negotiator_reply = otemp;
	(void) sprintf(buffer, "%s=%s", NEGOTIATOR_REPLY, ipath);
	vputenv(VStrcpy(buffer));

	negotiator_request = itemp;
	(void) sprintf(buffer, "%s=%s", NEGOTIATOR_REQUEST, opath);
	vputenv(VStrcpy(buffer));

	return(0);
}



/**************************************************************
*
* MODULE NAME: kstop_negotiator()
*
*     PURPOSE: This function stops the negotiation process by
*	       actually closing the transport and removing it
*	       from the user's environment variables.
*
*       INPUT: none
*
*      OUTPUT: returns true or false depending on whether we were
*	       able stop the negotiator
*
* CALLED FROM: various kfile utilities (public)
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int kstop_negotiator()
{
	char  *filename;
	kfile *ifile, *ofile;


	if (kget_negotiator(&ifile, &ofile) == -1)
	   return(-1);

	if (ifile != NULL)
	{
	   kfclose(ifile);
	   if (!(filename = getenv(NEGOTIATOR_REPLY))) kunlink(filename);
	   vremenv(NEGOTIATOR_REPLY);
	}

	if (ofile != NULL)
	{
	   kfclose(ofile);
	   if (!(filename = getenv(NEGOTIATOR_REQUEST))) kunlink(filename);
	   vremenv(NEGOTIATOR_REQUEST);
	}
	return(0);
}



/*
 *  The following are private routines that the user should not call but
 *  should be private to the phantom library that enables a negotiation
 *  method between processes thru some user defined negotiator.
 *
 *	routines:	negotiate_open
 *			negotiate_close
 */



/**************************************************************
*
* MODULE NAME: negotiate_open()
*
*     PURPOSE: This function is used to call the negotiator
*	       if one exists.  The negotiator is informed
*	       that the this process will be opening this process.
*
*       INPUT: path - the path that negotiator should be informed of
*
*      OUTPUT: returns the negotiated path or the original
*
* CALLED FROM: various kfile utilities
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int negotiate_open(path, newpath)

char	*path;
char	*newpath;
{
	int   result;
	kfile *ifile, *ofile;
	char  buffer[LENGTH];


	if (newpath != NULL && path != newpath)
	   strcpy(path, newpath);

	if (kget_negotiator(&ifile, &ofile) == -1 || !ifile || !ofile)
	   return(0);

	(void) sprintf(buffer, "%d: open '%s'\n", getpid(), path);

	(void) kflock(ifile->id, LOCK_EX);
	if (kfputs(buffer, ofile) != EOF)
	{
	   if (kfgets(buffer, LENGTH, ifile) != NULL)
	   {
	      vlower_string(buffer, buffer);
	      if (strcmp(buffer, "yes") == 0)
	         result = 0;
	      else
	         result = -1;
	   }
	}
	else
	   result = 0;

	(void) kflock(ifile->id, LOCK_UN);
	return(result);
}



/**************************************************************
*
* MODULE NAME: negotiate_close()
*
*     PURPOSE: This function is used to call the negotiator
*	       if one exists.  The negotiator is informed
*	       that this process has closed this transport path.
*
*       INPUT: path - the path that negotiator should be informed of
*
*      OUTPUT: returns the negotiated path or the original
*
* CALLED FROM: various kfile utilities
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int negotiate_close(path)

char	*path;
{
	int   result;
	kfile *ifile, *ofile;
	char  buffer[LENGTH];


	if (kget_negotiator(&ifile, &ofile) == -1 || !ifile || !ofile)
	   return(0);

	(void) sprintf(buffer, "%d: close '%s'\n", getpid(), path);

	(void) kflock(ifile->id, LOCK_EX);
	if (kfputs(buffer, ofile) != EOF)
	{
	   if (kfgets(buffer, LENGTH, ifile) != NULL)
	   {
	      vlower_string(buffer, buffer);
	      if (strcmp(buffer, "yes") == 0)
	         result = 0;
	      else
	         result = -1;
	   }
	}
	else
	   result = 0;

	(void) kflock(ifile->id, LOCK_UN);
	return(result);
}
