/*-
 * importquota.c --
 *	A program to set the availability of the local host.
 *
 * 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: importquota.c,v 1.24 1996/08/18 15:27:16 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */

#include    <sys/time.h>
#include    <stdio.h>

#include    "customs.h"

/*-
 *-----------------------------------------------------------------------
 * gettime --
 *	Get a time value from a string. It must be in the form
 *	    mm:ss
 *	where 'm' is a minute's digit and 's' is a second's digit.
 *	seconds may not be greater than 60, for obvious reasons.
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
int
gettime (str)
    char    *str;
{
    int	    min,
	    sec;

    for (min = 0;
	 *str != '\0' && *str != ':' ;
	 min = 10 * min + *str++ - '0') {
	     continue;
    }
    if (*str == '\0') {
	sec = min;
	min = 0;
    } else {
	for (sec = 0, str++; *str != '\0'; sec = 10 * sec + *str++ - '0') {
	    continue;
	}
    }
    if (sec >= 60) {
	fprintf (stderr, "malformed time\n");
	exit(1);
    }
    return (min * 60 + sec);
}

/*-
 *-----------------------------------------------------------------------
 * Usage --
 *	Print out the flags we accept and die.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The process exits.
 *
 *-----------------------------------------------------------------------
 */
void
Usage ()
{
    fprintf (stderr, "Usage: importquota [<options>]\n");
    fprintf (stderr, "\t-check <time>    Set availability check interval\n");
    fprintf (stderr, "\t-localjobs <0|1> Allow jobs from local clients\n");
    fprintf (stderr, "\t-idle <time>     Set minimum idle time\n");
    fprintf (stderr, "\t-idlecrit <num>  Set idle criterion\n");
    fprintf (stderr, "\t-load <loadavg>  Set maximum load average\n");
    fprintf (stderr, "\t-swap <percent>  Set minimum free swap space\n");
    fprintf (stderr, "\t-jobs <num>      Set maximum imported jobs\n");
    fprintf (stderr, "\t-proc <num>      Set minimum free processes\n");
    fprintf (stderr, "\t-cpu <time>      Set cpu time limit for imported jobs\n");
    fprintf (stderr, "\t-memory <kbytes> Set memory use limit for imported jobs\n");
    fprintf (stderr, "\t-nice <level>    Set nice increment for imported jobs\n");
    fprintf (stderr, "\t-npri <prio>     Set non-degrading priority for imported jobs\n");
    fprintf (stderr, "\t-access <level>  Set user access level\n");
    fprintf (stderr, "\t-evict <time>    Set eviction delay for imported jobs\n");
    fprintf (stderr, "\t-default         Set all criteria to defaults\n");
    fprintf (stderr, "\t-h[elp]          Print this summary\n");
    fprintf (stderr, "\t-q[uiet]         Don't print current criteria\n");
}

/*-
 *-----------------------------------------------------------------------
 * main --
 *	Usage:
 *	    importquota -check mm:ss -idle mm:ss -swap pct -load float
 *
 *	-check	  	interval at which to check availability
 *	-localjobs	whether or not we allow local clients to 'import' jobs
 *	-idle	  	minimum time the keyboard must be idle for the
 *	    	  	host to be available
 *	-swap	  	minimum percentage of swap space that must
 *	    	  	be free for the host to be available
 *	-load	  	a floating-point number which is the maximum
 *	    	  	load average the host can have before it becomes
 *	    	  	unavailable.
 *	-jobs	  	maximum number of imported jobs allowed at once
 *	-proc		minumum number of free process slots
 *	-cpu		cpu time limit for imported jobs
 *	-memory		memory use limit for imported jobs
 *	-nice		nice increment for imported jobs
 *	-npri		non-degrading priority for imported jobs (SGI)
 *	-access		user access level
 *
 *	The -idle, -swap,-load, -proc, -cpu, -memory, -nice, -npri and -access
 *	have certain limits (e.g. you cannot say -idle 400:00). To turn off
 *	the checking of any criterion, give a 0 value for it (e.g. -load 0
 *	means not to worry about the load averages on the machine).
 *
 * Results:
 *	None, really.
 *
 * Side Effects:
 *	The criteria are altered if all are within bounds.
 *
 *-----------------------------------------------------------------------
 */
