/*
*         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.
*/

/*
** System dependent code to gather information for an OS X/Darwin machine.
**
** Resources known by this code:
**  cput  cpu time for a pid or session
**  mem  memory size for a pid or session in KB
**  resi  resident memory size for a pid or session in KB
**  sessions list of sessions in the system
**  pids  list of pids in a session
**  ncpus  number of cpus
**  nsessions number of sessions in the system
**  nusers  number of users in the system
**  physmem  physical 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
**  quota  quota information (sizes in KB)
*/

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

#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <pwd.h>
#include <nlist.h>
#include <fstab.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/mount.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <sys/uio.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/sysctl.h>
#include <sys/vmmeter.h>
#include <ufs/ufs/quota.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <signal.h>
#include <vector>

#include <mach/mach_init.h>
#include <mach/vm_map.h>
#include <mach/mach_host.h>
#include <mach/mach_port.h>
#include <mach/mach_traps.h>
#ifdef HAVE_MACH_SHARED_REGION_H
#include <mach/shared_region.h>
#else
#include <mach/shared_memory_server.h>
#endif
#include <mach/task.h>
#include <mach/thread_act.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 "mom_mach.h"
#include "resmon.h"
#include "mom_func.h"
#include "utils.h"
#include "../rm_dep.h"

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


typedef struct kinfo_proc kinfo_proc;


/*
** 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  int     LOGLEVEL;
extern  long    system_ncpus;
extern  int     ignwalltime;
extern  int     igncput;
extern  int     ignvmem;
extern  int     ignmem;
extern char   *ret_string;
extern char    extra_parm[];
extern char    no_parm[];
extern  char   *msg_momsetlim;

extern  time_t  time_now;

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

extern int errno;

/*
** local functions
*/

static char *resi(struct rm_attribute *attrib);
static char *physmem(struct rm_attribute *attrib);
static char *walltime(struct rm_attribute *attrib);
static char *quota(struct rm_attribute *attrib);
static char *ncpus(struct rm_attribute *attrib);
static char *totmem(struct rm_attribute *attrib);
static char *availmem(struct rm_attribute *attrib);
static char *netload(struct rm_attribute *attrib);
static int  get_tinfo_by_pid(struct task_basic_info *t_info,
                                   unsigned int pid);
static int  get_time_info_by_pid(struct task_thread_times_info *t_info,
                                       unsigned int pid);
static kinfo_proc *get_bsd_process_list(int *procCount);

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

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

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

  { "_cnt", 0, 0, 0, 0 },  /* 1 */
  { "_averunnable", 0, 0, 0, 0 }, /* 2 */
  { "", 0, 0, 0, 0 }
  };

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

/*
 * Time decoding macro.  Accepts a timeval structure.  Returns unsigned long
 * time in seconds.
 */
#define tvk(val) ((val).tv_sec+((unsigned long)(val).tv_usec+500000)/1000000)

time_t   wait_time = 10;

struct kinfo_proc *proc_tbl = NULL;
pid_t   *sess_tbl = NULL;
int   nproc = 0;
char   nokernel[] = "kernel not available";
char   noproc[] = "process %d does not exist";
static  int  nncpus = 0;
static  int  gpagesize;
static  int  cputype;
#if defined HAVE_MACH_SHARED_REGION_H
static  unsigned long  sharedregionbase;
static  unsigned int  sharedregionsize;
#endif


void
dep_initialize(void)

  {
  int    mib[2];
  size_t len;


  mib[0] = CTL_HW; /* get number of processors */
  mib[1] = HW_NCPU;

  len = sizeof(nncpus);

  sysctl(mib, 2, &nncpus, &len, NULL, 0);

  len = sizeof(gpagesize);

  if (sysctlbyname("hw.pagesize", &gpagesize, &len, NULL, 0) != 0)
    {
    gpagesize = 4096;
    }

  len = sizeof(cputype);

  if (sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) != 0)
    {
    cputype = CPU_TYPE_X86;
    }

#if defined(HAVE_MACH_SHARED_REGION_H)
  switch (cputype)
    {
    case CPU_TYPE_X86:
      sharedregionbase = SHARED_REGION_BASE_I386; /* same as x86_64? */
      sharedregionsize = SHARED_REGION_SIZE_I386; /* same as x86_64? */
    case CPU_TYPE_POWERPC:
      sharedregionbase = SHARED_REGION_BASE_PPC;
      sharedregionsize = SHARED_REGION_SIZE_PPC;
    case CPU_TYPE_POWERPC64:
      sharedregionbase = SHARED_REGION_BASE_PPC64;
      sharedregionsize = SHARED_REGION_SIZE_PPC64;
    default:
      sharedregionbase = SHARED_REGION_BASE_I386; /* i386 is current standard */
      sharedregionsize = SHARED_REGION_SIZE_I386; /* i386 is current standard */
    }
#endif

  return;
  }




void
dep_cleanup(void)

  {
  char *id = "dep_cleanup";

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

  return;
  }




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

void
dep_main_loop_cycle(void)
  {
  /* No periodic functions. */
  }



/*
 * 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.
 *
 *
 */

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

/*
** Scan a list of tasks and return true if one of them matches
** the process (sid or pid) represented by *psp.
*/

static int injob(

  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)
      {
      DBPRT(("ti_sid: %d -- sesid: %d\n",
             ptask->ti_qs.ti_sid, sesid));

      return(TRUE);
      }
    }

  return(FALSE);
  }

#define MINPROC 10
#define MAXPROC 10000




