/*-
 * customsExport.c --
 *	Initial job export
 *
 * Copyright (c) 1988, 1989 by the Regents of the University of California
 * Copyright (c) 1988, 1989 by Adam de Boor
 * Copyright (c) 1989 by Berkeley Softworks
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any non-commercial purpose
 * and without fee is hereby granted, provided that the above copyright
 * notice appears in all copies.  The University of California,
 * Berkeley Softworks and Adam de Boor make no representations about
 * the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 */
#ifndef lint
static char *rcsid =
"$Id: customsExport.c,v 1.3 1994/07/07 03:57:27 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */

#include    <string.h>
#include    <stdio.h>

#include    "customs.h"

char customsImportError[128];	    /* Last error message from import server */


/*-
 *-----------------------------------------------------------------------
 * Customs_RawExport --
 *	Start a job running on another machine, but don't fork an
 *	"export" job to handle it -- just return the tcp socket open
 *	to the remote job, or -1 if the job could not be exported.
 *
 * Results:
 *	socket to remote job if ok. If < 0, value is:
 *	    -100    Couldn't find host
 *	    -101    Couldn't create return socket
 *	    -102    Couldn't get name of return socket
 *	    -104    Remote side refused import
 *	   <-200    -(result+200) gives the return status from the
 *		    CUSTOMS_IMPORT call.
 *	This is hokey, but was done quickly to provide debugging info in
 *	pmake.
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
int
Customs_RawExport(file, argv, cwd, flags, retSockPtr, permitPtr)
    char    	  *file;    	    /* File to exec */
    char    	  **argv;   	    /* Arguments to give it */
    char    	  *cwd;	    	    /* Current directory. NULL if not
				     * determined */
    int	    	  flags; 	    /* Flags to pass to Customs_Host */
    int	    	  *retSockPtr;	    /* Socket on which return call should be
				     * made when process exits. If < 0, will
				     * return a socket for the purpose. */
    ExportPermit  *permitPtr;	    /* OUT: permit returned by agent */
{
    return Customs_RawExportAttr(file, argv, cwd, flags, NILLST,
				 retSockPtr, permitPtr);
}

/*-
 *-----------------------------------------------------------------------
 * Customs_RawExportAttr --
 *	Start a job running on another machine with specific attributes.
 *
 * Results:
 *	As for Customs_RawExport.  If exportation fails but the local
 *	host has all attributes, the id field in the export permit is
 *	set to non-zero.
 *
 * Side Effects:
 *	As for Customs_RawExport.
 *
 *-----------------------------------------------------------------------
 */
