/*
 * Copyright (C) 1994-2018 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * PBS Pro is free software. You can redistribute it and/or modify it under the
 * terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * PBS Pro is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Commercial License Information:
 *
 * For a copy of the commercial license terms and conditions,
 * go to: (http://www.pbspro.com/UserArea/agreement.html)
 * or contact the Altair Legal Department.
 *
 * Altair’s dual-license business model allows companies, individuals, and
 * organizations to create proprietary derivative works of PBS Pro and
 * distribute them - whether embedded or bundled with other software -
 * under a commercial license agreement.
 *
 * Use of Altair’s trademarks, including but not limited to "PBS™",
 * "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
 * trademark licensing policies.
 *
 */

/**
 * @file
 * @brief
*	System dependent code to gather information for a Darwin (MacOS) machine.
*
* @par 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 (see mom_main.c)
*		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	<signal.h>
#include	<dirent.h>
#include	<string.h>
#include	<fcntl.h>
#include	<errno.h>
#include	<pwd.h>
#include	<fstab.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	<sys/quota.h>
#include	<mach/vm_map.h>


/*  additional header files required for darwin 8.0.0 */

#include	<paths.h>
#include	<mach/mach.h>
#include 	<mach/mach_host.h>
#include	<mach/mach_port.h>
#include	<mach/mach_traps.h>
#include	<mach/shared_memory_server.h>
#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	"job.h"
#include	"mom_mach.h"
#include	"mom_func.h"
#include	"resmon.h"
#include	"../rm_dep.h"


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

/*
** external functions and data
*/
extern	int	nice_val;
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	num_acpus;
extern	int	num_pcpus;
extern	int	exiting_tasks;

/*
** local functions
*/
static char	*resi		(struct rm_attribute *attrib);
       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);
extern char	*loadave	(struct rm_attribute *attrib);
extern char	*nullproc	(struct rm_attribute *attrib);

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


struct	config	dependent_config[] = {
	{ "resi", { resi } },
	{ "physmem", { physmem } },
	{ "ncpus",   { ncpus } },
	{ "loadave", { loadave } },
	{ "walltime", { walltime } },
	{ "quota", { quota } },
	{ NULL, { nullproc } }
};

time_t			wait_time = 10;
int			mom_does_chkpnt = MOM_CHECKPOINT;
struct	kinfo_proc	*proc_tbl = NULL;
pid_t			*sess_tbl = NULL;
int			nproc = 0;
extern	char		*ret_string;
extern	char		extra_parm[];
extern	char		no_parm[];
char			nokernel[] = "kernel not available";
char			noproc[] = "process %d does not exist";
static  int		nncpus = 0;
static  int     myproc_max = 0;		/* entries in Proc_lnks  */
pbs_plinks     *Proc_lnks = NULL;       /* process links table head */
static time_t   sampletime_ceil;
static time_t   sampletime_floor;
#define TBL_INC 20


int qs_cmp(const void *a, const void *b)
{
	long val;
	val = (long)((struct kinfo_proc *)a)->kp_eproc.e_paddr - (long)((struct kinfo_proc *)b)->kp_eproc.e_paddr;
	if (val > 0)
		return(1);
	if (val < 0)
		return(-1);
	return(0);
}


typedef struct kinfo_proc kinfo_proc;

/**
 * @brief
 *      initialize the platform-dependent topology information
 *
 * @return      Void
 *
 */
void
dep_initialize()
{
	int	 mib[2];
	size_t	 len;

	mib[0] = CTL_HW;	/* get number of processors */
	mib[1] = HW_NCPU;
	len    = sizeof(nncpus);
	(void)sysctl(mib, 2, &nncpus, &len, NULL,  0);
	num_acpus = nncpus;
	num_pcpus = nncpus;

	return;
}

void
dep_cleanup()
{

}

/**
 * @brief
 *      Don't need any periodic procsessing except in some special cases.
 *
 * @return      Void
 *
 */
void
end_proc()
{
	return;
}

extern	time_t			time_now;

/*
 * Time decoding macro.  Accepts a timeval structure.  Returns unsigned long
 * time in seconds.
 */

#define tv(val) ((val).tv_sec+((unsigned long)(val).tv_usec+500000)/1000000)
#if __FreeBSD_version >= 300000
/* a u_int64_t in micro-seconds */
#define tvk(val) ((unsigned long)(val)/1000000)
#define p_rtime  p_runtime
#else
#define tvk tv
#endif	/* __FreeBSD_version */

/**
 * @brief
 *       Scan a list of tasks and return true if one of them matches sesid.
 *
 * @param[in] pjob - job pointer
 * @param[in] sesid - session id
 *
 * @return      Bool
 * @retval      TRUE    Success
 * @retval      FALSE   Error
 *
 */
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)
			return TRUE;
	}
	return FALSE;
}

#define	MINPROC	10
#define	MAXPROC	10000


