 /*
  * Khoros: $Id: Socket.c,v 1.4 1992/03/25 17:30:49 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: Socket.c,v 1.4 1992/03/25 17:30:49 dkhoros Exp $";
#endif

 /*
  * $Log: Socket.c,v $
 * Revision 1.4  1992/03/25  17:30:49  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 */


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name: Socket.c					<<<<
   >>>>								<<<<
   >>>>   description: Khoros socket (bsd) routines		<<<<
   >>>>								<<<<
   >>>>      routines: socket_tempnam				<<<<
   >>>>		       socket_open				<<<<
   >>>>		       socket_close				<<<<
   >>>>		       socket_read				<<<<
   >>>>		       socket_write				<<<<
   >>>>		       socket_lseek				<<<<
   >>>>		       socket_tell				<<<<
   >>>>		       socket_access				<<<<
   >>>>		       socket_unlink				<<<<
   >>>>		       socket_lock				<<<<
   >>>>		       socket_descriptors			<<<<
   >>>>		       socket_exec				<<<<
   >>>>								<<<<
   >>>>		       socket_daemon				<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


#ifdef KSOCKET

#include "internals.h"	
#include "vsignal.h"	
#include "Socket.h"	


static SocketList *socket_list = NULL;

/**************************************************************
*
* MODULE NAME: socket_tempnam
*
*     PURPOSE: This function initializes a tempnam for a "socket"
*	       type transport.  Given a request with a filename of
*	       the following syntax:
*
*			"socket=XXXXXXXXXX"
*
*
*       INPUT: 
*
*
*      OUTPUT:  depending whether we sucessfully created the template
*      		returns 0 or -1 if an error occurs
*
* CALLED FROM:  internal routine called from ktempnam()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_tempnam(identifier, template, result)

char *identifier;
char *template;
char *result;
{
	SocketList  *temp;
	int     i, sock, port;
	struct	sockaddr_in server;



	/*
	 *  Create either a local UNIX or TCP connection based socket
	 */
	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
	{
	   perror("socket_tempnam:  Creation failure....");
	   return(-1);
	}

	for (port = -1, i = 1024; i < 5000; i++)
	{
	   /*
	    *  Assign our server's address and then bind to the returned
	    *  socket "server_sock".
	    */
	   bzero((char *) &server, sizeof(server));
	   server.sin_port		= htons(i);
	   server.sin_family		= AF_UNIX;
	   server.sin_addr.s_addr	= htonl(INADDR_ANY);
	   if (bind(sock, (struct sockaddr *) &server, sizeof(server)) >= 0)
	   {
	      port = i;
	      break;
	   }
	}

	/*
	 *  Check to see if any port exists
	 */
	if (port == -1)
	{
	   close(sock);
	   errno = EAGAIN;
	   perror("socket_tempnam:  Creation failure....");
	   return(-1);
	}

	if ((temp = (SocketList *) calloc(1, sizeof(SocketList))) == NULL)
	{
	   (void) fprintf(stderr,"socket_tempnam:  Not enough memory....\n\n");
	   (void) fprintf(stderr,"  Unable to malloc (%d) bytes for the khoros \
temporary socket list structure.\n", sizeof(SocketList));
	   close(sock);
	   return(-1);
	}
	temp->socket = sock;
	temp->port   = port;
	temp->next   = socket_list;
	socket_list  = temp;

	if (identifier == NULL)
	   (void) sprintf(result, "%d", port);
	else
	   (void) sprintf(result,"%s=%d", identifier, port);
	return(0);
}



