/*
*         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 <dirent.h>
#include <fcntl.h>
#include <malloc.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>

#include <nlist.h>
#include <sys/unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/procfs.h>
#include <sys/proc.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/sysopr.h>
#include <sys/oprtbl.h>
#include <vm/anon.h>

#include "pbs_error.h"
#include "portability.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 "resmon.h"
#include "../rm_dep.h"

/*
** System dependent code to gather information for the resource
** monitor for a Fujitsu VPP300 machine running UXP/V
**
** Resources known by this code:
**  cput  cpu time for a pid or job
**  mem  memory size for a pid or job in KB
**  resi  resident memory size for a pid or job in KB
**  sessions list of sessions in the system
**  pids  list of pids in a job
**  nsessions number of sessions in the system
**  nusers  number of users in the system
**  ncpus  number of cpus
**  physmem  physical memory size in KB
**  availmem available memory size in KB
**  totmem  total memory size in KB
**  size  size of a file or filesystem in KB
**  idletime seconds of idle time
**  walltime wall clock time for a pid
**  loadave  current load average
*/

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

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

#ifndef MAX
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif /* MAX */

/*
** 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 double cputfactor;
extern double wallfactor;
extern  long    system_ncpus;
extern  int     ignwalltime;
extern  int     igncput;
extern  int     ignvmem;
extern  int     ignmem;

/*
** local functions and data
*/
static char *resi     (struct rm_attribute *attrib);
static char *physmem  (struct rm_attribute *attrib);
static char *availmem (struct rm_attribute *attrib);
static char *totmem   (struct rm_attribute *attrib);
static char *ncpus    (struct rm_attribute *attrib);
static char *walltime (struct rm_attribute *attrib);

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

char  procfs[] = "/proc";
char  procfmts[] = "/proc/%s";
char  procfmtd[] = "/proc/%05.5d";
DIR  *pdir;
extern char *ret_string;
int  kd = -1;
time_t  wait_time = 10;
int  nproc = 0;
int  max_proc = 0;
prstatus_t *proc_status = NULL;
prpsinfo_t *proc_info = NULL;

#define TBL_INC 20

extern time_t   time_now;

extern char extra_parm[];
extern char no_parm[];
char  no_count[] = "count not found";
char  nokernel[] = "kernel not available";
char  nomemory[] = "no memory";
static long page_size;

/*
** local resource array
*/

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

  { "totmem", {totmem} },
  { "availmem", {availmem} },
  { "physmem", {physmem} },
  { "ncpus", {ncpus} },
  { "loadave", {loadave} },
  { "walltime", {walltime} },
  { NULL,  {nullproc} },
  };

struct nlist nl[] =
  {
    { "anoninfo"
    },  /* 0 */

  { "physmem" },  /* 1 */
  { "avenrun" },  /* 2 */
  { "" }
  };

#define KSYM_ANON  0
#define KSYM_PHYS  1
#define KSYM_LOAD  2

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

void
dep_main_loop_cycle(void)
  {
  /* Don't need any periodic processing. */
  }

void
dep_initialize(void)
  {
  int     i;
  static char id[] = "dep_initialize";
  char kernel[] = "/unix";
  char kmem[] = "/dev/kmem";

  page_size = sysconf(_SC_PAGESIZE);

  if ((pdir = opendir(procfs)) == NULL)
    {
    log_err(errno, id, "opendir");
    return;
    }

  if (kd == -1)
    {
    if ((kd = open(kmem, O_RDONLY)) == -1)
      {
      log_err(errno, id, kmem);
      return;
      }

    /* insure /dev/kmem closed on exec */

    if ((i = fcntl(kd,  F_GETFD)) == -1)
      {
      log_err(errno, id, "F_GETFD");
      }

    i |= FD_CLOEXEC;

    if (fcntl(kd, F_SETFD, i) == -1)
      {
      log_err(errno, id, "F_SETFD");
      }
    }

  if (nlist(kernel, nl) == -1)
    {
    log_err(errno, id, kernel);
    }
  }

void
dep_cleanup(void)
  {
  static char id[] = "dep_cleanup";

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

  if (pdir)
    closedir(pdir);

  close(kd);

  kd = -1;
  }

/*
 * 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.
 *
 *  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);
  }

/*
 * Time decoding macro.  Accepts a timestruc_t pointer.  Returns unsigned long
 * time in seconds.
 */