u_long
pcputime(char *id, int i)
{
	u_long			cputime;
	int ret;
	struct kinfo_proc	*pp = &proc_tbl[i];
	task_basic_info_data_t	mach_task_stats;
	task_thread_times_info_data_t	mach_time_info;
	time_value_t	total_time;

	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)
		{
			return cputime;
		}

		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", __func__,
			sess_tbl[i], pp->kp_proc.p_pid, cputime))

	return cputime;
}

/**
 * @brief
 *      Internal session cpu time decoding routine.
 *
 * @param[in] job - a job pointer.
 *
 * @return      ulong
 * @retval      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)
{
	int			i;
	u_long			cputime = 0;
	int			nps = 0;
	int			taskprocs;
	task			*ptask;
	u_long			tcput;

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

		/* DEAD task */
		if (ptask->ti_qs.ti_sid <= 1) {
			cputime += ptask->ti_cput;
			continue;
		}

		tcput = 0;
		taskprocs = 0;
		for (i=0; i<nproc; i++) {
			/* is this process part of any task? */
			if (ptask->ti_qs.ti_sid != sess_tbl[i])
				continue;

			nps++;
			taskprocs++;
			tcput += pcputime(__func__, i);
		}

		if (tcput > ptask->ti_cput)
		  ptask->ti_cput = tcput;

		cputime += ptask->ti_cput;
		DBPRT(("%s: task %08.8X cput %lu total %lu\n", __func__,
			ptask->ti_qs.ti_task, ptask->ti_cput, cputime))

		if (taskprocs == 0) {
			sprintf(log_buffer,
					"no active process for task %8.8X",
					ptask->ti_qs.ti_task);
			log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB,
					LOG_INFO, pjob->ji_qs.ji_jobid,
					log_buffer);
			ptask->ti_qs.ti_exitstat = 0;
			ptask->ti_qs.ti_status = TI_STATE_EXITED;
			if (pjob->ji_qs.ji_un.ji_momt.ji_exitstat >= 0)
				pjob->ji_qs.ji_un.ji_momt.ji_exitstat = 0;
			task_save(ptask);
			exiting_tasks = 1;
		}
	}

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

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

/**
 * @brief
 *      Internal session memory usage function.
 *
 * @param[in] job - job pointer
 *
 * @return      ulong
 * @retval      the total number of bytes of address
 *              space consumed by all current processes within the job.
 *
 */
static unsigned long
mem_sum(job *pjob)
{
	int		i;
	unsigned long	memsize;
	struct task_basic_info t_info;
	unsigned int pid;

	memsize = 0;
	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;

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

	return (memsize);
}

/**
 * @brief
 *      Internal session workingset size function.
 *
 * @param[in] pjob - job pointer
 *
 * @return      ulong
 * @retval      new resident set size   Success
 * @retval      old resident set size   Error
 *
 */
static unsigned long
resi_sum(job *pjob)
{
	int		i;
	unsigned long	memsize;
	struct task_basic_info t_info;
	unsigned int pid;

	memsize = 0;

	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;

		memsize += t_info.resident_size;

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

  return(memsize);
}

/**
 * @brief
 *      Establish system-enforced limits for the job.
 *
 *      Run through the resource list, checking the values for all items
 *      we recognize.
 *
 * @param[in] pjob - job pointer
 * @param[in]  set_mode - setting mode
 *
 *      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.
 *
 * @return      int
 * @retval      PBSE_NONE       Success
 * @retval      PBSE_*          Error
 *
 */
