/*-
 * customsMWB.c --
 *	Create a Customs WayBill
 *
 * 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: customsMWB.c,v 1.10 1996/06/22 21:35:08 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */

#include    <string.h>
#include    <sys/types.h>
#include    <sys/stat.h>

#include    "customs.h"


/*-
 *-----------------------------------------------------------------------
 * Customs_MakeWayBill --
 *	Create a WayBill to be passed to the CUSTOMS_IMPORT function.
 *
 * Results:
 *	Length of the buffer, or -1 if MAX_DATA_SIZE is exceeded.
 *
 * Side Effects:
 *	The passed buffer is overwritten.
 *
 *-----------------------------------------------------------------------
 */
int
Customs_MakeWayBill(permitPtr, cwd, file, argv, environ, port, buf)
    ExportPermit  	*permitPtr; /* Permit for the job */
    char    	  	*cwd;	    /* The current working directory */
    char    	  	*file;	    /* File to execute */
    char    	  	**argv;	    /* Arguments for it */
    char    	  	**environ;  /* Environment in which it should run */
    unsigned short	port;	    /* Port of udp socket for CUSTOMS_EXIT
				     * return RPC call */
    char    	  	buf[MAX_DATA_SIZE];
				    /* Place to stuff the information */
{
    register char 	*cp;
    register int  	len, i;
    register WayBill	*wb;
    GID_T		groups[MAX_NUM_GROUPS];
    struct rlimit	rlim;
#if !defined(RLIMIT_FSIZE) || !defined(RLIMIT_NOFILE)
    long		ulim;
    extern long		ulimit();
#endif

    wb = (WayBill *)buf;

#define CHECK_SIZE(size) \
    if ((size) > MAX_DATA_SIZE) { \
	return (-1); \
    }

    CHECK_SIZE(sizeof(WayBill));

    /*
     * We compute a deadline for the remote side to start its process.
     * This is to prevent bogus timeouts that could lead to duplicate
     * exports.  To be on the safe side, the deadline is half the caller
     * side RPC timeout.
     */
#ifdef DOUBLECHECK_TIMEOUT
    wb->deadline = time((time_t *)0) + CUSTOMS_TIMEOUT/2;
#else
    wb->deadline = 0;
#endif

    /*
     * First the constant information:
     *	    - permit ID
     *	    - return call port
     *	    - real user id
     *	    - effective user id
     *	    - real group id
     *	    - effective group id
     *	    - array of groups process is in
     *	    - file creation mask
     *      - process priority
     *      - resource limits
     */
    wb->id = permitPtr->id;
    wb->port = port;
    wb->ruid = getuid();
    wb->euid = geteuid();
    wb->rgid = getgid();
    wb->egid = getegid();
    /*
     * We can't just use wb->groups directly with getgroups because of the
     * different sizes used for GID_T.
     */
    wb->ngroups = getgroups(MAX_NUM_GROUPS, groups);
    for (i = 0; i < wb->ngroups; i++) {
	wb->groups[i] = groups[i];
    }
    wb->umask = umask(0);
    (void) umask(wb->umask);
#ifndef PRIO_PROCESS
    wb->priority = nice(0);
#else
    wb->priority = getpriority(PRIO_PROCESS,0);
#endif
    /*
     * This could be simplified if we could rely on rlimits being numbered
     * the same in all OSes, and rlim_cur and rlim_max where 4 bytes on all
     * machines.
     */
#define INF_RLIMIT(index) \
	Customs_RlimCur(*wb,index) =  CUSTOMS_NORLIM; \
	Customs_RlimMax(*wb,index) =  CUSTOMS_NORLIM;

#define GET_RLIMIT(index, resource) \
    if (getrlimit(resource, &rlim) < 0) { \
	INF_RLIMIT(index); \
    } else { \
        Customs_RlimCur(*wb,index) = \
	    (rlim.rlim_cur == RLIM_INFINITY || \
	     rlim.rlim_cur >= CUSTOMS_NORLIM) ? \
		CUSTOMS_NORLIM : rlim.rlim_cur; \
        Customs_RlimMax(*wb,index) = \
	    (rlim.rlim_max == RLIM_INFINITY || \
	     rlim.rlim_max >= CUSTOMS_NORLIM) ? \
		CUSTOMS_NORLIM : rlim.rlim_max; \
    }

#ifdef RLIMIT_CPU
    GET_RLIMIT(CUSTOMS_RLIMCPU, RLIMIT_CPU);
#else
    INF_RLIMIT(CUSTOMS_RLIMCPU);
#endif
#ifdef RLIMIT_FSIZE
    GET_RLIMIT(CUSTOMS_RLIMFSIZE, RLIMIT_FSIZE);
#ifdef FSIZE_SCALE
    if (Customs_RlimCur(*wb, CUSTOMS_RLIMFSIZE) != RLIM_INFINITY)
	Customs_RlimCur(*wb, CUSTOMS_RLIMFSIZE) *= FSIZE_SCALE;
    if (Customs_RlimMax(*wb, CUSTOMS_RLIMFSIZE) != RLIM_INFINITY)
	Customs_RlimMax(*wb, CUSTOMS_RLIMFSIZE) *= FSIZE_SCALE;
#endif /* FSIZE_SCALE */
#else /* ! RLIMIT_FSIZE */
    /*
     * Emulate RLIMIT_FSIZE using System V ulimit(2).
     */
    ulim = ulimit(1, 0) * 512;
    if (ulim < 0 || ulim > RLIM_INFINITY - 512) {
	INF_RLIMIT(CUSTOMS_RLIMFSIZE);
    } else {
	Customs_RlimCur(*wb, CUSTOMS_RLIMFSIZE) =
	Customs_RlimMax(*wb, CUSTOMS_RLIMFSIZE) = ulim;
    }
#endif /*RLIMIT_FSIZE */
#ifdef RLIMIT_DATA
    GET_RLIMIT(CUSTOMS_RLIMDATA, RLIMIT_DATA);
#else
    INF_RLIMIT(CUSTOMS_RLIMDATA);
#endif
#ifdef RLIMIT_STACK
    GET_RLIMIT(CUSTOMS_RLIMSTACK, RLIMIT_STACK);
#else
    INF_RLIMIT(CUSTOMS_RLIMSTACK);
#endif
#ifdef RLIMIT_CORE
    GET_RLIMIT(CUSTOMS_RLIMCORE, RLIMIT_CORE);
#else
    INF_RLIMIT(CUSTOMS_RLIMCORE);
#endif
#ifdef RLIMIT_RSS
    GET_RLIMIT(CUSTOMS_RLIMRSS, RLIMIT_RSS);
#else
    INF_RLIMIT(CUSTOMS_RLIMRSS);
#endif
#ifdef RLIMIT_NOFILE
    GET_RLIMIT(CUSTOMS_RLIMNOFILE, RLIMIT_NOFILE);
#else
    /*
     * Emulate RLIMIT_NOFILE using System V ulimit(2) or BSD getdtablesize(2).
     */
#ifdef SYSV
    ulim = ulimit(4, 0);
#else
    ulim = getdtablesize();
#endif
    if (ulim < 0) {
	INF_RLIMIT(CUSTOMS_RLIMNOFILE);
    } else {
	Customs_RlimCur(*wb, CUSTOMS_RLIMNOFILE) =
	Customs_RlimMax(*wb, CUSTOMS_RLIMNOFILE) = ulim;
    }
#endif
#ifdef RLIMIT_VMEM
    GET_RLIMIT(CUSTOMS_RLIMVMEM, RLIMIT_VMEM);
#else
    INF_RLIMIT(CUSTOMS_RLIMVMEM);
#endif

    /*
     * Then the variable-length part:
     *	    - the absolute path of the current working directory
     *	    - the file to execute (needn't be absolute)
     *	    - the number of arguments (stored on a longword boundary)
     *	    - the argument strings
     *	    - the number of environment strings (stored on a 32-bit boundary)
     *	    - the environment strings themselves
     */
    cp = (char *)&wb[1];

    len = strlen(cwd) + 1;
    CHECK_SIZE(cp - buf + len);
    strcpy(cp, cwd);
    cp += len;

    len = strlen(file) + 1;
    CHECK_SIZE(cp - buf + len);
    strcpy(cp, file);
    cp += len;

    cp = Customs_Align(cp, char *);
    CHECK_SIZE(cp - buf + sizeof(Rpc_Long));

    for (i = 0; argv[i]; i++) {
	;
    }
    *(Rpc_Long *)cp = i;
    cp += sizeof(Rpc_Long);

    for (i = 0; argv[i]; i++) {
	len = strlen(argv[i]) + 1;
	CHECK_SIZE(cp - buf + len);
	strcpy(cp, argv[i]);
	cp += len;
    }
    cp = Customs_Align(cp, char *);
    CHECK_SIZE(cp - buf + sizeof(Rpc_Long));

    for (i = 0; environ[i]; i++) {
	;
    }
    *(Rpc_Long *)cp = i;
    cp += sizeof(Rpc_Long);

    for (i = 0; environ[i]; i++) {
	len = strlen(environ[i]) + 1;
	CHECK_SIZE(cp - buf + len);
	strcpy(cp, environ[i]);
	cp += len;
    }
    return (cp - buf);
}