/**************************************************************
*
* MODULE NAME: socket_open
*
*     PURPOSE: This function opens a "socket" connection.  It is the
*	       internal driver to open a file, which is called by
*	       the user when they call kopen().  kopen() calls when
*	       it gets an open request with a filename of the following
*	       syntax:
*
*			"filename"
*			"/path/filename"
*			"./path/filename"
*			"file=XXXXXXXXXX"
*			     ...etc....
*
*
*       INPUT: 
*
*        
*      OUTPUT:  returns 0 or -1 if an error occurs
*
* CALLED FROM:  internal routine called from kopen()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_open(path, flags, mode, file)

char *path;
int  flags;
int  mode;
kfile *file;
{
	ResourceStruct *resources;
	int   num, socket, port;
	char  hostname[LENGTH], filename[LENGTH], command[LENGTH],
	      error[LENGTH], type[LENGTH];



	/*
	 *  parse the socket argument
	 */
	if (socket_parseline(path, hostname, filename, &port) < 0)
	   return(-1);

	/*
	 *  Connect to the socket
	 */
	if ((socket = socket_connect(port, hostname)) < 0)
	   return(-1);

	/*
	 *  Create the socket structure.
	 */
	if (!(resources=(ResourceStruct *) calloc(1, sizeof(ResourceStruct))))
	{
	   (void) fprintf(stderr,"socket_open:  Not enough memory....\n\n");
	   (void) fprintf(stderr,"  Unable to malloc (%d) bytes for the khoros \
socket structure.\n", sizeof(ResourceStruct));
	   return(-1);
	}
	resources->socket = socket;
	file->resources = (caddr_t) resources;

	/*
	 *  open: 'filename' flags mode
	 */
	(void) fopen_flags(flags, type);
	(void) sprintf(command,"open: '%s' '%s' %d\n", filename, type, mode);

	if (socket_writeline(socket, command, 0) <= 0)
	   return(-1);

	if (socket_readline(socket, command, LENGTH) <= 0)
	{
	   (void) fprintf(stderr,"socket_open:  Error reading response from \
remote worker...\n");
	   return(-1);
	}

	vlower_string(command, command);
	if (strncmp(command, "yes", 3) == 0)
	   return(0);
	else if (strncmp(command, "no", 2) == 0)
	{
	   sprintf(error, "socket_open:  Open failure for opening file '%s' on \
machine '%s'", filename, hostname);
	   errno = EACCES;
	   perror(error);
	   socket_close(file);
	   return(-1);
	}
	else
	{
	   (void) fprintf(stderr,"socket_open:  Unknown response from \
remote worker...\n\n(%s).\n\n", command);
	   return(-1);
	}
}



/**************************************************************
*
* MODULE NAME: socket_close
*
*     PURPOSE: This function closes a "socket".  It is the internal driver
*	       to close a data socket, which is called by the user
*	       when they call kclose().  kclose() calls the "socket" internal
*	       drivers by the nature of the transport, which is dictated
*	       by the initial kopen().
*
*
*       INPUT:  file - the kfile structure.
*
*      OUTPUT:  returns whether we were able to close the socket
*
* CALLED FROM:  internal routine called from kclose()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_close(file)

kfile *file;
{
	int	     socket, status;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	/*
	 *  Simply call close() since "kfile" is just a regular file
	 *  descriptor.
	 */
	socket = resources->socket;
	status = close(socket);
	return(status);
}



/**************************************************************
*
* MODULE NAME: socket_read
*
*     PURPOSE: This function reads a "socket".  It is the internal driver
*	       to read data from a socket, which is called by the user
*	       when they call kread().  kread() calls the "socket" internal
*	       drivers by the nature of the transport, which is dictated
*	       by the kopen().
*
*
*       INPUT:  file   - the kfile structure.
*		ptr    - the pointer to store the data into.
*		nbytes - the number of bytes to read.
*
*      OUTPUT:  returns the number of bytes read from the file
*
* CALLED FROM:  internal routine called from kread()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_read(file, ptr, nbytes)

kfile *file;
char  *ptr;
int   nbytes;
{
	int	     socket, count;
	int	     numread = 0;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	/*
	 *  Simply call read() since "kfile" is just a regular file
	 *  descriptor.
	 */
	socket = resources->socket;
	do
	{
	   count = MIN(nbytes - numread, DefaultPageSize);
	   count = read(socket, (char *) (ptr + numread), count);
	   numread += count;
	}
	while (numread < nbytes && count > 0);

	return(numread);
}



/**************************************************************
*
* MODULE NAME: socket_write
*
*     PURPOSE: This function writes to a "socket".  It is the internal driver
*	       to write data from the supplied data array to a socket, which
*	       is called by the user when they call kwrite().  kwrite() calls
*	       the "socket" internal drivers by the nature of the transport,
*	       which is dictated by the kopen().
*
*
*       INPUT:  file   - the kfile structure.
*		ptr    - the pointer to write the data from.
*		nbytes - the number of bytes to read.
*
*      OUTPUT:  returns the number of bytes written to the kfile
*
* CALLED FROM:  internal routine called from kread()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_write(file, ptr, nbytes)

kfile *file;
char  *ptr;
int   nbytes;
{
	int	     socket, count;
	int	     numwrite = 0;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	/*
	 *  Simply call write() since "kfile" is just a regular file
	 *  descriptor.
	 */
	socket = resources->socket;
	do
	{
	    count = MIN(nbytes - numwrite, DefaultPageSize);
	    count = write(socket, (char *) (ptr + numwrite), count);
	    numwrite += count;
	} while (numwrite < nbytes && count > 0);

	return(numwrite);
}



