/*
*         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:
*
* 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 <vector>
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <strings.h>
#include <pwd.h>
#include <mntent.h>
#include <asm/types.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/procfs.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <sys/sysmacros.h>
#include <sys/resource.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <csv.h>

#include "quota.h"
/*    exteed of <sys/quota.h> and <syscall.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 "mom_func.h"
#include "resmon.h"
#include "utils.h"
#include "../rm_dep.h"
#include "mom_config.h"

#include <windows.h>
#include <sys/cygwin.h>
#include <iphlpapi.h>
#include <winbase.h>
#include <grp.h>


/*
** System dependent code to gather information for the resource
** monitor for a Linux i386 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
**  nsessions number of sessions in the system
**  nusers  number of users in the system
**  totmem  total memory size in KB
**  availmem available memory size in KB
**  ncpus  number of cpus
**  physmem  physical memory size in KB
**  size  size of a file or filesystem
**  idletime seconds of idle time
**  walltime wall clock time for a pid
**  loadave  current load average
**  quota  quota information (sizes in kb)
**              netload         number of bytes transferred for all interfaces
*/

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

/***************************************************/
/***  setrlimit don't support on CYGWIN till now ***/
/***************************************************/
#undef HAVE_GYGWIN_SETRLIMIT

static char    procfs[] = "/proc";
static DIR    *pdir = NULL;
static int     pagesize;

extern char *ret_string;
extern char extra_parm[];
extern char no_parm[];

extern time_t   time_now;

#define TBL_INC 200            /* initial proc table */

static proc_stat_t   *proc_array = NULL;
static int            nproc = 0;
static int            max_proc = 0;

/*
** external functions and data
*/
extern struct	config		*search (struct config *,char *);
extern struct	rm_attribute	*momgetattr (char *);
extern int                      rm_errno;
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 *);
static char *totmem   (struct rm_attribute *);
static char *availmem (struct rm_attribute *);
static char *physmem  (struct rm_attribute *);
static char *ncpus    (struct rm_attribute *);
static char *walltime (struct rm_attribute *);
static char *quota    (struct rm_attribute *);
static char *netload  (struct rm_attribute *);

#ifndef mbool_t
#define mbool_t char
#endif /* mbool_t */

mbool_t ProcIsChild(char *,char *,char *);

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

time_t wait_time = 10;

typedef struct proc_mem
  {
  unsigned long long mem_total;
  unsigned long long mem_used;
  unsigned long long mem_free;
  unsigned long long swap_total;
  unsigned long long swap_used;
  unsigned long long swap_free;
  } proc_mem_t;

/*
** local resource array
*/

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

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

unsigned linux_time = 0;

/*
 * support routine for getting system time -- sets linux_time
 */

void
proc_get_btime(void)

  {
  FILE *fp;
  char  label[256];

  if ((fp = fopen("/proc/stat", "r")) == NULL)
    {
    return;
    }

  while (!feof(fp))
    {
    if (fscanf(fp, "%s", label) != 1)
      {
      fclose(fp);
      return;
      }

    if (strcmp(label, "btime"))
      {
      if (fscanf(fp, "%*[^\n]%*c") != 0)
        {
        fclose(fp);
        return;
        }
      }
    else
      {
      if (fscanf(fp, "%u", &linux_time) != 1) {}

      fclose(fp);

      return;
      }
    }    /* END while (!feof(fp)) */

  fclose(fp);

  return;
  }  /* END proc_get_btime() */

/* NOTE:  see 'man 5 proc' for /proc/pid/stat format and description */

/* NOTE:  leading '*' indicates that field should be ignored */

/* FORMAT: <PID> <COMM> <STATE> <PPID> <PGRP> <SESSION> [<TTY_NR>] [<TPGID>] <FLAGS> [<MINFLT>] [<CMINFLT>] [<MAJFLT>] [<CMAJFLT>] <UTIME> <STIME> <CUTIME> <CSTIME> [<PRIORITY>] [<NICE>] [<0>] [<ITREALVALUE>] <STARTTIME> <VSIZE> <RSS> [<RLIM>] [<STARTCODE>] ... */

static char stat_str[] = " %c %d %d %d %*d %*d %u %*u \
                         %*u %*u %*u %lu %lu %lu %lu %*ld %*ld %*u %*ld %lu %llu %lld %*lu %*lu \
                         %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu";

/*
 * Convert jiffies to seconds.
 *
 * Hertz is sysconf(_SC_CLK_TCK) in get_proc_stat()
*/

#define JTOS(x)  (x) / Hertz;

/*
 * Linux /proc status routine.
 *
 * Returns a pointer to a static proc_stat_t structure given
 * a process number, or NULL if there is an error.  Takes the
 * place of the ioctl call PIOCSTATUS in the irix imp of mom_mach.c
 *
 */

proc_stat_t *get_proc_stat(

  int pid)  /* I */

  {
  static proc_stat_t ps;
  static char  path[1024];
  static char  readbuf[4096];
  static char          *lastbracket;
  FILE                 *fd;
  unsigned long         jstarttime;  /* number of jiffies since OS start time when process started */

  struct stat  sb;

  static int Hertz  = 0;
  int Hertz_errored = 0;

  if (Hertz <= 0)
    {
    Hertz = sysconf(_SC_CLK_TCK);  /* returns 0 on error */

    if (Hertz <= 0)
      {
      /* FAILURE */

      if (!Hertz_errored)
        log_err(errno, "get_proc_stat", (char *)"sysconf(_SC_CLK_TCK) failed, unable to monitor processes");

      Hertz_errored = 1;

      return(NULL);
      }
    }

  Hertz_errored = 0;

  sprintf(path, "/proc/%d/stat",
          pid);

  if ((fd = fopen(path, "r")) == NULL)
    {
    /* FAILURE */

    return(NULL);
    }

  /* use 'man 5 proc' for /proc/pid/stat format */

  if (!fgets(readbuf, sizeof(readbuf), fd))
    {
    fclose(fd);

    return(NULL);
    }

  lastbracket = strrchr(readbuf, ')');

  if (lastbracket == NULL)
    {
    fclose(fd);

    return(NULL);
    }

  *lastbracket = '\0'; /* We basically split the string here, overwriting the ')'. */

  lastbracket++;

  if (sscanf(readbuf,"%d (%[^\n]",&ps.pid,path) != 2)
    {
    /* FAILURE */

    fclose(fd);

    return(NULL);
    }

  /* see stat_str[] value for mapping 'stat' format */

  if (sscanf(lastbracket,stat_str,
        &ps.state,     /* state (one of RSDZTW) */
        &ps.ppid,      /* ppid */
        &ps.pgrp,      /* pgrp */
        &ps.session,   /* session id */
        &ps.flags,     /* flags - kernel flags of the process, see the PF_* in <linux/sched.h> */
        &ps.utime,     /* utime - jiffies that this process has been scheduled in user mode */
        &ps.stime,     /* stime - jiffies that this process has been scheduled in kernel mode */
        &ps.cutime,    /* cutime - jiffies that this process’s waited-for children have been scheduled in user mode */
        &ps.cstime,    /* cstime - jiffies that this process’s waited-for children have been scheduled in kernel mode */
        &jstarttime,   /* starttime */
        &ps.vsize,     /* vsize */
        &ps.rss) != 12)   /* rss */
    {
    /* FAILURE */

    fclose(fd);

    return(NULL);
    }

  if (fstat(fileno(fd), &sb) == -1)
    {
    /* FAILURE */

    fclose(fd);

    return(NULL);
    }

  ps.uid = sb.st_uid;

  ps.start_time = linux_time + JTOS(jstarttime);
  ps.name = path;

  ps.utime = JTOS(ps.utime);
  ps.stime = JTOS(ps.stime);
  ps.cutime = JTOS(ps.cutime);
  ps.cstime = JTOS(ps.cstime);

  /* SUCCESS */

  fclose(fd);

  return(&ps);
  }  /* END get_proc_stat() */