main (argc, argv)
    int	    argc;
    char    **argv;
{
    Avail_Data	  	criteria;
    struct timeval	interval;
    double  	  	maxLoad;
    Rpc_Stat   	  	rstat;
    int			status = 0;
    Boolean		quiet = False;
    
    interval.tv_sec = interval.tv_usec = 0;
    criteria.changeMask = 0;

    for (argc--, argv++; argc > 0; argc--, argv++) {
	if (strcmp (*argv, "-q") == 0 ||
            strcmp (*argv, "-quiet") == 0) {
	    quiet = True;
	} else if (strcmp (*argv, "-h") == 0 ||
                   strcmp (*argv, "-help") == 0) {
	    Usage();
	    exit(0);
	} else if (strcmp (*argv, "-default") == 0) {
		interval.tv_sec = DEF_CHECK_TIME;
		criteria.localJobs = DEF_LOCAL_JOBS;
		criteria.idleTime = DEF_MIN_IDLE;
		criteria.idleCrit = DEF_IDLECRIT;
		criteria.swapPct = DEF_MIN_SWAP;
		criteria.loadAvg = DEF_MAX_LOAD * LOADSCALE;
		criteria.imports = DEF_MAX_IMPORTS;
		criteria.procs = DEF_MIN_PROC;
		criteria.cpuLimit = DEF_CPU_LIMIT;
		criteria.memLimit = DEF_MEM_LIMIT;
		criteria.niceLevel = DEF_NICE_LEVEL;
		criteria.npriLevel = DEF_NPRI_LEVEL;
		criteria.checkUser = DEF_CHECK_USER;
		criteria.evictDelay = DEF_EVICT_DELAY;
		criteria.changeMask |= AVAIL_TOLOCAL | AVAIL_IDLE | AVAIL_SWAP |
				       AVAIL_LOAD | AVAIL_IMPORTS | AVAIL_PROC |
				       AVAIL_CPU | AVAIL_MEMORY | AVAIL_NICE | 
				       AVAIL_NPRI | AVAIL_CHECK | AVAIL_EVICT;
	} else if (argc < 2) {
	    fprintf (stderr, "Extra argument: %s\n", *argv);
	    Usage();
	    exit(1);
	} else {
	    if (strcmp (*argv, "-check") == 0) {
		interval.tv_sec = gettime (argv[1]);
	    } else if (strcmp (*argv, "-localjobs") == 0) {
		criteria.localJobs = atoi (argv[1]);
		criteria.changeMask |= AVAIL_TOLOCAL;
	    } else if (strcmp (*argv, "-idle") == 0) {
		criteria.idleTime = gettime (argv[1]);
		criteria.changeMask |= AVAIL_IDLE;
	    } else if (strcmp (*argv, "-idlecrit") == 0) {
		criteria.idleCrit = atoi (argv[1]);
		criteria.changeMask |= AVAIL_IDLECRIT;
	    } else if (strcmp (*argv, "-swap") == 0) {
		criteria.swapPct = atoi (argv[1]);
		criteria.changeMask |= AVAIL_SWAP;
	    } else if (strcmp (*argv, "-load") == 0) {
		maxLoad = atof (argv[1]);
		criteria.loadAvg = (int)(maxLoad * LOADSCALE);
		criteria.changeMask |= AVAIL_LOAD;
	    } else if (strcmp (*argv, "-jobs") == 0) {
		criteria.imports = atoi(argv[1]);
		criteria.changeMask |= AVAIL_IMPORTS;
	    } else if (strcmp (*argv, "-proc") == 0) {
		criteria.procs = atoi (argv[1]);
		criteria.changeMask |= AVAIL_PROC;
	    } else if (strcmp (*argv, "-cpu") == 0) {
		criteria.cpuLimit = gettime (argv[1]);
		criteria.changeMask |= AVAIL_CPU;
	    } else if (strcmp (*argv, "-memory") == 0) {
		criteria.memLimit = atoi (argv[1]);
		criteria.changeMask |= AVAIL_MEMORY;
	    } else if (strcmp (*argv, "-nice") == 0) {
		criteria.niceLevel = atoi (argv[1]);
		criteria.changeMask |= AVAIL_NICE;
	    } else if (strcmp (*argv, "-npri") == 0) {
		criteria.npriLevel = atoi (argv[1]);
		criteria.changeMask |= AVAIL_NPRI;
	    } else if (strcmp (*argv, "-access") == 0) {
		criteria.checkUser = atoi (argv[1]);
		criteria.changeMask |= AVAIL_CHECK;
	    } else if (strcmp (*argv, "-evict") == 0) {
		criteria.evictDelay = gettime (argv[1]);
		criteria.changeMask |= AVAIL_EVICT;
	    } else {
		fprintf (stderr, "Unknown flag: %s\n", *argv);
		Usage();
		exit(1);
	    }
	    argc--; argv++;
	}
    }

    /* check for presence of local daemon first */
    if ( Customs_Ping() != RPC_SUCCESS ) {
	fprintf(stderr, "You don't seem to have a local customs daemon running\n");
	exit(2);
    }

    if (interval.tv_sec) {
	rstat = Customs_AvailInterval(&interval);
	if (rstat != RPC_SUCCESS) {
	    Customs_PError("Customs_AvailInterval");
	    fprintf(stderr, "Could not change availability interval\n");
	    exit(2);
	}
    }

    rstat = Customs_SetAvail(&criteria);

    if (rstat != RPC_SUCCESS) {
	Customs_PError("Customs_SetAvail");
	fprintf (stderr, "Could not change criteria\n");
        exit(2);
    } else if (criteria.changeMask) {
	fprintf (stderr, "Values out of range:\n");
	if (criteria.changeMask & AVAIL_IDLE) {
	    fprintf (stderr, "\tidle time too long (%d:%02d maximum)\n",
		     MAX_IDLE / 60, MAX_IDLE % 60);
	}
	if (criteria.changeMask & AVAIL_SWAP) {
	    fprintf (stderr, "\tswap percentage too high (%d%% maximum)\n",
		     MAX_SWAP);
	}
	if (criteria.changeMask & AVAIL_LOAD) {
	    fprintf (stderr, "\tload average too low (%f minimum)\n",
		     (double) MIN_LOAD / LOADSCALE);
	}
	if (criteria.changeMask & AVAIL_IMPORTS) {
	    fprintf (stderr, "\tjob imports too low (%d minimum)\n",
		     MIN_IMPORTS);
	}
	if (criteria.changeMask & AVAIL_PROC) {
	    fprintf (stderr, "\tfree processes number too high (%d maximum)\n",
		     MAX_PROC);
	}
	if (criteria.changeMask & AVAIL_CPU) {
#ifdef RLIMIT_CPU
	    fprintf (stderr, "\tCPU time limit too short (%d:%02d minimum)\n",
		     MIN_CPU / 60, MIN_CPU % 60);
#else /* !RLIMIT_CPU */
	    fprintf (stderr, "\tCPU time limit not supported\n");
#endif /* RLIMIT_CPU */
	}
	if (criteria.changeMask & AVAIL_MEMORY) {
#ifdef RLIMIT_RSS
	    fprintf (stderr, "\tmemory limit too low (%dK minimum)\n",
		     MIN_MEMORY);
#else /* !RLIMIT_RSS */
	    fprintf (stderr, "\tmemory limit not supported\n");
#endif /* !RLIMIT_RSS */
	}
	if (criteria.changeMask & AVAIL_NICE) {
	    fprintf (stderr, "\tillegal nice level (0..%d)\n",
		     MAX_NICE);
	}
	if (criteria.changeMask & AVAIL_NPRI) {
#ifdef sgi
	    fprintf (stderr, "\tillegal priority (%d..%d)\n",
		     NDPLOMAX, NDPLOMIN);
#else /* !sgi */
	    fprintf (stderr, "\tnon-degrading priorities not supported\n");
#endif /* sgi */
	}
	if (criteria.changeMask & AVAIL_CHECK) {
	    fprintf (stderr, "\tillegal user access checking level (%d..%d)\n",
		     DONT_CHECK, MAX_CHECK);
	}
	if (criteria.changeMask & AVAIL_EVICT) {
	    fprintf (stderr, "\teviction delay too short (%d:%02d minimum)\n",
		     MIN_EVICT / 60, MIN_EVICT % 60);
	}
	status = 3;
    }
    
    if (!quiet) {
	printf ("Current criteria:\n");
	if (criteria.localJobs) {
	    printf ("\t     local jobs:  allowed\n");
	}
	if (criteria.idleTime) {
	    printf ("\t      idle time:  %d:%02d\n", criteria.idleTime / 60,
		    criteria.idleTime % 60);
	    printf ("\t  idle criteria:  %d\n", criteria.idleCrit);
	}
	if (criteria.swapPct) {
	    printf ("\tswap percentage:  %d%%\n", criteria.swapPct);
	}
	if (criteria.loadAvg) {
	    printf ("\t   load average:  %f\n",(double)criteria.loadAvg/LOADSCALE);
	}
	if (criteria.imports) {
	    printf ("\t  imported jobs:  %d\n", criteria.imports);
	}
	if (criteria.procs) {
	    printf ("\t free processes:  %d\n", criteria.procs);
	}
	if (criteria.cpuLimit) {
	    printf ("\t CPU time limit:  %d:%02d\n", criteria.cpuLimit / 60,
		    criteria.cpuLimit % 60);
	}
	if (criteria.memLimit) {
	    printf ("\t   memory limit:  %dK\n", criteria.memLimit);
	}
	if (criteria.niceLevel) {
	    printf ("\t     nice level:  %d\n", criteria.niceLevel);
	}
	if (criteria.npriLevel) {
	    printf ("\t       priority:  %d\n", criteria.npriLevel);
	}
	if (criteria.checkUser) {
	    printf ("\t    user access:  level %d\n", criteria.checkUser);
	}
	if (criteria.evictDelay) {
	    printf ("\t eviction delay:  %d:%02d\n", criteria.evictDelay / 60,
		    criteria.evictDelay % 60);
	}
    }

    exit(status);
}
			 