int
Customs_RawExportAttr(file, argv, cwd, flags, attributes, retSockPtr, permitPtr)
    char    	  *file;    	    /* File to exec */
    char    	  **argv;   	    /* Arguments to give it */
    char    	  *cwd;	    	    /* Current directory. NULL if not
				     * determined */
    int	    	  flags; 	    /* Flags to pass to Customs_Host */
    Lst		  attributes;	    /* Attributes to pass to Customs_HostAttr */
    int	    	  *retSockPtr;	    /* Socket on which return call should be
				     * made when process exits. If < 0, will
				     * return a socket for the purpose. */
    ExportPermit  *permitPtr;	    /* OUT: permit returned by agent */
{
    extern char   	**environ;  /* Current process environment */
    int	    	  	sock;	    /* TCP socket connecting to exported
				     * process */
    struct sockaddr_in 	importServer;	/* Address of server running our
					 * process */
    Rpc_Stat	  	rstat;	    /* Return status from RPC calls */
    char    	  	buf[MAX_DATA_SIZE]; /* Buffer for outgoing waybill */
    int	    	  	buflen;	    /* Length of buffer */
    char    	  	loccwd[MAXPATHLEN]; /* Place to stuff cwd if not
					     * given */
    struct timeval	timeout;    /* Timeout for IMPORT request (since it's
				     * TCP, there's a different timeout than
				     * normal Customs calls) */
    u_short 	  	retPort;    /* Port number of return call socket for
				     * import server to return the process'
				     * exit status */
    Boolean 	  	ourSock;    /* True if we allocated the return call
				     * socket */
    ExportPermit  	locPermit;  /* Local permit if the caller isn't
				     * interested */

    /*
     * If they haven't already figured out the current working directory,
     * we have to do it for them.
     * Do this now rather than later so we don't have to close sockets etc.
     * if something goes wrong.
     */
    if (cwd == (char *)NULL) {
	if (!GETWD(loccwd)) {
	    return(CUSTOMS_NOCWD);
	}
	if (Customs_NormPath(loccwd, sizeof(loccwd)) < 0) {
	    return(CUSTOMS_NOCWD);
	}
	cwd = loccwd;
    }

    if (permitPtr == (ExportPermit *)NULL) {
	permitPtr = &locPermit;
    }
    memset (permitPtr, 0, sizeof(*permitPtr));

    /*
     * Find out where we may go, first.
     * Avoid the more expensive Customs_HostAttr if possible.
     */
    if (Lst_IsEmpty(attributes)) {
	rstat = Customs_Host(flags, permitPtr);
    } else {
	rstat = Customs_HostAttr(flags, attributes, permitPtr);
    }
    if (rstat != RPC_SUCCESS) {
	return (-(int)rstat);
    } else if (CUSTOMS_FAIL(&permitPtr->addr)) {
	return(CUSTOMS_NOEXPORT);
    }

    /*
     * We have somewhere to go. Now we need to set up the return-call
     * socket so we can pass its port number to the import server. If the
     * caller already has a socket in mind, ourSock is set False.
     */
    if (*retSockPtr < 0) {
	ourSock = True;
	*retSockPtr = Rpc_UdpCreate(True, 0);
	if (*retSockPtr < 0) {
	    return(CUSTOMS_NORETURN);
	}
    } else {
	ourSock = False;
    }
    
    /*
     * Figure out the port number. If this fails, we can't export...
     */
    buflen = sizeof(importServer);
    if (getsockname(*retSockPtr, (struct sockaddr *)&importServer,
							&buflen) < 0) {
	if (ourSock) {
	    (void) close(*retSockPtr);
	}
	return (CUSTOMS_NONAME);
    }
    retPort = importServer.sin_port;
    
    /*
     * Create the TCP socket for talking to the remote process and set up
     * the address of the remote server for doing the RPC
     */
    sock = Rpc_TcpCreate(False, 0);
    if (sock < 0) {
	if (ourSock) {
	    (void) close(*retSockPtr);
	}
	return(CUSTOMS_NOIOSOCK);
    }
    importServer.sin_family = AF_INET;
    importServer.sin_port = htons(Customs_Port());
    importServer.sin_addr = permitPtr->addr;

    /*
     * Using all this information, create a WayBill buffer to pass to
     * the server.
     */
    buflen = Customs_MakeWayBill(permitPtr, cwd, file, argv, environ,
				 retPort, buf);
    if (buflen < 0) {
	if (ourSock) {
	    (void) close(*retSockPtr);
	}
	return (-200-(int)RPC_TOOBIG);
    }

    /*
     * Call the server. We only send one message, since TCP is "reliable".
     * If we don't get a response in CUSTOMS_TIMEOUT seconds, the export failed.
     */
    timeout.tv_sec = CUSTOMS_TIMEOUT;
    timeout.tv_usec = 0;
    rstat = Rpc_Call(sock, &importServer, (Rpc_Proc)CUSTOMS_IMPORT,
		     buflen, (Rpc_Opaque)buf,
		     sizeof(customsImportError),
		     (Rpc_Opaque)customsImportError,
		     1, &timeout);
#ifdef DEBUG_TIMEOUT
    if (rstat == RPC_TIMEDOUT) {
	printf("CUSTOMS_IMPORT to %s timed out after %.2f secs (msg size = %d)\n",
		InetNtoA(*(struct in_addr *)&importServer.sin_addr),
		(float)(timeout.tv_sec + 0.0000001 * timeout.tv_usec),
		buflen);
    }
#endif
    customs_Status = rstat;
    /*
     * There are two ways an IMPORT call may fail -- if the server is down,
     * we'll get a RPC error code. If the server denies permission to export,
     * for some reason, it'll return some message other than "Ok".
     * In both cases, we clean up and return < 0 to indicate failure.
     */
    if (rstat != RPC_SUCCESS) {
	if (ourSock) {
	    (void)close(*retSockPtr);
	}
	(void)close(sock);
	return (-200-(int)rstat);
    } else if (strcmp(customsImportError, "Ok") == 0) {
	return (sock);
    } else {
	if (ourSock) {
	    (void)close(*retSockPtr);
	}
	(void) close(sock);
	return (CUSTOMS_ERROR);
    }
}