proc_mem_t *get_proc_mem(void)

  {
  static proc_mem_t   mm;
  FILE               *fp;
  char                str[32];

  if ((fp = fopen("/proc/meminfo","r")) == NULL)
    {
    return(NULL);
    }

  if (fscanf(fp,"%30s",str) != 1)
    {
    return(NULL);
    }

  if (!strncmp(str,"total:",sizeof(str)))
    {
    /* old format */

    if (fscanf(fp,"%*[^\n]%*c") != 0)     /* remove text header */
      {
      return(NULL);
      }

    /* umu vmem patch */

    if (fscanf(fp, "%*s %llu %llu %llu %*u",
               &mm.mem_total,
               &mm.mem_used,
               &mm.mem_free) != 3)
       {
      return(NULL);
      }

    /*
        if (fscanf(fp,"%*s %lu %lu %lu %*[^\n]%*c",
          &mm.mem_total,
          &mm.mem_used,
          &mm.mem_free) != 3)
          {
          return(NULL);
          }
    */

    if (fscanf(fp, "%*s %llu %llu %llu %*[^\n]%*c",
               &mm.swap_total,
               &mm.swap_used,
               &mm.swap_free) != 3)
      {
      return(NULL);
      }
    }
  fclose(fp);

  return(&mm);
  }  /* END get_proc_mem() */




void dep_initialize(void)

  {
  char *id = "dep_initialize";

  pagesize = getpagesize();

  if ((pdir = opendir(procfs)) == NULL)
    {
    log_err(errno, id, (char *)"opendir");

    return;
    }

  proc_get_btime();

  return;
  }  /* END dep_initialize() */





void
dep_cleanup(void)

  {
  char *id = "dep_cleanup";

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

  if (pdir)
    {
    closedir(pdir);

    pdir = NULL;
    }

  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.
 *
 *  sizeof(word) = sizeof(int)
 */




static int mm_getsize(

  resource *pres,  /* I */
  unsigned long *ret)   /* O */

  {
  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);
  }  /* END mm_getsize() */





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






static int injob(

  job   *pjob,
  pid_t  sid)

  {
  task *ptask;

  for (ptask = (task *)GET_NEXT(pjob->ji_tasks);
       ptask != NULL;
       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);
  }  /* END injob() */





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

  {
  char          *id = "cput_sum";
  ulong          cputime;
  int            nps = 0;
  int            i;
  proc_stat_t   *ps;

  cputime = 0;

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "proc_array loop start - jobid = %s",
            pjob->ji_qs.ji_jobid);

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

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

    if ((LOGLEVEL >= 6) && (ps == NULL))
      {
      sprintf(log_buffer, "proc_array loop end - nproc=%d, i=%d, ps is null",
              nproc,
              i);

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

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

    nps++;

    cputime += (ps->utime + ps->stime + ps->cutime + ps->cstime);

    if (LOGLEVEL >= 6)
      {
      sprintf(log_buffer, "%s: session=%d pid=%d cputime=%lu (cputfactor=%f)",
              id,
              ps->session,
              ps->pid,
              cputime,
              cputfactor);

      log_record(PBSEVENT_SYSTEM, 0, id, log_buffer);
      }
    }    /* 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() */






/*
 * Return TRUE if any process in the job is over limit for cputime usage.
 */

static int overcpu_proc(

  job  *pjob,
  unsigned long  limit)  /* I */

  {
  char  *id = "overcpu_proc";
  ulong  memsize;

  struct dirent *dent;
  ulong  cputime;
  proc_stat_t *ps;

  memsize = 0;

  rewinddir(pdir);

  while ((dent = readdir(pdir)) != NULL)
    {
    if (!isdigit(dent->d_name[0]))
      continue;

    if ((ps = get_proc_stat(atoi(dent->d_name))) == NULL)
      {
      if (errno != ENOENT)
        {
        sprintf(log_buffer, "%s: get_proc_stat",
                dent->d_name);

        log_err(errno, id, log_buffer);
        }

      continue;
      }

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

    /* change from ps->cutime to ps->utime, and ps->cstime to ps->stime */

    cputime = (ulong)((double)(ps->utime + ps->stime) * cputfactor);

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

  return(FALSE);
  }  /* END overcpu_proc() */





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

/* NOTE:  routine should be modified to return llu */

static unsigned long long mem_sum(

  job *pjob)

  {
  char   *id = "mem_sum";
  int                    i;
  unsigned long long     segadd;
  proc_stat_t  *ps;

  segadd = 0;

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "proc_array loop start - jobid = %s",
            pjob->ji_qs.ji_jobid);

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

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

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

    segadd += ps->vsize;
    }  /* END for (i) */

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





/*
 * Internal session workingset size function.
 */

static unsigned long resi_sum(

  job *pjob)

  {
  char  *id = "resi_sum";

  ulong          resisize;
  int            i;
  proc_stat_t *ps;

  resisize = 0;

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "proc_array loop start - jobid = %s",
            pjob->ji_qs.ji_jobid);

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

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

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

    resisize += ps->rss * pagesize;
    }  /* END for (i) */

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




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

static int overmem_proc(

  job       *pjob,  /* I */
  unsigned long long  limit) /* I */

  {
  char  *id = "overmem_proc";
  unsigned long long memsize;
  int            i;
  proc_stat_t *ps;

  memsize = 0;

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "proc_array loop start - jobid = %s",
            pjob->ji_qs.ji_jobid);

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

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

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

    if (ps->vsize > limit)
      {
      return(TRUE);
      }
    }    /* END for (i) */

  return(FALSE);
  }  /* END overmem_proc() */




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

  fprintf(stderr, msg_momsetlim, string, message);

  fflush(stderr);

  return(value);
  }  /* END error() */





/*
 * 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 being called to modify
 * existing limits.  Cannot alter those set by setrlimit (kernel)
 * because we are the wrong process.
 */