/**************************************************************
*
* MODULE NAME: socket_lseek
*
*     PURPOSE: This function is used to do a "lseek".  It is the internal
*	       driver to rewind to a specific point so that the data can be
*	       skipped or re-read.  This is called when the user calls
*	       klseek().  klseek() calls the "socket" internal drivers by the
*	       nature of the transport, which is dictated by the kopen().
*
*
*       INPUT:  file   - the kfile structure.
*		offset - the offset in which to seek
*		whence - the control of how the offset will be applied
*
*      OUTPUT:  returns the number of bytes written to the kfile
*
* CALLED FROM:  internal routine called from kread()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_lseek(file, offset, whence)

kfile *file;
int   offset;
int   whence;
{
	int	     socket, pos;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	/*
	 *  Simply call lseek() since "kfile" is just a regular file
	 *  descriptor.
	 */
	socket = resources->socket;
	pos = lseek(socket, offset, whence);
	return(pos);
}



/**************************************************************
*
* MODULE NAME: socket_tell
*
*     PURPOSE: This function is used to do a "lseek".  It is the internal
*	       driver to rewind to a specific point so that the data can be
*	       skipped or re-read.  This is called when the user calls
*	       ktell().  ktell() calls the "socket" internal drivers by the
*	       nature of the transport, which is dictated by the kopen().
*
*
*       INPUT:  file   - the kfile structure.
*		offset - the offset in which to seek
*		whence - the control of how the offset will be applied
*
*      OUTPUT:  returns the number of bytes written to the kfile
*
* CALLED FROM:  internal routine called from kread()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_tell(file)

kfile *file;
{
	int	     socket, pos;
	ResourceStruct *resources = (ResourceStruct *) file->resources;

	/*
	 *  Simply call tell() since "kfile" is just a regular file
	 *  descriptor.
	 */
	socket = resources->socket;
	pos = tell(socket);
	return(pos);
}



/**************************************************************
*
* MODULE NAME: socket_access
*
*     PURPOSE: This function is used to do see if a file is accessable.
*	       This is called when the user calls kaccess().  kaccess()
*	       calls the "socket" internal drivers by the nature of the
*	       transport, which is dictated by the transport_identifier().
*
*
*       INPUT:  path     - the initial path.
*		filename - the filename that they wish to access
*		mode     - the access mode information
*
*      OUTPUT:  returns the result
*
* CALLED FROM:  internal routine called from kaccess()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_access(path, filename, mode)

char *path;
char *filename;
int  mode;
{
	kfile file;
	int   num, socket, port;
	char  hostname[LENGTH], command[LENGTH];


	/*
	 */
	file.path = VStrcpy(path);
	file.flags = 0;

	/*
	 *  parse the socket argument
	 */
	if (socket_parseline(path, hostname, filename, &port) < 0)
	   return(-1);

	/*
	 *  Connect to the socket
	 */
	if ((socket = socket_connect(port, hostname)) < 0)
	   return(-1);

	/*
	 *  access: 'filename'
	 */
	(void) sprintf(command, "access: '%s'\n", filename);
#ifdef DEBUG
(void) fprintf(stderr, "%s", command);
#endif

	if (socket_writeline(socket, command, 0) <= 0)
	   return(-1);

	if (socket_readline(socket, command, LENGTH) <= 0)
	{
	   (void) fprintf(stderr,"socket_access:  Error reading response from \
remote worker...\n");
	   return(-1);
	}
	(void) close(socket);
	vlower_string(command, command);

	if (strncmp(command, "yes", 3) == 0)
	   return(0);
	else if (strncmp(command, "no", 2) == 0)
	   return(-1);
	else
	{
	   (void) fprintf(stderr,"socket_access:  Unknown response from \
remote worker...\n\n(%s).\n\n", command);
	   return(-1);
	}
}