/*
 * Internal session cpu time decoding routine.
 *
 * Accepts a job id.  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)

  {
#ifndef NDEBUG
  static char id[] = "cput_sum";
#endif
  int         i;
  u_long      cputime;
  int         nps = 0;
  time_value_t   total_time;
  task_basic_info_data_t     mach_task_stats;
  task_thread_times_info_data_t   mach_time_info;
  int ret;

  cputime = 0;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (!injob(pjob, sess_tbl[i]))
      continue;

    nps++;

    cputime += tvk(pp->kp_proc.p_rtime);

    if (pp->kp_proc.p_ru == NULL)
      {
      ret = get_tinfo_by_pid(&mach_task_stats, pp->kp_proc.p_pid);

      if (ret != 0)
        {
        continue;
        }

      total_time =  mach_task_stats.user_time;

      time_value_add(&total_time, &(mach_task_stats.system_time))

      ret = get_time_info_by_pid(&mach_time_info, pp->kp_proc.p_pid);

      if (ret == 0)
        {
        time_value_add(&total_time, &(mach_time_info.user_time));
        time_value_add(&total_time, &(mach_time_info.system_time));
        }

      cputime += total_time.seconds;

      }
    else
      {
      cputime += pp->kp_proc.p_ru->ru_utime.tv_sec + pp->kp_proc.p_ru->ru_stime.tv_sec;
      }

    DBPRT(("%s: ses %d pid %d cputime %lu\n",

           id,
           sess_tbl[i],
           pp->kp_proc.p_pid,
           cputime))
    }  /* END for (i) */

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

  return((unsigned long)((double)cputime * cputfactor));
  } /* END cput_sum() */






/*
 * Internal session memory usage function.
 *
 * Accepts a job ID.  Returns the total number of bytes of address
 * space consumed by all current tasks within the job.
 */

static unsigned long mem_sum(

  job *pjob)  /* I */

  {
#ifndef NDEBUG
  char         *id = "mem_sum";
#endif
  int           i;
  unsigned long memsize = 0;

  struct task_basic_info t_info;

  unsigned int pid;

  DBPRT(("mem_sum entered.\n"));

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (!injob(pjob, sess_tbl[i]))
      continue;

    pid = pp->kp_proc.p_pid;

    if (get_tinfo_by_pid(&t_info, pid) != 0)
      continue;

    /* mach does says virtual_size is number of pages,
       but after noticing very wrong values, I took a look at the
       apple header files and they say it is the size in bytes...
    memsize += ctob(t_info.virtual_size); */
    memsize += t_info.virtual_size;

    DBPRT(("%s: ses %d pid=%d totmem=%lu\n",
           id, sess_tbl[i], pp->kp_proc.p_pid, memsize))
    }  /* END for (i) */

  return(memsize);
  }  /* END mem_sum */




/*
 * Internal session mem (workingset) size function.
 */

static unsigned long resi_sum(

  job *pjob)

  {
#ifndef NDEBUG
  char          *id = "resi_sum";
#endif
  int   i;
  unsigned long  memsize = 0;

  struct task_basic_info t_info;
  unsigned int pid;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (!injob(pjob, sess_tbl[i]))
      continue;

    pid = pp->kp_proc.p_pid;

    if (get_tinfo_by_pid(&t_info, pid) != 0)
      continue;

    /* see note in mem_sum
    memsize += ctob(t_info.resident_size); */
    memsize += t_info.resident_size;

    DBPRT(("%s: pid=%d ses=%d mem=%ld totmem=%lu\n",
           id, pp->kp_proc.p_pid, sess_tbl[i], (long)t_info.resident_size, memsize))
    }  /* END for (i) */

  return(memsize);
  }  /* END resi_sum() */





/*
 * Return TRUE if any task in the job is over limit for memory usage.
 */

static int overmem_proc(

  job           *pjob,
  unsigned long  limit)

  {
  int i;

  struct task_basic_info t_info;
  unsigned int pid;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (!injob(pjob, sess_tbl[i]))
      continue;

    pid = pp->kp_proc.p_pid;

    if (get_tinfo_by_pid(&t_info, pid) != 0)
      continue;

    if (t_info.virtual_size > limit)
      {
      return(TRUE);
      }

    }  /* END for (i) */

  return(FALSE);
  }