int mom_set_limits(

  job *pjob,     /* I */
  int  set_mode) /* SET_LIMIT_SET or SET_LIMIT_ALTER */

  {
  char  *id = "mom_set_limits";

  char  *pname = NULL;
  int  retval;
  unsigned long value; /* place in which to build resource value */
  resource *pres;

  struct rlimit reslim;
  unsigned long vmem_limit = 0;
  unsigned long mem_limit = 0;

  /* NOTE:  log_buffer is exported */

  if (LOGLEVEL >= 2)
    {
    sprintf(log_buffer, "%s(%s,%s) entered",
            id,
            (pjob != NULL) ? pjob->ji_qs.ji_jobid : "NULL",
            (set_mode == SET_LIMIT_SET) ? "set" : "alter");

    log_record(PBSEVENT_SYSTEM, 0, id, log_buffer);

    log_buffer[0] = '\0';
    }

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

  memset(&reslim, 0, sizeof(reslim));

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

      if (r.rs_defin != NULL)
        pname = r.rs_defin->rs_name;
      else
        pname = NULL;

      if (LOGLEVEL >= 2)
        {
        sprintf(log_buffer, "setting limit for attribute '%s'",
                (pname != NULL) ? pname : "NULL");

        log_record(PBSEVENT_SYSTEM, 0, id, log_buffer);

        log_buffer[0] = '\0';
        }

      assert(r.rs_defin != NULL);

      assert(pname != NULL);

      assert(pname[0] != '\0');

      if (!strcmp(pname, "cput"))
        {
        if (igncput == FALSE)
          {
          /* cpu time - check, if less than pcput use it */

          retval = mm_gettime(&r, &value);

          if (retval != PBSE_NONE)
            {
            sprintf(log_buffer, "cput mm_gettime failed in %s",id);

            return(error(pname, retval));
            }
          }
        }
      else if (!strcmp(pname, "pcput"))
        {
        if (igncput == FALSE)
          {
          if (set_mode == SET_LIMIT_SET)
            {
            /* process cpu time - set */

            retval = mm_gettime(&r, &value);

            if (retval != PBSE_NONE)
              {
              sprintf(log_buffer, "pcput mm_gettime failed in %s",id);

              return(error(pname, retval));
              }

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

            if (LOGLEVEL >= 2)
              {
              sprintf(log_buffer, "setting cpu time limit to %ld for job %s",
                (long int)reslim.rlim_cur,
                pjob->ji_qs.ji_jobid);

              log_record(PBSEVENT_SYSTEM, 0, id, log_buffer);

              log_buffer[0] = '\0';
              }

#ifdef HAVE_GYGWIN_SETRLIMIT
            if (setrlimit(RLIMIT_CPU, &reslim) < 0)
              {
              sprintf(log_buffer, "setrlimit for RLIMIT_CPU failed in %s, errno=%d (%s)",
                id,
                errno, strerror(errno));

              return(error("RLIMIT_CPU", PBSE_SYSTEM));
              }
#endif  /*  HAVE_GYGWIN_SETRLIMIT */

            }    /* END if (set_mode == SET_LIMIT_SET) */
          }
        }
      else if (!strcmp(pname, "file"))
        {
        /* set */

        if (set_mode == SET_LIMIT_SET)
          {
          retval = mm_getsize(&r, &value);

          if (retval != PBSE_NONE)
            {
            sprintf(log_buffer, "mm_getsize() failed for file in %s",
                    id);

            return(error(pname, retval));
            }

          if (value > ULONG_MAX)
            {
            if (LOGLEVEL >= 0)
              {
              sprintf(log_buffer, "cannot set file limit to %ld for job %s (value too large)",
                      (long int)reslim.rlim_cur,
                      pjob->ji_qs.ji_jobid);

              log_err(-1, id, log_buffer);

              log_buffer[0] = '\0';
              }

            return(error(pname, PBSE_BADATVAL));
            }

          reslim.rlim_cur = reslim.rlim_max = value;

#ifdef HAVE_GYGWIN_SETRLIMIT
          if (setrlimit(RLIMIT_FSIZE, &reslim) < 0)
            {
            sprintf(log_buffer, "cannot set file limit to %ld for job %s (setrlimit failed - check default user limits)",
                    (long int)reslim.rlim_max,
                    pjob->ji_qs.ji_jobid);

            log_err(errno, id, log_buffer);

            log_buffer[0] = '\0';

            return(error(pname, PBSE_SYSTEM));
            }
#endif  /*  HAVE_GYGWIN_SETRLIMIT */
          }
        }
      else if (!strcmp(pname, "vmem"))
        {
        if (ignvmem == FALSE)
          {
          /* check */

          retval = mm_getsize(&r, &value);

          if (retval != PBSE_NONE)
            {
            sprintf(log_buffer, "mm_getsize() failed for vmem in %s",id);

            return(error(pname, retval));
            }

          if ((vmem_limit == 0) || (value < vmem_limit))
            vmem_limit = value;
          }
        }
      else if (!strcmp(pname, "pvmem"))
        {
        if (ignvmem == FALSE)
          {
          /* set */

          if (set_mode == SET_LIMIT_SET)
            {
            retval = mm_getsize(&r, &value);

            if (retval != PBSE_NONE)
              {
              sprintf(log_buffer, "mm_getsize() failed for pvmem in %s",
                id);

              return(error(pname, retval));
              }

            if (value > ULONG_MAX)
              {
              log_buffer[0] = '\0';

              sprintf(log_buffer, "invalid value returned by mm_getsize() for pvmem in %s",
                id);

              return(error(pname, PBSE_BADATVAL));
              }

            if ((vmem_limit == 0) || (value < vmem_limit))
              vmem_limit = value;
            }
          }
        }
      else if ((!strcmp(pname,"mem") && (pjob->ji_numnodes != 1)) ||
                !strcmp(pname,"mppmem"))
        {
        /* ignore. If we ever get rid of support for the UNICOS OS then we can
           remove the ATR_DFLAG_MOM | ATR_DFLAG_ALTRUN flags from mppmem */
        }
      else if ((!strcmp(pname, "mem") && (pjob->ji_numnodes == 1)) ||
               !strcmp(pname, "pmem"))
        {
        if (ignmem == FALSE)
          {
          /* set */

          if (set_mode == SET_LIMIT_SET)
            {
            retval = mm_getsize(&r, &value);

            if (retval != PBSE_NONE)
              {
              sprintf(log_buffer, "mm_getsize() failed for mem/pmem in %s",
                id);

              return(error(pname, retval));
              }

            reslim.rlim_cur = reslim.rlim_max = value;

#ifdef HAVE_GYGWIN_SETRLIMIT
            if (setrlimit(RLIMIT_DATA, &reslim) < 0)
              {
              sprintf(log_buffer, "cannot set data limit to %ld for job %s (setrlimit failed w/errno=%d (%s) - check default user limits)",
                (long int)reslim.rlim_max,
                pjob->ji_qs.ji_jobid,
                errno,
                strerror(errno));

              return(error("RLIMIT_DATA", PBSE_SYSTEM));
              }

            if (setrlimit(RLIMIT_RSS, &reslim) < 0)
              {
              sprintf(log_buffer, "cannot set RSS limit to %ld for job %s (setrlimit failed w/errno=%d (%s) - check default user limits)",
                (long int)reslim.rlim_max,
                pjob->ji_qs.ji_jobid,
                errno,
                strerror(errno));

              return(error("RLIMIT_RSS", PBSE_SYSTEM));
              }
#endif  /*  HAVE_GYGWIN_SETRLIMIT */

            mem_limit = value;

            if (getrlimit(RLIMIT_STACK, &reslim) >= 0)
              {
              /* NOTE:  mem_limit no longer used with UMU patch in place */

              mem_limit = value + reslim.rlim_cur;
              }
            }
          }
        }    /* END else if (!strcmp(pname,"mem") && ... */
      else if (!strcmp(pname, "walltime"))
        {
        /* check */

        retval = mm_gettime(&r, &value);

        if (retval != PBSE_NONE)
          {
          sprintf(log_buffer, "mm_gettime() failed for walltime in %s\n",
                  id);

          return(error(pname, retval));
          }
        }
      else if (!strcmp(pname, "nice"))
        {
        /* set nice */

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

          if ((nice((int)r.rs_value.at_val.at_long) == -1) && (errno != 0))
            {
            sprintf(log_buffer, "nice() failed w/errno=%d (%s) in %s\n",
                    errno,
                    strerror(errno),
                    id);

            return(error(pname, PBSE_BADATVAL));
            }
          }
        }
      else if (!strcmp(pname, "size"))
        {
        /* ignore */

        /* NO-OP */
        }
      else if(!strcmp(pname, "prologue"))
        {
        }
      else if(!strcmp(pname, "epilogue"))
        {
        }
      else if ((r.rs_defin->rs_flags & ATR_DFLAG_RMOMIG) == 0)
        {
        /* don't recognize and not marked as ignore by mom */

        sprintf(log_buffer, "do not know how to process resource '%s' in %s\n",
                pname,
                id);

        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 (vmem_limit != 0)
      {
      /* Don't make (p)vmem < pmem */

      if (mem_limit > vmem_limit)
        {
        vmem_limit = mem_limit;
        }

      reslim.rlim_cur = reslim.rlim_max = vmem_limit;

#ifdef HAVE_GYGWIN_SETRLIMIT
      if ((ignvmem == 0) && (setrlimit(RLIMIT_AS, &reslim) < 0))
        {
        sprintf(log_buffer, "setrlimit() failed setting AS for vmem_limit mod in %s\n",
                id);

        return(error("RLIMIT_AS", PBSE_SYSTEM));
        }
#endif  /*  HAVE_GYGWIN_SETRLIMIT */
      }
    }

  if (LOGLEVEL >= 5)
    {
    sprintf(log_buffer, "%s(%s,%s) completed",
            id,
            (pjob != NULL) ? pjob->ji_qs.ji_jobid : "NULL",
            (set_mode == SET_LIMIT_SET) ? "set" : "alter");

    log_record(PBSEVENT_SYSTEM, 0, id, log_buffer);

    log_buffer[0] = '\0';
    }

  /* SUCCESS */

  return(PBSE_NONE);
  }  /* END mom_set_limits() */




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

  {
  char  *pname;
  resource *pres;

  assert(pjob != NULL);

  if (LOGLEVEL >= 4)
    {
    log_record(
      PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      "evaluating limits for job");
    }

  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, "pcput") == 0 ||
          strcmp(pname, "pvmem") == 0 ||
          strcmp(pname, "vmem") == 0)
        {
        return(TRUE);
        }
      }
    }

  return(FALSE);
  }  /* END mom_do_poll() */





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