#define tv(val) (ulong)((val).tv_sec + ((val).tv_nsec + 500000000)/1000000000)

static
int
injob(pjob, sid)
job  *pjob;
pid_t sid;
  {
  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 == sid)
      return TRUE;
    }

  return FALSE;
  }

/*
 * Internal session cpu time decoding routine.
 *
 * Accepts a job pointer.  Returns the sum of all cpu time
 * consumed for all tasks executed by the job, in seconds, adjusted
 * by cputfactor.
 */
static unsigned long
cput_sum(job *pjob)
  {
  char   *id = "cput_sum";
  ulong   cputime, addtime;
  int   i;
  int   nps = 0;
  prstatus_t  *ps;
  prpsinfo_t  *pi;

  cputime = 0;

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (!injob(pjob, pi->pr_sid))
      continue;

    nps++;

    if (pi->pr_state == SZOMB)
      {
      cputime += tv(pi->pr_time);
      DBPRT(("%s: ses %d pid %d (zombie) cputime %d\n",
             id, pi->pr_sid, pi->pr_pid, cputime))
      continue;
      }

    ps = &proc_status[i];

    addtime = tv(ps->pr_utime) + tv(ps->pr_stime) +
              tv(ps->pr_cutime) + tv(ps->pr_cstime);

    cputime += addtime;
    DBPRT(("%s: ses %d pid %d cputime %d\n",
           id, pi->pr_sid, ps->pr_pid, cputime))
    }

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

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

/*
 * Return TRUE if any process in the job is over limit for cputime usage.
 */
static int
overcput_proc(job *pjob, unsigned long limit)
  {
  char  *id = "overcput_proc";
  ulong  memsize;
  ulong  cputime;
  int  i;
  prstatus_t *ps;

  memsize = 0;

  for (i = 0; i < nproc; i++)
    {
    ps = &proc_status[i];

    if (!injob(pjob, ps->pr_sid))
      continue;

    cputime = (ulong)(cputfactor * (double)(
                        tv(ps->pr_utime) + tv(ps->pr_stime) +
                        tv(ps->pr_cutime) + tv(ps->pr_cstime)));

    if (cputime > limit)
      return (TRUE);
    }

  return (FALSE);
  }

/*
 * Internal session memory usage function.
 *
 * Returns the total number of bytes of address
 * space consumed by all current tasks within the session.
 */
static unsigned long
mem_sum(job *pjob)
  {
  char  *id = "mem_sum";
  ulong  memsize;
  int  i;
  prpsinfo_t *pi;

  memsize = 0;

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (!injob(pjob, pi->pr_sid))
      continue;

    memsize += pi->pr_size * page_size;
    }

  return (memsize);
  }

/*
 * Internal session mem (workingset) size function.
 */
static unsigned long
resi_sum(job *pjob)
  {
  char  *id = "resi_sum";
  ulong  resisize;
  int  i;
  prpsinfo_t *pi;

  resisize = 0;

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (!injob(pjob, pi->pr_sid))
      continue;

    resisize += pi->pr_rssize * page_size;
    }

  return (resisize);
  }

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);
  }

/*
 * Establish system-enforced limits for the 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.  Cannot alter those set by setrlimit (kernel)
 * because we are the wrong process.
 */