/**************************************************************
*
* MODULE NAME: socket_unlink
*
*     PURPOSE: This function initializes a tempnam for a "socket"
*	       type transport.  Given a request with a filename of
*	       the following syntax:
*
*			"socket=XXXXXXXXXX"
*
*       INPUT: 
*
*        
*      OUTPUT:  returns 0 or -1 depending whether we
*		sucessfully created the template
*
* CALLED FROM:  internal routine called from kunlink()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_unlink(identifier, filename)

char *identifier;
char *filename;
{
	int	port;
	SocketList *current, *last;


	if (sscanf(filename,"%d", &port) != 1)
	   return(-1);

	last = current = socket_list;
	while (current != NULL)
	{
	   if (current->port == port)
	      break;

	   last = current;
	   current = current->next;
	}

	if (current != NULL)
	{
	   if (socket_list == current)
	      socket_list = socket_list->next;
	   else
	      last->next = current->next;

	   (void) close(current->socket);
	   free(current);
	}
	return(0);
}



/**************************************************************
*
* MODULE NAME: socket_lock
*
*     PURPOSE: This function locks a "socket" transport.  Given
*	       a request with a filename of the following syntax:
*
*			"socket=xxxxx"
*
*       INPUT: 
*
*        
*      OUTPUT:  returns 0 or -1 depending whether we
*		sucessfully created the template
*
* CALLED FROM:  internal routine called from kflock()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_lock(file, operation)

kfile *file;
int   operation;
{
	int	status;
	ResourceStruct *resources = (ResourceStruct *) file->resources;

	return(0);
}



/**************************************************************
*
* MODULE NAME: socket_descriptors
*
*     PURPOSE: This function shouldn't exist, but there is occasion
*	       when the user wants to know the actual file descriptor
*	       associated with the file descriptor.
*
*       INPUT: file structure
*
*        
*      OUTPUT:  inum - the input file descriptor
*
* CALLED FROM:  internal routine called from kdescriptros()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_descriptors(file, inum, onum)

kfile *file;
int   *inum, *onum;
{
	int	status;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	if (inum != NULL) *inum = resources->socket;
	if (onum != NULL) *onum = resources->socket;
	return(0);
}



/*********************************************************************
*
*		Remote Socket Routines (client)
*
*********************************************************************/

/**************************************************************
*
* MODULE NAME: socket_exec
*
*     PURPOSE: This function is used to exec a process thru a
*	       stream.
*
*       INPUT: 
*
*        
*      OUTPUT:  returns 0 or -1 depending whether we
*		sucessfully created the template
*
* CALLED FROM:  internal routine called from kexec() or ksystem() or
*		kfork(), etc.
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_exec(routine, arguments)

char	*routine;
char	*arguments[];
{
	ResourceStruct *resources;

	kfile  file;
	int    socket, i, num, port;
	fd_set readfd, writefd, exceptfd;
	char   arg0[LENGTH], **args, data[10000], hostname[LENGTH],
	       command[5*LENGTH], temp[5*LENGTH];


	/*
	 *  Find the machine in which to execute this process
	 */
	file.path = VStrcpy(routine);
	file.mode  = file.flags = 0;


	/*
	 *  parse the socket argument
	 */
	if (socket_parseline(arguments[0], hostname, arg0, &port) < 0)
	   return(-1);

	/*
	 *  Connect to the socket
	 */
	if ((socket = socket_connect(port, hostname)) < 0)
	   return(-1);

	/*
	 *  Create the socket structure.
	 */
	if (!(resources = (ResourceStruct *) calloc(1, sizeof(ResourceStruct))))
	{
	   (void) fprintf(stderr,"socket_exec:  Not enough memory....\n\n");
	   (void) fprintf(stderr,"  Unable to malloc (%d) bytes for the khoros \
socket structure.\n", sizeof(ResourceStruct));
	   return(-1);
	}

	resources->socket = socket;
	file.resources = (caddr_t) resources;

	/*
	 *  exec: 'arg0 arguments'
	 */
	args = &arguments[1];
	build_command(args, temp);
	if (temp[0] != '\0')
	   (void) sprintf(command, "exec: '%s %s'", arg0, temp);
	else
	   (void) sprintf(command, "exec: '%s'", arg0);
	strcat(command,"\n");

#ifdef DEBUG
(void) fprintf(stderr, "%s", command);
#endif

	if (socket_writeline(socket, command, 0) <= 0) 
	   return(-1);

	while (1)
	{
	   FD_ZERO(&readfd); FD_ZERO(&writefd); FD_ZERO(&exceptfd);
	   FD_SET(0, &readfd); FD_SET(socket, &readfd);

	   i = select(sizeof(fd_set), &readfd, NULL, NULL, NULL);
	   if (i == -1)
	   {
	      perror("socket_exec: select error.");
	      break;
	   }

	   if (FD_ISSET(0, &readfd) != 0)
	   {
	      if ((num = read(0, temp, 5*LENGTH)) > 0)
		 (void) write(socket, temp, num);
	   }

	   if (FD_ISSET(socket, &readfd) != 0)
	   {
	      if ((num = read(socket, temp, 5*LENGTH)) > 0)
	         (void) fwrite(temp, num, 1, stderr);
	      else
		 break;
	   }
	}
	return(0);
}