int
mom_set_limits(job *pjob, int set_mode)
{
	char		*pname;
	int		retval;
	unsigned long	value;	/* place in which to build resource value */
	resource	*pres;
       	struct rlimit	reslim;
	unsigned long	mem_limit  = 0;
	unsigned long	vmem_limit  = 0;
	unsigned long	cpu_limit  = 0;

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

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

	/* mem and vmem limits come from the local node limits, not the job */
	mem_limit  = pjob->ji_hosts[pjob->ji_nodeid].hn_nrlimit.rl_mem << 10;
	vmem_limit = pjob->ji_hosts[pjob->ji_nodeid].hn_nrlimit.rl_vmem << 10;

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

		if (strcmp(pname, "cput") == 0 ||
		    strcmp(pname, "pcput") == 0) {		/* set */
			retval = local_gettime(pres, &value);
			if (retval != PBSE_NONE)
			        return (error(pname, retval));
			if ((cpu_limit == 0) || (value < cpu_limit))
				cpu_limit = value;
		} else if (strcmp(pname, "pvmem") == 0) {	/* set */
			retval = local_getsize(pres, &value);
			if (retval != PBSE_NONE)
			        return (error(pname, retval));
			if ((vmem_limit == 0) || (value < vmem_limit))
				vmem_limit = value;
		} else if (strcmp(pname, "pmem") == 0) {	/* set */
			retval = local_getsize(pres, &value);
			if (retval != PBSE_NONE)
				return (error(pname, retval));
			if ((mem_limit == 0) || (value < mem_limit))
				mem_limit = value;
		} else if (strcmp(pname, "walltime") == 0) {	/* Check */
			retval = local_gettime(pres, &value);
			if (retval != PBSE_NONE)
			        return (error(pname, retval));
		} else if (strcmp(pname, "nice") == 0) {	/* set nice */
			if (set_mode == SET_LIMIT_SET)  {
			    errno = 0;
			    if ((nice((int)pres->rs_value.at_val.at_long) == -1)
				&& (errno != 0))
				return (error(pname, PBSE_BADATVAL));
			}
		} else if (strcmp(pname, "file") == 0) {	/* set */
			retval = local_getsize(pres, &value);
			if (retval != PBSE_NONE)
				return (error(pname, retval));
			if (set_mode == SET_LIMIT_SET)  {
				reslim.rlim_cur = reslim.rlim_max = value;
				if (setrlimit(RLIMIT_FSIZE, &reslim) < 0)
					return (error(pname, PBSE_SYSTEM));
			}
		}
		pres = (resource *)GET_NEXT(pres->rs_link);
	}

	if (set_mode == SET_LIMIT_SET)  {

	    /* if either vmem or pvmem was given, set sys limit to lesser */
	    if (vmem_limit != 0) {
		reslim.rlim_cur = reslim.rlim_max = vmem_limit;
		if (setrlimit(RLIMIT_DATA, &reslim) < 0)
	        	return (error("RLIMIT_DATA", PBSE_SYSTEM));
		if (setrlimit(RLIMIT_STACK, &reslim) < 0)
	        	return (error("RLIMIT_STACK", PBSE_SYSTEM));
	    }

	    /* if either mem or pmem was given, set sys limit to lesser */
	    if (mem_limit != 0) {
		reslim.rlim_cur = reslim.rlim_max = mem_limit;
		if (setrlimit(RLIMIT_RSS, &reslim) < 0)
	       		return (error("RLIMIT_RSS", PBSE_SYSTEM));
	    }

	    /* if either cput or pcput was given, set sys limit to lesser */
	    if (cpu_limit != 0) {
		reslim.rlim_cur = reslim.rlim_max =
			(unsigned long)((double)cpu_limit / cputfactor);
		if (setrlimit(RLIMIT_CPU, &reslim) < 0)
	       		return (error("RLIMIT_CPU", PBSE_SYSTEM));
	    }
	}
	return (PBSE_NONE);
}

/**
 * @brief
 *      State whether MOM main loop has to poll this job to determine if some
 *      limits are being exceeded.
 *
 * @param[in] pjob - job pointer
 *
 * @return      int
 * @retval      TRUE    if polling is necessary
 * @retval      FALSE   otherwise.
 *
 * NOTE: Actual polling is done using the mom_over_limit machine-dependent function.
 *
 */
int
mom_do_poll(job *pjob)
{
	char		*pname;
	resource	*pres;

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

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

		if (strcmp(pname, "walltime") == 0 ||
		    strcmp(pname, "cput") == 0 ||
		    strcmp(pname, "mem")  == 0 ||
		    strcmp(pname, "vmem") == 0 ||
		    strcmp(pname, "ncpus") == 0)
			return (TRUE);
		pres = (resource *)GET_NEXT(pres->rs_link);
	}

	return (FALSE);
}

/**
 * @brief
 *      Setup for polling.
 *      Open kernel device and get namelist info.
 *
 * @return      int
 * @retval      PBSE_NONE               Success
 * @retval      PBSE_SYSTEM             Error
 *
 */
int
mom_open_poll(void)
{
	DBPRT(("%s: entered\n", id))
	return (PBSE_NONE);
}

static struct kinfo_proc *apple_getprocs(int *nprocs)
{
  int mib[4];
  int miblen;
  size_t size;
  int st;
  struct kinfo_proc *procs, *newprocs;

  size = 0;
  mib[0] = CTL_KERN;
  mib[1] = KERN_PROC;
  mib[2] = KERN_PROC_ALL;
  mib[3] = 0;
  miblen = 3;
  procs = NULL;

  st = sysctl( mib, miblen, procs, &size, NULL, 0 );
  do
  {
    size += size / 10;
    newprocs = realloc( procs, size );
    if( newprocs == 0 )
    {
      if( procs )
        free( procs );
      fprintf(stderr, "could not reallocate memory" );
      return NULL;
    }
    procs = newprocs;
    st = sysctl( mib, miblen, procs, &size, NULL, 0 );
  }
  while( st == -1 && errno == ENOMEM );
  if( st == -1 )
  {
    fprintf( stderr, "could not sysctl(KERN_PROC)" );
    return NULL;
  }
  if( size % sizeof(struct kinfo_proc) != 0 )
  {
    fprintf( stderr, "proc size mismatch (%zu total, %zu chunks)\n", size,
              sizeof(struct kinfo_proc) );
    return NULL;
  }
  *nprocs = (int) size / sizeof(struct kinfo_proc);

