/*
*         OpenPBS (Portable Batch System) v2.3 Software License
*
* Copyright (c) 1999-2000 Veridian Information Solutions, Inc.
* All rights reserved.
*
* ---------------------------------------------------------------------------
* For a license to use or redistribute the OpenPBS software under conditions
* other than those described below, or to purchase support for this software,
* please contact Veridian Systems, PBS Products Department ("Licensor") at:
*
*    www.OpenPBS.org  +1 650 967-4675                  sales@OpenPBS.org
*                        877 902-4PBS (US toll-free)
* ---------------------------------------------------------------------------
*
* This license covers use of the OpenPBS v2.3 software (the "Software") at
* your site or location, and, for certain users, redistribution of the
* Software to other sites and locations.  Use and redistribution of
* OpenPBS v2.3 in source and binary forms, with or without modification,
* are permitted provided that all of the following conditions are met.
* After December 31, 2001, only conditions 3-6 must be met:
*
* 1. Commercial and/or non-commercial use of the Software is permitted
*    provided a current software registration is on file at www.OpenPBS.org.
*    If use of this software contributes to a publication, product, or
*    service, proper attribution must be given; see www.OpenPBS.org/credit.html
*
* 2. Redistribution in any form is only permitted for non-commercial,
*    non-profit purposes.  There can be no charge for the Software or any
*    software incorporating the Software.  Further, there can be no
*    expectation of revenue generated as a consequence of redistributing
*    the Software.
*
* 3. Any Redistribution of source code must retain the above copyright notice
*    and the acknowledgment contained in paragraph 6, this list of conditions
*    and the disclaimer contained in paragraph 7.
*
* 4. Any Redistribution in binary form must reproduce the above copyright
*    notice and the acknowledgment contained in paragraph 6, this list of
*    conditions and the disclaimer contained in paragraph 7 in the
*    documentation and/or other materials provided with the distribution.
*
* 5. Redistributions in any form must be accompanied by information on how to
*    obtain complete source code for the OpenPBS software and any
*    modifications and/or additions to the OpenPBS software.  The source code
*    must either be included in the distribution or be available for no more
*    than the cost of distribution plus a nominal fee, and all modifications
*    and additions to the Software must be freely redistributable by any party
*    (including Licensor) without restriction.
*
* 6. All advertising materials mentioning features or use of the Software must
*    display the following acknowledgment:
*
*     "This product includes software developed by NASA Ames Research Center,
*     Lawrence Livermore National Laboratory, and Veridian Information
*     Solutions, Inc.
*     Visit www.OpenPBS.org for OpenPBS software support,
*     products, and information."
*
* 7. DISCLAIMER OF WARRANTY
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT
* ARE EXPRESSLY DISCLAIMED.
*
* IN NO EVENT SHALL VERIDIAN CORPORATION, ITS AFFILIATED COMPANIES, OR THE
* U.S. GOVERNMENT OR ANY OF ITS AGENCIES BE LIABLE FOR ANY DIRECT OR INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This license will be governed by the laws of the Commonwealth of Virginia,
* without reference to its choice of law rules.
*/

#include <pbs_config.h>   /* the master config generated by configure */

#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <strings.h>
#include <signal.h>
#include <time.h>
#include <udb.h>
#include <tmpdir.h>
#include <dirent.h>
#include <mntent.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/session.h>
#include <sys/param.h>
#include <sys/aoutdata.h>
#include <sys/net_local.h>
#include <sys/quota.h>
#include <sys/restart.h>
#include <sys/table.h>
#include <sys/map.h>
#include <sys/swap.h>
#include <sys/sysinfo.h>
#include <sys/pws.h>
#include <sys/target.h>
#include <sys/aoutdata.h>
#include <sys/sema.h>
#include <sys/cred.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/sysmacros.h>
#include <sys/category.h>
#include <sys/resource.h>

#include "portability.h"
#include "pbs_error.h"
#include "log.h"
#include "list_link.h"
#include "server_limits.h"
#include "attribute.h"
#include "resource.h"
#include "pbs_job.h"
#include "log.h"
#include "mom_mach.h"
#include "mom_func.h"

#include "resmon.h"
#include "../rm_dep.h"

static char ident[] = "@(#) c90/$RCSfile$ $Revision$";

#ifndef max
#define max(a,b) (a>b ? a : b)
#endif

#ifndef TRUE
#define FALSE 0
#define TRUE 1
#endif /* TRUE */

/*
** System dependent code to gather information for the resource
** monitor for a Cray c90 machine.
**
** Resources known by this code:
**  cput  cpu time for a pid or session
**  mem  memory size for a pid or session in KB
**  sessions list of sessions in the system
**  pids  list of pids in a session
**  nsessions number of sessions in the system
**  nusers  number of users in the system
**  totmem  total memory size in KB
**  availmem available memory size in KB
**  ncpus  number of cpus
**  physmem  physical memory size in KB
**  size  size of a file or filesystem in KB
**  idletime seconds of idle time
**  quota  quota information
**  srfs_reserve set srfs reservation
**  loadave  current load average
**  swaprate swap activity in chars / sec
**  swapinrate swapin activity in chars / sec
**  swapoutrate swapout activity in chars / sec
**  swapused chars swapped out in KB
**  swapavail KB available in swap area
**  swaptotal total KB in swap area
**  cpusysw  percent cpu system wait
**  cpuunix  percent cpu unicos
**  cpuuser  percent cpu user
**  cpuidle  percent cpu idle
**  cpuguest percent cpu guest
*/

#if SRFS
#include <sys/srfs.h>

dev_t  srfs_tmp_dev = -1;
dev_t  srfs_big_dev = -1;
dev_t  srfs_fast_dev = -1;

extern int ignmem;
extern int ignvmem;
extern int igncput;

static char **v_name = NULL;
static char **v_value = NULL;

void
var_init(void)
  {
  char *id = "var_init";
  char *var_conf = "/etc/tmpdir.conf";
  char line[200], *cp;
  int len, i;
  FILE *fil;

  if (v_name == NULL)
    {
    v_name = (char **)calloc(10, sizeof(char *));
    v_value = (char **)calloc(10, sizeof(char *));
    }

  if ((fil = fopen(var_conf, "r")) == NULL)
    {
    log_err(errno, id, var_conf);
    return;
    }

  for (i = 0; fgets(line, sizeof(line), fil);)
    {
    if (line[0] == '#')
      continue;

    len = strlen(line);

    if (line[len-1] == '\n')
      line[--len] = '\0';

    for (cp = line; *cp; cp++)
      {
      if (isspace(*cp))
        break;
      }

    if (*cp)
      *cp++ = '\0';

    for (; *cp; cp++)
      {
      if (!isspace(*cp))
        break;
      }

    if (*cp == '\0')
      cp = JTMPDIR;

    DBPRT(("%s: var(%d) $%s = %s\n", id, i, line, cp))
    v_name = realloc(v_name, (i + 2) * sizeof(char *));

    v_value = realloc(v_value, (i + 2) * sizeof(char *));

    v_name[i] = strdup(line);

    v_value[i++] = strdup(cp);
    }

  v_name[i] = NULL;

  v_value[i] = NULL;
  fclose(fil);

  return;
  }

void
var_cleanup(void)
  {
  register i;

  if (v_name == NULL)
    return;

  for (i = 0; v_name[i]; i++)
    {
    free(v_name[i]);
    free(v_value[i]);
    }

  return;
  }

char *
var_value(char *name)
  {
  register i;

  for (i = 0; v_name[i]; i++)
    {
    if (strcmp(v_name[i], name) == 0)
      break;
    }

  return v_value[i];
  }

#endif /* SRFS */

static struct sess *session_table;
static long  session_table_size;

static struct proc *process_table;
static long  process_table_size;

extern time_t time_now;
extern char *path_checkpoint;

/*
** external functions and data
*/

extern struct config  *search(struct config *, char *);

extern struct rm_attribute *momgetattr(char *);
extern int   rm_errno;
extern unsigned int reqnum;
extern char   *ret_string;
extern double cputfactor;
extern double wallfactor;
extern  long    system_ncpus;
extern  int     ignwalltime;
extern  int     igncput;

/*
** local functions and data
*/
static char *ncpus(struct rm_attribute *attrib);
static char *totmem(struct rm_attribute *attrib);
static char *availmem(struct rm_attribute *attrib);
static char *physmem(struct rm_attribute *attrib);
static char *quota(struct rm_attribute *attrib);
static char *srfs_reserve(struct rm_attribute *attrib);
static char *swaprate(struct rm_attribute *attrib);
static char *swapinrate(struct rm_attribute *attrib);
static char *swapoutrate(struct rm_attribute *attrib);
static char *swapused(struct rm_attribute *attrib);
static char *swapavail(struct rm_attribute *attrib);
static char *swaptotal(struct rm_attribute *attrib);
static char *cpuidle(struct rm_attribute *attrib);
static char *cpuguest(struct rm_attribute *attrib);
static char *cpuuser(struct rm_attribute *attrib);
static char *cpuunix(struct rm_attribute *attrib);
static char *cpusysw(struct rm_attribute *attrib);

extern char *loadave(struct rm_attribute *attrib);
extern char *nullproc(struct rm_attribute *attrib);

extern char extra_parm[];
extern char no_parm[];

char no_srfs[] = "SRFS request not supported";
char procfs[] = "/proc";

#define FILTER 0.75
#define SAMPLE_DELTA 10

struct target tinfo;

struct sysinfo sysinfo;

double swap_rate;
double swapin_rate;
double swapout_rate;
double cpu_sysw;
double cpu_unix;
double cpu_user;
double cpu_idle;
double cpu_guest;
time_t wait_time;
long last_time = 0;

/*
** local resource array
*/

struct config dependent_config[] =
  {
    { "ncpus", {ncpus}
    },

  { "totmem", {totmem} },
  { "availmem", {availmem} },
  { "physmem", {physmem} },
  { "loadave", {loadave} },
  { "quota", {quota} },
  { "srfs_reserve", {srfs_reserve} },
  { "swaprate", {swaprate} },
  { "swapinrate", {swapinrate} },
  { "swapoutrate", {swapoutrate} },
  { "swapused", {swapused} },
  { "swapavail", {swapavail} },
  { "swaptotal", {swaptotal} },
  { "cpuidle", {cpuidle} },
  { "cpuguest", {cpuguest} },
  { "cpuuser", {cpuuser} },
  { "cpuunix", {cpuunix} },
  { "cpusysw", {cpusysw} },
  { NULL,  {nullproc} },
  };

/*
 * This routine is called on each cycle of the main loop.
 */