/**************************************************************
*
* MODULE NAME: socket_machtype
*
*     PURPOSE: This function is used to get the hosttype from
*	       the remote machine.  It is similar to the machtype()
*	       system routine.  The only difference is that it also
*	       accepts a machine name in which to get the remote machine
*	       name.
*
*       INPUT: machine_name - the machine in which to get the official
*			      name.
*
*        
*      OUTPUT:  returns the type or -1 depending whether we
*		sucessfully got the hosttype
*
* CALLED FROM:  internal routine called from kgethostname()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int socket_machtype(machine_name, hosttype)

char	*machine_name;
char	*hosttype;
{
	return(-1);
}



/**************************************************************
*
* MODULE NAME: socket_gethostname
*
*     PURPOSE: This function is used to get the official hostname from
*	       the remote machine.  It is similar to the gethostname()
*	       system routine.  The only difference is that it also
*	       accepts a machine name in which to get the remote machine
*	       name.
*
*       INPUT: machine_name - the machine in which to get the official
*			      name.
*
*        
*      OUTPUT:  returns 0 or -1 depending whether we
*		sucessfully got the hostname
*
* CALLED FROM:  internal routine called from kgethostname()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int socket_gethostname(machine, hostname, namlen)

char	*machine;
char	*hostname;
int	namlen;
{
	unsigned long addr;
	struct	hostent *host_machine;


	/*
	 *  Get host by machine name, but first try to see if the address
	 *  is specified by internet address form...
	 */
	if ((host_machine = gethostbyname(machine)) != NULL)
	{
	   if (VStrlen(host_machine->h_name) > namlen)
	   {
	      strncpy(hostname, host_machine->h_name, namlen-1);
	      hostname[namlen-1] = '\0';
	   }
	   else
	      strcpy(hostname, host_machine->h_name);
	}
	else if ((addr = inet_addr(machine)) != INADDR_NONE && addr != ~0L)
	{
	   /*
	    *  return that the machine name is a proper addr...
	    */
	   strcpy(hostname, machine);
	}
	else
	{
	   /*
	    *  failed so return -1 to indicate failure...
	    */
           return(-1);
	}
	return(0);
}



/*********************************************************************
*
*		Remote Socket Routines (server/daemon)
*
*********************************************************************/


/**************************************************************
*
* MODULE NAME: socket_daemon
*
*     PURPOSE: This function is used to start a daemon so that
*	       remote jobs can execute.  All of the above routines
*	       have been clients, but this is the server that those
*	       routines contact to accomplish those tasks.  The
*	       daemon is actually quite simple.  It simply listens
*	       and accepts connections as they become available.
*	       The timeout is used so that if no jobs are scheduled
*	       within the elapsed time then this process will terminate
*	       itself.
*
*       INPUT: timeout - the time to wait before terminating in minutes.
*
*        
*      OUTPUT:  returns 0 if terminating successfully otherwise -1 if
*		an un-recoverable error has occurred.
*
* CALLED FROM:  internal routine called from kstart_daemon()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

static struct server_info
{
	int timeout;
	int number;
	int socket;
} sinfo;


int socket_daemon(max_connections, timeout)

int	max_connections;
int	timeout;
{
	static int daemon_handler();
	static vsignal signal_handler();

	struct	linger  ling;
	struct	servent *service;
	struct	sockaddr_in server;
	struct	sockaddr_in daemon;
	int	server_sock, daemon_sock, port_number, daemon_size, reuse_bool;


	/*
	 *  Get service by name
	 */
	if ((service = getservbyname(KhorosServiceName, "tcp")) == NULL)
	   port_number = htons(KhorosServicePort);
	else
	   port_number = service->s_port;

	/*
	 *  Create either a local UNIX or TCP connection based socket
	 */
	if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
	   perror("socket_daemon:  Open failure....  unable to create socket");
	   return(-1);
	}

	/*
	 *  Allow local address to be re-used
	 */
	reuse_bool = FALSE;	
	if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, 
			&reuse_bool, sizeof(reuse_bool)) < 0)
	{
	   perror("socket_daemon:  Cannot set option (REUSEADDR)....");
	   return(-1);
	}

	/*
	 *  Set linger option to be off
	 */
	ling.l_onoff = FALSE;
	if (setsockopt(server_sock,SOL_SOCKET,SO_LINGER,&ling,sizeof(ling)) < 0)
	{
	   perror("socket_daemon:  Cannot set option (_LINGER FALSE)....");
	   return(-1);
	}

	/*
	 *  Assign our server's address and then bind to the returned
	 *  socket "server_sock".
	 */
	bzero((char *) &server, sizeof(server));
	server.sin_port		= port_number;
	server.sin_family	= AF_INET;
	server.sin_addr.s_addr	= htonl(INADDR_ANY);
	if (bind(server_sock, (struct sockaddr *) &server, sizeof(server)) < 0)
	{
	   perror("socket_daemon:  Bind failure....  unable to bind to socket");
	   return(-1);
	}

	/*
	 *  Start listening to the socket with the maximum number of
	 *  connections.
	 */
	if (listen(server_sock, MIN(max_connections, 8)) < 0)
	{
	   perror("socket_daemon: Listen failure.. unable to listen on socket");
	   return(-1);
	}

	/*
	 *  accept connections forever... or until the timeout occurs
	 */
	(void) signal(SIGALRM, signal_handler);
	(void) signal(SIGCHLD, signal_handler);
	(void) signal(SIGINT, signal_handler);
	sinfo.timeout = FALSE; sinfo.number = 0; sinfo.socket = server_sock;

	while (sinfo.timeout == FALSE)
	{
	    alarm(timeout);
	    daemon_size = sizeof(daemon);
	    if ((daemon_sock = accept(server_sock,(struct sockaddr *) &daemon,
					&daemon_size)) < 0)
	    {
	       if (errno == EINTR)
		  continue;

	       if (sinfo.timeout == FALSE)
	       {
	          perror("socket_daemon: Accept failure.. unable to accept new \
connections on socket");
	          return(-1);
	       }
	       else
	          break;
	    }
	    if (fork() == 0)
	    {
	       signal(SIGALRM, SIG_DFL);
	       signal(SIGCHLD, SIG_DFL);
	       signal(SIGINT, SIG_DFL);
	       close(server_sock);
	       if (daemon_sock != 0) dup2(daemon_sock, 0);
	       if (daemon_sock != 1) dup2(daemon_sock, 1);
	       if (daemon_sock != 2) dup2(daemon_sock, 2);
	       (void) daemon_handler(daemon_sock, &daemon, timeout);
	       exit(1);
	    }
	    else
	    {
	       close(daemon_sock);
	       sinfo.number++;
	    }
	}
	close(server_sock);
	return(0);
}