  return procs;
}
/**
 * @brief
 * 	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.
 *
 * @return      int
 * @retval      PBSE_INTERNAL   Dir pdir in NULL
 * @retval      PBSE_NONE       Success
 *
 */
int
mom_get_sample()
{
  int           i;

  struct        kinfo_proc      *kp;

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

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

  proc_tbl = apple_getprocs(&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.
   */
  if (proc_tbl == NULL)
    {
    sprintf(log_buffer,"process list error");
    log_err(errno,__func__,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,__func__,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);
  }


/**
 * @brief
 * 	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.
 *
 * @return int
 * @retval PBSE_NONE    for success.
 */
int
mom_set_use(job	*pjob)
{
	resource	*pres;
	resource	*pres_req;
	attribute	*at_req;
	attribute	*at;
	resource_def	*rd;
	unsigned long	*lp, lnum, oldcput;
	long             ncpus_req;


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

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

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

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

		rd = find_resc_def(svr_resc_def, "ncpus", svr_resc_size);
		assert(rd != NULL);
		pres = add_resource_entry(at, rd);
		pres->rs_value.at_flags |= ATR_VFLAG_SET;
		pres->rs_value.at_type = ATR_TYPE_LONG;
		/*
		 * get pointer to list of resources *requested* for the job
		 * so the ncpus used can be set to ncpus requested
		*/
		at_req = &pjob->ji_wattr[(int)JOB_ATR_resource];
		assert(at->at_type == ATR_TYPE_RESC);

		pres_req = find_resc_entry(at_req,rd);
		if ((pres_req != NULL) &&
				((ncpus_req=pres_req->rs_value.at_val.at_long) !=0 ))
			pres->rs_value.at_val.at_long = ncpus_req;
		else
			pres->rs_value.at_val.at_long = 0;

		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, "cpupercent", 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;
		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;
	oldcput = *lp;
	lnum = cput_sum(pjob);
	lnum = MAX(*lp, lnum);
	*lp = lnum;

	/* now calculate weight moving average cpu usage percentage */
	calc_cpupercent(pjob, oldcput, lnum, sampletime_ceil);
	pjob->ji_sampletim = sampletime_floor;

	rd = find_resc_def(svr_resc_def, "vmem", svr_resc_size);
	assert(rd != NULL);
	pres = find_resc_entry(at, rd);
	assert(pres != NULL);
	lp = (unsigned long *)&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 = (unsigned long *)&pres->rs_value.at_val.at_size.atsv_num;
	lnum = (resi_sum(pjob) + 1023) >> 10;	/* in KB */
	*lp = MAX(*lp, lnum);

	return (PBSE_NONE);
}

/**
 * @brief
 *      bld_ptree - establish links (parent, child, and sibling) for processes
 *      in a given session.
 *
 *      The PBS_PROC_* macros are defined in resmom/.../mom_mach.h
 *      to refer to the correct machine dependent table.
 *
 * @param[in] sid - session id
 *
 * @return      int
 * @retval      number of processes in session  Success
 *
 */
int
bld_ptree(pid_t sid)
{

	int     myproc_ct;	/* count of processes in a session */
	int	i, j;

	if (Proc_lnks == NULL) {
		Proc_lnks = (pbs_plinks *)malloc(TBL_INC * sizeof(pbs_plinks));
		assert(Proc_lnks != NULL);
		myproc_max = TBL_INC;
	}

	/*
	 * Build links for processes in the session in question.
	 * First, load with the processes in the session.
	 */

	myproc_ct = 0;
	for (i = 0; i < nproc; i++) {
		if (PBS_PROC_PID(i) <= 1)
			continue;
		if ((int)PBS_PROC_SID(i) == sid) {
			Proc_lnks[myproc_ct].pl_pid    = PBS_PROC_PID(i);
			Proc_lnks[myproc_ct].pl_ppid   = PBS_PROC_PPID(i);
			Proc_lnks[myproc_ct].pl_parent = -1;
			Proc_lnks[myproc_ct].pl_sib    = -1;
			Proc_lnks[myproc_ct].pl_child  = -1;
			Proc_lnks[myproc_ct].pl_done   = 0;
			if (++myproc_ct == myproc_max) {
				void * hold;

				myproc_max += TBL_INC;
				hold = realloc((void *)Proc_lnks,
						myproc_max*sizeof(pbs_plinks));
				assert(hold != NULL);
				Proc_lnks = (pbs_plinks *)hold;
			}
		}
	}

	/* Now build the tree for those processes */

	for (i = 0; i < myproc_ct; i++) {
		/*
		 * Find all the children for this process, establish links.
		 */
		for (j = 0; j < myproc_ct; j++) {
			if (j == i)
				continue;
			if (Proc_lnks[j].pl_ppid == Proc_lnks[i].pl_pid) {
				Proc_lnks[j].pl_parent = i;
			        Proc_lnks[j].pl_sib = Proc_lnks[i].pl_child;
				Proc_lnks[i].pl_child = j;
			}
		}
	}
	return (myproc_ct);	/* number of processes in session */
}

/**
 * @brief
 *      kill_ptree - traverse the process tree, killing the processes as we go
 *
 * @param[in]   idx:    current pid index
 * @param[in]   flag:   traverse order, top down (1) or bottom up (0)
 * @param[in]   sig:    the signal to send
 *
 * @return      Void
 *
 */
static void
kill_ptree(int idx, int flag, int sig)
{
	int		 child;

	if (flag && !Proc_lnks[idx].pl_done) {		/* top down */
		DBPRT(("%s: pid %d sig %d\n", __func__, Proc_lnks[idx].pl_pid, sig))
		(void)kill(Proc_lnks[idx].pl_pid, sig);
		Proc_lnks[idx].pl_done = 1;
	}
	child = Proc_lnks[idx].pl_child;
	while (child != -1) {
		kill_ptree(child, flag, sig);
		child = Proc_lnks[child].pl_sib;
	}
	if (!flag && !Proc_lnks[idx].pl_done) {		/* bottom up */
		DBPRT(("%s: pid %d sig %d\n", __func__, Proc_lnks[idx].pl_pid, sig))
		(void)kill(Proc_lnks[idx].pl_pid, sig);
		Proc_lnks[idx].pl_done = 1;
	}
}

/**
 * @brief
 *      kill task session
 *
 * @param[in] ptask - pointer to pbs_task structure
 * @param[in] sig - signal number
 * @param[in] dir - indication how to kill
 *                  0 - kill child first
 *                  1 - kill parent first
 *
 * @return      int
 * @retval      number of tasks
 *
 */
int
kill_task(pbs_task *ptask, int sig, int dir)
{
	return kill_session(ptask->ti_qs.ti_sid, sig, dir);
}

/**
 * @brief
 *      Kill a task session.
 *      Call with the task pointer and a signal number.
 *
 * @param[in] sesid - session id
 * @param[in] sig - signal number
 * @param[in] dir - indication how to kill
 *                  0 - kill child first
 *                  1 - kill parent first
 *
 * @return      int
 * @retval      number of tasks
 *
 */
int
kill_session(pid_t sesid, int sig, int dir)
{
	int		ct = 0;
	int		i;

	DBPRT(("%s: entered sid %d\n", __func__, sesid))
	if (sesid <= 1)
		return 0;

	(void)mom_get_sample();
	ct = bld_ptree(sesid);
	DBPRT(("%s: bld_ptree %d\n", __func__, ct))

	/*
	** Find index into the Proc_lnks table for the session lead.
	*/
	for (i = 0; i < ct; i++) {
		if (Proc_lnks[i].pl_pid == sesid) {
			kill_ptree(i, dir, sig);
			break;
		}
	}
	/*
	** Now do a linear pass looking for orphans.
	*/
	for (i = 0; i < ct; i++) {
		if (Proc_lnks[i].pl_done)
			continue;
		DBPRT(("%s: cleanup %d\n", __func__, Proc_lnks[i].pl_pid))
		kill(Proc_lnks[i].pl_pid, sig);
	}

	return ct;
}

/**
 * @brief
 * 	Clean up everything related to polling.
 *
 * @return      int
 * @retval      PBSE_NONE       Success
 * @retval      PBSE_SYSTEM     Error
 *
 */
int
mom_close_poll(void)
{
	DBPRT(("mom_close_poll entered\n"))

	return (PBSE_NONE);
}

/**
 * @brief
 *      Checkpoint the job.
 *
 * @param[in] ptask - pointer to task
 * @param[in] file - filename
 * @param[in] abort - value indicating abort
 *
 * If abort is true, kill it too.
 *
 * @return      int
 * @retval      -1	error
 * @retval	0	Success
 */
int
mach_checkpoint(task *ptask, char *file, int abort)
{
	static	int	ret = -1;
#if MOM_CHECKPOINT == 1
	if (abort)
		ret = kill(ptask->ti_qs.ti_sid, SIGKILL);
	else
		ret = 0;
#endif
       	return ret;
}

/**
 * @brief
 *      Restart the job from the checkpoint file.
 *
 * @param[in] ptask - pointer to task
 * @param[in] file - filename
 *
 * @return      long
 * @retval      session id      Success
 * @retval      -1              Error
 */
long
mach_restart(task *ptask, char *file)
{
	static	long	ret = -1;

	return ret;
}

/**
 * @brief
 *      Return 1 if proc table can be read, 0 otherwise.
 */
int
getprocs()
{
	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;
}

/**
 * @brief
 *      computes and returns the cpu time process with  pid jobid
 *
 * @param[in] jobid - process id for job
 *
 * @return      string
 * @retval      cputime         Success
 * @retval      NULL            Error
 *
 */
char *
cput_job(pid_t jobid)
{
	int			i;
	unsigned long		cputime;

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

	cputime = 0;
	for (i=0; i<nproc; i++) {
		if (jobid != sess_tbl[i])
			continue;

		cputime += pcputime(__func__, i);
	}

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

/**
 * @brief
 *      computes and returns the cpu time process with  pid pid.
 *
 * @param[in] pid - process id
 *
 * @return      string
 * @retval      cputime         Success
 * @retval      NULL            Error
 *
 */
char *
cput_proc(pid_t pid)
{

	uint			i, cputime;

	for (i=0; i<nproc; i++) {
		struct kinfo_proc	*pp = &proc_tbl[i];

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

		cputime = pcputime(__func__, i);
		sprintf(ret_string, "%.2f", (double)cputime * cputfactor);
		return ret_string;
	}
	rm_errno = RM_ERR_EXIST;
	return NULL;
}

/**
 * @brief
 *      wrapper function for cput_proc and cput_job.
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      cputime         Success
 * @retval      NULL            ERRor
 *
 */
char *
cput(struct rm_attribute *attrib)
{
	int			value;

	if (attrib == NULL) {
		log_err(-1, __func__, 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, __func__, log_buffer);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	if (momgetattr(NULL)) {
		log_err(-1, __func__, 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;
	}
}

/**
 * @brief
 *      computes and returns the memory for session with  pid sid..
 *
 * @param[in] sid - process id
 *
 * @return      string
 * @retval      memsize         Success
 * @retval      NULL            Error
 *
 */
char *
mem_job(pid_t jobid)
{
	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;
	}
	if (found) {
		sprintf(ret_string, "%ukb", memsize >> 10); /* KB */
		return ret_string;
	}

	rm_errno = RM_ERR_EXIST;
	return NULL;
}

/**
 * @brief
 *      computes and returns the memory for process with  pid sid..
 *
 * @param[in] pid - process id
 *
 * @return      string
 * @retval      memsize         Success
 * @retval      NULL            Error
 *
 */
char *
mem_proc(pid_t pid)
{
	int			i, memsize;
	struct task_basic_info t_info;

	if (getprocs() == 0) {
		rm_errno = RM_ERR_SYSTEM;
		return NULL;
	}
	for (i=0; i<nproc; i++) {
		struct kinfo_proc	*pp = &proc_tbl[i];

		if (pid != pp->kp_proc.p_pid)
			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;
}

/**
 * @brief
 *      wrapper function for mem_job and mem_proc..
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      memsize         Success
 * @retval      NULL            ERRor
 *
 */
char *
mem(struct rm_attribute *attrib)
{
	int			value;

	if (attrib == NULL) {
		log_err(-1, __func__, 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, __func__, log_buffer);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	if (momgetattr(NULL)) {
		log_err(-1, __func__, 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;
	}
}

/**
 * @brief
 *      computes and returns resident set size for job
 *
 * @param[in] jobid - pid for job
 *
 * @return      string
 * @retval      resident set size       Success
 * @retval      NULL                    Error
 *
 */
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;
}

/**
 * @brief
 *      computes and returns resident set size for process
 *
 * @param[in] pid - process id
 *
 * @return      string
 * @retval      resident set size       Success
 * @retval      NULL                    Error
 *
 */
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;
}

/**
 * @brief
 *      wrapper function for mem_job and mem_proc..
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      resident set size       Success
 * @retval      NULL                    ERRor
 *
 */
static char *
resi(struct rm_attribute *attrib)
{
	int			value;

	if (attrib == NULL) {
		log_err(-1, __func__, 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, __func__, log_buffer);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	if (momgetattr(NULL)) {
		log_err(-1, __func__, extra_parm);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}

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

/**
 * @brief
 *      returns the number of sessions
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      sessions        Success
 * @retval      NULL            error
 *
 */
char *
sessions(struct rm_attribute *attrib)
{
	int			i, j;
	char			*fmt;
	int			njids = 0;
	pid_t			*jids, jobid;

	if (attrib) {
		log_err(-1, __func__, 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, __func__, "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",
		       __func__, (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);
		sprintf(fmt, " %d", (int)jids[j]);
		fmt += strlen(fmt);
	}
	free(jids);
	return ret_string;
}

/**
 * @brief
 *      wrapper function for sessions().
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      sessions        Success
 * @retval      0               error
 *
 */
char *
nsessions(struct rm_attribute *attrib)
{
	char	*result, *ch;
	int	num = 0;

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

	for (ch=result; *ch; ch++) {
		if (*ch == ' ')		/* count blanks */
			num++;
	}
	sprintf(ret_string, "%d", num);
	return ret_string;
}

/**
 * @brief
 *      returns the number of processes in session
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      process        Success
 * @retval      NULL            error
 *
 */
char *
pids(struct rm_attribute *attrib)
{
	pid_t			jobid;
	int			i;
	char			*fmt;
	int			num_pids = 0;

	if (attrib == NULL) {
		log_err(-1, __func__, 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, __func__, log_buffer);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	if (momgetattr(NULL)) {
		log_err(-1, __func__, 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",
		       __func__, 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;
}

/**
 * @brief
 *      returns all the process ids
 *
 * @return      pid_t
 * @retval      pids    Success
 * @retval      NULl    Error
 *
 */
pid_t *
allpids(void)
{
	int			 i;
	static	pid_t		*pids = NULL;

	if (getprocs() == 0)
		return NULL;

	if (pids != NULL)
		free(pids);
	if ((pids = (pid_t *)calloc(nproc+1, sizeof(pid_t))) == NULL) {
		log_err(errno, __func__, "no memory");
		return NULL;
	}

	for (i=0; i<nproc; i++) {
		struct kinfo_proc	*pp = &proc_tbl[i];

		pids[i] = pp->kp_proc.p_pid;	/* add pid to list */
	}
	pids[nproc] = -1;
	return pids;
}

/**
 * @brief
 *      returns the number of users
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      users        Success
 * @retval      NULL            error
 *
 */
char *
nusers(struct rm_attribute *attrib)
{
	int			i, j;
	int			nuids = 0;
	uid_t			*uids, uid;

	if (attrib) {
		log_err(-1, __func__, 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, __func__, "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",
		       __func__, (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;
}

/**
 * @brief
 *      return the number of cpus
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      number of cpus  Success
 * @retval      NULL            Error
 *
 */
static char *
ncpus(struct rm_attribute *attrib)
{
	if (attrib) {
		log_err(-1, "ncpus", extra_parm);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	sprintf(ret_string, "%d", nncpus);
	return ret_string;
}

/**
 * @brief
 *      returns the total physical memory
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      tot physical memory     Success
 * @retval      NULL                    Error
 *
 */
char *
physmem(struct rm_attribute *attrib)
{
	int	mib[2];
	int	physmem;
	size_t len = sizeof(physmem);

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

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

	if (sysctl(mib, 2, &physmem, &len, NULL, 0) < 0) {
		log_err(errno, __func__, "sysctl");
		rm_errno = RM_ERR_SYSTEM;
		return NULL;
	}

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

/**
 * @brief
 *      returns the size of file system present in machine
 *
 * @param[in] param - attribute value(file system)
 *
 * @return      string
 * @retval      size of file system     Success
 * @retval      NULL                    Error
 *
 */
char *
size_fs(char *param)
{
	struct	statfs	fsbuf;

	if (param[0] != '/') {
		sprintf(log_buffer, "%s: not full path filesystem name: %s",
			__func__, param);
		log_err(-1, __func__, log_buffer);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	if (statfs(param, &fsbuf) == -1) {
		log_err(errno, __func__, "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;
}

/**
 * @brief
 *      get file attribute(size) from param and put them in buffer.
 *
 * @param[in] param - file attributes
 *
 * @return      string
 * @retval      size of file    Success
 * @retval      NULL            Error
 *
 */
char *
size_file(char *param)
{
	struct	stat	sbuf;

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

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

	sprintf(ret_string, "%lukb",
		(unsigned long)sbuf.st_size >> 10); /* in KB */
	return ret_string;
}

/**
 * @brief
 *      wrapper function for size_file which returns the size of file system
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      size of file system     Success
 * @retval      NULL                    Error
 *
 */
char *
size(struct rm_attribute *attrib)
{
	char	*param;

	if (attrib == NULL) {
		log_err(-1, __func__, no_parm);
		rm_errno = RM_ERR_NOPARAM;
		return NULL;
	}
	if (momgetattr(NULL)) {
		log_err(-1, __func__, 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;
	}
}

/**
 * @brief
 *      computes and returns walltime for process or session.
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return      string
 * @retval      walltime        Success
 * @retval      NULL            Error
 *
 */
static char *
walltime(struct rm_attribute *attrib)
{
	pid_t			value;
	int			i, job, found = 0;
	time_t			now, start;

	if (attrib == NULL) {
		log_err(-1, __func__, 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, __func__, log_buffer);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	if (momgetattr(NULL)) {
		log_err(-1, __func__, 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, __func__, "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;
		}

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

/**
 * @brief
 *      reads load avg from file and returns
 *
 * @param[out] rv - var to hold load avg
 *
 * @return      int
 * @retval      0                       Success
 * @retval      RM_ERR_SYSTEM(15205)    error
 *
 */
int
get_la(double *rv)
{
	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, __func__, "sysctl(VM_LOADAVG)");
		return (rm_errno = RM_ERR_SYSTEM);
	}

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

/**
 * @brief
 *      computes and returns the gracetime
 *
 * @param[in] secs - time
 *
 * @return      u_long
 * @retval      time in secs    if time limit > 0
 * @retval      0               if time limit < 0
 */
u_long
gracetime(u_long secs)
{
	time_t	now = time(NULL);

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

/**
 * @brief
 *      return the disk quota for file depending on the type of file
 *
 * @param[in] attrib - pointer to rm_attribute structure(type)
 *
 * @return      string
 * @retval      quota val       Success
 * @retval      NULL            error
 *
 */
static char     *
quota(struct  rm_attribute  *attrib)
{
	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, __func__, 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, __func__, 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, __func__, log_buffer);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}

	if ((attrib = momgetattr(NULL)) == NULL) {
		log_err(-1, __func__, 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, __func__, 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, __func__, 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, __func__, 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, __func__, "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, __func__, 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, __func__, log_buffer);
		rm_errno = RM_ERR_EXIST;
		return NULL;
	}

	if ((attrib = momgetattr(NULL)) == NULL) {
		log_err(-1, __func__, 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, __func__, log_buffer);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	if ((uid = (uid_t)atoi(attrib->a_value)) == 0) {
		if ((pw = getpwnam(attrib->a_value)) == NULL) {
			sprintf(log_buffer,
				"user not found: %s", attrib->a_value);
			log_err(-1, __func__, log_buffer);
			rm_errno = RM_ERR_EXIST;
			return NULL;
		}
		uid = pw->pw_uid;
	}

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

	/* all sizes in KB */
	switch (type) {
	case harddata:
		sprintf(ret_string, "%lukb",
			(unsigned long)(qi.dqb_bhardlimit >> 10));
		break;
	case softdata:
		sprintf(ret_string, "%lukb",
			(unsigned long)(qi.dqb_bsoftlimit >> 10));
		break;
	case currdata:
		sprintf(ret_string, "%lukb",
			(unsigned long)(qi.dqb_curbytes >> 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;
}

/**
 * @brief
 *      set priority of processes.
 *
 * @return      Void
 *
 */
void mom_nice()
{
        if ((nice_val != 0) && (setpriority(PRIO_PROCESS, 0, nice_val) == -1)) {
                (void)sprintf(log_buffer, "failed to nice(%d) mom", nice_val);
                log_err(errno, __func__, log_buffer);
        }

}

/**
 * @brief
 *      Unset priority of processes.
 *
 * @return      Void
 *
 */
void mom_unnice()
{
        if ((nice_val != 0) && (setpriority(PRIO_PROCESS, 0, 0) == -1)) {
                (void)sprintf(log_buffer, "failed to nice(%d) mom", nice_val);
                log_err(errno, __func__, log_buffer);
        }
}

/**
 * @brief
 *      Get the info required for tm_attach.
 *
 * @param[in] pid - process id
 * @param[in] sid - session id
 * @param[in] uid - user id
 * @param[in] comm - command name
 * @param[in] len - size of command
 *
 * @return      int
 * @retval      TM_OKAY                 Success
 * @retval      TM_ENOPROC(17011)       Error
 *
 */
int
dep_procinfo(pid_t pid, pid_t *sid, uid_t *uid, char *comm, size_t len)
{
	int	i;

	if (getprocs() == 0)
		return TM_ESYSTEM;

	for (i=0; i<nproc; i++) {
		struct kinfo_proc	*pp = &proc_tbl[i];

		if (pid == pp->kp_proc.p_pid) {
			*sid = sess_tbl[i];
			*uid = pp->kp_eproc.e_pcred.p_ruid;
			memset(comm, '\0', len);
			memcpy(comm, pp->kp_proc.p_comm,
				MIN(len-1, sizeof(pp->kp_proc.p_comm)));
			return TM_OKAY;
		}
	}
	return TM_ENOPROC;
}

/**
 * @brief
 *	No special attach functionality is required.
 *
 * @retturn 	int
 * @retval	TM_OKAY
 *
 */
int
dep_attach(task *ptask)
{
	return TM_OKAY;
}

/**
 * @brief
 *	This is basically a wrapper for the MACH t_info and vm_region calls
 *
 * @param[in] t_info - pointer to task_basic_info structure
 * @param[in] pid - process id
 *
 * @return	int
 * @retval	0	Success
 * @retval	-1	error
 * @retval	errno	error
 */

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_64;
#endif

  struct vm_region_basic_info_64 	vm_info;
  vm_size_t			size;
  mach_port_t			object_name;
  vm_address_t			address = GLOBAL_SHARED_TEXT_SEGMENT;
  kern_return_t			errno;

  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,
          (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 'ps' source
   * "Check for firmware split libraries"
   * We'll modify t_info inline here.
   */

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

  return(0);
  }


/**
 * @brief
 *	get the system time and user time for live threads for a task by pid
 *
 * @param[in] t_info - pointer to task_basic_info structure
 * @param[in] pid - process id
 *
 * @return      int
 * @retval      0       Success
 * @retval      -1      error
 * @retval      errno   error
 */
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);
}