int
mom_open_poll(void)

  {
  char *id = "mom_open_poll";

  if (LOGLEVEL >= 6)
    {
    log_record(PBSEVENT_SYSTEM, 0, id, "started");
    }

  pagesize = getpagesize();

  proc_array = (proc_stat_t *)calloc(TBL_INC, sizeof(proc_stat_t));

  if (proc_array == NULL)
    {
    log_err(errno, id, (char *)"calloc");

    return(PBSE_SYSTEM);
    }

  max_proc = TBL_INC;

  return(PBSE_NONE);
  }  /* END mom_open_poll() */




/*
 * Declare start of polling loop.
 *
 * This function caches information about all of processes
 * on the compute node (pbs_mom calls this function). Each process
 * in /proc/ is queried by looking at the 'stat' file. Statistics like
 * CPU usage time, memory consumption, etc. are gathered in the proc_array
 * list. This list is then used throughout the pbs_mom to get information
 * about tasks it is monitoring.
 *
 * This function is called from the main MOM loop once every "check_poll_interval"
 * seconds.
 *
 * @see get_proc_stat() - child
 * @see mom_set_use() - Aggregates data collected here
 *
 * NOTE:  populates global 'proc_array[]' variable.
 * NOTE:  reallocs proc_array[] as needed to accomodate processes.
 *
 * @see mom_open_poll() - allocs proc_array table.
 * @see mom_close_poll() - frees procs_array.
 * @see setup_program_environment() - parent - called at pbs_mom start
 * @see main_loop() - parent - called once per iteration
 * @see mom_set_use() - populate job structure with usage data for local use or to send to mother superior
 */

int
mom_get_sample(void)

  {
  char          *id = "mom_get_sample";

  struct dirent *dent;
  proc_stat_t   *pi;
  proc_stat_t   *ps;

  rewinddir(pdir);

  nproc = 0;

  pi = proc_array;

  if (LOGLEVEL >= 6)
    {
    log_record(PBSEVENT_DEBUG, PBS_EVENTCLASS_SERVER, id, "proc_array load started");
    }

  while ((dent = readdir(pdir)) != NULL)
    {
    if (!isdigit(dent->d_name[0]))
      continue;

    if ((ps = get_proc_stat(atoi(dent->d_name))) == NULL)
      {
      if (errno != ENOENT)
        {
        sprintf(log_buffer, "%s: get_proc_stat",
                dent->d_name);

        log_err(errno, id, log_buffer);
        }

      continue;
      }

    /* nproc++; -- we need to increment AFTER assigning this ps to
       the proc_array--otherwise we could skip it in for loops */

    if ((nproc + 1) >= max_proc)
      {
      proc_stat_t *hold;

      if (LOGLEVEL >= 8)
        {
        log_record(PBSEVENT_DEBUG, PBS_EVENTCLASS_SERVER, id, "alloc more proc_array");
        }

      max_proc *= 2;

      hold = (proc_stat_t *)realloc(proc_array, (max_proc + 1) * sizeof(proc_stat_t));

      if (proc_array == NULL)
        {
        log_err(errno, id, (char *)"unable to realloc space for proc_array sample");

        return(PBSE_SYSTEM);
        }

      proc_array = hold;

      memset(&proc_array[(nproc+1)], '\0', sizeof(proc_stat_t) * (max_proc >> 1));
      }  /* END if ((nproc+1) == max_proc) */

    pi = &proc_array[nproc++];

    memcpy(pi, ps, sizeof(proc_stat_t));
    }  /* END while ((dent = readdir(pdir)) != NULL) */

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "proc_array loaded - nproc=%d",
            nproc);

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

  return(PBSE_NONE);
  }  /* END mom_get_sample() */





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

int mom_over_limit(

  job *pjob)  /* I */

  {
  char  *pname;
  int  retval;
  unsigned long value;
  unsigned long num;
  unsigned long long numll;

  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;

  if (resources != NULL)
    {
    for (size_t i = 0; i < resources->size(); i++)
      {
      resources &r = resource->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") == 0))
        {
        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 ((igncput == FALSE) && (strcmp(pname, "pcput") == 0))
        {
        retval = mm_gettime(&r, &value);

        if (retval != PBSE_NONE)
          continue;

        if (overcpu_proc(pjob, value))
          {
          sprintf(log_buffer, "pcput exceeded limit %lu",
                  value);

          return(TRUE);
          }
        }
      else if (strcmp(pname, "vmem") == 0)
        {
        retval = mm_getsize(&r, &value);

        if (retval != PBSE_NONE)
          continue;

        if ((ignvmem == 0) && ((numll = mem_sum(pjob)) > value))
          {
          sprintf(log_buffer, "vmem %llu exceeded limit %lu",
                  numll,
                  value);

          return(TRUE);
          }
        }
      else if (strcmp(pname, "pvmem") == 0)
        {
        unsigned long long valuell;

        retval = mm_getsize(&r, &value);

        if (retval != PBSE_NONE)
          continue;

        valuell = (unsigned long long)value;

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

          return(TRUE);
          }
        }
      else if (ignwalltime == 0 && strcmp(pname, "walltime") == 0)
        {

        /* no need to check walltime on sisters, MS will get it */
        if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) == 0)
          continue;

        retval = mm_gettime(&r, &value);

        if (retval != PBSE_NONE)
          continue;

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

        if (num > value)
          {
          sprintf(log_buffer, "walltime %ld exceeded limit %ld",
                  num,
                  value);

          return(TRUE);
          }
        }
      }  /* END for each resource */
    }

  return(FALSE);
  }  /* END mom_over_limit() */