static vsignal signal_handler(signal, flags, context)

int     signal, flags;
struct  sigcontext *context;
{
	if (signal == SIGCHLD)
	{
	   while (vwait3(NULL, WNOHANG, NULL) > 0)
	   {
	      if (sinfo.number > 0) sinfo.number--;
	   }
	}
	else if ((signal == SIGALRM && sinfo.number == 0) || signal == SIGINT)
	{
	   sinfo.timeout = TRUE;
	   close(sinfo.socket);
	}
}

static int daemon_handler(sock, daemon, timeout)

int	sock, timeout;
struct	sockaddr_in *daemon;
{
	int  mode, flags, info, num_bytes, mfile, status;
	char buf[LENGTH], command[LENGTH], filename[LENGTH],
	     sflags[LENGTH], data[10000], *args[MAX_ARGS], *temp,
	     *exec;


	if (socket_writeline(sock, "connected\n", 0) <= 0)
	{
	   return(-1);
	}

	if ((status = socket_readline(sock, buf, LENGTH)) <= 0)
	   return(status);

#ifdef DEBUG
   fprintf(stderr,"daemon: return from readline w/ '%s'\n", buf);
#endif

	sscanf(buf,"%s:",command);
	if (!strcmp(command,"access:"))
	{
	   sscanf(buf,"%[^:]%*[:]%*[ ]%*[']%[^']", command, filename);
	   info = kaccess(filename, R_OK);
	   if (!info)
	      socket_writeline(sock, "yes\n", 0);
	   else
	      socket_writeline(sock, "no\n", 0);
	}
	else if (!strcmp(command,"open:"))
	{
	   sscanf(buf,"%[^:]%*[:]%*[ ]%*[']%[^']%*[']%*[ ]%*[']%[^']%*['] %d",
			command, filename, sflags, &mode);
	   flags = open_flags(sflags);
	   mfile = kopen(filename, flags, mode);
	   if (mfile < 0)
	      socket_writeline(sock, "no\n", 0);
	   else
	   {
	       socket_writeline(sock, "yes\n", 0);
	       if (flags == O_RDONLY) 
	       {
#ifdef DEBUG
   fprintf(stderr,"daemon: starting read loop for file %s\n",filename);
#endif
		  while ((num_bytes = kread(mfile, data, 10000)) > 0)
		  {
#ifdef DEBUG
   fprintf(stderr,"daemon: read from socket %d bytes\n",num_bytes);
#endif
		     write(sock, data, num_bytes);
		  }
	       }
	       else 
	       {
#ifdef DEBUG
   fprintf(stderr,"daemon: starting write loop for file %s\n",filename);
#endif
		  while ((num_bytes = read(sock, data, 10000)) > 0)
		  {
#ifdef DEBUG
   fprintf(stderr,"daemon: writing to socket w/ %d\n",num_bytes);
#endif
		     kwrite(mfile, data, num_bytes);
		  }
	       }
	       kclose(mfile);
	   }
	}
	else if (!strcmp(command,"exec:"))
	{
#ifdef DEBUG
   fprintf(stderr,"daemon: about to parse exec line '%s' \n", buf);
#endif

	   if ((exec = strchr(buf, '\'')) == NULL)
	   {
	      fprintf(stderr,"phantomd: error exec'ing the following '%s'\n",
				buf);
	      return(0);
	   }
	   else
	      exec++;

	   if ((temp = strrchr(exec, '\'')) != NULL)
	      *temp = '\0';
	
#ifdef DEBUG
   fprintf(stderr,"daemon: exec'ing the following %s \n", exec);
#endif
	   command_args(exec, args);
	   execvp(args[0], args);
	   (void) fprintf(stderr,"phantomd: failed to exec the following \
		   command:\n '%s'\n", buf);
	}
	return(0);
}



