 /*
  * Khoros: $Id: distribute.c,v 1.3 1992/03/20 22:44:03 dkhoros Exp $
  */

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

 /*
  * $Log: distribute.c,v $
 * Revision 1.3  1992/03/20  22:44:03  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 "cantata.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name:  distribute.c				<<<<
   >>>>								<<<<
   >>>>   description:						<<<<
   >>>>								<<<<
   >>>>      routines:  xvl_get_machine()			<<<<
   >>>>      		xvl_add_machine()			<<<<
   >>>>      		xvl_update_machine()			<<<<
   >>>>								<<<<
   >>>>			xvl_change_machine()			<<<<
   >>>>			xvl_remote_machines()			<<<<
   >>>>			xvl_machine_icon()			<<<<
   >>>>			xvl_start_phantomd()			<<<<
   >>>>			xvl_stop_phantomd()			<<<<
   >>>>								<<<<
   >>>>			xvl_get_machname()			<<<<
   >>>>			xvl_get_machlabel()			<<<<
   >>>>			xvl_get_machinfo()			<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */

#define ALLOCSIZE 100


static MachineInfo *machine_list = NULL;
static char	   **machine_pixmaps = NULL;


/************************************************************
*
* Routine Name: xvl_get_machine
*
*      Purpose: retrieves the machine information used for
*		scheduling glyph's to be executed on remote
*		machines. 
*
*        Input: machine_name - the machine's name or ip address
*
*	Output: returns the machine information or NULL if
*		the machine is not valid or could not be
*		initialized.
*
*
*   Written By: Mark Young
*
*************************************************************/


MachineInfo *xvl_get_machine(machine_name)

char *machine_name;
{
	MachineInfo *temp;
	char	    name[MaxLength], hostname[MaxLength];


	/*
	 *  Make sure that we have a valid name to look for
	 */
	if (machine_name == NULL)
	   return(NULL);
	else if (vcleanup_string(machine_name, name) == NULL)
	   return(NULL);

	if (kgethostname(name, hostname, MaxLength) == -1)
	   strcpy(hostname, name);

	temp = machine_list;
	while (temp != NULL)
	{
	   if (strcmp(temp->name, name) == 0)
	      return(temp);
	   else if (strcmp(temp->hostname, hostname) == 0)
	      return(temp);

	   temp = temp->next;
	}
	temp = xvl_add_machine(machine_name);
	return(temp);
}



/************************************************************
*
* Routine Name: xvl_add_machine
*
*      Purpose: initialize the machine and add it to the machine
*		list so that it can be used later.
*
*        Input: machine_name - the machine's name or ip address
*
*	Output: returns the machine information or NULL if
*		the machine is not valid or could not be
*		initialized.
*
*
*   Written By: Mark Young
*
*************************************************************/


MachineInfo *xvl_add_machine(machine_name)

char *machine_name;
{
	MachineInfo *temp;

	int	    machtype;
	char	    error[MaxLength], name[MaxLength], hosttype[MaxLength],
		    hostname[MaxLength];


	/*
	 *  Make sure that we have a valid name to look for
	 */
	if (machine_name == NULL)
	   return(NULL);
	else if (vcleanup_string(machine_name, name) == NULL)
	   return(NULL);


	if ((temp = (MachineInfo *) calloc(1, sizeof(MachineInfo))) == NULL)
	{
           (void) sprintf(error,"Not enough memory!  Tried to malloc %d \
bytes.", sizeof(MachineInfo));
           xvf_error_wait(error, "xvl_add_machine", NULL);
	   return(NULL);
	}

	/*
	 *   Get the official hostname for the supplied machine name.
	 */
	if (kgethostname(name, hostname, MaxLength) == -1)
	   strcpy(hostname, name);

	temp->name     = xvf_strcpy(name);
	temp->hostname = xvf_strcpy(hostname);
	temp->next     = machine_list;
	machine_list   = temp;

	if ((machtype = kmachtype(hostname, hosttype)) == -1)
	{
	   temp->active = False;
/*
           (void) sprintf(error,"Unable to contact machine:\n\n '%s'",hostname);
           xvf_error_wait(error, "xvl_add_machine", NULL);
 */
	}
	else
	{
	   temp->active   = True;
	   temp->machtype = machtype;
	   temp->hosttype = xvf_strcpy(hosttype);
	}
	(void) xvl_machine_icon(temp);
	return(temp);
}