/*
 * Update the job attribute for resources used.
 *
 * The first time this function is called for a job,
 * it sets up resource entries for
 * each resource that can be reported for this machine.
 *
 * Subsequent calls update the resource usage information based on
 * stats gathered by the mom_get_sample() function. This function
 * is often called by "im_request()" as a result of POLL_JOB query
 * from the mother superior.
 *
 * @see im_request() - parent - respond to poll_job request from mother superior
 * @see examine_all_running_jobs() - parent - update local use on mother superior
 * @see TMomFinalizeJob1() - parent - update serial job immediately at job start
 *
 * @return An error code if something goes wrong.
 */

int mom_set_use(

  job *pjob)  /* I (modified) */

  {
  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)
    {
    /* initialize usage structures */

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

    assert(rd != NULL);

    pres = add_resource_entry(at, rd);

    pres->rs_value.at_flags |= ATR_VFLAG_SET;
    pres->rs_value.at_type = ATR_TYPE_SIZE;
    pres->rs_value.at_val.at_size.atsv_shift = 10; /* KB */
    pres->rs_value.at_val.at_size.atsv_units = ATR_SV_BYTESZ;
    }  /* END if ((at->at_flags & ATR_VFLAG_SET) == 0) */

  /* get cputime */

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

  /* get swap */

  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) >> pres->rs_value.at_val.at_size.atsv_shift; /* as KB */

  *lp = MAX(*lp, lnum);

  /* get walltime */

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

  assert(rd != NULL);

  pres = find_resc_entry(at, rd);

  assert(pres != NULL);

  /* NOTE: starting jobs can come through here before stime is recorded */
  if (pjob->ji_qs.ji_stime == 0)
    pres->rs_value.at_val.at_long = 0;
  else
    pres->rs_value.at_val.at_long =
      (long)((double)(time_now - pjob->ji_qs.ji_stime) * wallfactor);

  /* get memory */

  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) >> pres->rs_value.at_val.at_size.atsv_shift; /* as KB */

  *lp = MAX(*lp, lnum);

  return(PBSE_NONE);
  }  /* END mom_set_use() */





/**
 * Kill a task session.
 * Call with the task pointer and a signal number.
 *
 * @return number of tasks signalled (0 = failure)
 *
 * @see kill_job() - parent
 *
 * NOTE:  should support killpg() or killpidtree() - (NYI)
 *        may be required for suspend/resume
 */