int
mom_set_limits(
  job *pjob,
  int set_mode /* SET_LIMIT_SET or SET_LIMIT_ALTER */
)
  {
  char  *id = "mom_set_limits";
  char  *pname;
  int  retval;
  unsigned long value; /* place in which to build resource value */
  resource *pres;

  struct rlimit reslim;
  unsigned long mem_limit  = 0;

  log_buffer[0] = '\0';

  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);

  /*
   * 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)
        {
        /* cpu time - check, if less than pcput use it */
        retval = mm_gettime(pres, &value);

        if (retval != PBSE_NONE)
          return (error(pname, retval));
        }
      }
    else if (strcmp(pname, "pcput") == 0)
      {
      if (igncput == FALSE)
        {
        /* process cpu time - set */
        retval = mm_gettime(pres, &value);

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

        reslim.rlim_cur = reslim.rlim_max =
          (unsigned long)((double)value / cputfactor);

        if (setrlimit(RLIMIT_CPU, &reslim) < 0)
          return (error("RLIMIT_CPU", PBSE_SYSTEM));
        }
      }
    else if (strcmp(pname, "file") == 0)   /* set */
      {
      if (set_mode == SET_LIMIT_SET)
        {
        retval = mm_getsize(pres, &value);

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

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

        reslim.rlim_cur = reslim.rlim_max = value;

        if (setrlimit(RLIMIT_FSIZE, &reslim) < 0)
          return (error(pname, PBSE_SYSTEM));
        }
      }
    else if (strcmp(pname, "mem") == 0)    /* check */
      {
      if (ignmem == FALSE)
        {
        retval = mm_getsize(pres, &value);

        if (retval != PBSE_NONE)
          return (error(pname, retval));
        }
      }
    else if (strcmp(pname, "vmem") == 0)   /* check */
      {
      if (ignvmem == FALSE)
        {
        retval = mm_getsize(pres, &value);

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

        if ((mem_limit == 0) || (value < mem_limit))
          mem_limit = value;
        }
      }
    else if (strcmp(pname, "pvmem") == 0)   /* set */
      {
      if (ignvmem == FALSE)
        {
        if (set_mode == SET_LIMIT_SET)
          {
          retval = mm_getsize(pres, &value);

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

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

          if ((mem_limit == 0) || (value < mem_limit))
            mem_limit = value;
          }
        }
      }
    else if (strcmp(pname, "pmem") == 0)   /* ignore */
      {
      }
    else if (strcmp(pname, "walltime") == 0)   /* Check */
      {
      retval = mm_gettime(pres, &value);

      if (retval != PBSE_NONE)
        return (error(pname, retval));
      }
    else if (strcmp(pname, "nice") == 0)   /* set nice */
      {
      if (set_mode == SET_LIMIT_SET)
        {
        errno = 0;

        if ((nice((int)pres->rs_value.at_val.at_long) == -1)
            && (errno != 0))
          return (error(pname, PBSE_BADATVAL));
        }
      }
    else if ((pres->rs_defin->rs_flags & ATR_DFLAG_RMOMIG) == 0)
      /* don't recognize and not marked as ignore by mom */
      return (error(pname, PBSE_UNKRESC));

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

  if (set_mode == SET_LIMIT_SET)
    {
    /* if either of vmem or pvmem was given, set sys limit to lesser */
    if (mem_limit != 0)
      {
      reslim.rlim_cur = reslim.rlim_max = mem_limit;

      if ((ignvmem == 0) && (setrlimit(RLIMIT_VMEM, &reslim) < 0))
        return (error("RLIMIT_VMEM", 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)
  {
  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, "walltime") == 0 ||
        strcmp(pname, "cput") == 0 ||
        strcmp(pname, "pcput") == 0 ||
        strcmp(pname, "vmem") == 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)
  {
  char  *id = "mom_open_poll";

  DBPRT(("%s: entered\n", id))
  proc_status = (prstatus_t *)calloc(sizeof(prstatus_t), TBL_INC);
  proc_info = (prpsinfo_t *)calloc(sizeof(prpsinfo_t), TBL_INC);

  if (proc_status == NULL || proc_info == NULL)
    {
    log_err(errno, id, "calloc");
    return (PBSE_SYSTEM);
    }

  max_proc = TBL_INC;

  return (PBSE_NONE);
  }

/*
 * Declare start of polling loop.
 */
int
mom_get_sample(void)
  {
  static char  id[] = "mom_get_sample";
  int   fd;

  struct dirent  *dent;
  char   procname[100];
  prstatus_t  *ps;
  prpsinfo_t  *pi;

  DBPRT(("%s: entered\n", id))
  rewinddir(pdir);
  ps = proc_status;
  pi = proc_info;
  nproc = 0;

  for (fd = -1; (dent = readdir(pdir)) != NULL; close(fd))
    {
    if (!isdigit(dent->d_name[0]))
      continue;

    sprintf(procname, procfmts, dent->d_name);

    if ((fd = open(procname, O_RDONLY)) == -1)
      continue;

    if (ioctl(fd, PIOCPSINFO, pi) == -1)
      {
      sprintf(log_buffer,
              "%s: ioctl(PIOCPSINFO)", procname);
      log_err(errno, id, log_buffer);
      continue;
      }

    if (ioctl(fd, PIOCSTATUS, ps) == -1)
      {
      if (errno != ENOENT)
        {
        sprintf(log_buffer,
                "%s: ioctl(PIOCSTATUS)", procname);
        log_err(errno, id, log_buffer);
        }

      memset(ps, sizeof(prstatus_t), '\0');
      }

    if (++nproc == max_proc)
      {
      void *hold;

      DBPRT(("%s: alloc more table space %d\n", id, nproc))
      max_proc += TBL_INC;

      hold = realloc((void *)proc_status,
                     max_proc * sizeof(prstatus_t));
      assert(hold != NULL);
      proc_status = (prstatus_t *)hold;

      hold = realloc((void *)proc_info,
                     max_proc * sizeof(prpsinfo_t));
      assert(hold != NULL);
      proc_info = (prpsinfo_t *)hold;
      }

    ps = &proc_status[nproc];

    pi = &proc_info[nproc];
    }

  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)
  {
  char  *id = "mom_over_limit";
  char  *pname;
  int  retval;
  unsigned long value, num;
  resource *pres;

  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\n", id))

  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 ((igncput == FALSE) && (strcmp(pname, "cput") == 0))
      {
      retval = mm_gettime(pres, &value);

      if (retval != PBSE_NONE)
        continue;

      if ((num = cput_sum(pjob)) > value)
        {
        sprintf(log_buffer,
                "cput %lu exceeded limit %lu",
                num, value);
        return (TRUE);
        }
      }
    else if ((igncput == FALSE) && (strcmp(pname, "pcput") == 0))
      {
      retval = mm_gettime(pres, &value);

      if (retval != PBSE_NONE)
        continue;

      if (overcput_proc(pjob, value))
        {
        sprintf(log_buffer,
                "pcput exceeded limit %lu",
                value);
        return (TRUE);
        }
      }
    else if (strcmp(pname, "vmem") == 0)
      {
      retval = mm_getsize(pres, &value);

      if (retval != PBSE_NONE)
        continue;

      if ((ignvmem == 0) && ((num = mem_sum(pjob)) > value))
        {
        sprintf(log_buffer,
                "vmem %lu exceeded limit %lu",
                num, value);
        return (TRUE);
        }
      }
    else 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) * cputfactor);

      if ((time_now - pjob->ji_qs.ji_stime) > 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.
 *
 * Assumes that the session ID attribute has already been set.
 */
int
mom_set_use(job *pjob)
  {
  char   *id = "mom_set_use";
  resource  *pres;
  attribute  *at;
  resource_def  *rd;
  unsigned long  *lp, lnum;

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

  at->at_flags |= ATR_VFLAG_MODIFY;

  if ((at->at_flags & ATR_VFLAG_SET) == 0)
    {
    at->at_flags |= ATR_VFLAG_SET;

    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, "vmem", 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, "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, "cput", svr_resc_size);

  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  lp = (unsigned long *) & pres->rs_value.at_val.at_long;
  lnum = cput_sum(pjob);
  *lp = MAX(*lp, lnum);

  rd = find_resc_def(svr_resc_def, "vmem", svr_resc_size);
  assert(rd != NULL);
  pres = find_resc_entry(at, rd);
  assert(pres != NULL);
  lp = &pres->rs_value.at_val.at_size.atsv_num;
  lnum = (mem_sum(pjob) + 1023) >> 10; /* in KB */
  *lp = MAX(*lp, lnum);

  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)
  {
  char  *id = "kill_task";
  int  ct = 0;
  prpsinfo_t *pi;
  int  i, sesid;

  sesid = ptask->ti_qs.ti_sid;

  if (sesid <= 1)
    return 0;

  (void)mom_get_sample();

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (sesid == pi->pr_sid)
      {
      (void)kill(pi->pr_pid, sig);
      ++ct;
      }
    }

  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)
  {
  char *id = "mom_close_poll";

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

  if (proc_status)
    free(proc_status);

  if (proc_info)
    free(proc_info);

  if (pdir)
    {
    if (closedir(pdir) != 0)
      {
      log_err(errno, id, "closedir");
      return (PBSE_SYSTEM);
      }
    }

  return (PBSE_NONE);
  }

/*
 * mom_does_checkpoint
 */

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

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

int
mach_checkpoint(task *ptask, char *file, int abort)
  {
  return (-1);
  }

/*
 * Restart the job from the checkpoint file.
 *
 * Return -1 on error or sid if okay.
 */

long
mach_restart(task *ptask, char *file)
  {
  return (-1);
  }

/*
** Return 1 if proc table can be read, 0 otherwise.
*/
int
getprocs(void)
  {
  static unsigned int lastproc = 0;
  static char  id[] = "getprocs";

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

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

  lastproc = reqnum;

  return 1;
  }

double
dsecs(t)
timestruc_t     *t;
  {
  DBPRT(("\tsecs: %d\tnsecs: %d\n", t->tv_sec, t->tv_nsec))
  return ((double)t->tv_sec + ((double)t->tv_nsec * 1.0e-9));
  }

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

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

  cputime = 0.0;

  for (i = 0; i < nproc; i++)
    {
    ps = &proc_status[i];

    if (jobid != ps->pr_sid)
      continue;

    found = 1;

    addtime = dsecs(&ps->pr_utime) +
              dsecs(&ps->pr_stime) +
              dsecs(&ps->pr_cutime) +
              dsecs(&ps->pr_cstime);

    cputime += addtime;

    DBPRT(("%s: total %.2f pid %d %.2f\n", id, cputime,
           ps->pr_pid, addtime))

    }

  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";
  double   cputime;
  int   i;
  prstatus_t  *ps;

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

  for (i = 0; i < nproc; i++)
    {
    ps = &proc_status[i];

    if (ps->pr_pid == pid)
      break;
    }

  if (i == nproc)
    {
    rm_errno = RM_ERR_EXIST;
    return NULL;
    }

  cputime = dsecs(&ps->pr_utime) +

            dsecs(&ps->pr_stime) +
            dsecs(&ps->pr_cutime) +
            dsecs(&ps->pr_cstime);

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


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;
  int   found = 0;
  prpsinfo_t  *pi;

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

  memsize = 0;

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (jobid != pi->pr_sid)
      continue;

    found = 1;

    memsize += pi->pr_size;

    DBPRT(("%s: total %d pid %d %d\n", id, memsize*page_size,
           pi->pr_pid, pi->pr_size*page_size))
    }

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

  rm_errno = RM_ERR_EXIST;

  return NULL;
  }