void
dep_main_loop_cycle(void)
  {
  char *id = "dep_main_loop_cycle";
  long now, delta;

  struct tbs info;

  struct pw pw;

  int nidlt, nuset, nunit, nguet, nsyst, i, newsios;
  double idlt, uset, unit, guet, syst, timec, srate;
  static int oidlt = 0;
  static int ouset = 0;
  static int ounit = 0;
  static int oguet = 0;
  static int osyst = 0;
  static int oldsios = 0;
  static int oldswapin = 0;
  static int oldswapout = 0;

  now = rtclock();
  delta = now - last_time;

  if (delta <= SAMPLE_DELTA*tinfo.mc_clktck)
    {
    wait_time = (int)((SAMPLE_DELTA * tinfo.mc_clktck - delta) /
                      tinfo.mc_clktck);
    return;
    }

  DBPRT(("%s: delta is %.3f\n", id,

         (double)delta / (double)tinfo.mc_clktck))

  wait_time = SAMPLE_DELTA;

  if (tabinfo(PWS, &info) == -1)
    {
    log_err(errno, id, (char *)"tabinfo(PWS)");
    return;
    }

  if (tabread(PWS, (char *)&pw,
              info.ent * info.len, info.head) == -1)
    {
    log_err(errno, id, (char *)"tabread(PWS)");
    return;
    }

  if (tabinfo(SINFO, &info) == -1)
    {
    log_err(errno, id, (char *)"tabinfo(SINFO)");
    return;
    }

  if (tabread(SINFO, (char *)&sysinfo,
              info.ent * info.len, info.head) == -1)
    {
    log_err(errno, id, (char *)"tabread(SINFO)");
    return;
    }

  last_time = now;

  /* User, sys and idle time calc */
  nidlt = 0;
  nuset = 0;
  nunit = 0;
  nguet = 0;
  nsyst = 0;

  for (i = 0; i < MAXCPUS; i++)
    {
    nidlt += pw.pws[i].pw_idlec;
    nuset += pw.pws[i].pw_userc;
    nunit += pw.pws[i].pw_unixc;
    nguet += pw.pws[i].pw_guestc;
    nsyst += pw.pws[i].pw_syswc;
    }

  idlt = (double)(nidlt - oidlt);

  uset = (double)(nuset - ouset);
  unit = (double)(nunit - ounit);
  guet = (double)(nguet - oguet);
  syst = (double)(nsyst - osyst);
  timec = idlt + uset + unit + guet + syst;

  if (timec <= 0.0)
    {
    sprintf(log_buffer, "bad time counter %.2f", timec);
    log_err(-1, id, log_buffer);
    return;
    }

  idlt = (idlt * 100.0) / timec;

  uset = (uset * 100.0) / timec;
  unit = (unit * 100.0) / timec;
  guet = (guet * 100.0) / timec;
  syst = (syst * 100.0) / timec;

  oidlt = nidlt;
  ouset = nuset;
  ounit = nunit;
  oguet = nguet;
  osyst = nsyst;

  cpu_sysw = FILTER * syst + (1 - FILTER) * cpu_sysw;
  DBPRT(("%s: syst %.2f cpu_sysw %.2f\n", id, syst, cpu_sysw))

  cpu_unix = FILTER * unit + (1 - FILTER) * cpu_unix;
  DBPRT(("%s: unit %.2f cpu_unix %.2f\n", id, unit, cpu_unix))

  cpu_user = FILTER * uset + (1 - FILTER) * cpu_user;
  DBPRT(("%s: uset %.2f cpu_user %.2f\n", id, uset, cpu_user))

  cpu_idle = FILTER * idlt + (1 - FILTER) * cpu_idle;
  DBPRT(("%s: idlt %.2f cpu_idle %.2f\n", id, idlt, cpu_idle))

  cpu_guest = FILTER * guet + (1 - FILTER) * cpu_guest;
  DBPRT(("%s: guet %.2f cpu_guest %.2f\n", id, guet, cpu_guest))

  srate = (double)((sysinfo.bswapin - oldswapin) * tinfo.mc_clktck)
          / (double)delta;
  srate *= (double)BSIZE;
  swapin_rate = FILTER * srate + (1 - FILTER) * swapin_rate;
  DBPRT(("%s: swapin %d srate %.2f swapin_rate %.2f\n",
         id, sysinfo.bswapin - oldswapin, srate, swapin_rate))
  oldswapin = sysinfo.bswapin;

  srate = (double)((sysinfo.bswapout - oldswapout) * tinfo.mc_clktck)
          / (double)delta;
  srate *= (double)BSIZE;
  swapout_rate = FILTER * srate + (1 - FILTER) * swapout_rate;
  DBPRT(("%s: swapout %d srate %.2f swapout_rate %.2f\n",
         id, sysinfo.bswapout - oldswapout, srate, swapout_rate))
  oldswapout = sysinfo.bswapout;

  newsios = sysinfo.bswapin + sysinfo.bswapout;
  srate = (double)((newsios - oldsios) * tinfo.mc_clktck) / (double)delta;
  srate *= (double)BSIZE;
  swap_rate = FILTER * srate + (1 - FILTER) * swap_rate;
  DBPRT(("%s: sios %d srate %.2f swap_rate %.2f\n",
         id, newsios - oldsios, srate, swap_rate))
  oldsios = newsios;
  }

void
dep_initialize(void)
  {
  char *id = "dep_initialize";

  limit(C_PROC, 0, L_CPU, 0); /* set unlimited cpu */

  if (target(MC_GET_TARGET, &tinfo) == -1)
    {
    log_err(errno, id, (char *)"target");
    die(0);
    }

  DBPRT(("Primary machine type name = %s\n", &tinfo.mc_pmt))

  DBPRT(("Number of memory banks = %ld\n", tinfo.mc_bank))
  DBPRT(("Number of started processors = %ld\n", tinfo.mc_ncpu))
  DBPRT(("Instruction Buffer Size (words) = %ld\n", tinfo.mc_ibsz))
  DBPRT(("Main memory size (words) = %ld\n", tinfo.mc_msz))
  DBPRT(("Number of clocks for a memory read = %ld\n", tinfo.mc_mspd))
  DBPRT(("Clock period in picoseconds = %ld\n", tinfo.mc_clk))
  DBPRT(("Number of cluster register sets = %ld\n", tinfo.mc_ncl))
  DBPRT(("Memory bank busy time in clocks = %ld\n", tinfo.mc_bbsy))
  DBPRT(("Number of clock ticks per second = %ld\n", tinfo.mc_clktck))
  DBPRT(("System serial number = %ld\n", tinfo.mc_serial))
  DBPRT(("UNICOS release level = %ld\n", tinfo.mc_rls))

#if SRFS
  var_init();
#endif
  dep_main_loop_cycle();
  }

void
dep_cleanup(void)
  {
  char *id = "dep_cleanup";
  int i;

  log_record(PBSEVENT_SYSTEM, 0, id, "dependent cleanup");

#if SRFS
  var_cleanup();
#endif
  }

/*
 * Internal size decoding routine.
 *
 * Accepts a resource pointer and a pointer to the unsigned long integer
 * to receive the decoded value.  It returns a PBS error code, and the
 * decoded value in the unsigned long integer.
 *
 * For Unicos,
 *
 *  sizeof(word) = sizeof(int)
 */

static int
mm_getsize(resource *pres, unsigned long *ret)
  {
  unsigned long value;

  if (pres->rs_value.at_type != ATR_TYPE_SIZE)
    return (PBSE_ATTRTYPE);

  value = pres->rs_value.at_val.at_size.atsv_num;

  if (pres->rs_value.at_val.at_size.atsv_units ==
      ATR_SV_WORDSZ)
    {
    if (value > ULONG_MAX / sizeof(int))
      return (PBSE_BADATVAL);

    value *= sizeof(int);
    }

  if (value > ULONG_MAX >>
      pres->rs_value.at_val.at_size.atsv_shift)
    return (PBSE_BADATVAL);

  *ret = value << pres->rs_value.at_val.at_size.atsv_shift;

  return (PBSE_NONE);
  }

/*
 * Internal time decoding routine.
 *
 * Accepts a resource pointer and a pointer to the unsigned long integer
 * to receive the decoded value.  It returns a PBS error code, and the
 * decoded value of time in seconds in the unsigned long integer.
 */

static int
mm_gettime(resource *pres, unsigned long *ret)
  {

  if (pres->rs_value.at_type != ATR_TYPE_LONG)
    return (PBSE_ATTRTYPE);

  if (pres->rs_value.at_val.at_long < 0)
    return (PBSE_BADATVAL);

  *ret = pres->rs_value.at_val.at_long;

  return (PBSE_NONE);
  }

/*
 * Internal long decoding routine.
 *
 * Accepts a resource pointer and a pointer to a long integer
 * to receive the decoded value.  It returns a PBS error code, and the
 * decoded value of number in the long integer.
 */

static int
getlong(resource *pres, long *ret)
  {

  if (pres->rs_value.at_type != ATR_TYPE_LONG)
    return (PBSE_ATTRTYPE);

  if (pres->rs_value.at_val.at_long < 0)
    return (PBSE_BADATVAL);

  *ret = pres->rs_value.at_val.at_long;

  return (PBSE_NONE);
  }

/*
 * Internal boolean decoding routine.
 *
 * Accepts a resource pointer and a pointer to the unsigned integer
 * to receive the decoded value.  It returns a PBS error code, and the
 * decoded value of true (1) or false (0).
 */

static int
getbool(resource *pres, unsigned int *ret)
  {
  unsigned int val;

  if (pres->rs_value.at_type != ATR_TYPE_LONG)
    return (PBSE_ATTRTYPE);

  val = pres->rs_value.at_val.at_long;

  if (val != 0 && val != 1)
    return (PBSE_BADATVAL);

  *ret = val;

  return (PBSE_NONE);
  }

static
int
injob(pjob, sesid)
job   *pjob;
pid_t  sesid;
  {
  task  *ptask;

  for (ptask = (task *)GET_NEXT(pjob->ji_tasks);
       ptask;
       ptask = (task *)GET_NEXT(ptask->ti_jobtask))
    {
    if (ptask->ti_qs.ti_sid <= 1)
      continue;

    if (ptask->ti_qs.ti_sid == sesid)
      return TRUE;
    }

  return FALSE;
  }

/*
 * Internal job cpu count decoding routine.
 *
 * Accepts a job pointer.  Returns the sum of all "multi-task"
 * procs in a job.
 */
static unsigned long
cpus_sum(job *pjob)
  {
  static char  *id = "cpus_sum";

  struct proc  *pproc;
  int   i;
  ulong   cpus;

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  cpus = 0;
  pproc = process_table;

  for (i = 0; i < process_table_size; i++, pproc++)
    {
    if (pproc->p_stat == 0) continue;

    if (!injob(pjob, pproc->p_pcomm.pc_sid)) continue;

    if (pproc->p_pcomm.pc_maxcnt <= 1) continue;

    cpus += pproc->p_pcomm.pc_maxcnt;

    DBPRT(("Session: %d, maxcnt = %d, tot =  %d\n",
           pproc->p_pcomm.pc_sid, pproc->p_pcomm.pc_maxcnt, cpus))
    }

  return (cpus);
  }

/*
 * Internal job cpu time decoding routine.
 *
 * Accepts a job pointer.  Returns the sum of all cpu time
 * consumed for all processes executed by the job, in seconds.
 */
static unsigned long
cput_sum(job *pjob)
  {
  static char  *id = "cput_sum";

  struct sess  *psess;
  int   i;
  ulong   cputime = 0;
  int   nps = 0;

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  psess = session_table;

  for (i = 0; i < session_table_size; i++, psess++)
    {
    if (!injob(pjob, psess->s_sid)) continue;

    nps++;

    cputime += (psess->s_ucputime + psess->s_scputime) / CLK_TCK;

    DBPRT(("Session: %d, cput = %d\n", psess->s_sid, cputime))
    }

  if (nps == 0)
    pjob->ji_flags |= MOM_NO_PROC;
  else
    pjob->ji_flags &= ~MOM_NO_PROC;

  return ((unsigned long)((double)cputime * cputfactor));
  }