/************************************************************
*
* Routine Name: xvl_update_machine
*
*      Purpose: update the machine type and whether the machine
*		is currently active.
*
*        Input: machine_info - the machine information
*
*	Output: returns whether the machine is currently active
*		or not.
*
*
*   Written By: Mark Young
*
*************************************************************/


int xvl_update_machine(info)

MachineInfo *info;
{
	int	    machtype;
	char	    hostname[MaxLength], hosttype[MaxLength], error[MaxLength];


	if ((machtype = kmachtype(info->hostname, hosttype)) == -1)
	{
	   info->active = False;
	   if (info->hosttype != NULL)
	      free(info->hosttype);
	   info->hosttype = NULL;

           (void) sprintf(error,"The following machine is currently \
unreachable:\n\n '%s'",hostname);
           xvf_error_wait(error, "xvl_update_machine", NULL);
	}
	else
	{
	   info->active   = True;
	   info->machtype = machtype;
	   info->hosttype = xvf_strcpy(hosttype);
	}
	if (info->machine_icon != None)
	   XFreePixmap(display, info->machine_icon);

	(void) xvl_machine_icon(info);
	return(info->active);
}



/************************************************************
*
* Routine Name: xvl_change_machine
*
*      Purpose: Change the machine in which the glyphs will be running
*
*	 Input: glyphlist - the glyphs to be changed
*
*	Output: none
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_change_machine(glyphlist)

GlyphList *glyphlist;
{
	char	hostname[MaxLength], temp[MaxLength];
	char	*prompt = "Choose machine in which to execute the Glyph(s):";
	char	*label = "Local and Remote machines:";

	int	    i;
	MachineInfo *info;
	Glyph	    *glyph;
	static char **machines;
	char  *local_machine[2], *machine;
	static int  num, lnum = 0, initialized = False;


	/*
	 *  Make sure that there is at least one glyph to remote.
	 */
	if (glyphlist == NULL)
	   return;

	/*
	 *  Only initialize the bitmap list once.
	 */
	if (initialized == False)
	{
	   local_machine[lnum++] = xvf_strcpy("localhost");
	   if (kgethostname(NULL, hostname, MaxLength) == 0)
	      local_machine[lnum++] = xvf_strcpy(hostname);

	   initialized = True;
	   if ((machines = khost_list(&num)) != NULL)
	   {
	      machines = vmergelist(local_machine, machines, lnum, num, True);
	      num += lnum;
	   }
	   else
	   {
	      machines = vcopylist(local_machine, lnum, True);
	      num = lnum;
	   }
	}

	/*
	 *  Get the remote machine structure in which to execute the glyph.
	 */
	if ((machine = xvl_distribute_menu(label, prompt, machines, num))!=NULL)
	{
	   for (i = 0; i < lnum; i++)
	   {
	      /*
	       *  Look to see if they specified a local machine.
	       */
	      if (VStrcmp(machine, machines[i]) == 0)
	      {
		 info = NULL;
		 break;
	      }
	   }

	   if (i == lnum)
	      info = xvl_get_machine(machine);

	   while (glyphlist != NULL)
	   {
	      glyph = glyphlist->glyph;
	      if (info != glyph->machine)
	      {
		 /*
		  *  Clear the old label before it gets redrawn by the the
		  *  xvl_update_distributed() routine.
		  */
	         (void) xvl_clear_label(glyph, xvl_get_machlabel(glyph, temp));

		 /*
		  *  Set the new machine info so that we know where we are
		  *  supposed to remote to.
		  */
	         glyph->machine = info;
		 if (glyph->type == PROCEDURE)
		    glyph->val.macro->machine = info;

	         /*
	          *  Update the fact the glyph has been modified to run an
	          *  another machine.
	          */
	         xvl_update_distributed(glyph);

		 /*
		  *  Indicate that the glyph should be re-run (modified)
		  *  for execution.  If the glyph is a procedure then
		  *  we really should reset only the glyph's that aren't
		  *  explicitly scheduled to execute to a different machine.
		  */
	         glyph->modified = True;
	         xvl_update_modified(glyph);
		 if (glyph->type == PROCEDURE)
		    xvl_reset_workspace(glyph->val.macro);
	      }
	      glyphlist = glyphlist->next;
	   }
	}
}