/*
 * 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 */
)
  {
#ifndef NDEBUG
  char  *id = "mom_set_limits";
#endif
  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);
  std::vector<resource> *resources = (std::vector<resource> *)pjob->ji_wattr[JOB_ATR_resource].at_val.at_ptr;

  /*
   * Cycle through all the resource specifications,
   * setting limits appropriately (SET_LIMIT_SET).
   */
  if (resources != NULL)
    {
    for (size_t i = 0; i < resources->size(); i++)
      {
      resource &r = resources->at(i);

      assert(r.rs_defin != NULL);
      pname = r.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(&r, &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(&r, &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(&r, &value);

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

          if (value > ULONG_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, "vmem") == 0)   /* check */
        {
        if (ignvmem == FALSE)
          {
          retval = mm_getsize(&r, &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(&r, &value);

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

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

            if ((mem_limit == 0) || (value < mem_limit))
              mem_limit = value;
            }
          }
        }
      else if (strcmp(pname, "mem") == 0)    /* ignore */
        {
        }
      else if (strcmp(pname, "pmem") == 0)   /* set */
        {
        if (ignmem == FALSE)
          {
          if (set_mode == SET_LIMIT_SET)
            {
            retval = mm_getsize(&r, &value);

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

            reslim.rlim_cur = reslim.rlim_max = value;

            if (setrlimit(RLIMIT_RSS, &reslim) < 0)
              return (error("RLIMIT_RSS", PBSE_SYSTEM));
            }
          }
        }
      else if (strcmp(pname, "walltime") == 0)   /* Check */
        {
        retval = mm_gettime(&r, &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)r.rs_value.at_val.at_long) == -1)
              && (errno != 0))
            return (error(pname, PBSE_BADATVAL));
          }
        }
      else if ((r.rs_defin->rs_flags & ATR_DFLAG_RMOMIG) == 0)
        /* don't recognize and not marked as ignore by mom */
        return (error(pname, PBSE_UNKRESC));
      }

  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_DATA, &reslim) < 0))
        return (error("RLIMIT_DATA", PBSE_SYSTEM));

      if ((ignvmem == 0) && (setrlimit(RLIMIT_STACK, &reslim) < 0))
        return (error("RLIMIT_STACK", 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)
  {
#ifndef NDEBUG
  char  *id = "mom_do_poll";
#endif
  char  *pname;

  DBPRT(("%s: entered\n", id))
  assert(pjob != NULL);
  assert(pjob->ji_wattr[(int)JOB_ATR_resource].at_type == ATR_TYPE_RESC);
  std::vector<resource> *resources = (std::vector<resource> *)pjob->ji_wattr[JOB_ATR_resource].at_val.at_ptr;

  if (resources != NULL)
    {
    for (size_t i = 0; i < resources->size(); i++)
      {
      pname = resources->at(i).rs_defin->rs_name;
      assert(pname != NULL);
      assert(*pname != '\0');

      if (strcmp(pname, "walltime") == 0 ||
          strcmp(pname, "cput") == 0 ||
          strcmp(pname, "pvmem") == 0 ||
          strcmp(pname, "vmem") == 0)
        {
        return (TRUE);
        }
      }
    }

  return(FALSE);
  }




/*
 * Setup for polling.
 *
 * Open kernel device and get namelist info.
 */

int
mom_open_poll(void)

  {
#ifndef NDEBUG
  char *id = "mom_open_poll";
#endif

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


  return(PBSE_NONE);
  }



int qs_cmp(

  const void *a,
  const void *b)

  {
  return((long)((struct kinfo_proc *)a)->kp_eproc.e_paddr - (long)((struct kinfo_proc *)b)->kp_eproc.e_paddr);
  }


/* get a list of BSD style processes from the kernel
   these will be returned as an array of kinfo_proc structures  */

static kinfo_proc *get_bsd_process_list(

  int        *procCount)  /*O*/

  {
  int                 err;
  bool                done;
  static const int    name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
  size_t              length;
  kinfo_proc         *result;

  assert(procCount != NULL);

  *procCount = 0;
  result = NULL;
  done = false;

  do
    {

    /* Call sysctl with a NULL buffer to get the size of the
       buffer we'll need. a NULL buffer results in length being
       set to indicate the amount of data that could be returned,
       if buffer is non-NULL then length is set to indicate the
       amount of data actually returned */

    length = 0;

    err = sysctl(
            (int *)name,
            (sizeof(name) / sizeof(*name)) - 1,
            NULL,
            &length,
            NULL,
            0);

    if (err == -1)
      {
      err = errno;
      }

    /* if success calloc buffer based on the results of previous call */
    if (err == 0)
      {
      result = calloc(1, length);

      if (result == NULL)
        {
        err = ENOMEM;
        }
      }

    /* Call sysctl again with the new buffer.  If we get an ENOMEM
     * error, free buffer and try this all again because size changed
     * between previous sysctl to get size and this one.
     */
    if (err == 0)
      {
      err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1,
                   result, &length,
                   NULL, 0);

      if (err == -1)
        {
        err = errno;
        }

      if (err == 0)
        {
        done = true;
        }
      else if (err == ENOMEM) /* buffer not big enough, try again */
        {
        assert(result != NULL);
        free(result);
        result = NULL;
        err = 0; /* clear err so we'll loop again */
        }
      }
    }
  while (err == 0 && !done);

  /* Clean up and establish post conditions. */

  if (err != 0 && result != NULL)
    {
    free(result);
    result = NULL;
    }

  /* set int pointed to by procCount to the number of kinfo_proc
     structures that will be returned */
  if (err == 0)
    {
    *procCount = length / sizeof(kinfo_proc);
    }
  else
    {
    *procCount = err;  /* send err back via procCount */
    }

  /* if our logic is right, if err == 0 then result will never be NULL
     and vice versa */
  assert((err == 0) == (result != NULL));

  return(result);
  }