/*********************************************************************
*
*		Private Socket Routines
*
*********************************************************************/




/**************************************************************
*
* MODULE NAME: socket_parseline
*
*     PURPOSE: This function parses the "socket" argument.  It is an
*	       internal driver to parse the different command line options
*	       so that we know what is expected.
*
*			"socket=XXXXXXXXXX"
*				or
*			"XXXXXXXXXX@machine"
*
*
*       INPUT:  path - the socket argument.
*        
*      OUTPUT:  returns -1 or 0 upon failure or success
*
*		port_number - the port number to connect to at the
*			      machine.
*		machine     - the machine to connect to...
*
*
* CALLED FROM:  internal routine called from kopen()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_parseline(path, hostname, filename, port)

char	*path;
char	*hostname;
char	*filename;
int	*port;
{
	char	*machine;


	/*
	 *  Get the machine to connet to, the file component of the path,
	 *  and the port to use (or -1 if none exists).
	 */
	if ((machine = strrchr(path, '@')) == NULL)
	{
	   if (sscanf(path,"%d",port)!=1 && sscanf(path,"socket=%d",port)!=1)
	      *port = -1;

	   strcpy(filename, path);
	   gethostname(hostname, LENGTH);
	}
	else
	{
	   strncpy(filename, path, machine-path);
	   filename[machine-path] = '\0';
	   strcpy(hostname, ++machine);
	   *port = -1;
	}
	return(0);
}