/************************************************************
*
* Routine Name: xvl_remote_machines
*
*      Purpose: initialize the list of machine bitmaps to be used
*		using the following information: 
*
*	 Input: none
*
*	Output: returns the list of current machines
*		num_entries - the number of machines
*
*
*   Written By: Mark Young
*
*************************************************************/


char **xvl_remote_machines(num_entries)

int  *num_entries;
{
	int	i, num = ALLOCSIZE;
        char	**list;
	MachineInfo *machines;


	if (machine_list == NULL)
	{
	   *num_entries = 0;
	   return(NULL);
	}

	if ((list = (char **) kmalloc(num * sizeof(char *))) == NULL)
	{
           (void) fprintf(stderr,"distribute_list:  Not enough memory....\n\n");
           (void) fprintf(stderr,"  Unable to malloc (%d) bytes for the khoros \
distributed machine list.\n", num*sizeof(char *));
           return(NULL);
        }

	machines = machine_list; i = 0;
        while (machines != NULL)
        {
            if (i >= num)
	    {
               num += ALLOCSIZE;
               if (!(list = (char **) krealloc(list, num * sizeof(char *))))
               {
                  fprintf(stderr,"transport_list:  Not enough memory..\n\n");
                  fprintf(stderr,"  Unable to malloc (%d) bytes for the \
khoros transport list.\n", num*sizeof(char *));
                     return(NULL);
               }
            }
            list[i++] = machines->hostname;
            machines = machines->next;
        }
        *num_entries = i;
        return(list);
}



/************************************************************
*
* Routine Name: xvl_machine_icon
*
*      Purpose: initialize the list of machine bitmaps to be used
*		using the following information: 
*
*        	hostname - the machine's name or ip address
*		hosttype - a description of the type of machine
*		machtype - the type of machine
*
*	 Input: machine_info - the machine information structure
*			       in which the above information
*			       will be used to retrieve an appropriate
*			       machine icon.
*
*	Output: returns whether we were able to initialize an icon or not.
*
*
*   Written By: Mark Young
*
*************************************************************/


int xvl_machine_icon(info)

MachineInfo *info;
{
	int    i;
	static char **alias_list;
	char   *bitmap_file, **vlistfile();
	static int num_alias, initialized = False;


	/*
	 *  Only initialize the bitmap list once.
	 */
	if (initialized == False)
	{
	   initialized = True;
	   alias_list = vlistfile(wresource.icon_alias_file,
		"$KHOROS_HOME/repos/cantata/distribute", True, &num_alias);
	}

	bitmap_file = NULL;
	for (i = 0; i < num_alias; i += 2)
	{
	   if (info->hostname != NULL && alias_list[i] != NULL)
	   {
	      if (strcmp(info->hostname, alias_list[i]) == 0)
	      {
		 bitmap_file = alias_list[i+1];
		 continue;
	      }
	   }

	   if (info->hosttype != NULL && alias_list[i] != NULL)
	   {
	      if (strcmp(info->hosttype, alias_list[i]) == 0)
	      {
		 bitmap_file = alias_list[i+1];
		 continue;
	      }
	   }
	}

	if (bitmap_file != NULL)
	{
	   if (!(info->machine_icon = xvl_create_bitmap(bitmap_file)))
	      return(False);
	   else
	      return(True);
	}
	return(False);
}