char *
mem_proc(pid)
pid_t pid;
  {
  char   *id = "mem_proc";
  prpsinfo_t  *pi;
  int   i;

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

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (pi->pr_pid == pid)
      break;
    }

  if (i == nproc)
    {
    rm_errno = RM_ERR_EXIST;
    return NULL;
    }

  sprintf(ret_string, "%ukb", (pi->pr_size * page_size) >> 10); /* KB */

  return ret_string;
  }

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;
    }
  }

static char *
resi_job(jobid)
pid_t jobid;
  {
  char   *id = "resi_job";
  int   resisize;
  int   i;
  int   found = 0;
  prpsinfo_t  *pi;

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

  resisize = 0;

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (jobid != pi->pr_sid)
      continue;

    found = 1;

    resisize += pi->pr_rssize;
    }

  if (found)
    {
    /* in KB */
    sprintf(ret_string, "%ukb", (resisize * page_size) >> 10);
    return ret_string;
    }

  rm_errno = RM_ERR_EXIST;

  return NULL;
  }

static char *
resi_proc(pid)
pid_t pid;
  {
  char   *id = "resi_proc";
  prpsinfo_t  *pi;
  int   i;

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

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (pi->pr_pid == pid)
      break;
    }

  if (i == nproc)
    {
    rm_errno = RM_ERR_EXIST;
    return NULL;
    }

  /* in KB */
  sprintf(ret_string, "%ukb", (pi->pr_rssize * page_size) >> 10);

  return ret_string;
  }