#define CLICKS 4096
/*
 * Internal job memory decoding routine.
 *
 * Accepts a job pointer.  Returns the sum of all memory
 * consumed for all processes executed by the job, in bytes.
 */
static unsigned long
mem_sum(job *pjob)
  {
  static char  *id = "mem_sum";

  struct sess  *psess;
  int   i;
  ulong   memsize = 0;

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  psess = session_table;

  for (i = 0; i < session_table_size; psess++, i++)
    {
    if (!injob(pjob, psess->s_sid)) continue;

    memsize += ((psess->s_memhiwat) * CLICKS);

    DBPRT(("Session: %d, mem = %d\n", psess->s_sid, memsize))
    }

  return (memsize);
  }

#define BLOCKS 4096
/*
 * Internal job file space decoding routine.
 *
 * Accepts a job pointer.  Returns the sum of all file space
 * consumed for all processes executed by the job, in bytes.
 */
static long
pf_sum(job *pjob)
  {
  static char  *id = "pf_sum";

  struct sess  *psess;
  int   i;
  long   fsize = 0;

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  psess = session_table;

  for (i = 0; i < session_table_size; psess++, i++)
    {
    if (!injob(pjob, psess->s_sid)) continue;

    fsize += ((psess->s_fsblkused) * BLOCKS);

    DBPRT(("Session: %d, pf = %d\n", psess->s_sid, fsize))
    }

  return (fsize);
  }

/*
 * Internal session SDS decoding routine.
 *
 * Accepts a job pointer.  Returns the sum of all SDS
 * consumed for all processes executed by the job, in bytes.
 */
static unsigned long
sds_sum(job *pjob)
  {
  static char  *id = "sds_sum";

  struct sess  *psess;
  int   i;
  ulong   sdssize = 0;

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  psess = session_table;

  for (i = 0; i < session_table_size; psess++, i++)
    {
    if (!injob(pjob, psess->s_sid)) continue;

    sdssize += ((psess->s_sdsuse) * CLICKS);

    DBPRT(("Session: %d, sds = %d\n", psess->s_sid, sdssize))
    }

  return (sdssize);
  }

#if SRFS
/*
 * Internal session SRFS decoding routine.
 *
 * Accepts a job pointer and device num.  Returns the max of all SRFS
 * consumed for the device for all processes executed by the session,
 * in bytes.
 */
static unsigned long srfs_sum(pjob, dev)
job  *pjob;
dev_t dev;
  {
  static char  *id = "srfs_sum";

  struct sess  *psess;
  int   i;
  ulong   srfssize;

  DBPRT(("%s: entered dev %d %s\n", id, dev, pjob->ji_qs.ji_jobid))

  if (dev == -1)
    return 0;

  srfssize = 0;

  psess = session_table;

  for (i = 0; i < session_table_size; psess++, i++)
    {
    if (!injob(pjob, psess->s_sid)) continue;

    srfssize += ((psess->s_srfs[dev].hiwater) * BLOCKS);

    DBPRT(("Session: %d, sds = %d\n", psess->s_sid, srfssize))
    }

  return (srfssize);
  }

static int
srfs_error(char *mes1, char *mes2)
  {
  char buf[512];

  sprintf(buf, "%s (%s) errno=%d", mes1, mes2, errno);
  log_err(errno, mes1, mes2);
  return (error(buf, PBSE_RESCUNAV));
  }

#endif /* SRFS */

/*
 * Internal session procs decoding routine.
 *
 * Accepts a job pointer id.  Returns the number of processes
 * active for the job.
 */
static unsigned long
proc_cnt(job *pjob)
  {
  static char  *id = "proc_cnt";

  struct sess  *psess;
  int   i;
  ulong   nprocs = 0;

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  psess = session_table;

  for (i = 0; i < session_table_size; psess++, i++)
    {
    if (!injob(pjob, psess->s_sid)) continue;

    nprocs += psess->s_nprocs;

    DBPRT(("Session: %d, nprocs = %d\n", psess->s_sid, nprocs))
    }

  return (nprocs);
  }

/*
 * Internal session mpp time decoding routine.
 *
 * Accepts a session id.  Returns the sum of all moo time
 * consumed for all processes executed by the session, in seconds.
 */
static unsigned long
mppt_sum(job *pjob)
  {
  static char  *id = "mppt_ses";

  struct sess  *psess;
  int   i;
  ulong   mpptime = 0;

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  psess = session_table;

  for (i = 0; i < session_table_size; psess++, i++)
    {
    if (!injob(pjob, psess->s_sid)) continue;

    mpptime += psess->s_mpptimeused;

    DBPRT(("Session: %d, mppt = %d\n", psess->s_sid, mpptime))
    }

  return (mpptime);
  }

extern char *msg_momsetlim;

/*
 * Internal error routine
 */
int
error(char *string, int value)
  {
  char  *message;

  assert(string != NULL);
  assert(*string != '\0');

  message = pbse_to_txt(value);

  assert(message != NULL);
  assert(*message != '\0');

  (void)fprintf(stderr, msg_momsetlim, string, message);

  (void)fflush(stderr);

  return (value);
  }

/*
 * which_limit - set either user's limit or udb limit:
 *
 * If user's limit is a default, then use lesser of it or udb
 *
 * Else, use user's limit unless it is greater than the udb and the
 * the udb limit is not unlimited.  This case is an error.
 */
static int
which_limit(
  long rlimit,  /* limit value from resource_limit  */
  long udblimit, /* limit for that resource from UDB */
  int r_flags,  /* resource entry flags (ATR_VFLAG_DEFLT)   */
  int zlimit,  /* true if 0 in udb mean real limit of zero */
  long *rtn_limit /* RETURN: the limit to set */
)
  {
  if (r_flags & ATR_VFLAG_DEFLT)
    {

    /* User's limit is a default value, if default > UDB, use UDB */

    if ((udblimit == MAXUE_LONG) ||
        ((udblimit == 0) && (zlimit == 0)) ||
        (rlimit < udblimit))
      *rtn_limit = rlimit;
    else
      *rtn_limit = udblimit;

    }
  else
    {

    /* user specified an actual limit */

    if (zlimit)
      {
      if ((udblimit != MAXUE_LONG) && (rlimit > udblimit))
        return (PBSE_EXLIMIT);
      }
    else
      {
      if (((udblimit != 0) || (udblimit != MAXUE_LONG)) &&
          (rlimit > udblimit))
        return (PBSE_EXLIMIT);
      }

    *rtn_limit = rlimit;
    }

  return (0);
  }

/*
 * Establish system-enforced limits for the tasks of a job.
 *
 * Run through the resource list, checking the values for all items
 * we recognize.
 *
 * If set_mode is SET_LIMIT_SET, then also set hard limits for the
 * system enforced limits (not-polled).
 * If anything goes wrong with the process, return a PBS error code
 * and print a message on standard error.  A zero-length resource list
 * is not an error.
 *
 * If set_mode is SET_LIMIT_SET the entry conditions are:
 *     1. MOM has already forked, and we are called from the child.
 *     2. The child is still running as root.
 *     3.  Standard error is open to the user's file.
 *
 * If set_mode is SET_LIMIT_ALTER, we are beening called to modify
 * existing limits.  Unlike bsd based systems that use setrlimit, the
 * Cray limit() call can set limits for another session.  Hence all
 * limits can be adjusted.
 */