/*
 * 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)

  {
  char          *id = "mom_get_sample";
  int           i;

  struct        kinfo_proc      *kp;

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

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

  proc_tbl = get_bsd_process_list(&nproc);

  /* proc_tbl now contains array of pairs of struct proc followed by
   * corresponding struct eproc's.
   *
   * The entire process table resides in proc_tbl.
   * nproc = number of processes, unless get_bsd_process_list returns NULL,
   * in which it will contain an error code.
   */

  if (proc_tbl == NULL)
    {

    sprintf(log_buffer, "get_bsd_process_list returned NULL (err = %d)", nproc);

    log_err(errno, id, log_buffer);

    return(PBSE_SYSTEM);
    }

  sess_tbl = (pid_t *)calloc(nproc, sizeof(pid_t));

  /* Create array of size nproc */

  if (sess_tbl == NULL)
    {
    sprintf(log_buffer, "can't allocate memory for session table");

    log_err(errno, id, log_buffer);

    return(PBSE_SYSTEM);
    }

  qsort(proc_tbl, nproc, sizeof(struct kinfo_proc), qs_cmp);

  for (i = 0, kp = proc_tbl;i < nproc;i++, kp++)
    {
    sess_tbl[i] = getsid(kp->kp_proc.p_pid);
    }

  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)

  {
#ifndef NDEBUG
  char  *id = "mom_over_limit";
#endif
  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);

  std::vector<resource> *resources = (std::vector<resource> *)pjob->ji_wattr[JOB_ATR_resource].at_val.at_ptr;

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

  if (resources != NULL)
    {
    for (size_t i = 0; i < resources->size(); i++)
      {
      resource &r = resources->at(i);
      assert(r.rs_defin != NULL);

      pname = r.rs_defin->rs_name;

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

      if ((igncput == FALSE) && (!strcmp(pname, "cput")))
        {
        retval = mm_gettime(&r, &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 (!strcmp(pname, "mem"))
        {
        retval = mm_getsize(&r, &value);

        if (retval != PBSE_NONE)
          continue;

        if ((num = resi_sum(pjob)) > value)
          {
          sprintf(log_buffer, "mem %lu exceeded limit %lu",
                  num, value);

          return(TRUE);
          }
        }
      else if (!strcmp(pname, "vmem"))
        {
        retval = mm_getsize(&r, &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 (!strcmp(pname, "pvmem"))
        {
        retval = mm_getsize(&r, &value);

        if (retval != PBSE_NONE)
          continue;

        if ((ignvmem == 0) && (overmem_proc(pjob, value)))
          {
          sprintf(log_buffer, "pvmem exceeded limit %lu",
                  value);

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

        retval = mm_gettime(&r, &value);

        if (retval != PBSE_NONE)
          continue;

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

        if (num > value)
          {
          sprintf(log_buffer, "walltime %lu exceeded limit %lu",
                  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)

  {
  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);
    assert(pres != NULL);
    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_LONG;
    pres->rs_value.at_val.at_long = 0;

    rd = find_resc_def(svr_resc_def, "vmem", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    assert(pres != NULL);
    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;
    pres->rs_value.at_val.at_size.atsv_num = 0;

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

    rd = find_resc_def(svr_resc_def, "mem", svr_resc_size);
    assert(rd != NULL);
    pres = add_resource_entry(at, rd);
    assert(pres != NULL);
    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;
    pres->rs_value.at_val.at_size.atsv_num = 0;
    }

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

  rd = find_resc_def(svr_resc_def, "mem", 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 = (resi_sum(pjob) + 1023) >> 10; /* in KB */
  *lp = MAX(*lp, lnum);

  return (PBSE_NONE);
  }



/*
 * Kill a job task.
 * Call with the job and a signal number.
 */

int kill_task(

  task *ptask,
  int   sig,
  int   pg)

  {
  char *id = "kill_task";
  int ct = 0;
  int i, err;
  int sesid;

  sesid = ptask->ti_qs.ti_sid;

  if (sesid <= 1)
    {
    return(0);
    }

  DBPRT(("%s sending %d\n",

         id,
         sig))

  if ((err = mom_get_sample()) != PBSE_NONE)
    {
    return(0);
    }

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (sesid != sess_tbl[i])
      continue;

    if (sig == SIGKILL)
      {

      struct timespec req;
      int i;

      req.tv_sec = 0;
      req.tv_nsec = 250000000;

      /* give the process some time to quit gracefully first */

      if (kill(pp->kp_proc.p_pid, SIGTERM) == -1)
        break;

      for (i = 0;i < 20;i++)
        {
        if (kill(pp->kp_proc.p_pid, 0) == -1)
          break;

        nanosleep(&req, NULL);
        }  /* END for (i = 0) */
      }

    sprintf(log_buffer, "%s: killing pid %d task %d with sig %d",

            id,
            pp->kp_proc.p_pid,
            ptask->ti_qs.ti_task,
            sig);

    log_record(
      PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      ptask->ti_job->ji_qs.ji_jobid,
      log_buffer);

    DBPRT(("%s: send signal %d to pid %d\n",
           id,
           sig,
           pp->kp_proc.p_pid))

    kill(pp->kp_proc.p_pid, sig);

    ++ct;
    }

  return(ct);
  }  /* END kill_task() */


/*
 * Clean up everything related to polling.
 *
 */

int
mom_close_poll(void)

  {
  char *id = "mom_close_poll";

  if (LOGLEVEL >= 4)
    {
    log_record(PBSEVENT_SYSTEM, 0, id, "entered");
    }

  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,  /*I*/
  char *file,   /*I*/
  int abort)    /*I*/

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

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

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

  lastproc = reqnum;

  return 1;
  }


char *cput_job(

  pid_t jobid)

  {
#ifndef NDEBUG
  char   *id = "cput_job";
#endif
  int   i;
  unsigned long  cputime;
  time_value_t   total_time;
  time_value_t   system_time;
  task_basic_info_data_t     mach_task_stats;
  task_thread_times_info_data_t   mach_time_info;
  int ret;

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

  cputime = 0;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (jobid != sess_tbl[i])
      continue;

    cputime += tvk(pp->kp_proc.p_rtime);

    if (pp->kp_proc.p_ru == NULL)
      {
      ret = get_tinfo_by_pid(&mach_task_stats, pp->kp_proc.p_pid);

      if (ret != 0)
        {
        continue;
        }


      total_time =  mach_task_stats.user_time;

      system_time = mach_task_stats.system_time;
      time_value_add(&total_time, &system_time)

      ret = get_time_info_by_pid(&mach_time_info, pp->kp_proc.p_pid);

      if (ret == 0)
        {
        time_value_add(&total_time, &(mach_time_info.user_time));
        time_value_add(&total_time, &(mach_time_info.system_time));
        }

      cputime += total_time.seconds;
      }
    else
      {
      cputime += pp->kp_proc.p_ru->ru_utime.tv_sec +
                 pp->kp_proc.p_ru->ru_stime.tv_sec;
      }

    DBPRT(("%s: ses %d pid %d cputime %lu\n", id,

           jobid, pp->kp_proc.p_pid, cputime))

    }

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

  return(ret_string);

  }




char *cput_proc(

  pid_t pid)

  {
#ifndef NDEBUG
  char  *id = "cput_proc";
#endif
  uint  cputime;
  int           i;
  time_value_t   total_time;
  time_value_t   system_time;
  task_basic_info_data_t     mach_task_stats;
  task_thread_times_info_data_t   mach_time_info;
  int ret;


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

    struct kinfo_proc *pp = &proc_tbl[i];

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

    cputime = tvk(pp->kp_proc.p_rtime);

    if (pp->kp_proc.p_ru == NULL)
      {
      ret = get_tinfo_by_pid(&mach_task_stats, pp->kp_proc.p_pid);

      if (ret != 0)
        {
        continue;
        }


      total_time =  mach_task_stats.user_time;

      system_time = mach_task_stats.system_time;
      time_value_add(&total_time, &system_time)

      ret = get_time_info_by_pid(&mach_time_info, pp->kp_proc.p_pid);

      if (ret == 0)
        {
        time_value_add(&total_time, &(mach_time_info.user_time));
        time_value_add(&total_time, &(mach_time_info.system_time));
        }

      cputime += total_time.seconds;
      }
    else
      {
      cputime += pp->kp_proc.p_ru->ru_utime.tv_sec + pp->kp_proc.p_ru->ru_stime.tv_sec;
      }

    DBPRT(("%s: pid %d cputime %d\n", id, pid, cputime))

    sprintf(ret_string, "%.2f",
            (double)cputime * 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(

  pid_t jobid)

  {
#ifndef NDEBUG
  char *id = "mem_job";
#endif
  int   i;
  int   memsize, addmem;
  int   found = 0;

  struct task_basic_info t_info;
  unsigned int pid;

  if (getprocs() == 0)
    {
    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  memsize = 0;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (jobid != sess_tbl[i])
      continue;

    found = 1;

    pid = pp->kp_proc.p_pid;

    if (get_tinfo_by_pid(&t_info, pid) != 0)
      continue;

    addmem = t_info.virtual_size;

    memsize += addmem;

    DBPRT(("%s: ses %d pid=%d totmem=%d\n",
           id, sess_tbl[i], pp->kp_proc.p_pid, memsize));
    }

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

    return(ret_string);
    }

  rm_errno = RM_ERR_EXIST;

  return(NULL);
  }




char *mem_proc(

  pid_t pid)

  {
  int   i;
  int   memsize;

  struct task_basic_info t_info;

  if (getprocs() == 0)
    {
    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  memsize = 0;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (pid != sess_tbl[i])
      continue;

    pid = pp->kp_proc.p_pid;

    if (get_tinfo_by_pid(&t_info, pid) != 0)
      continue;

    memsize = t_info.virtual_size;

    sprintf(ret_string, "%ukb",
            memsize >> 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;
    }
  }




static char *resi_job(

  pid_t jobid)

  {
  int i, found;
  int resisize;

  struct task_basic_info t_info;
  unsigned int pid;

  if (getprocs() == 0)
    {
    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  resisize = 0;

  found = 0;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (jobid != sess_tbl[i])
      continue;

    found = 1;

    pid = pp->kp_proc.p_pid;

    if (get_tinfo_by_pid(&t_info, pid) != 0)
      continue;

    resisize += t_info.resident_size;
    }

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

    return(ret_string);
    }

  rm_errno = RM_ERR_EXIST;

  return(NULL);
  }




static char *resi_proc(

  pid_t pid)

  {
  int i;
  int resisize;

  struct task_basic_info t_info;

  if (getprocs() == 0)
    {
    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  resisize = 0;

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

    struct kinfo_proc *pp = &proc_tbl[i];

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

    if (get_tinfo_by_pid(&t_info, pp->kp_proc.p_pid) != 0)
      continue;

    resisize = t_info.resident_size;

    sprintf(ret_string, "%ukb",
            resisize >> 10); /* KB */

    return(ret_string);
    }

  rm_errno = RM_ERR_EXIST;

  return(NULL);
  }




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"))
    {
    return(resi_job((pid_t)value));
    }
  else if (!strcmp(attrib->a_qualifier, "proc"))
    {
    return(resi_proc((pid_t)value));
    }

  /* FAILURE */

  rm_errno = RM_ERR_BADPARAM;

  return(NULL);
  }  /* END resi() */





char *sessions(

  struct rm_attribute *attrib)

  {
  char   *id = "sessions";
  int   i, j;
  char   *fmt;
  int   njids = 0;
  pid_t   *jids, jobid;

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

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

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

  /*
  ** Search for job
  */
  for (i = 0; i < nproc; i++)
    {

    struct kinfo_proc *pp = &proc_tbl[i];

    if (pp->kp_eproc.e_pcred.p_ruid == 0)
      continue;

    jobid = sess_tbl[i];

    DBPRT(("%s: pid %d sid %u\n",
           id, (int)pp->kp_proc.p_pid, jobid))
    for (j = 0; j < njids; j++)
      {
      if (jids[j] == jobid)
        break;
      }

    if (j == njids)   /* not found */
      jids[njids++] = jobid; /* so add it 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);

  if (njids == 0)
    {
    return(NULL);            /* by analogy with linux version */
    }

  return(ret_string);
  }  /* END sessions() */





char *nsessions(

  struct rm_attribute *attrib)

  {
  char *result, *ch;
  int   num = 1;

  if ((result = sessions(attrib)) == NULL)
    {
    sprintf(ret_string, "%d",
            num);

    return(ret_string);
    }

  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;
  char   *fmt;
  int   num_pids = 0;

  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;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    DBPRT(("%s[%d]: pid %d sid %u\n",
           id, num_pids, pp->kp_proc.p_pid, sess_tbl[i]))

    if (jobid != sess_tbl[i])
      continue;

    checkret(&fmt, 100);

    sprintf(fmt, " %d", pp->kp_proc.p_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;
  int   nuids = 0;
  uid_t   *uids, uid;

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

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

  if ((uids = (uid_t *)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++)
    {

    struct kinfo_proc *pp = &proc_tbl[i];

    if ((uid = pp->kp_eproc.e_pcred.p_ruid) == 0)
      continue;

    DBPRT(("%s: pid %d uid %u\n",
           id, (int)pp->kp_proc.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);
  }  /* END nusers() */




static char *totmem(

  struct rm_attribute *attrib)

  {
  char             *id = "totmem";
#ifdef HW_MEMSIZE
  uint64_t          mem = 0;
#else
  int               mem = 0;
#endif
  size_t            len;

#ifdef VM_SWAPUSAGE
  int               mib[2];

  struct xsw_usage  swap;
#endif /* VM_SWAPUSAGE */


  if (attrib != NULL)
    {
    log_err(-1, id, extra_parm);

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }

  /* Get Physical Memory Size
   * We have to be careful as hw.memsize is not
   * defined on pre 10.3
   */

  len = sizeof(mem);

#ifdef HW_MEMSIZE
  if (sysctlbyname("hw.memsize", &mem, &len, NULL, 0) != 0)
    {
    sprintf(log_buffer, "sysctl failed with errno %d",
            errno);
    log_err(-1, id, log_buffer);
    return(NULL);
    }

#else /* HW_MEMSIZE */
  if (sysctlbyname("hw.physmem", &mem, &len, NULL, 0) != 0)
    {
    sprintf(log_buffer, "sysctl failed with errno %d",
            errno);
    log_err(-1, id, log_buffer);
    return(NULL);
    }

#endif /* HW_MEMSIZE */

  /* Get Swap Size */

#ifdef VM_SWAPUSAGE
  mib[0] = CTL_VM;

  mib[1] = VM_SWAPUSAGE;

  len = sizeof(struct xsw_usage);

  if (sysctl(mib, 2, &swap, &len, NULL, 0) != 0)
    {
    sprintf(log_buffer, "sysctl failed with errno %d",
            errno);

    log_err(-1, id, (char *)"log_buffer");

    return(NULL);
    }

  mem += swap.xsu_total;

#else /* VM_SWAPUSAGE */
  /* need to load swap info from alternate source */

  /* (not implemented) */
#endif /* VM_SWAPUSAGE */

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "%s: total mem=%lu\n",
            id,
            (unsigned long) mem);

    log_record(PBSEVENT_SYSTEM, 0, id, log_buffer);
    }

  sprintf(ret_string, "%lukb",

          (unsigned long)(mem / 1024.0));  /* KB */

  return(ret_string);
  }  /* END totmem() */



static char *availmem(

  struct rm_attribute *attrib)

  {
  char *id = "availmem";

  struct vm_statistics stat;
  mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
  uint64_t mem = 0;
#ifdef VM_SWAPUSAGE
  int      mib[2];
  size_t   len;

  struct xsw_usage swap;
#endif /* VM_SWAPUSAGE */

  if (attrib != NULL)
    {
    log_err(-1, id, extra_parm);

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }

  /* Get Swap Info */

#ifdef VM_SWAPUSAGE
  mib[0] = CTL_VM;

  mib[1] = VM_SWAPUSAGE;

  len = sizeof(struct xsw_usage);

  if (sysctl(mib, 2, &swap, &len, NULL, 0) != 0)
    {
    sprintf(log_buffer, "sysctl failed with errno %d",
            errno);
    log_err(-1, id, (char *)"log_buffer");
    return(NULL);
    }

  mem += swap.xsu_avail;

#else /* VM_SWAPUSAGE */
  /* need to load swap info */

  /* (not implemented) */
#endif /* VM_SWAPUSAGE */

  if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&stat,
                      &count) != KERN_SUCCESS)
    {
    log_err(-1, id, (char *)"host_statistics failed");
    rm_errno = RM_ERR_SYSTEM;
    return(NULL);
    }

  mem += (uint64_t)stat.free_count * gpagesize;

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "%s: free mem=%lu\n", id, (unsigned long)mem);
    log_record(PBSEVENT_SYSTEM, 0, id, log_buffer);
    }

  sprintf(ret_string, "%lukb", (unsigned long)(mem / 1024)); /* KB */

  return(ret_string);
  }  /* END availmem() */




/*
 *  netload.  right now this just sums the number of bytes sent/recieved on an
 *  interface this appears to be what netload in linux/res_mom.c does, but
 *  maybe we should be doing something different, I'm not really sure what
 *  netload is supposed to be
 */

static char *netload(

  struct rm_attribute *attrib)

  {
  char *id = "netload";

  struct ifaddrs *ifap, *ifa;

  struct if_data *ifd;
  unsigned long bytes = 0;

  if (attrib != NULL)
    {
    log_err(-1, id, extra_parm);

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }


  if (getifaddrs(&ifap) < 0)
    {
    sprintf(log_buffer, "getifaddrs error");
    log_record(PBSEVENT_SYSTEM, 0, id, log_buffer);

    return(NULL);
    }

  for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
    {
    if ((ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_LOOPBACK))
      {
      if (ifa->ifa_addr->sa_family != AF_LINK)
        continue;

      ifd = (struct if_data *)ifa->ifa_data;

      bytes += ifd->ifi_ibytes;

      bytes += ifd->ifi_obytes;
      }
    }    /* END for (ifa) */

  freeifaddrs(ifap);

  sprintf(ret_string, "%lu",
          bytes);

  return(ret_string);
  }  /* END netload() */




static char *ncpus(

  struct rm_attribute *attrib)

  {
  if (attrib != NULL)
    {
    log_err(-1, "ncpus", extra_parm);

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }

  sprintf(ret_string, "%d",

          nncpus);

  system_ncpus = nncpus;

  return(ret_string);
  }




static char *physmem(

  struct rm_attribute *attrib)

  {
  char *id = "physmem";

  if (attrib != NULL)
    {
    log_err(-1, id, extra_parm);

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }

#ifdef HW_MEMSIZE

    {
    /* NOTE:  patch by UMaine */

    uint64_t physmem = 0;

    size_t len = sizeof(physmem);

    if (sysctlbyname("hw.memsize", &physmem, &len, NULL, 0) < 0)
      {
      log_err(errno, id, (char *)"sysctl");

      rm_errno = RM_ERR_SYSTEM;

      return(NULL);
      }

    sprintf(ret_string, "%ukb",

            (uint32_t)(physmem >> 10));
    }  /* END BLOCK */

#else

    {
    unsigned int physmem = 0;  /* change to unsigned long? (proposed) */

    size_t   len = sizeof(physmem);

    int      mib[2];

    mib[0] = CTL_HW;
    mib[1] = HW_PHYSMEM;

    if (sysctl(mib, 2, &physmem, &len, NULL, 0) < 0)
      {
      log_err(errno, id, (char *)"sysctl");

      rm_errno = RM_ERR_SYSTEM;

      return(NULL);
      }

    /* change to "%ulkb" (proposed) */

    sprintf(ret_string, "%ukb",
            physmem >> 10);     /* in KB */

    }  /* END BLOCK */

#endif /* HW_MEMSIZE */

  return(ret_string);
  }  /* END physmem() */




char *size_fs(

  char *param)

  {
  char *id = "size_fs";

  struct statfs fsbuf;

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

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

  /* in KB */

  sprintf(ret_string, "%lukb",
          (unsigned long)(((double)fsbuf.f_bsize * (double)fsbuf.f_bavail) / 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",
            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, "%lldkb", 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)
    {
    DBPRT(("setmax: dev %s access %ld replaces max %ld\n",
           dev, (long)sb.st_atime, (long)maxtm))

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

  setmax("/dev/mouse");

  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, "%ld", (long)MAX(0, curtm - maxtm));
  return ret_string;
  }




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

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

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

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

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

  start = now;

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

    struct kinfo_proc *pp = &proc_tbl[i];

    if (job)
      {
      if (value != sess_tbl[i])
        continue;
      }
    else
      {
      if (value != pp->kp_proc.p_pid)
        continue;
      }

    if (pp->kp_proc.p_stat != SIDL ||
        pp->kp_proc.p_stat != SRUN ||
        pp->kp_proc.p_stat != SSLEEP ||
        pp->kp_proc.p_stat != SSTOP ||
        pp->kp_proc.p_stat != SZOMB)
      {
      rm_errno = RM_ERR_SYSTEM;
      return NULL;
      }

    found = 1;

    start = MIN(start, pp->kp_proc.p_un.__p_starttime.tv_sec);
    }

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

  rm_errno = RM_ERR_EXIST;

  return NULL;
  }

int get_la(
  double *rv)
  {
  char *id = "get_la";
  int mib[2];

  struct loadavg la;
  size_t len = sizeof(la);


  mib[0] = CTL_VM;
  mib[1] = VM_LOADAVG;

  if (sysctl(mib, 2, &la, &len, NULL, 0) < 0)
    {
    log_err(errno, id, (char *)"sysctl(VM_LOADAVG)");
    return (rm_errno = RM_ERR_SYSTEM);
    }

  *rv = (double)la.ldavg[0] / la.fscale;

  return (0);
  }


u_long gracetime(
  u_long secs)
  {
  time_t now = time((time_t *)NULL);

  if (secs > (u_long)now) /* time is in the future */
    return (secs - now);
  else
    return (0);
  }

static char *quota(
  struct rm_attribute *attrib)
  {
  char *id = "quota";
  int type;
  dev_t dirdev;
  uid_t uid;

  struct stat    sb;

  struct fstab  *fs;

  struct dqblk   qi;

  struct passwd *pw;

  static char *type_array[] =
    {
    "harddata",
    "softdata",
    "currdata",
    "hardfile",
    "softfile",
    "currfile",
    "timedata",
    "timefile",
    };

  enum type_name
    {
    harddata,
    softdata,
    currdata,
    hardfile,
    softfile,
    currfile,
    timedata,
    timefile,
    type_end
    };

  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 */
    sprintf(log_buffer,
            "not an absolute path: %s", attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_BADPARAM;
    return (NULL);
    }

  if (stat(attrib->a_value, &sb) == -1)
    {
    sprintf(log_buffer, "stat: %s", attrib->a_value);
    log_err(errno, id, log_buffer);
    rm_errno = RM_ERR_EXIST;
    return (NULL);
    }

  dirdev = sb.st_dev;

  DBPRT(("dir has devnum %d\n", dirdev))

  if (setfsent() == 0)
    {
    log_err(errno, id, (char *)"setfsent");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  while ((fs = getfsent()) != NULL)
    {
    if (strcmp(fs->fs_type, FSTAB_XX) == 0 ||
        strcmp(fs->fs_type, FSTAB_SW) == 0)
      continue;

    if (stat(fs->fs_file, &sb) == -1)
      {
      sprintf(log_buffer, "stat: %s", fs->fs_file);
      log_err(errno, id, log_buffer);
      continue;
      }

    DBPRT(("%s\t%s\t%d\n", fs->fs_spec, fs->fs_file, sb.st_dev))

    if (sb.st_dev == dirdev)
      break;
    }

  endfsent();

  if (fs == NULL)
    {
    sprintf(log_buffer, "filesystem %s not found", attrib->a_value);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_EXIST;
    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, "user") != 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 ((uid = (uid_t)atoi(attrib->a_value)) == 0)
    {
    char *buf;

    if ((pw = getpwnam_ext(&buf, attrib->a_value)) == NULL)
      {
      sprintf(log_buffer, "user not found: %s", attrib->a_value);
      log_err(-1, id, log_buffer);
      rm_errno = RM_ERR_EXIST;
      return (NULL);
      }

    uid = pw->pw_uid;
    free_pwnam(pw, buf);
    }

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

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

    case harddata:
      sprintf(ret_string, "%ukb", dbtob(qi.dqb_bhardlimit, 1) >> 10);
      break;

    case softdata:
      sprintf(ret_string, "%ukb", dbtob(qi.dqb_bsoftlimit, 1) >> 10);
      break;

    case currdata:
      sprintf(ret_string, "%ukb", dbtob(qi.dqb_curbytes, 1) >> 10);
      break;

    case hardfile:
      sprintf(ret_string, "%u", qi.dqb_ihardlimit);
      break;

    case softfile:
      sprintf(ret_string, "%u", qi.dqb_isoftlimit);
      break;

    case currfile:
      sprintf(ret_string, "%u", qi.dqb_curinodes);
      break;

    case timedata:
      sprintf(ret_string, "%lu", gracetime(qi.dqb_btime));
      break;

    case timefile:
      sprintf(ret_string, "%lu", gracetime(qi.dqb_itime));
      break;
    }

  return ret_string;
  }


void scan_non_child_tasks(void)

  {
  /* NYI */

  return;
  }  /* END scan_non_child_tasks() */


/* This is basically a wrapper for the MACH t_info and vm_region calls */
static int get_tinfo_by_pid(

  struct task_basic_info *t_info,
  unsigned int            pid)

  {
  task_t task;

  mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
#ifdef VM_REGION_BASIC_INFO_64
  mach_msg_type_number_t vm_info_count = VM_REGION_BASIC_INFO_COUNT_64;
#else
  mach_msg_type_number_t vm_info_count = VM_REGION_BASIC_INFO_COUNT;
#endif

  struct vm_region_basic_info_64 vm_info;

  vm_size_t     size;
  mach_port_t   object_name;
  vm_address_t address;
  kern_return_t errno;


#if defined(HAVE_MACH_SHARED_REGION_H)
  address = sharedregionbase;
#else
  address = GLOBAL_SHARED_TEXT_SEGMENT;
#endif

  if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS)
    {
    DBPRT(("get_tinfo_by_pid:  pid is not a valid process id.\n"));
    return(-1);
    }

  if ((errno = task_info(
                 task,
                 TASK_BASIC_INFO,
                 (task_info_t)t_info,
                 &t_info_count) != KERN_SUCCESS))
    {
    DBPRT(("get_tinfo_by_pid: error(%d) in task_info\n",
           errno));
    return(errno);
    }

#ifdef VM_REGION_BASIC_INFO_COUNT_64
  if ((errno = vm_region_64(
                 task,
                 &address,
                 &size,
                 VM_REGION_BASIC_INFO_64,
                 (vm_region_info_t) & vm_info,
                 &vm_info_count,
                 &object_name)) != KERN_SUCCESS)
    {
    DBPRT(("get_tinfo_by_pid: error(%d) in vm_region_64\n",
           errno));
    return(errno);
    }

#else
  if ((errno = vm_region(
                 task,
                 &address,
                 &size,
                 VM_REGION_BASIC_INFO,
                 (vm_region_info_t) & vm_info,
                 &vm_info_count,
                 &object_name)) != KERN_SUCCESS)
    {
    DBPRT(("get_tinfo_by_pid: error(%d) in vm_region\n",
           errno));
    return(errno);
    }

#endif


  /*
   * From Darwin 'ps' source
   * "Check for firmware split libraries"
   * We'll modify t_info inline here.
   */

#if! defined(HAVE_MACH_SHARED_REGION_H)
/* FIXME: What do we need in the mach/shared_region.h case? */
  if (vm_info.reserved &&
      (size == SHARED_TEXT_REGION_SIZE) &&
      (t_info->virtual_size > (SHARED_DATA_REGION_SIZE + SHARED_TEXT_REGION_SIZE)))
    {
    t_info->virtual_size -= (SHARED_DATA_REGION_SIZE + SHARED_TEXT_REGION_SIZE);
    }
#endif

  return(0);
  }



/* get the system time and user time for live threads for a task by pid */
static int get_time_info_by_pid(

  struct task_thread_times_info *t_info,
  unsigned int            pid)

  {
  task_t task;
  mach_msg_type_number_t t_info_count = TASK_THREAD_TIMES_INFO_COUNT;
  kern_return_t errno;

  if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS)
    {
    DBPRT(("get_time_info_by_pid:  pid is not a valid process id.\n"));
    return(-1);
    }

  if ((errno = task_info(
                 task,
                 TASK_THREAD_TIMES_INFO,
                 (task_info_t)t_info,
                 &t_info_count) != KERN_SUCCESS))
    {
    DBPRT(("get_time_info_by_pid: error(%d) in task_info\n",
           errno));
    return(errno);
    }

  return(0);
  }