/************************************************************
*
* Routine Name: xvl_start_phantomd
*
*      Purpose: Starts the local phantom daemon so that
*
*	 Input: none
*
*	Output: sets the khoros_phantomd_pid to the local daemon pid
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_start_phantomd()
{
	char	command[MaxLength];


	(void) sprintf(command, "phantomd -daemon false");
	if (!(khoros_phantomd_pid = xvf_fork(command, xvl_phantomd_cb, NULL)))
	{
	   xvf_error_wait("Unable to start the 'phantomd' program.  This \
program is the daemon responsible for doing distributed processing or execing \
glyphs on remote machines.", "xvl_start_phantomd", NULL);
	}
}



/************************************************************
*
* Routine Name: xvl_stop_phantomd
*
*      Purpose: Stops the local phantom daemon
*
*	 Input: none
*
*	Output: sets the khoros_phantomd_pid to the local daemon pid
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_stop_phantomd()
{
	if (khoros_phantomd_pid > 0)
	{
#ifdef DEBUG
           (void) fprintf(stderr,"kill phantomd for pid %d = %d\n",
                khoros_phantomd_pid, kill(khoros_phantomd_pid, SIGINT));
#else
           (void) kill(khoros_phantomd_pid, SIGINT);
#endif
	}
}




/************************************************************
*
* Routine Name: xvl_get_machname
*
*      Purpose: get the name of the machine in which this
*		glyph will be executed.
*
*	 Input: glyph -
*		name  -
*
*	Output: returns the name or NULL
*
*
*   Written By: Mark Young
*
*************************************************************/


char *xvl_get_machname(glyph, name)

Glyph	*glyph;
char	*name;
{
	MachineInfo  *info;


	if ((info = xvl_get_machinfo(glyph)) == NULL)
	   return(NULL);

	if (name == NULL)
	   return(xvf_strcpy(info->hostname));
	else
	   strcpy(name, info->hostname);

	return(name);
}



/************************************************************
*
* Routine Name: xvl_get_machlabel
*
*      Purpose: get the label of the machine in which this
*		glyph will be executed.
*
*	 Input: none
*
*	Output: returns the name or NULL
*
*
*   Written By: Mark Young
*
*************************************************************/


char *xvl_get_machlabel(glyph, label)

Glyph	*glyph;
char	*label;
{
	MachineInfo  *info;
	char	     temp[MaxLength], domain[MaxLength], *machlabel;


	if ((info = xvl_get_machinfo(glyph)) == NULL)
	   return(NULL);

	if (current_domain != NULL)
	{
	   (void) sprintf(domain, ".%s", current_domain);
	   machlabel = vreplace_string(info->hostname, domain, "", temp);
	   if (machlabel == NULL)
	      machlabel = info->hostname;
	}
	else
	   machlabel = info->hostname;

	if (label)
	   return(xvf_strcpy(machlabel));
	else
	   strcpy(label, machlabel);

	return(label);
}



/************************************************************
*
* Routine Name: xvl_get_machinfo
*
*      Purpose: get the machine info for which this
*		glyph will be executed.
*
*	 Input: none
*
*	Output: returns the name or NULL
*
*
*   Written By: Mark Young
*
*************************************************************/


MachineInfo *xvl_get_machinfo(glyph)

Glyph	*glyph;
{
	MachineInfo  *info;
	Workspace    *attributes;


	/*
	 *  Check to see if remote_execution is enabled
	 */
	attributes = xvl_get_attributes(glyph->workspace);
	if (attributes->remote_execution == False)
	   return(NULL);

	if (glyph->machine != NULL)
	   return(glyph->machine);

	/*
	 *  Really need to see if this glyph is part of a sub-loop and if
	 *  so then if the control loop is set to be remotely executed.
	 */
	info = glyph->workspace->machine;
	return(info);
}