int kill_task(

  task *ptask,  /* I */
  int   sig,    /* I */
  int   pg)     /* I (1=signal process group, 0=signal master process only) */

  {
  char          *id = "kill_task";

  int            ct = 0;  /* num of processes killed */
  int            NumProcessesFound = 0; /* number of processes found with session ID */

  struct dirent *dent;
  proc_stat_t   *ps;
  int            sesid;
  pid_t          mompid;

  sesid = ptask->ti_qs.ti_sid;
  mompid = getpid();

  if (LOGLEVEL >= 5)
    {
    sprintf(log_buffer, "%s: sending signal %d to task %d, session %d",
      id,
      sig,
      ptask->ti_qs.ti_task,
      sesid);

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

  if (sesid <= 1)
    {
    if (LOGLEVEL >= 3)
      {
      sprintf(log_buffer, "cannot send signal %d to task (no session id)",
        sig);

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

    /* FAILURE */

    return(0);
    }

  /* pdir is global */

  /* NOTE:  do not use cached proc-buffer since we need up-to-date info */

  /* pdir is global */

  rewinddir(pdir);

  while ((dent = readdir(pdir)) != NULL)
    {
    if (!isdigit(dent->d_name[0]))
      continue;

    if ((ps = get_proc_stat(atoi(dent->d_name))) == NULL)
      {
      if (errno != ENOENT)
        {
        sprintf(log_buffer, "%s: get_proc_stat",
                dent->d_name);

        log_err(errno,id,log_buffer);
        }

      continue;
      }

    if ((sesid == ps->session) ||
        (ProcIsChild(procfs,dent->d_name,ptask->ti_job->ji_qs.ji_jobid) == TRUE))

      {
      NumProcessesFound++;

      if ((ps->state == 'Z') || (ps->pid == 0))
        {
        /*
         * Killing a zombie is sure death! Its pid is zero,
         * which to kill(2) means 'every process in the process
         * group of the current process'.
         */

        sprintf(log_buffer, "%s: not killing process (pid=%d/state=%c) with sig %d",
                id,
                ps->pid,
                ps->state,
                sig);

        log_record(
          PBSEVENT_JOB,
          PBS_EVENTCLASS_JOB,
          ptask->ti_job->ji_qs.ji_jobid,
          log_buffer);
        }  /* END if ((ps->state == 'Z') || (ps->pid == 0)) */
      else
        {
        int i = 0;

        if (ps->pid == mompid)
          {
          /*
          * there is a race condition with newly started jobs that
           * can be killed before they've established their own
           * session id.  This means the child tasks still have MOM's
           * session id.  We check this to make sure MOM doesn't kill
           * herself.
           */

          if (LOGLEVEL >= 3)
            {
            sprintf(log_buffer, "%s: not killing process %d. Avoid sending signal because child task still has MOM's session id", id, ps->pid);

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

          if (LOGLEVEL >= 3)
            {
            sprintf(log_buffer, "%s: not killing process %d. Avoid sending signal because child task still has MOM's session id", id, ps->pid);

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

          continue;
          }  /* END if (ps->pid == mompid) */

        if (sig == SIGKILL)
          {
          struct timespec req;

          req.tv_sec = 0;
          req.tv_nsec = 250000000;  /* .25 seconds */

          /* give the process some time to quit gracefully first (up to .25*20=5 seconds) */

          sprintf(log_buffer, "%s: killing pid %d task %d gracefully with sig %d",
                  id,
                  ps->pid,
                  ptask->ti_qs.ti_task,
                  SIGTERM);

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

          if (pg == 0)
            kill(ps->pid, SIGTERM);
          else
            killpg(ps->pid, SIGTERM);

          for (i = 0;i < 20;i++)
            {
            /* check if process is gone */

            if (kill(ps->pid, 0) == -1)
              break;

            nanosleep(&req, NULL);
            }  /* END for (i = 0) */
          }    /* END if (sig == SIGKILL) */
        else
          {
          i = 20;
          }

        if (i >= 20)
          {
          /* NOTE: handle race-condition where process goes zombie as a result of previous SIGTERM */

          /* update proc info from /proc/<PID>/stat */

          if ((ps = get_proc_stat(ps->pid)) != NULL)
            {
            if (ps->state == 'Z')
              {
              /*
               * Killing a zombie is sure death! Its pid is zero,
               * which to kill(2) means 'every process in the process
               * group of the current process'.
               */

              sprintf(log_buffer, "%s: not killing process (pid=%d/state=%c) with sig %d",
                      id,
                      ps->pid,
                      ps->state,
                      sig);

              log_record(
                PBSEVENT_JOB,
                PBS_EVENTCLASS_JOB,
                ptask->ti_job->ji_qs.ji_jobid,
                log_buffer);
              }  /* END if ((ps->state == 'Z') || (ps->pid == 0)) */
            else
              {
              /* kill process hard */

              /* why is this not killing with SIGKILL? */

              sprintf(log_buffer, "%s: killing pid %d task %d with sig %d",
                      id,
                      ps->pid,
                      ptask->ti_qs.ti_task,
                      sig);

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

              if (pg == 0)
                kill(ps->pid, sig);
              else
                killpg(ps->pid, sig);
              }
            }    /* END if ((ps = get_proc_stat(ps->pid)) != NULL) */
          }      /* END if (i >= 20) */

        ++ct;
        }  /* END else ((ps->state == 'Z') || (ps->pid == 0)) */
      }    /* END if (sesid == ps->session) */
    }      /* END while ((dent = readdir(pdir)) != NULL) */

  /* NOTE:  to fix bad state situations resulting from a hard crash, the logic
            below should be triggered any time no processes are found (NYI) */

  if (IS_ADOPTED_TASK(ptask->ti_qs.ti_task) && (NumProcessesFound == 0))
    {
    /* no process was found, but for an adopted task this is OK (we don't find
     * out about the adopted task's termination via waitpid()--so we can safely
     * say that we have "killed" the task, even though the task was killed/died
     * some other way */

    ct++;

    /* do code to mark task as finished (borrowed from Linux scan_for_terminating())... */

    ptask->ti_qs.ti_exitstat = 0;  /* assume successful completion */
    ptask->ti_qs.ti_status   = TI_STATE_EXITED;

    task_save(ptask);

    sprintf(log_buffer, "%s: job %s adopted task %d was marked as terminated because task's PID was no longer found, sid=%d",
            id,
            ptask->ti_job->ji_qs.ji_jobid,
            ptask->ti_qs.ti_task,
            ptask->ti_qs.ti_sid);

    LOG_EVENT(
      PBSEVENT_DEBUG,
      PBS_EVENTCLASS_JOB,
      ptask->ti_job->ji_qs.ji_jobid,
      log_buffer);
    }

  if ((NumProcessesFound == 0) && (ct <= 0))
    {
    /* we can't find any processes belonging to given session, so we can safely say
     * that we "killed" the task and have TORQUE clean it up */

    ct++;

    /* do code to mark task as finished (borrowed from Linux scan_for_terminating())... */

    ptask->ti_qs.ti_exitstat = 0;  /* assume successful completion */
    ptask->ti_qs.ti_status   = TI_STATE_EXITED;

    task_save(ptask);

    if (LOGLEVEL >= 5)
      {
      sprintf(log_buffer, "%s: could not send signal %d to task %d (session %d)--no process was found with this session ID (marking task as killed)!",
              id,
              sig,
              ptask->ti_qs.ti_task,
              sesid);

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

  /* SUCCESS */

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





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

int
mom_close_poll(void)

  {
  char *id = "mom_close_poll";

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

  if (pdir != NULL)
    {
    if (closedir(pdir) != 0)
      {
      log_err(errno, id, (char *)"closedir");

      return(PBSE_SYSTEM);
      }

    pdir = NULL;
    }

  if (proc_array != NULL)
    {
    free(proc_array);
    }

  return(PBSE_NONE);
  }  /* END mom_close_poll() */




/*
 * mom_does_checkpoint
 *
 * @returns CST values as described in resmon.h.
 */

int
mom_does_checkpoint(void)
  {
  return(CST_BLCR); /* Use the BLCR checkpointing system. */
  }

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

int mach_checkpoint(

  task *ptask,  /* I */
  char *file,  /* I */
  int  abort)  /* I */

  {
  return(-1);
  }  /* END mach_checkpoint() */





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

long mach_restart(

  task *ptask,
  char *file)

  {
  return(-1);
  }





#define dsecs(val) ( (double)(val) )

char *cput_job(

  pid_t jobid)

  {
  char         *id = "cput_job";
  int           found = 0;
  int           i;

  double        cputime, addtime;
  proc_stat_t  *ps;

  cputime = 0.0;

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "proc_array loop start - jobid = %d",
            jobid);

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

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

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

    found = 1;

    /* add utime and stime (AKE) */

    addtime =
      dsecs(ps->utime) +
      dsecs(ps->stime) +
      dsecs(ps->cutime) +
      dsecs(ps->cstime);

    cputime += addtime;

    DBPRT(("%s: total %.2f pid %d %.2f\n",
           id,
           cputime,
           ps->pid,
           addtime))
    }  /* END for (i) */

  if (!found)
    {
    rm_errno = RM_ERR_EXIST;

    return(NULL);
    }

  sprintf(ret_string, "%.2f",

          cputime * cputfactor);

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




char *cput_proc(

  pid_t pid)

  {
  char  *id = "cput_proc";
  double cputime;
  proc_stat_t *ps;

  cputime = 0.0;

  if ((ps = get_proc_stat(pid)) == NULL)
    {
    if (errno != ENOENT)
      {
      sprintf(log_buffer, "%d: get_proc_stat",
              pid);

      log_err(errno, id, log_buffer);
      }

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  cputime = dsecs(ps->utime) + dsecs(ps->stime);

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

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




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

  if (strcmp(attrib->a_qualifier, "proc") == 0)
    {
    return(cput_proc((pid_t)value));
    }

  rm_errno = RM_ERR_BADPARAM;

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




char *mem_job(

  pid_t sid)  /* I */

  {
  static char         id[] = "mem_job";
  unsigned long long  memsize;
  int                 i;

  proc_stat_t        *ps;

  /* max memsize ??? */

  memsize = 0;

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "proc_array loop start - sid = %d",
            sid);

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

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

    if (sid != ps->session)
      continue;

    memsize += ps->vsize;
    }  /* END for (i) */

  if (memsize == 0)
    {
    rm_errno = RM_ERR_EXIST;

    return(NULL);
    }

  sprintf(ret_string, "%llukb",

          memsize >> 10); /* KB */

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




char *mem_proc(

  pid_t pid)

  {
  char   *id = "mem_proc";
  proc_stat_t  *ps;

  if ((ps = get_proc_stat(pid)) == NULL)
    {
    if (errno != ENOENT)
      {
      sprintf(log_buffer, "%d: get_proc_stat",
              pid);

      log_err(errno, id, log_buffer);
      }

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  sprintf(ret_string, "%llukb",

          (unsigned long long)ps->vsize >> 10); /* KB */

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





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

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




static char *resi_job(

  pid_t jobid)

  {
  char         *id = "resi_job";
  ulong         resisize;
  int           found = 0;
  proc_stat_t  *ps;
  int           i;

  resisize = 0;

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "proc_array loop start - jobid = %d",
            jobid);

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

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

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

    found = 1;

    resisize += ps->rss;
    }  /* END for (i) */

  if (found)
    {
    /* in KB */

    sprintf(ret_string, "%lukb",
            (resisize * (ulong)pagesize) >> 10);

    return(ret_string);
    }

  rm_errno = RM_ERR_EXIST;

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





static char *resi_proc(

  pid_t pid)

  {
  char        *id = "resi_proc";
  proc_stat_t *ps;

  if ((ps = get_proc_stat(pid)) == NULL)
    {
    if (errno != ENOENT)
      {
      sprintf(log_buffer, "%d: get_proc_stat(PIOCPSINFO)",
              pid);

      log_err(errno, id, log_buffer);
      }

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  /* in KB */

  sprintf(ret_string, "%lukb",
          ((ulong)ps->rss * (ulong)pagesize) >> 10);

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




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

  if (strcmp(attrib->a_qualifier, "proc") == 0)
    {
    return(resi_proc((pid_t)value));
    }

  rm_errno = RM_ERR_BADPARAM;

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




char *sessions(

  struct rm_attribute *attrib) /* I */

  {
  char          *id = "sessions";
  int            i;
  int            j;
  proc_stat_t   *ps;
  char          *fmt;
  int            njids = 0;
  pid_t         *jids, *hold;
  static int     maxjid = 200;
  register pid_t jobid;

  if (attrib != NULL)
    {
    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, "no memory");

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  /* Search for members of session */

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

    if (ps->uid == 0)
      continue;

    if ((jobid = ps->session) == 0)
      continue;

    if (LOGLEVEL >= 6)
      {
      sprintf(log_buffer, "%s[%d]: pid %d sid %d",
              id,
              njids,
              ps->pid,
              jobid);

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

    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, (char *)"realloc");

          rm_errno = RM_ERR_SYSTEM;

          free(jids);

          return(NULL);
          }

        jids = hold;
        }

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

  if (njids == 0)
    {
    free(jids);

    return(NULL);
    }

  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);
    }  /* END for (j) */

  free(jids);

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





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++;
    }  /* END for (ch) */

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

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





char *pids(

  struct rm_attribute *attrib)  /* I */

  {
  char         *id = "pids";
  pid_t         jobid;
  proc_stat_t  *ps;
  char         *fmt;
  int           i;
  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);
    }

  /* Search for members of session */

  fmt = ret_string;

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

    if (LOGLEVEL >= 6)
      {
      DBPRT(("%s[%d]: pid: %d  sid: %d\n",
             id,
             num_pids,
             ps->pid,
             ps->session))
      }

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

    sprintf(fmt, "%d ",
            ps->pid);

    fmt += strlen(fmt);

    num_pids++;
    }  /* END for (i) */

  if (num_pids == 0)
    {
    rm_errno = RM_ERR_EXIST;

    return(NULL);
    }

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





char *nusers(

  struct rm_attribute *attrib)

  {
  char         *id = "nusers";
  int  j;
  int           i;
  proc_stat_t  *ps;
  int  nuids = 0;
  uid_t  *uids, *hold;
  static int maxuid = 200;
  register uid_t uid;

  if (attrib != NULL)
    {
    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, (char *)"no memory");

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

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

    if ((uid = ps->uid) == 0)
      continue;

    if (LOGLEVEL >= 6)
      {
      sprintf(log_buffer, "%s[%d]: pid %d uid %d",
              id,
              nuids,
              ps->pid,
              uid);

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

    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, (char *)"realloc");

          rm_errno = RM_ERR_SYSTEM;

          free(uids);

          return(NULL);
          }

        uids = hold;
        }

      uids[nuids++] = uid; /* add uid to list */
      }
    }    /* END for (i) */

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

  free(uids);

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




static char *totmem(

  struct rm_attribute *attrib)

  {
  char *id = "totmem";
  proc_mem_t *mm;

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

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }

  if ((mm = get_proc_mem()) == NULL)
    {
    log_err(errno, id, (char *)"get_proc_mem");

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "%s: total mem=%llu",
            id,
            mm->mem_total + mm->swap_total);

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

  sprintf(ret_string, "%lukb",

          (ulong)((mm->mem_total >> 10) + (mm->swap_total >> 10))); /* KB */

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




/*
 * Has been used Win API.
 * Cygwin's /proc/meminfo also takes information from here
 * but does it incorrectly till now
*/
static char *availmem(

  struct rm_attribute *attrib)

  {
  char *id = "availmem";
/*  proc_mem_t *mm; */
  MEMORYSTATUS memstat;

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

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }
/*
  if ((mm = get_proc_mem()) == NULL)
    {
    log_err(errno, id, "get_proc_mem");

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }  */

  GlobalMemoryStatus(&memstat);

  if (LOGLEVEL >= 6)
    {
    sprintf(log_buffer, "%s: free mem=%lu",
            id,
            memstat.dwAvailPageFile);

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

  sprintf(ret_string, "%lukb",

          (ulong)(memstat.dwAvailPageFile >> 10)); /* KB */

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




static char *ncpus(

  struct rm_attribute *attrib)

  {
  char  *id = "ncpus";
  char           label[128];
  FILE  *fp;
  int   procs;

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

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }

  if ((fp = fopen("/proc/cpuinfo", "r")) == NULL)
    {
    return(NULL);
    }

  procs = 0;

  while (!feof(fp))
    {
    if (fscanf(fp, "%s %*[^\n]%*c", label) == 0)
      {
      getc(fp);  /* must do something to get to eof */
      }
    else if (strcmp("processor", label) == 0)
      procs++;
    }

  sprintf(ret_string, "%d",

          procs);

  system_ncpus = procs;

  fclose(fp);

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





#define PMEMBUF_SIZE  2048


static const char path_meminfo[] = "/proc/meminfo";

static char *physmem(

  struct rm_attribute *attrib)

  {
  char *id = "physmem";
  char tmpBuf[PMEMBUF_SIZE];

  char *BPtr;
  int   BSpace;

  unsigned long long mem;
  FILE *fp;

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

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }

  if (!(fp = fopen(path_meminfo, "r")))
    {
    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  BPtr = tmpBuf;

  BSpace = sizeof(tmpBuf);

  BPtr[0] = '\0';

  while (!feof(fp))
    {
    if (fgets(BPtr, BSpace, fp) == NULL)
      {
      break;
      }

    BSpace -= strlen(BPtr);

    BPtr   += strlen(BPtr);
    }

  fclose(fp);

  /* FORMAT:  '...\nMemTotal:   XXX kB\n' */

  if ((BPtr = strstr(tmpBuf, "MemTotal:")) != NULL)
    {
    BPtr += strlen("MemTotal:");

    if (sscanf(BPtr, "%llu",
               &mem) != 1)
      {
      rm_errno = RM_ERR_SYSTEM;

      return(NULL);
      }

    /* value specified in kb */
    }
  else
    {
    /* attempt to load first numeric value */

    if (sscanf(BPtr, "%*s %llu",
               &mem) != 1)
      {
      rm_errno = RM_ERR_SYSTEM;

      return(NULL);
      }

    /* value specified in bytes */

    mem >>= 10;
    }

  sprintf(ret_string, "%llukb",

          mem);

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

  sprintf(ret_string, "%lukb:%lukb",

          (ulong)(((double)fsbuf.f_bsize * (double)fsbuf.f_bfree) / 1024.0),
          (ulong)(((double)fsbuf.f_bsize * (double)fsbuf.f_blocks) / 1024.0)); /* KB */

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




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, "%lukb",

          (unsigned long)sbuf.st_size >> 10); /* KB */

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




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

  if (strcmp(attrib->a_qualifier, "fs") == 0)
    {
    return(size_fs(param));
    }

  rm_errno = RM_ERR_BADPARAM;

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





/*
 * For a recovering (-p) mom, look through existing tasks in existing
 * jobs for things that have exited that are not owned by us through a
 * parent-child relationship.  Otherwise we cannot report back to tm
 * clients when tasks have exited.
 */

void scan_non_child_tasks(void)

  {
  char *id = "scan_non_child_tasks";

  job *job;
  extern tlist_head svr_alljobs;

  DIR *pdir;  /* use local pdir to prevent race conditions associated w/global pdir (VPAC) */

  pdir = opendir("/proc");

  for (job = GET_NEXT(svr_alljobs);job != NULL;job = GET_NEXT(job->ji_alljobs))
    {
    task *task;

    for (task = GET_NEXT(job->ji_tasks);task != NULL;task = GET_NEXT(task->ti_jobtask))
      {

      struct dirent *dent;
      int found;

      /* only check on tasks that we think should still be around */

      if (task->ti_qs.ti_status != TI_STATE_RUNNING)
        continue;

      /* look for processes with this session id */

      found = 0;

      /* NOTE:  on linux systems, the session master should have pid == sessionid */

      if (kill(task->ti_qs.ti_sid, 0) != -1)
        {
        found = 1;
        }
      else
        {
        /* session master cannot be found, look for other pid in session */

        rewinddir(pdir);

        while ((dent = readdir(pdir)) != NULL)
          {
          proc_stat_t *ps;

          if (!isdigit(dent->d_name[0]))
            continue;

          ps = get_proc_stat(atoi(dent->d_name));

          if (ps == NULL)
            continue;

          if (ps->session == task->ti_qs.ti_sid)
            {
            found = 1;

            break;
            }
          }    /* END while ((dent) != NULL) */
        }

      if (!found)
        {
        char buf[1024];

        extern int exiting_tasks;

        sprintf(buf, "found exited session %d for task %d in job %s",
                task->ti_qs.ti_sid,
                task->ti_qs.ti_task,
                job->ji_qs.ji_jobid);

        log_event(
          PBSEVENT_JOB,
          PBS_EVENTCLASS_JOB,
          id,
          buf);

        task->ti_qs.ti_exitstat = 0;  /* actually unknown */
        task->ti_qs.ti_status = TI_STATE_EXITED;

        task_save(task);

        exiting_tasks = 1;
        }
      }
    }    /* END for (job = GET_NEXT(svr_alljobs)) */

  closedir(pdir);

  return;
  }  /* END scan_non_child_tasks() */




/*
 * Cygwin has a virtual /dev/tty (and other /dev/...) only.
 * This path has a specified value of time of last access.
 * As variant, it is proposed to return time from a system reboot moment.
*/
char *idletime(

  struct rm_attribute *attrib)

  {
  char         *id = "idletime";

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

    rm_errno = RM_ERR_BADPARAM;

    return(NULL);
    }

  sprintf(ret_string, "%ld", GetTickCount()/1000);

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




static char *walltime(

  struct rm_attribute *attrib)

  {
  char         *id = "walltime";
  int  value, job, found = 0;
  time_t now, start;
  proc_stat_t  *ps;

  int           i;

  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, (char *)"time");

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  start = now;

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

    if (job != 0)
      {
      if (value != ps->session)
        continue;
      }
    else
      {
      if (value != ps->pid)
        continue;
      }

    found = 1;

    start = MIN((unsigned)start, ps->start_time);
    }  /* END for (i) */

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

    return(ret_string);
    }

  rm_errno = RM_ERR_EXIST;

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