static char *
resi(struct rm_attribute *attrib)
  {
  char   *id = "resi";
  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 (resi_job((pid_t)value));
  else if (strcmp(attrib->a_qualifier, "proc") == 0)
    return (resi_proc((pid_t)value));
  else
    {
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }
  }

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

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

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

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

  /*
  ** Search for members of job
  */
  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (pi->pr_uid == 0)
      continue;

    if ((jobid = pi->pr_sid) == 0)
      continue;

    DBPRT(("%s[%d]: pid %d sid %d\n",
           id, njids, pi->pr_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);

        if (hold == NULL)
          {
          log_err(errno, id, "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 *
pids(struct rm_attribute *attrib)
  {
  char  *id = "pids";
  pid_t  jobid;
  int  i, j;
  prpsinfo_t *pi;
  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 (getprocs() == 0)
    {
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

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

  num_pids = 0;

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    DBPRT(("%s[%d]: pid: %d sid %d\n",
           id, num_pids, pi->pr_pid, pi->pr_sid))

    if (jobid != pi->pr_sid)
      continue;

    sprintf(fmt, "%d ", pi->pr_pid);

    fmt += strlen(fmt);

    num_pids++;
    }

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

  return ret_string;
  }

char *
nusers(struct rm_attribute *attrib)
  {
  char   *id = "nusers";
  int   i, j;
  prpsinfo_t  *pi;
  int   nuids = 0;
  uid_t   *uids, *hold;
  static  int maxuid = 200;
  register uid_t uid;

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

  if ((uids = (uid_t *)calloc(maxuid, sizeof(uid_t))) == NULL)
    {
    log_err(errno, id, nomemory);
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

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

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if ((uid = pi->pr_uid) == 0)
      continue;

    DBPRT(("%s[%d]: pid %d uid %d\n",
           id, nuids, pi->pr_pid, uid))

    for (j = 0; j < nuids; j++)
      {
      if (uids[j] == uid)
        break;
      }

    if (j == nuids)    /* not found */
      {
      if (nuids == maxuid)   /* need more space */
        {
        maxuid += 100;
        hold = (uid_t *)realloc(uids, maxuid);

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

        uids = hold;
        }

      uids[nuids++] = uid; /* add uid to list */
      }
    }

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

  free(uids);
  return ret_string;
  }

/*
 * The following code provoded by Fujitsu and modified for this fuction.
 *
 * Return the number of "running" PEs
 *
 * Earl J. Dodd, FAI
 * 4-Oct-1997
 */
static char *
ncpus(struct rm_attribute *attrib)
  {
  char  *id = "ncpus";
  unsigned int pmem;
  int  i, npes;
  int  maxlistlen;
  size_t  ppnsize;
  opr_peid_blk_t *peidlist;

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

  npes = 0;

  ppnsize = 0;

  /* get the PE table size */
  ppnsize = syscall(SYS_sysopr, SKAIGETPPN);

  if (ppnsize <= 0)
    {
    log_err(errno, id, "SKAIGETPPN");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  /* alloc the receiver PE table */
  peidlist = (opr_peid_blk_t *)calloc(sizeof(opr_peid_blk_t), ppnsize);

  if (peidlist == NULL)
    {
    log_err(errno, id, nomemory);
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  /* retrieve individual PE information */
  maxlistlen = syscall(SYS_sysopr, SKAIGETPEIDL, ppnsize, peidlist);

  /*
  ** maxlistlen should = 16 (I think; really don't know what the
  ** return codes are)
  */
  for (i = 0; i < ppnsize; i++)
    {
    if (peidlist[i].ppid >= 0 && peidlist[i].state > 0)
      npes++;

#ifdef DEBUG
    fprintf(stderr, "peid: %d, ppid    = %d\n",
            i, peidlist[i].ppid);

    fprintf(stderr, "peid: %d, peid    = %d\n",
            i, peidlist[i].peid);

    fprintf(stderr, "peid: %d, pp_type = 0x%x\n",
            i, peidlist[i].pp_type);

    fprintf(stderr, "peid: %d, state   = 0x%x\n",
            i, peidlist[i].state);

#endif
    }

  free(peidlist);

  sprintf(ret_string, "%d", npes);
  system_ncpus = npes;
  return ret_string;
  }

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

  if (kd == -1)
    {
    log_err(-1, id, nokernel);
    return (rm_errno = RM_ERR_SYSTEM);
    }

  if (nl[KSYM_LOAD].n_type == 0)
    {
    log_err(-1, id, "loadaverage count not found");
    return (rm_errno = RM_ERR_SYSTEM);
    }

  if (lseek(kd, nl[KSYM_LOAD].n_value, SEEK_SET) == -1)
    {
    log_err(errno, id, "lseek");
    return (rm_errno = RM_ERR_SYSTEM);
    }

  if (read_ac_socket(kd, (char *)&load, sizeof(load)) != sizeof(load))
    {
    log_err(errno, id, "read");
    return (rm_errno = RM_ERR_SYSTEM);
    }

  *rv = (double)load / 256.0;

  return 0;
  }

struct anoninfo ai;

int
getanon(char *id)
  {
  static unsigned int lastai = 0;

  if (lastai == reqnum) /* already have anoninfo */
    return 0;

  if (kd == -1)
    {
    log_err(-1, id, nokernel);
    rm_errno = RM_ERR_SYSTEM;
    return 1;
    }

  if (nl[KSYM_ANON].n_type == 0)
    {
    log_err(-1, id, "anoninfo struct not found");
    rm_errno = RM_ERR_SYSTEM;
    return 1;
    }

  if (lseek(kd, nl[KSYM_ANON].n_value, SEEK_SET) == -1)
    {
    log_err(errno, id, "lseek");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  if (read_ac_socket(kd, (char *)&ai, sizeof(struct anoninfo)) !=
      sizeof(struct anoninfo))
    {
    log_err(errno, id, "read");
    rm_errno = RM_ERR_SYSTEM;
    return 1;
    }

  lastai = reqnum;

  return 0;
  }

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

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

  if (getanon(id))
    return NULL;

  sprintf(ret_string, "%ukb", ctob(ai.ani_max) >> 10); /* KB */

  return ret_string;
  }

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

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

  if (getanon(id))
    return NULL;

  /* in KB */
  sprintf(ret_string, "%ukb", ctob(ai.ani_max - ai.ani_resv) >> 10);

  return ret_string;
  }

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

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

  if (kd == -1)
    {
    log_err(-1, id, nokernel);
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  if (nl[KSYM_PHYS].n_type == 0)
    {
    log_err(-1, id, "physmem count not found");
    rm_errno = RM_ERR_SYSTEM;
    return 0;
    }

  if (lseek(kd, nl[KSYM_PHYS].n_value, SEEK_SET) == -1)
    {
    log_err(errno, id, "lseek");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  if (read_ac_socket(kd, (char *)&pmem, sizeof(pmem)) != sizeof(pmem))
    {
    log_err(errno, id, "read");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  pmem *= getpagesize();

  sprintf(ret_string, "%ukb", pmem >> 10); /* in KB */
  return ret_string;
  }

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

  struct mntent *mp;

  struct statvfs 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 (statvfs(param, &fsbuf) == -1)
    {
    log_err(errno, id, "statvfs");
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

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

  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, "stat");
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  sprintf(ret_string, "%ukb", sbuf.st_size >> 10); /* in 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/pts")) == NULL)
    {
    log_err(errno, id, "opendir /dev");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  maxtm = 0;

  curtm = time(NULL);

  setmax("/dev/mouse");

  while ((de = readdir(dp)) != NULL)
    {
    char *name = de->d_name;

    if (maxtm >= curtm)
      break;

    if (*name == '.')
      continue;

    sprintf(ttyname, "/dev/pts/%s", name);

    setmax(ttyname);
    }

  closedir(dp);

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



static char *
walltime(struct rm_attribute *attrib)
  {
  char   *id = "walltime";
  int   value;
  int   job;
  int   found = 0;
  int   i;
  time_t   now, start;
  prpsinfo_t  *pi;

  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, "proc") == 0)
    job = 0;
  else if (strcmp(attrib->a_qualifier, "session") == 0)
    job = 1;
  else
    {
    rm_errno = RM_ERR_BADPARAM;
    return NULL;
    }

  if ((now = time(NULL)) <= 0)
    {
    log_err(errno, id, "time");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

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

  start = now;

  for (i = 0; i < nproc; i++)
    {
    pi = &proc_info[i];

    if (job)
      {
      if (value != pi->pr_sid)
        continue;
      }
    else
      {
      if ((pid_t)value != pi->pr_pid)
        continue;
      }

    found = 1;

    start = MIN(start, pi->pr_start.tv_sec);
    }

  if (found)
    {
    sprintf(ret_string, "%ld", (long)((double)(now - start) * wallfactor));;
    return ret_string;
    }

  rm_errno = RM_ERR_EXIST;

  return NULL;
  }




void scan_non_child_tasks(void)

  {
  /* NYI */

  return;
  }  /* END scan_non_child_tasks() */