/**************************************************************
*
* MODULE NAME: socket_connect
*
*     PURPOSE: This function opens a "socket" connection.  It is an
*	       internal driver to open a socket, which is called by
*	       the the internal socket routines such as socket_open()
*	       socket_exec().
*
*			"socket=XXXXXXXXXX"
*				or
*			"XXXXXXXXXX@machine"
*
*
*       INPUT:  port_number - the port number to connect to at the
*			      machine.
*		machine     - the machine to connect to...
*
*        
*      OUTPUT:  returns -1 or the socket id
*
* CALLED FROM:  internal routine called from kopen()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_connect(port, machine)

int	port;
char	*machine;
{
	unsigned long inaddr;
	struct	linger  ling;
	struct	sockaddr_in client;

	int	sock, reuse_bool;
	struct	servent *service;
	struct	hostent *host_machine;

	static  int retry = 0;
	char	response[LENGTH], error[LENGTH];


	/*
	 *  Get service by name
	 */
	if (port == -1)
	{
	   if ((service = getservbyname(KhorosServiceName, "tcp")) == NULL)
	      port = htons(KhorosServicePort);
	   else
	      port = service->s_port;
	}

	/*
	 *  Get host by machine name, but first try to see if the address
	 *  is specified by internet address form...
	 */
	bzero(&client, sizeof(client));
	client.sin_port   = port;
	if ((host_machine = gethostbyname(machine)) != NULL)
	{
	   client.sin_family = host_machine->h_addrtype;
	   bcopy(host_machine->h_addr, (char *) &client.sin_addr,
			host_machine->h_length);
	}
	else if ((inaddr=inet_network(machine)) != INADDR_NONE && inaddr != ~0L)
	{
	   inaddr = htonl(inaddr);
	   client.sin_family = AF_INET;
	   bcopy(&inaddr, (char *) &client.sin_addr, sizeof(inaddr));
	}
	else
	{
	   /*
	    *  failed to find the name..
	    */
	   errno = ENETUNREACH;
	   (void) sprintf(error,"socket_connect: Unknown machine or network \
address '%s'\n", machine);
	   perror(error);
           return(-1);
	}

	/*
	 *  Create either a local UNIX or TCP connection based socket
	 */
	if ((sock = socket(client.sin_family, SOCK_STREAM, 0)) < 0)
	{
	   if (retry > DaemonRetry)
	   {
	      perror("socket_connect:  Open failure....");
	      return(-1);
	   }
	   else
	   {
	      retry++;
	      if (daemon_start("socket", machine) != 0)
	      {
		 errno = ECONNREFUSED;
	         perror("socket_connect:  Start failure on remote machine....");
		 return(-1);
	      }
	      sock = socket_connect(port, machine);
	      retry--;
	      return(sock);
	   }
	}


	/*
	 *  Allow local address to be re-used
	 */
	reuse_bool = TRUE;	
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 
			&reuse_bool, sizeof(reuse_bool)) < 0)
	{
	   perror("socket_connect:  Cannot set option (REUSEADDR)....");
	   return(-1);
	}

	/*
	 *  Set linger option to be off
	 */
	ling.l_onoff = FALSE;
	if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) < 0)
	{
	   perror("socket_connect:  Cannot set option (_LINGER FALSE)....");
	   return(-1);
	}

	/*
	 *  connect to socket
	 */
	if (connect(sock, (struct sockaddr *) &client, sizeof(client)) < 0)
	{
	   if (retry > DaemonRetry)
	   {
	      perror("socket_open:  Unable to connect to specified socket");
	      return(-1);
	   }
	   else
	   {
	      close(sock);
	      retry++;
	      if (daemon_start("socket", machine) != 0)
	      {
		 errno = ECONNREFUSED;
	         perror("socket_connect:  Start failure on remote machine....");
		 return(-1);
	      }
	      sock = socket_connect(port, machine);
	      retry--;
	      return(sock);
	   }
	}

	/*
	 *  listen()
	 */
	if (socket_readline(sock, response, LENGTH) <= 0)
	{
	   (void) fprintf(stderr,"socket_connect:  Error reading connection \
response from remote daemon...\n\nWe were able to successfully connect to the \
remote machine, but failed\nto start the daemon.  Please check to make sure \
that a remote daemon is\navailable on the machine '%s'\n", machine);
	   return(-1);
	}

	vlower_string(response, response);
	if (strncmp(response, "connected", 9) != 0)
	{
	   (void) fprintf(stderr,"socket_connect:  Unknown response from \
remote worker...\n\n(%s).\n\n", response);
	   return(-1);
	}
	return(sock);
}



/**************************************************************
*
* MODULE NAME: socket_readline
*
*     PURPOSE: This function reads a single line fgets().
*
*
*       INPUT:  socket - the socket to read from
*		string - the string to read into
*		length - the length of the string
*
*        
*      OUTPUT:  returns -1 or the socket id
*
* CALLED FROM:  internal routine called from kopen()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_readline(socket, string, length)

int   socket;
char  *string;
int   length;
{
	char	     *ptr;
	int	     numread = 0;


	/*
	 *  Simply call read() since "kfile" is just a regular file
	 *  descriptor.
	 */
	ptr = string;
	do
	{

	   /*
	    *  Check to see if we got top the end the getline
	    */
	   if (read(socket, ptr, 1) > 0)
	   {
	      numread++;
	      if (*ptr == '\0' || *ptr++ == '\n')
	      {
	        *ptr = '\0';
	        break;
	      }
	   }
	   else
	       break;
	}
	while (numread < length);

	if (numread == length)
	   string[length-1] = '\0';

#ifdef DEBUG
   fprintf(stderr,"%s\n", string);
#endif

	return(numread);
}



/**************************************************************
*
* MODULE NAME: socket_writeline
*
*     PURPOSE: This function puts a single line fputs().
*
*
*       INPUT:  socket - the socket to write to
*		string - the string to write from
*		length - the length of the string
*
*        
*      OUTPUT:  returns -1 or the socket id
*
* CALLED FROM:  internal routine called from kopen()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int socket_writeline(socket, string, length)

int   socket;
char  *string;
int   length;
{
	int	     count;
	int	     numwrite = 0;


	/*
	 *  Simply call read() since "kfile" is just a regular file
	 *  descriptor.
	 */
	count = VStrlen(string);
	if (length != 0 && count > length) count = length;
#ifdef DEBUG
   fprintf(stderr,"%s\n", string);
#endif

	numwrite = write(socket, string, count);
	return(numwrite);
}


#endif  /* KSOCKET */
/* don`t add after the endif */