int get_la(

  double *rv)  /* O */

  {
  FILE *fp;
  char *id = "get_la";
  float load;

  if ((fp = fopen("/proc/loadavg", "r")) == NULL)
    {
    rm_errno = RM_ERR_SYSTEM;

    return(rm_errno);
    }

  if (fscanf(fp, "%f",
             &load) != 1)
    {
    log_err(errno, id, (char *)"fscanf of load in /proc/loadavg");

    fclose(fp);

    rm_errno = RM_ERR_SYSTEM;

    return(rm_errno);
    }

  *rv = (double)load;

  fclose(fp);

  return(0);
  }  /* END get_la() */




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

  return(0);
  }


/* May be it is required in the future */
static char *quota(

  struct rm_attribute *attrib)

  {
  char *id = "quota";
  int  type;
  dev_t  dirdev;
  uid_t  uid;

  struct stat sb;

  struct mntent *me;

  struct dqblk qi;
  FILE  *m;

  struct passwd *pw;

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

  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 = (dev_t)sb.st_dev;

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

  if ((m = setmntent(MOUNTED, "r")) == NULL)
    {
    log_err(errno, id, (char *)"setmntent");
    rm_errno = RM_ERR_SYSTEM;
    return NULL;
    }

  while ((me = getmntent(m)) != NULL)
    {
    if (strcmp(me->mnt_type, MNTTYPE_IGNORE) == 0)
      continue;

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

    if (LOGLEVEL >= 6)
      DBPRT(("%s\t%s\t%d\n", me->mnt_fsname, me->mnt_dir, (int)dirdev))

      if (!memcmp(&sb.st_dev, &dirdev, sizeof(dev_t)))
        break;
    }

  endmntent(m);

  if (me == 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 defined(MNTOPT_NOQUOTA)
  if (hasmntopt(me, MNTOPT_NOQUOTA) != NULL)
    {
    sprintf(log_buffer,
            "no quotas on filesystem %s", me->mnt_dir);
    log_err(-1, id, log_buffer);
    rm_errno = RM_ERR_EXIST;
    return NULL;
    }

#endif /* MNTOPT_NOQUOTA */

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

  /* Cygwin doesn't yet support quota
   * It is stub */
  if (quotactl(
        QCMD(Q_GETQUOTA, USRQUOTA),
        me->mnt_fsname,
        uid,
        (caddr_t)&qi) == -1)

    {
    log_err(errno, id, (char *)"quotactl");

    rm_errno = RM_ERR_SYSTEM;

    return(NULL);
    }

  /* sizes in KB */

  switch (type)
    {

    case harddata:

      sprintf(ret_string, "%lukb",
              (u_long)qi.dqb_bhardlimit >> 10);

      break;

    case softdata:

      sprintf(ret_string, "%lukb",
              (u_long)qi.dqb_bsoftlimit >> 10);

      break;

    case currdata:

#if defined(TENABLEQUOTA)
      sprintf(ret_string, "%lukb",
              (u_long)qi.dqb_curspace >> 10);
#endif /* TENABLEQUOTA */

      break;

    case hardfile:

      sprintf(ret_string, "%lu",
              (u_long)qi.dqb_ihardlimit);

      break;

    case softfile:

      sprintf(ret_string, "%lu",
              (u_long)qi.dqb_isoftlimit);

      break;

    case currfile:

      sprintf(ret_string, "%lu",
              (u_long)qi.dqb_curinodes);

      break;

    case timedata:

      sprintf(ret_string, "%lu",
              gracetime((u_long)qi.dqb_btime));

      break;

    case timefile:

      sprintf(ret_string, "%lu",
              gracetime((u_long)qi.dqb_itime));

      break;
    }  /* END switch() */

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




/* needs gcc -liphlpapi */
static char *netload(

  struct rm_attribute *attrib)
  {
  MIB_IPSTATS  Stats;

  PMIB_IPSTATS  pStats=&Stats;

  if (NO_ERROR == GetIpStatistics(pStats))
  {
	sprintf(ret_string, "%lu", pStats->dwInReceives + pStats->dwOutRequests);

	return(ret_string);
  }
  rm_errno = RM_ERR_SYSTEM;

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




mbool_t ProcIsChild(

  char *Dir,    /* I */
  char *PID,    /* I */
  char *JobID)  /* I */

  {
  return(FALSE);
  }  /* END ProcIsChild() */

/* END mom_mach.c */