int
mom_set_limits(
  job *pjob,
  int set_mode /* unused here */
)
  {
  static char *id = "mom_set_limits";
  int         acid;
  int  maxcore;
  int  maxcput;
  int  maxfdm;
  int  maxpcput;
  int  maxmem;
  int  maxmt[MAXUE_TAPETYPE];
  int  maxpmem;
  int  maxpf;
  int  maxppf;
  int  maxsds;
  int  maxpsds;
  int  maxnproc;
  int  maxmppe;
  int  maxmppt;
  int  maxpmppt;
  int  maxsockbf;
  int  niceval;
  int  num;
  char  *pc;
  char  *phost;
  char  *pname;

  struct udb *pudb;
  int  sessid;
  int  retval;
  attribute *pat;
  resource *pres;
  task  *ptask;
  unsigned long value; /* place in which to build resource value */
#if SRFS
  int  srfs_tmp = -1;
  int  srfs_big = -1;
  int  srfs_fast = -1;
  int  srfs_assist = 0;
  extern char *fast_dir;
  extern char *big_dir;
  extern char *tmp_dir;

  struct  srfs_req_s srfs_brk;

  struct  fsres_s  srfsinfo;

  log_buffer[0] = '\0';

#define SRFS_SET(fs, val) \
  if (val > 0) { \
    DBPRT(("%s: going to set %s to %d\n", id, fs, val)) \
    if (srfs_assist) { \
      if (quotactl(fs, SRFS_INFO, (caddr_t)&srfsinfo) == -1) \
        return (srfs_error(fs, "SRFS_INFO")); \
      num = srfsinfo.reserve + val; \
      if (quotactl(fs, SRFS_RESERVE, (caddr_t)&num) == -1) \
        return (srfs_error(fs, "SRFS_RESERVE")); \
      } \
    srfs_brk.count = val; \
    if (quotactl(fs, SRFS_SBRK, (char *)&srfs_brk) == -1) \
      return (srfs_error(fs, "SRFS_SBRK")); \
    }

#endif /* SRFS */

  DBPRT(("%s: entered\n", id))
  assert(pjob != NULL);
  assert(pjob->ji_wattr[(int)JOB_ATR_resource].at_type == ATR_TYPE_RESC);

  /* initialize limits to values from user's UDB */

  if (set_mode == SET_LIMIT_SET)
    {
    getsysudb(); /* get the priviled (locked) udb */
    }

  pudb = getudbuid(pjob->ji_qs.ji_un.ji_momt.ji_exuid);

  if (pudb == UDB_NULL)
    return (PBSE_BADUSER);

  if (set_mode == SET_LIMIT_SET)
    {

    /* can this user run batch jobs */

    if (pudb->ue_permbits & PERMBITS_NOBATCH)
      {
      endudb();
      return (PBSE_QACESS);
      }

    /* set Account ID: 1 - if supplied and is valid acid, */
    /* or 2 - default acid from UDB         */

    pat = &pjob->ji_wattr[(int)JOB_ATR_account];

    if (pat->at_flags & ATR_VFLAG_SET)
      {
      pname = pat->at_val.at_str;

      if ((acid = nam2acid(pname)) == -1)
        {
        for (pc = pname; *pc; ++pc)
          {
          if (!isdigit((int)*pc))
            return (error(pname, PBSE_BADACCT));
          }

        if (acid2nam(acid = atoi(pname)) == (char *)0)
          return (error(pname, PBSE_BADACCT));
        }

      for (num = 0; num < MAXVIDS; ++num)
        {
        if (pudb->ue_acids[num] == acid)
          break;
        }

      if (num == MAXVIDS)
        return (error(pname, PBSE_BADACCT));
      }
    else
      {
      acid = pudb->ue_acids[0];
      }

    if (acctid(0, acid) == -1)
      return (error("Account", PBSE_BADACCT));



    /* lock and update the UDB with the batch host/time */

    if (lockudb() < 0)
      {
      log_err(udb_errno, id, (char *)"Unable to lock UDB");
      }
    else
      {
      pudb->ue_batchtime = time_now;

      phost = arst_string("PBS_O_HOST",
                          &pjob->ji_wattr[(int)JOB_ATR_variables]);

      if ((phost == (char *)0) ||
          ((phost = strchr(phost, (int)'=')) == (char *)0))
        log_err(-1, id, (char *)"PBS_O_HOST not set");

      strncpy(pudb->ue_batchhost, phost + 1, MAXUE_HOSTNAME);

      pudb->ue_batchhost[MAXUE_HOSTNAME] = '\0';

      if (rewriteudb(pudb) < 0)
        log_err(udb_errno, id, (char *)"UDB Update failed");
      }
    }

  endudb();

  /*
   * set basic limits to values from the user's UDB entry;  later
   * we have to check for values == MAXUE_LONG, meaning unlimited,
   * where in the limit() call it must be changed to 0.
   * A click is 512 words = 4096 bytes
   */

  maxcput  = pudb->ue_jcpulim[UDBRC_BATCH]; /* seconds */
  maxpcput = pudb->ue_pcpulim[UDBRC_BATCH]; /* seconds */
  maxmem   = pudb->ue_jmemlim[UDBRC_BATCH]; /* clicks */
  maxpmem  = pudb->ue_pmemlim[UDBRC_BATCH]; /* clicks */
  maxpf    = pudb->ue_jfilelim[UDBRC_BATCH]; /* clicks */
  maxppf   = pudb->ue_pfilelim[UDBRC_BATCH]; /* clicks */
  maxsds   = pudb->ue_jsdslim[UDBRC_BATCH]; /* clicks */
  maxpsds  = pudb->ue_psdslim[UDBRC_BATCH]; /* clicks */
  maxnproc = pudb->ue_jproclim[UDBRC_BATCH];
  maxmppe  = pudb->ue_jpelimit[UDBRC_BATCH];
  maxmppt  = pudb->ue_jmpptime[UDBRC_BATCH];
  maxpmppt = pudb->ue_pmpptime[UDBRC_BATCH];
  niceval  = pudb->ue_nice[UDBRC_BATCH];
  maxcore  = pudb->ue_pcorelim[UDBRC_BATCH]; /* clicks */
  maxfdm   = pudb->ue_pfdlimit[UDBRC_BATCH];
  maxsockbf = pudb->ue_jsocbflim[UDBRC_BATCH];

  for (num = 0; num < MAXUE_TAPETYPE; ++num)
    maxmt[num] = pudb->ue_jtapelim[UDBRC_BATCH][num];

  pres = (resource *)
         GET_NEXT(pjob->ji_wattr[(int)JOB_ATR_resource].at_val.at_list);

  /*
   * Cycle through all the resource specifications,
   * setting limits appropriately.
   */

  while (pres != NULL)
    {
    assert(pres->rs_defin != NULL);
    pname = pres->rs_defin->rs_name;
    assert(pname != NULL);
    assert(*pname != '\0');

    if (strcmp(pname, "cput") == 0)
      {
      if (igncput == FALSE)
        {
        retval = mm_gettime(pres, &value);
        value = (unsigned long)((double)value / cputfactor);

        if (retval != PBSE_NONE)
          return (error(pname, retval));

        if (value > INT_MAX)
          return (error(pname, PBSE_BADATVAL));

        retval = which_limit(value, maxcput,
          pres->rs_value.at_flags, 0, &maxcput);

        if (retval != PBSE_NONE)
          return (error(pname, retval));
        }
      }
    else if (strcmp(pname, "pcput") == 0)
      {
      if (igncput == FALSE) 
        {
        retval = mm_gettime(pres, &value);
        value = (unsigned long)((double)value / cputfactor);

        if (retval != PBSE_NONE)
          return (error(pname, retval));

        if (value > INT_MAX)
          return (error(pname, PBSE_BADATVAL));

        retval = which_limit(value, maxpcput,
          pres->rs_value.at_flags, 0, &maxpcput);

        if (retval != PBSE_NONE)
          return (error(pname, retval));
      }
    else if (strcmp(pname, "mem") == 0)
      {
      if (ignmem == FALSE) 
        {
        retval = mm_getsize(pres, &value);

        if (retval != PBSE_NONE)
          return (error(pname, retval));

        if (value > INT_MAX)
          return (error(pname, PBSE_BADATVAL));

        retval = which_limit((value + 4095) / 4096, maxmem,
          pres->rs_value.at_flags, 0, &maxmem);

        if (retval != PBSE_NONE)
          return (error(pname, retval));
        }
      }
    else if (strcmp(pname, "pmem") == 0)
      {
      if (ignmem == FALSE) 
        {
        retval = mm_getsize(pres, &value);

        if (retval != PBSE_NONE)
          return (error(pname, retval));

        if (value > INT_MAX)
          return (error(pname, PBSE_BADATVAL));

        retval = which_limit((value + 4095) / 4096, maxpmem,
          pres->rs_value.at_flags, 0, &maxpmem);

         if (retval != PBSE_NONE)
           return (error(pname, retval));
        }
      }
    else if (strcmp(pname, "vmem")  == 0)   /* ignore */
      {
      }
    else if (strcmp(pname, "pvmem") == 0)   /* ignore */
      {
      }
    else if (strcmp(pname, "file")  == 0)   /* ignore */
      {
      }
    else if ((strcmp(pname, "mta") == 0) ||
             (strcmp(pname, "mtb") == 0) ||
             (strcmp(pname, "mtc") == 0) ||
             (strcmp(pname, "mtd") == 0) ||
             (strcmp(pname, "mte") == 0) ||
             (strcmp(pname, "mtf") == 0) ||
             (strcmp(pname, "mtg") == 0) ||
             (strcmp(pname, "mth") == 0))
      {
      num = (int) * (pname + 2) - (int)'a';
      maxmt[num] = pres->rs_value.at_val.at_long;
      retval = which_limit(pres->rs_value.at_val.at_long,
                           maxmt[num],
                           pres->rs_value.at_flags,
                           0,
                           &maxmt[num]);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "pf") == 0)
      {
      retval = mm_getsize(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      retval = which_limit((value + 4095) / 4096, maxpf,
                           pres->rs_value.at_flags,
                           0,
                           &maxpf);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "ppf") == 0)
      {
      retval = mm_getsize(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      retval = which_limit((value + 4095) / 4096, maxppf,
                           pres->rs_value.at_flags,
                           0,
                           &maxppf);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "sds") == 0)
      {
      retval = mm_getsize(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      retval = which_limit((value + 4095) / 4096, maxsds,
                           pres->rs_value.at_flags, 1, &maxsds);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "psds") == 0)
      {
      retval = mm_getsize(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      retval = which_limit((value + 4095) / 4096, maxpsds,
                           pres->rs_value.at_flags, 1, &maxpsds);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "procs") == 0)
      {
      retval = getlong(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      retval = which_limit(value, maxnproc,
                           pres->rs_value.at_flags, 0, &maxnproc);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "mppe") == 0)
      {
      retval = getlong(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      retval = which_limit(value, maxmppe,
                           pres->rs_value.at_flags, 1, &maxmppe);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "mppt") == 0)
      {
      retval = mm_gettime(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      retval = which_limit(value, maxmppt,
                           pres->rs_value.at_flags, 1, &maxmppt);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "pmppt") == 0)
      {
      retval = mm_gettime(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      retval = which_limit(value, maxpmppt,
                           pres->rs_value.at_flags, 1, &maxpmppt);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

#if SRFS
      }
    else if (strcmp(pname, "srfs_tmp") == 0)
      {
      retval = mm_getsize(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      value = (value + 4095) / 4096;

      srfs_tmp = value;
      }
    else if (strcmp(pname, "srfs_big") == 0)
      {
      retval = mm_getsize(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      value = (value + 4095) / 4096;

      srfs_big = value;
      }
    else if (strcmp(pname, "srfs_fast") == 0)
      {
      retval = mm_getsize(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      if (value > INT_MAX)
        return (error(pname, PBSE_BADATVAL));

      value = (value + 4095) / 4096;

      srfs_fast = value;
      }
    else if (strcmp(pname, "srfs_assist") == 0)
      {
      retval = getbool(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));

      srfs_assist = value;

#endif /* SRFS */
      }
    else if (strcmp(pname, "walltime") == 0)
      {
      retval = mm_gettime(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "ncpus") == 0)
      {
      char hold[16];

      extern struct var_table vtable;

      if ((int)pres->rs_value.at_val.at_long < 0)
        return (error(pname, PBSE_BADATVAL));

      sprintf(hold, "%d", (int)pres->rs_value.at_val.at_long);

      bld_env_variables(&vtable, "NCPUS", hold);
      }
    else if (strcmp(pname, "nice") == 0)
      {
      if (set_mode == SET_LIMIT_SET)
        {
        niceval = (int)pres->rs_value.at_val.at_long;
        }
      }
    else if ((pres->rs_defin->rs_flags & ATR_DFLAG_RMOMIG) == 0)
      /* don't recognize and nor marked as ignore by mom */
      return (error(pname, PBSE_UNKRESC));

    pres = (resource *)GET_NEXT(pres->rs_link);
    }

  /*
   * Set the limits
   *
   * If pcput, pmem, or ppf is unlimited but the corresponding session
   * limit is not, use the session limit for the process limit.
   */

  for (ptask = (task *)GET_NEXT(pjob->ji_tasks);
       ptask != NULL;
       ptask = (task *)GET_NEXT(ptask->ti_jobtask))
    {
    sessid = ptask->ti_qs.ti_sid;

    if (maxcput == MAXUE_LONG) /* if unlimited */
      maxcput = 0;

    if (limit(C_SESS, sessid, L_CPU, maxcput * CLK_TCK) < 0)
      return (error("cput", PBSE_SYSTEM));

    if ((maxpcput == MAXUE_LONG) ||
        (maxpcput == 0))   /* if unlimited */
      {
      if (maxcput != 0)
        maxpcput = maxcput;
      else
        maxpcput = 0;
      }
    else if ((maxpcput > maxcput) && (maxcput != 0))
      {
      maxpcput = maxcput;
      }

    if (limit(C_SESSPROCS, sessid, L_CPU, maxpcput * CLK_TCK) < 0)
      return (error("pcput", PBSE_SYSTEM));

    if (maxmem == MAXUE_LONG) /* if unlimited */
      maxmem = 0;

    if (limit(C_SESS, sessid, L_MEM, maxmem) < 0)
      return (error("mem", PBSE_SYSTEM));

    if ((maxpmem == MAXUE_LONG) ||
        (maxpmem == 0))   /* if unlimited */
      {
      if (maxmem != 0)
        maxpmem = maxmem;
      else
        maxpmem = 0;
      }
    else if ((maxpmem > maxmem) && (maxmem != 0))
      {
      maxpmem = maxmem;
      }

    if (limit(C_SESSPROCS, sessid, L_MEM, maxpmem) < 0)
      return (error("pmem", PBSE_SYSTEM));

    if (maxpf == MAXUE_LONG) /* if unlimited */
      maxpf = 0;

    if (limit(C_SESS, sessid, L_FSBLK, maxpf) < 0)
      return (error("pf", PBSE_SYSTEM));

    if ((maxppf == MAXUE_LONG) ||
        (maxppf == 0))   /* if unlimited */
      {
      if (maxpf != 0)
        maxppf = maxpf;
      else
        maxppf = 0;
      }
    else if ((maxppf > maxpf) && (maxpf != 0))
      {
      maxppf = maxpf;
      }

    if (limit(C_SESSPROCS, sessid, L_FSBLK, maxppf) < 0)
      return (error("ppf", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_SDS, maxsds) < 0)
      return (error("sds", PBSE_SYSTEM));

    if (limit(C_SESSPROCS, sessid, L_SDS, maxpsds) < 0)
      return (error("psds", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_CPROC, maxnproc) < 0)
      return (error("procs", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_TAPE0, maxmt[0]) < 0)
      return (error("mta", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_TAPE1, maxmt[1]) < 0)
      return (error("mtb", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_TAPE2, maxmt[2]) < 0)
      return (error("mtc", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_TAPE3, maxmt[3]) < 0)
      return (error("mtd", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_TAPE4, maxmt[4]) < 0)
      return (error("mte", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_TAPE5, maxmt[5]) < 0)
      return (error("mtf", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_TAPE6, maxmt[6]) < 0)
      return (error("mtg", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_TAPE7, maxmt[7]) < 0)
      return (error("mth", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_MPPE, maxmppe) < 0)
      return (error("mppe", PBSE_SYSTEM));

    if (limit(C_SESS, sessid, L_MPPT, maxmppt) < 0)
      return (error("mppt", PBSE_SYSTEM));

    if (limit(C_SESSPROCS, sessid, L_MPPT, maxpmppt) < 0)
      return (error("pmppt", PBSE_SYSTEM));

    if (maxcore == MAXUE_LONG) /* if unlimited */
      maxcore = 0;

    if (limit(C_SESSPROCS, sessid, L_CORE, maxcore) < 0)
      return (error("core", PBSE_SYSTEM));

    if (maxfdm == MAXUE_LONG) /* if unlimited */
      maxfdm = 0;

    if (limit(C_SESSPROCS, sessid, L_FDM, maxfdm) < 0)
      return (error("fdm", PBSE_SYSTEM));

    if (maxsockbf == MAXUE_LONG) /* if unlimited */
      maxsockbf = 0;

    if (limit(C_SESS, sessid, L_SOCKBF, maxsockbf) < 0)
      return (error("sockbuf", PBSE_SYSTEM));

#if SRFS
    srfs_brk.id = 0; /* current job */

    SRFS_SET(tmp_dir, srfs_tmp)
    SRFS_SET(big_dir, srfs_big)
    SRFS_SET(fast_dir, srfs_fast)

#endif /* SRFS */

    if (set_mode == SET_LIMIT_SET)
      {
      errno = 0;

      if ((nice(niceval) == -1) && (errno != 0))
        return (error("nice", PBSE_SYSTEM));
      }
    }

  return (PBSE_NONE);
  }

/*
 * State whether MOM main loop has to poll this job to determine if some
 * limits are being exceeded.
 *
 * Sets flag TRUE if polling is necessary, FALSE otherwise.  Actual
 * polling is done using the mom_over_limit machine-dependent function.
 */

int
mom_do_poll(job *pjob)
  {
  static char *id = "mom_do_poll";
  char  *pname;
  resource *pres;

  DBPRT(("%s: entered\n", id))
  assert(pjob != NULL);
  assert(pjob->ji_wattr[(int)JOB_ATR_resource].at_type == ATR_TYPE_RESC);
  pres = (resource *)
         GET_NEXT(pjob->ji_wattr[(int)JOB_ATR_resource].at_val.at_list);

  while (pres != NULL)
    {
    assert(pres->rs_defin != NULL);
    pname = pres->rs_defin->rs_name;
    assert(pname != NULL);
    assert(*pname != '\0');

    if (strcmp(pname, "ncpus") == 0) return (TRUE);

    if (strcmp(pname, "walltime") == 0) return (TRUE);

    pres = (resource *)GET_NEXT(pres->rs_link);
    }

  return (FALSE);
  }

/*
 * Setup for polling.
 *
 * Open kernel device and get namelist info.
 */
int
mom_open_poll(void)
  {
  static char *id = "mom_open_poll";

  DBPRT(("%s: entered\n", id))

#if SRFS
#define SETDEV(name, var) \
  if ((dir = var_value(name)) != NULL) { \
    i |= chk_file_sec(dir, 1, 1, S_IWGRP|S_IWOTH, 1, NULL); \
    if (quotactl(dir, SRFS_INFO, (caddr_t)&srfsinfo) == -1) \
      log_err(errno, id, dir); \
    else { \
      var = srfsinfo.index; \
      DBPRT(("%s: got %d for %s %s\n", id, var, name, dir)) \
      } \
    }

    {
    int  i;

    struct  fsres_s  srfsinfo;
    char  *dir, *var_value();

    var_init();

    i = 0;
    SETDEV("TMPDIR", srfs_tmp_dev)
    SETDEV("BIGDIR", srfs_big_dev)
    SETDEV("FASTDIR", srfs_fast_dev)
#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)

    if (i) return (PBSE_PERM);

#endif /* NO_SECURITY_CHECK */

    }

#else
#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)

  if (chk_file_sec(TMP_DIR, 1, 1, S_IWGRP | S_IWOTH, 1, NULL)) return (PBSE_PERM);

#endif /* NO_SECURITY_CHECK */
#endif /* SRFS */
  return (PBSE_NONE);
  }

/*
 * Declare start of polling loop.
 *
 * Until the next call to mom_get_sample, all mom_over_limit calls will
 * use the same data.  Returns a PBS error code.
 */

int
mom_get_sample(void)
  {
  static char *id = "mom_get_sample";

  struct tbs info;

  struct proc *pp;

  struct pcomm *pc;
  int  i, pbase;

  DBPRT(("%s: entered\n", id))

  if (session_table != NULL)
    free(session_table);

  if (tabinfo(SESS, &info) == -1)
    return (PBSE_SYSTEM);

  session_table_size = info.ent * info.len;

  session_table = (struct sess*)calloc(1, session_table_size);

  if (session_table == NULL)
    return (PBSE_SYSTEM);

  if (tabread(SESS, (char *) session_table, session_table_size,
              info.head) == -1)
    return (PBSE_SYSTEM);

  session_table_size = info.ent;

  if (process_table != NULL)
    free(process_table);

  if (tabinfo(PROCTAB, &info) == -1)
    return (PBSE_SYSTEM);

  process_table_size = info.ent * info.len;

  process_table = (struct proc*)calloc(1, process_table_size);

  if (process_table == NULL)
    return (PBSE_SYSTEM);

  if (tabread(PROCTAB, (char *) process_table, process_table_size,
              info.head) == -1)
    return (PBSE_SYSTEM);

  process_table_size = info.ent;

  pbase = (int)info.addr;

  for (pp = process_table, i = 0; i < process_table_size; pp++, i++)
    {
    if (pp->p_stat == 0)
      continue;

    if ((pc = pp->p_pc) != NULL)
      {
      pp->p_pc = (struct pcomm *)((int)pc - pbase +
                                  (int)process_table);
      }
    }

  return (PBSE_NONE);
  }

/*
 * Measure job resource usage and compare with its limits.
 *
 * If it has exceeded any well-formed polled limit return TRUE.
 * Otherwise, return FALSE.
 */

int
mom_over_limit(job *pjob)
  {
  static char *id = "mom_over_limit";
  char  *pname;
  int  retval;
  unsigned long value;
  resource *pres;
  int  num;

  assert(pjob != NULL);
  assert(pjob->ji_wattr[(int)JOB_ATR_resource].at_type == ATR_TYPE_RESC);
  pres = (resource *)
         GET_NEXT(pjob->ji_wattr[(int)JOB_ATR_resource].at_val.at_list);

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  for (; pres != NULL; pres = (resource *)GET_NEXT(pres->rs_link))
    {
    assert(pres->rs_defin != NULL);
    pname = pres->rs_defin->rs_name;
    assert(pname != NULL);
    assert(*pname != '\0');

    if ((strcmp(pname, "ncpus") == 0) && (igncput == FALSE))
      {
      retval = getlong(pres, &value);

      if (retval != PBSE_NONE)
        continue;

      if ((num = cpus_sum(pjob)) > value)
        {
        sprintf(log_buffer,
                "ncpus %d exceeded limit %d",
                num, value);
        return (TRUE);
        }
      }

    if (ignwalltime == 0 && strcmp(pname, "walltime") == 0)
      {
      if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) == 0)
        continue;

      retval = mm_gettime(pres, &value);

      if (retval != PBSE_NONE)
        continue;

      num = (unsigned long)((double)(time_now - pjob->ji_qs.ji_stime) * wallfactor);

      if (num > value)
        {
        sprintf(log_buffer,
                "walltime %d exceeded limit %d",
                num, value);
        return (TRUE);
        }
      }
    }

  return (FALSE);
  }

/*
 * Update the job attribute for resources used.
 *
 * The first time this is called for a job, set up resource entries for
 * each resource that can be reported for this machine.  Fill in the
 * correct values.  Return an error code.
 */
int
mom_set_use(job *pjob)
  {
  static char  *id = "mom_set_use";
  resource  *pres;
  attribute  *at;
  resource_def  *rd;
  unsigned long  *ulp, unum;
  long   *lp, num;

  assert(pjob != NULL);
  at = &pjob->ji_wattr[(int)JOB_ATR_resc_used];
  assert(at->at_type == ATR_TYPE_RESC);

  if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_Suspend) != 0)
    return (PBSE_NONE); /* job suspended, don't track it */

  DBPRT(("%s: entered %s\n", id, pjob->ji_qs.ji_jobid))

  at->at_flags |= ATR_VFLAG_MODIFY;

  if ((at->at_flags & ATR_VFLAG_SET) == 0)
    {
    at->at_flags |= ATR_VFLAG_SET;
#if SRFS
    rd = find_resc_def(svr_resc_def, "srfs_tmp", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_SIZE;
    pres->rs_value.at_val.at_size.atsv_shift = 10; /* KB */
    pres->rs_value.at_val.at_size.atsv_units = ATR_SV_BYTESZ;

    rd = find_resc_def(svr_resc_def, "srfs_big", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_SIZE;
    pres->rs_value.at_val.at_size.atsv_shift = 10; /* KB */
    pres->rs_value.at_val.at_size.atsv_units = ATR_SV_BYTESZ;

    rd = find_resc_def(svr_resc_def, "srfs_fast", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_SIZE;
    pres->rs_value.at_val.at_size.atsv_shift = 10; /* KB */
    pres->rs_value.at_val.at_size.atsv_units = ATR_SV_BYTESZ;
#endif /* SRFS */

    rd = find_resc_def(svr_resc_def, "ncpus", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_LONG;

    rd = find_resc_def(svr_resc_def, "cput", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_LONG;

    rd = find_resc_def(svr_resc_def, "mem", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_SIZE;
    pres->rs_value.at_val.at_size.atsv_shift = 10; /* KB */
    pres->rs_value.at_val.at_size.atsv_units = ATR_SV_BYTESZ;

    rd = find_resc_def(svr_resc_def, "pf", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_SIZE;
    pres->rs_value.at_val.at_size.atsv_shift = 10; /* KB */
    pres->rs_value.at_val.at_size.atsv_units = ATR_SV_BYTESZ;

    rd = find_resc_def(svr_resc_def, "sds", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_SIZE;
    pres->rs_value.at_val.at_size.atsv_shift = 10; /* KB */
    pres->rs_value.at_val.at_size.atsv_units = ATR_SV_BYTESZ;

    rd = find_resc_def(svr_resc_def, "procs", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_LONG;

    rd = find_resc_def(svr_resc_def, "mppt", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_LONG;

    rd = find_resc_def(svr_resc_def, "walltime", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_LONG;
    }

  rd = find_resc_def(svr_resc_def, "ncpus", svr_resc_size);

  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  lp = &pres->rs_value.at_val.at_long;
  num = cpus_sum(pjob);
  *lp = max(*lp, num);

  rd = find_resc_def(svr_resc_def, "cput", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  lp = &pres->rs_value.at_val.at_long;
  num = cput_sum(pjob);
  *lp = max(*lp, num);

  rd = find_resc_def(svr_resc_def, "mem", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  ulp = &pres->rs_value.at_val.at_size.atsv_num;
  unum = (mem_sum(pjob) + 1023) >> 10; /* KB */
  *ulp = max(*ulp, unum);

  rd = find_resc_def(svr_resc_def, "pf", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  lp = (long *) & pres->rs_value.at_val.at_size.atsv_num;
  num = (pf_sum(pjob) + 1023) >> 10; /* KB */
  *lp = max(*lp, num);

  rd = find_resc_def(svr_resc_def, "sds", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  ulp = &pres->rs_value.at_val.at_size.atsv_num;
  unum = (sds_sum(pjob) + 1023) >> 10; /* KB */
  *ulp = max(*ulp, unum);

#if SRFS
  rd = find_resc_def(svr_resc_def, "srfs_tmp", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  ulp = &pres->rs_value.at_val.at_size.atsv_num;
  unum = (srfs_sum(pjob, srfs_tmp_dev) + 1023) >> 10; /* KB */
  *ulp = max(*ulp, unum);

  rd = find_resc_def(svr_resc_def, "srfs_big", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  ulp = &pres->rs_value.at_val.at_size.atsv_num;
  unum = (srfs_sum(pjob, srfs_big_dev) + 1023) >> 10; /* KB */
  *ulp = max(*ulp, unum);

  rd = find_resc_def(svr_resc_def, "srfs_fast", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  ulp = &pres->rs_value.at_val.at_size.atsv_num;
  unum = (srfs_sum(pjob, srfs_fast_dev) + 1023) >> 10; /* KB */
  *ulp = max(*ulp, unum);
#endif /* SRFS */

  rd = find_resc_def(svr_resc_def, "procs", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  lp = &pres->rs_value.at_val.at_long;
  num = proc_cnt(pjob);
  *lp = max(*lp, num);

  rd = find_resc_def(svr_resc_def, "mppt", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  lp = &pres->rs_value.at_val.at_long;
  num = mppt_sum(pjob);
  *lp = max(*lp, num);

  rd = find_resc_def(svr_resc_def, "walltime", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  pres->rs_value.at_val.at_long = (long)((double)(time_now - pjob->ji_qs.ji_stime) * wallfactor);

  return (PBSE_NONE);
  }

/*
 * Kill a task session.
 * Call with the task pointer and a signal number.
 */
int
kill_task(task *ptask, int sig, int pg)
  {
  static char *id = "kill_task";
  int  ct = 1;
  int  sesid;

  sesid = ptask->ti_qs.ti_sid;

  if (sesid > 1)
    {
    if (killm(C_JOB, sesid, sig) == -1)
      {
      if (errno != ESRCH)
        {
        sprintf(log_buffer, "killm: sid=%d sig=%d", sesid, sig);
        log_err(errno, id, log_buffer);
        }
      else
        {
        ct = 0;
        sprintf(log_buffer, "killm: sid=%d sig=%d", sesid, sig);
        log_record(PBSEVENT_DEBUG, PBS_EVENTCLASS_JOB,
                   ptask->ti_job->ji_qs.ji_jobid, log_buffer);
        }
      }
    }

  return ct;
  }

/*
 * Clean up everything related to polling.
 *
 * In the case of the sun, close the kernal if it is open.
 */
int
mom_close_poll(void)
  {
  static char *id = "mom_close_poll";

  DBPRT(("%s: entered\n", id))

  return (PBSE_NONE);
  }

/*
 * mom_does_checkpoint
 */

int
mom_does_checkpoint(void)
  {
  return (CST_MACH_DEP);
  }

/*
 * Checkpoint the job.
 *
 * If abort is TRUE, kill it too.
 */

int
mach_checkpoint(task *ptask, char *path, int abort)
  {
  int cprtn;
  long flags = 0;

  if (abort)
    flags = CHKPNT_KILL;

  cprtn = checkpoint(C_SESS, ptask->ti_qs.ti_sid, path, flags);

  return cprtn;
  }

/*
 * Restart the job from the checkpoint file.
 *
 * Return the session/job id
 */

long
mach_restart(task *ptask, char *path)
  {
  int sid;

  sid = restart(path, 0);
  return sid;
  }

int
getprocs(void)
  {
  static unsigned int lastproc = 0;

  if (lastproc == reqnum) /* don't need new proc table */
    return process_table_size;

  if (mom_get_sample() != PBSE_NONE)
    return 0;

  lastproc = reqnum;

  return(process_table_size);
  }

char *
cput_job(jobid)
pid_t jobid;
  {
  char  *id = "cput_job";
  int  i, nproc;
  int  found = 0;
  time_t  addtime;
  double  cputime;

  if ((nproc = getprocs()) == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  cputime = 0.0;

  for (i = 0; i < nproc; i++)
    {

    register struct proc    *pp = &process_table[i];

    register struct pcomm *pc;

    if (pp->p_stat == 0)
      continue;

    if ((pc = pp->p_pc) == NULL)
      continue;

    if (jobid != pc->pc_sid)
      continue;

    found = 1;

    addtime = pp->p_utime + pp->p_stime + pp->p_sctime +
              pc->pc_cutime + pc->pc_cstime;

    DBPRT(("%s: pid %d time=%d (%d + %d + %d + %d + %d)\n",
           id, pp->p_pid, addtime,
           pp->p_utime, pp->p_stime, pp->p_sctime,
           pc->pc_cutime, pc->pc_cstime))

    cputime += (double)addtime / (double)CLK_TCK;

    }

  if (found)
    {
    sprintf(ret_string, "%.2f", cputime * cputfactor);
    return ret_string;
    }

  rm_errno = RM_ERR_EXIST;

  return NULL;
  }

char *
cput_proc(pid)
pid_t pid;
  {
  char  *id = "cput_pid";
  int  i, nproc;
  time_t  addtime;

  if ((nproc = getprocs()) == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  for (i = 0; i < nproc; i++)
    {

    register struct proc    *pp = &process_table[i];

    register struct pcomm *pc;

    if (pp->p_stat == 0)
      continue;

    if (pid != pp->p_pid)
      continue;

    if ((pc = pp->p_pc) == NULL)
      addtime = 0;
    else
      {
      addtime = pp->p_utime + pp->p_stime + pp->p_sctime +
                pc->pc_cutime + pc->pc_cstime;
      DBPRT(("%s: pid %d time=%d (%d + %d + %d + %d + %d)\n",
             id, pid, addtime,
             pp->p_utime, pp->p_stime, pp->p_sctime,
             pc->pc_cutime, pc->pc_cstime))
      }

    sprintf(ret_string, "%.2f", ((double)addtime / (double)CLK_TCK) * cputfactor);

    return ret_string;
    }

  rm_errno = RM_ERR_EXIST;

  return NULL;
  }

char *
cput(struct rm_attribute *attrib)
  {
  char   *id = "cput";
  int   value;

  if (attrib == NULL)
    {
    log_err(-1, id, no_parm);
    rm_errno = RM_ERR_NOPARAM;
    return NULL;
    }

  if ((value = atoi(attrib->a_value)) == 0)
    {
    sprintf(log_buffer, "bad param: %s", attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (momgetattr(NULL))
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (strcmp(attrib->a_qualifier, "session") == 0)
    return (cput_job((pid_t)value));
  else if (strcmp(attrib->a_qualifier, "proc") == 0)
    return (cput_proc((pid_t)value));
  else
    {
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }
  }

char *
mem_job(jobid)
pid_t jobid;
  {
  char  *id = "mem_job";
  int  memsize;
  int  i, nproc;
  int  found = 0;

  if ((nproc = getprocs()) == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  memsize = 0;

  for (i = 0; i < nproc; i++)
    {

    register struct proc    *pp = &process_table[i];

    register struct pcomm *pc;

    if (pp->p_stat == 0)
      continue;

    if ((pc = pp->p_pc) == NULL)
      continue;

    if (jobid != pc->pc_sid)
      continue;

    found = 1;

    memsize += pc->pc_size;

    DBPRT(("%s: found pid %d size=%d total=%d\n", id, pp->p_pid,
           ctob(pc->pc_size), ctob(memsize)))
    }

  if (found)
    {
    sprintf(ret_string, "%ukb", ctob(memsize) >> 10); /* KB */
    return ret_string;
    }

  rm_errno = RM_ERR_EXIST;

  return NULL;
  }

char *
mem_proc(pid)
pid_t pid;
  {
  char  *id = "mem_proc";
  int  i, nproc;

  if ((nproc = getprocs()) == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  for (i = 0; i < nproc; i++)
    {

    register struct proc    *pp = &process_table[i];

    register struct pcomm *pc;

    if (pp->p_stat == 0)
      continue;

    if (pid != pp->p_pid)
      continue;

    if ((pc = pp->p_pc) == NULL)
      break;

    sprintf(ret_string, "%ukb", ctob(pc->pc_size) >> 10); /* KB */

    return ret_string;
    }

  rm_errno = RM_ERR_EXIST;

  return NULL;
  }

char *
mem(struct rm_attribute *attrib)
  {
  char  *id = "mem";
  int  value;

  if (attrib == NULL)
    {
    log_err(-1, id, no_parm);
    rm_errno = RM_ERR_NOPARAM;
    return NULL;
    }

  if ((value = atoi(attrib->a_value)) == 0)
    {
    sprintf(log_buffer, "bad param: %s", attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (momgetattr(NULL))
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (strcmp(attrib->a_qualifier, "session") == 0)
    return (mem_job((pid_t)value));
  else if (strcmp(attrib->a_qualifier, "proc") == 0)
    return (mem_proc((pid_t)value));
  else
    {
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }
  }

char *
sessions(struct rm_attribute *attrib)
  {
  char   *id = "sessions";
  char   *fmt;
  int   i, j, nproc, njids = 0;
  pid_t   *jids, *hold;
  static  int maxjid = 300;
  register pid_t jobid;

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if ((nproc = getprocs()) == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  if ((jids = (pid_t *)calloc(maxjid, sizeof(pid_t))) == NULL)
    {
    log_err(errno, id, (char *)"no memory");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  /*
  ** Search for members of session
  */
  for (i = 0; i < nproc; i++)
    {

    register struct proc    *pp = &process_table[i];

    if (pp->p_stat == 0)
      continue;

    if (pp->p_pc == NULL)
      continue;

    if (pp->p_pc->pc_suid == 0)
      continue;

    jobid = pp->p_pc->pc_sid;

    DBPRT(("%s[%d]: pid: %d sid %d\n",
           id, njids, pp->p_pid, jobid))

    for (j = 0; j < njids; j++)
      {
      if (jids[j] == jobid)
        break;
      }

    if (j == njids)    /* not found */
      {
      if (njids == maxjid)   /* need more space */
        {
        maxjid += 100;
        hold = (pid_t *)realloc(jids,
                                maxjid * sizeof(pid_t));

        if (hold == NULL)
          {
          log_err(errno, id, (char *)"realloc");
          rm_errno = RM_ERR_SYSTEM;
          free(jids);
          return NULL;
          }

        jids = hold;
        }

      jids[njids++] = jobid; /* add jobid to list */
      }
    }

  fmt = ret_string;

  for (j = 0; j < njids; j++)
    {
    checkret(&fmt, 100);

    if (j == 0)
      sprintf(fmt, "%d", (int)jids[j]);
    else
      sprintf(fmt, " %d", (int)jids[j]);

    fmt += strlen(fmt);
    }

  free(jids);

  return ret_string;
  }

char *
nsessions(struct rm_attribute *attrib)
  {
  char *result, *ch;
  int num = 1;

  if ((result = sessions(attrib)) == NULL)
    return result;

  for (ch = result; *ch; ch++)
    {
    if (*ch == ' ')  /* count blanks */
      num++;
    }

  sprintf(ret_string, "%d", num);

  return ret_string;
  }

char *
nusers(struct rm_attribute *attrib)
  {
  char   *id = "nusers";
  int   nproc;
  int   i, j;
  int    nuids = 0;
  uid_t   *uids, uid;

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if ((nproc = getprocs()) == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  if ((uids = calloc(nproc, sizeof(uid_t))) == NULL)
    {
    log_err(errno, id, (char *)"no memory");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  for (i = 0; i < nproc; i++)
    {

    register struct proc    *pp = &process_table[i];

    if (pp->p_stat == 0)
      continue;

    if (pp->p_pc == NULL)
      continue;

    if ((uid = pp->p_pc->pc_suid) == 0)
      continue;

    DBPRT(("%s: pid %d uid %u\n",
           id, (int)pp->p_pid, uid))
    for (j = 0; j < nuids; j++)
      {
      if (uids[j] == uid)
        break;
      }

    if (j == nuids)                 /* not found */
      uids[nuids++] = uid;    /* so add it to list */
    }

  sprintf(ret_string, "%d", nuids);

  free(uids);
  return ret_string;
  }

char *
pids(struct rm_attribute *attrib)
  {
  char  *id = "pids";
  pid_t  jobid;

  struct pcomm *pc;
  int  i, nproc;
  char  *fmt;
  int  num_pids;

  if (attrib == NULL)
    {
    log_err(-1, id, no_parm);
    rm_errno = RM_ERR_NOPARAM;
    return NULL;
    }

  if ((jobid = (pid_t)atoi(attrib->a_value)) == 0)
    {
    sprintf(log_buffer, "bad param: %s", attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (momgetattr(NULL))
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (strcmp(attrib->a_qualifier, "session") != 0)
    {
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if ((nproc = getprocs()) == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  /*
  ** Search for members of session
  */
  fmt = ret_string;

  for (i = 0; i < nproc; i++)
    {

    register struct proc    *pp = &process_table[i];

    if (pp->p_stat == 0)
      continue;

    if ((pc = pp->p_pc) == NULL)
      continue;

    if (jobid != pc->pc_sid)
      continue;

    sprintf(fmt, " %d", pp->p_pid);

    fmt += strlen(fmt);

    num_pids++;
    }

  if (num_pids == 0)
    {
    rm_errno = RM_ERR_EXIST;
    return NULL;
    }

  return ret_string;
  }

static char *
totmem(struct rm_attribute *attrib)
  {
  char *id = "totmem";

  struct statfs fsbuf;

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (statfs(procfs, &fsbuf, sizeof(struct statfs), 0) == -1)
    {
    log_err(errno, id, (char *)"statfs");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  DBPRT(("%s: bsize=%d blocks=%d\n", id, fsbuf.f_bsize, fsbuf.f_blocks))

  /* in KB */
  sprintf(ret_string, "%ukb", (fsbuf.f_bsize * fsbuf.f_blocks) >> 10);
  return ret_string;
  }

static char *
availmem(struct rm_attribute *attrib)
  {
  char *id = "availmem";

  struct statfs fsbuf;

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (statfs(procfs, &fsbuf, sizeof(struct statfs), 0) == -1)
    {
    log_err(errno, id, (char *)"statfs");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  DBPRT(("%s: bsize=%d bfree=%d\n", id, fsbuf.f_bsize, fsbuf.f_bfree))

  /* in KB */
  sprintf(ret_string, "%ukb", (fsbuf.f_bsize * fsbuf.f_bfree) >> 10);
  return ret_string;
  }


static char *
ncpus(struct rm_attribute *attrib)
  {
  char  *id = "ncpus";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  sprintf(ret_string, "%ld", tinfo.mc_ncpu);

  system_ncpus = tinfo.mc_ncpu;
  return ret_string;
  }

static char *
physmem(struct rm_attribute *attrib)
  {
  char  *id = "physmem";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  /* in KB */
  sprintf(ret_string, "%lukb", (tinfo.mc_msz * sizeof(int)) >> 10);

  return ret_string;
  }

char *
size_fs(char *param)
  {
  char  *id = "size_fs";
  FILE  *mf;

  struct mntent *mp;

  struct statfs fsbuf;

  if (param[0] != '/')
    {
    sprintf(log_buffer, "%s: not full path filesystem name: %s\n",
            id, param);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (statfs(param, &fsbuf, sizeof(struct statfs), 0) == -1)
    {
    log_err(errno, id, (char *)"statfs");
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  /* in KB */
  sprintf(ret_string, "%lukb", (unsigned long)((fsbuf.f_bsize * fsbuf.f_bfree) >> 10));

  return ret_string;
  }

char *
size_file(char *param)
  {
  char  *id = "size_file";

  struct stat sbuf;

  if (param[0] != '/')
    {
    sprintf(log_buffer, "%s: not full path filesystem name: %s\n",
            id, param);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (stat(param, &sbuf) == -1)
    {
    log_err(errno, id, (char *)"stat");
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  sprintf(ret_string, "%ukb", sbuf.st_size >> 10); /* KB */

  return ret_string;
  }

char *
size(struct rm_attribute *attrib)
  {
  char *id = "size";
  char *param;

  if (attrib == NULL)
    {
    log_err(-1, id, no_parm);
    rm_errno = RM_ERR_NOPARAM;
    return NULL;
    }

  if (momgetattr(NULL))
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  param = attrib->a_value;

  if (strcmp(attrib->a_qualifier, "file") == 0)
    return (size_file(param));
  else if (strcmp(attrib->a_qualifier, "fs") == 0)
    return (size_fs(param));
  else
    {
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }
  }

time_t maxtm;

void
setmax(char *dev)
  {

  struct stat sb;

  if (stat(dev, &sb) == -1)
    return;

  if (maxtm < sb.st_atime)
    maxtm = sb.st_atime;

  return;
  }

char *
idletime(struct rm_attribute *attrib)
  {
  char *id = "idletime";
  DIR *dp;

  struct dirent *de;
  char ttyname[50];
  time_t curtm;

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if ((dp = opendir("/dev")) == NULL)
    {
    log_err(errno, id, (char *)"opendir /dev");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  maxtm = 0;

  curtm = time(NULL);

  while ((de = readdir(dp)) != NULL)
    {
    if (maxtm >= curtm)
      break;

    if (strncmp(de->d_name, "tty", 3))
      continue;

    sprintf(ttyname, "/dev/%s", de->d_name);

    setmax(ttyname);
    }

  closedir(dp);

  sprintf(ret_string, "%d", MAX(0, curtm - maxtm));
  return ret_string;
  }

int
quotasize(int blocks)
  {
  if (QFV_MINVALUE <= blocks && blocks <= QFV_MAXVALUE)
    {
    sprintf(ret_string, "%lukb", (BSIZE * blocks) >> 10); /* KB */
    return 0;
    }

  switch (blocks)
    {

    case QFV_DEFAULT:
      strcpy(ret_string, "default");
      return 1;

    case QFV_NOEVAL:
      strcpy(ret_string, "infinity");
      break;

    case QFV_PREVENT:
      strcpy(ret_string, "prevent");
      break;

    default:
      strcpy(ret_string, "unspecified");
      break;
    }

  return 0;
  }

typedef int (*ifunc)();

static char *
quota(struct rm_attribute *attrib)
  {
  char *id = "quota";
  time_t   now;
  int   ident;
  char   dirname[FILENAME_MAX];

  struct q_request qi;

  struct qf_header header;

  struct q_entry  *qu;
#if SRFS

  struct fsres_s  srfsinfo;
  static unsigned int lastproc = 0;
  static char  srfsdir[FILENAME_MAX];
#endif

  static char  *bean_type[] =
    {
    "account", /* 0 */
    "group", /* 1 */
    "user"  /* 2 */
    };
  static ifunc  bean_func[] =
    {
    nam2acid,
    nam2gid,
    nam2uid
    };
  enum bean_name
    {
    account,
    group,
    user,
    bean_end
    } bean;
  static char  *type_array[] =
    {
    "harddata",
    "softdata",
    "currdata",
    "hardfile",
    "softfile",
    "currfile",
    "timedata",
    "timefile",
    "snap_avail",  /* srfs */
    "ares_avail",  /* srfs */
    "res_total",  /* srfs */
    "soft_res",  /* srfs */
    "delta",  /* srfs */
    "reserve",  /* srfs */
    };
  enum type_name
    {
    harddata,
    softdata,
    currdata,
    hardfile,
    softfile,
    currfile,
    timedata,
    timefile,
    snap_avail,
    ares_avail,
    res_total,
    soft_res,
    delta,
    reserve,
    type_end
    } type;

  if (attrib == NULL)
    {
    log_err(-1, id, no_parm);
    rm_errno = RM_ERR_NOPARAM;
    return NULL;
    }

  if (strcmp(attrib->a_qualifier, "type"))
    {
    sprintf(log_buffer, "unknown qualifier %s",
            attrib->a_qualifier);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  for (type = 0; type < type_end; type++)
    {
    if (strcmp(attrib->a_value, type_array[type]) == 0)
      break;
    }

  if (type == type_end)    /* check to see if command is legal */
    {
    sprintf(log_buffer, "bad param: %s=%s",
            attrib->a_qualifier, attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if ((attrib = momgetattr(NULL)) == NULL)
    {
    log_err(-1, id, no_parm);
    rm_errno = RM_ERR_NOPARAM;
    return NULL;
    }

  if (strcmp(attrib->a_qualifier, "dir") != 0)
    {
    sprintf(log_buffer, "bad param: %s=%s",
            attrib->a_qualifier, attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (attrib->a_value[0] == '/')  /* must be absolute path */
    strcpy(dirname, attrib->a_value);

#if SRFS
  else if (attrib->a_value[0] == '$')   /* variable location */
    {
    char *var = &attrib->a_value[1];
    char *val;

    if ((val = (char *)var_value(var)) == NULL)
      {
      sprintf(log_buffer,
              "variable %s not found", attrib->a_value);
      log_err(-1, id, log_buffer);
      rm_errno = RM_ERR_BADPARAM;
      return NULL;
      }

    strcpy(dirname, val);
    }

#endif /* SRFS */
  else
    {
    sprintf(log_buffer,
            "not an absolute path: %s", attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  /*
  ** See if it's a srfs request.  They don't need an id.
  */
  if (type >= snap_avail)
    {
    if (momgetattr(NULL))
      {
      log_err(-1, id, extra_parm);
      rm_errno = RM_ERR_BADPARAM;
      return NULL;
      }

#if SRFS
    /* check to see if we need to call quotactl() */
    if (lastproc == reqnum &&
        strcmp(dirname, srfsdir) == 0)
      {
      DBPRT(("%s: already got srfsinfo\n", id))
      }
    else
      {
      if (quotactl(dirname, SRFS_INFO,
                   (caddr_t)&srfsinfo) == -1)
        {
        log_err(errno, id, (char *)"quotactl(SRFS_INFO)");
        rm_errno = RM_ERR_SYSTEM;
        return NULL;
        }

      strcpy(srfsdir, dirname);

      lastproc = reqnum;
      }

    /* all sizes in KB */
    switch (type)
      {

      case snap_avail:
        sprintf(ret_string, "%ukb",
                (BSIZE * srfsinfo.snap_avail) >> 10);
        break;

      case ares_avail:
        sprintf(ret_string, "%ukb",
                (BSIZE * srfsinfo.ares_avail) >> 10);
        break;

      case res_total:
        sprintf(ret_string, "%d",
                (BSIZE * srfsinfo.res_total) >> 10);
        break;

      case soft_res:
        sprintf(ret_string, "%s",
                srfsinfo.soft_res ? "true" : "false");
        break;

      case delta:
        sprintf(ret_string, "%lukb",
                (BSIZE * srfsinfo.delta) >> 10);
        break;

      case reserve:
        sprintf(ret_string, "%ld",
                (-BSIZE * srfsinfo.reserve) >> 10);
        break;
      }

    return ret_string;

#else
    log_err(errno, id, no_srfs);
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
#endif
    }

  /*
  ** Check type of id: user, group or acct.
  */
  if ((attrib = momgetattr(NULL)) == NULL)
    {
    log_err(-1, id, no_parm);
    rm_errno = RM_ERR_NOPARAM;
    return NULL;
    }

  for (bean = 0; bean < bean_end; bean++)
    {
    if (strcmp(attrib->a_qualifier, bean_type[bean]) == 0)
      break;
    }

  if (bean == bean_end)
    {
    sprintf(log_buffer, "bad param: %s=%s",
            attrib->a_qualifier, attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  ident = atoi(attrib->a_value);

  if (ident == 0)
    {
    ident = bean_func[bean](attrib->a_value);

    if (ident == -1)
      {
      sprintf(log_buffer, "%s not found: %s",
              bean_type[bean], attrib->a_value);
      log_err(-1, id, log_buffer);
      rm_errno = RM_ERR_EXIST;
      return NULL;
      }
    }

  if (momgetattr(NULL))
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  qi.qf_entry.id = ident;

  qi.qf_magic = QF_MAGIC;

  if (quotactl(dirname, Q_GETQUOTA, (caddr_t)&qi) == -1)
    {
    log_err(errno, id, (char *)"quotactl(Q_GETQUOTA)");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  qu = NULL;

  switch (bean)
    {

    case account:

      if (qi.acct)
        qu = &qi.qf_entry.acct_q;

      break;

    case group:
      if (qi.group)
        qu = &qi.qf_entry.group_q;

      break;

    case user:
      if (qi.user)
        qu = &qi.qf_entry.user_q;

      break;
    }

  if (qu == NULL)
    {
    sprintf(log_buffer, "%s quota information not returned",
            bean_type[bean]);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_EXIST;
    return NULL;
    }

  switch (type)
    {

    case harddata:

      if (quotasize(qu->f_quota))
        break;

      return ret_string;

    case softdata:
      if (quotasize(qu->f_warn))
        break;

      return ret_string;

    case currdata:
      sprintf(ret_string, "%ld", BSIZE*qu->f_use);

      return ret_string;

    case hardfile:
      if (quotasize(qu->i_quota))
        break;

      return ret_string;

    case softfile:
      if (quotasize(qu->i_warn))
        break;

      return ret_string;

    case currfile:
      sprintf(ret_string, "%ld", qu->i_use);

      return ret_string;

    case timedata:

    case timefile:
      now = time((time_t *)NULL);

      if (qu->f_wtime > now)  /* time is in the future */
        sprintf(ret_string, "%ld", qu->f_wtime - now);
      else
        strcpy(ret_string, "0");

      return ret_string;
    }

  /*
  ** If we get here, the default number is needed.
  */
  DBPRT(("%s: getting default info\n", id))
  header.qf_magic = QF_MAGIC;

  if (quotactl(dirname, Q_GETHEADER, (caddr_t)&header) == -1)
    {
    log_err(errno, id, (char *)"quotactl(Q_GETHEADER)");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  switch (type)
    {

    case harddata:
      (void)quotasize(header.user_h.def_fq);
      return ret_string;

    case softdata:
      (void)quotasize(header.user_h.warn_fq);
      return ret_string;

    case hardfile:
      sprintf(ret_string, "%ld", header.user_h.def_iq);
      return ret_string;

    case softfile:
      sprintf(ret_string, "%ld", header.user_h.warn_iq);
      return ret_string;
    }

  return ret_string;
  }

static char *
srfs_reserve(struct rm_attribute *attrib)
  {
  char *id = "srfs_reserve";

#if SRFS
  char *dir, *end, *name;
  long num;

  if (attrib == NULL)
    {
    log_err(-1, id, no_parm);
    rm_errno = RM_ERR_NOPARAM;
    return NULL;
    }

  for (;;)
    {
    if ((name = (char *)var_value(attrib->a_qualifier)) == NULL)
      {
      sprintf(log_buffer, "unknown qualifier %s",
              attrib->a_qualifier);
      log_err(-1, id, log_buffer);
      rm_errno = RM_ERR_BADPARAM;
      return NULL;
      }

    num = strtol(attrib->a_value, &end, 10);

    if (end == attrib->a_value)
      {
      sprintf(log_buffer, "bad number %s", attrib->a_value);
      log_err(-1, id, log_buffer);
      rm_errno = RM_ERR_BADPARAM;
      return NULL;
      }

    num = -(num + BSIZE - 1) / BSIZE; /* convert to blks */

    if (quotactl(name, SRFS_RESERVE, (caddr_t)&num) == -1)
      {
      log_err(errno, id, (char *)"quotactl(SRFS_RESERVE)");
      rm_errno = RM_ERR_SYSTEM;
      return NULL;
      }

    if ((attrib = momgetattr(NULL)) == NULL)
      break;
    }

  return "okay";

#else
  log_err(errno, id, no_srfs);
  rm_errno = RM_ERR_SYSTEM;
  return NULL;
#endif
  }

int
get_la(double *rv)
  {
  char  *id = "get_la";

  *rv = (double)sysinfo.avenrun[0];
  return 0;
  }

static char *
swaprate(struct rm_attribute *attrib)
  {
  char  *id = "swaprate";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (last_time == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  sprintf(ret_string, "%.2f", swap_rate);

  return ret_string;
  }

static char *
swapinrate(struct rm_attribute *attrib)
  {
  char  *id = "swapinrate";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (last_time == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  sprintf(ret_string, "%.2f", swapin_rate);

  return ret_string;
  }

static char *
swapoutrate(struct rm_attribute *attrib)
  {
  char  *id = "swapoutrate";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (last_time == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  sprintf(ret_string, "%.2f", swapout_rate);

  return ret_string;
  }

static struct swapper swapper;

int
getswap(void)
  {
  static unsigned int lastproc = 0;
  char  *id = "getswap";

  struct tbs info;

  if (lastproc == reqnum) /* don't need new swap info */
    return 0;

  if (tabinfo(SWAPTAB, &info) == -1)
    {
    log_err(errno, id, (char *)"tabinfo(SWAPTAB)");
    return 1;
    }

  if (tabread(SWAPTAB, (char *)&swapper,
              info.ent * info.len, info.head) == -1)
    {
    log_err(errno, id, (char *)"tabread(SWAPTAB)");
    return 1;
    }

  DBPRT(("Total swap space: %8d\tAvailable swap space: %8d\n",

         swapper.swp_map.bmp_total * SWP_WGHT,
         swapper.swp_map.bmp_avail * SWP_WGHT))
  lastproc = reqnum;
  return 0;
  }

static char *
swapused(struct rm_attribute *attrib)
  {
  char  *id = "swapused";
  int  sout;

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (getswap())
    return NULL;

  sout = swapper.swp_map.bmp_total - swapper.swp_map.bmp_avail;

  /* in KB */
  sprintf(ret_string, "%ukb", (ctob(swutoc(sout)) + 1023) >> 10);

  return ret_string;
  }

static char *
swapavail(struct rm_attribute *attrib)
  {
  char  *id = "swapused";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (getswap())
    return NULL;

  /* in KB */
  sprintf(ret_string, "%ukb",
          (ctob(swutoc(swapper.swp_map.bmp_avail)) + 1023) >> 10);

  return ret_string;
  }

static char *
swaptotal(struct rm_attribute *attrib)
  {
  char  *id = "swapused";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (getswap())
    return NULL;

  /* in KB */
  sprintf(ret_string, "%ukb",
          (ctob(swutoc(swapper.swp_map.bmp_total)) + 1023) >> 10);

  return ret_string;
  }

static char *
cpuidle(struct rm_attribute *attrib)
  {
  char  *id = "cpuidle";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (last_time == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  sprintf(ret_string, "%.2f", cpu_idle);

  return ret_string;
  }

static char *
cpusysw(struct rm_attribute *attrib)
  {
  char  *id = "cpusysw";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (last_time == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  sprintf(ret_string, "%.2f", cpu_sysw);

  return ret_string;
  }

static char *
cpuguest(struct rm_attribute *attrib)
  {
  char  *id = "cpuguest";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (last_time == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  sprintf(ret_string, "%.2f", cpu_guest);

  return ret_string;
  }

static char *
cpuuser(struct rm_attribute *attrib)
  {
  char  *id = "cpuuser";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (last_time == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  sprintf(ret_string, "%.2f", cpu_user);

  return ret_string;
  }

static char *
cpuunix(struct rm_attribute *attrib)
  {
  char  *id = "cpuunix";

  if (attrib)
    {
    log_err(-1, id, extra_parm);
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if (last_time == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  sprintf(ret_string, "%.2f", cpu_unix);

  return ret_string;
  }





void scan_non_child_tasks(void)

  {
  /* NYI */

  return;
  }  /* END scan_non_child_tasks() */


