/*
 * Copyright (c) 2002, Target Corporation.  All Rights Reserved.
 * This file is free software; you can redistribute it and/or modify
 * it under the same terms as Perl itself.
 *
 * Author: James FitzGibbon <james.fitzgibbon@target.com>
 *
 * based on aix.c distributed with Proc::ProcessTable v0.35, which
 * is Copyright (c) 1998, David Paquet.
 *
 */

#include <cf.h>
#include <odmi.h>
#include <procinfo.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/cfgodm.h>
#include <sys/types.h>

#include "EXTERN.h"
#include "perl.h"
#include "os/aix_getprocs.h"


/* convert a struct timeval to seconds *
 * TVALU_* presumes that tv_usec is microseconds
 * TVALN_* presumes that tv_usec is nanoseconds (struct procsinfo64)
 */
#define	TVALU_TO_SEC(x)		(x.tv_sec + ((double)x.tv_usec / 1000000.0))
#define	TVALN_TO_SEC(x)		(x.tv_sec + ((double)x.tv_usec / 1000000000.0))


void bless_procs(struct procsinfo64 *, int);


static unsigned long long memory;
static int pagesize = 0;
static long int ncpus = 0;
static double now_time = 0.0;
static char format[F_LASTFIELD+2];


char *OS_initialize() {

	struct CuAt* odm_object;
	int num_fetched;

	/* get the number of processors online */
	ncpus = sysconf(_SC_NPROCESSORS_ONLN);
	if( ncpus == -1 ) {		/* sysconf error */
		ncpus = 1;
	}

	/* get the page size in bytes */
	pagesize = getpagesize();

	/* get the amount of physical memory */
	if( 0 != odm_initialize() ) {
		/* fprintf(stderr, "cannot initialize ODM in Proc::ProcessTable::OS_initialize (AIX)!\n"); */
		ppt_warn("cannot initialize ODM in Proc::ProcessTable::OS_initialize (AIX)!");
	} else {
		odm_object = (struct CuAt*)getattr("sys0", "realmem", 0, &num_fetched);
		memory = strtoull(odm_object->value, 0, 10);
		odm_terminate();
	}

    memory = memory * 1024;

	return NULL;

}


void OS_get_table() {

	struct procsinfo64 *procs = NULL;
	int index = 0;
	int fetched = 0;
	struct timeval now_tval;

	/* allocate memory to hold the procs */
/*	procs = New(0, procs, PROCS_TO_FETCH, struct procsinfo64); */
    procs = (struct procsinfo64 *)malloc(sizeof(struct procsinfo64) * PROCS_TO_FETCH);
	if(NULL == procs) {
		/* fprintf(stderr, "cannot allocate memory in Proc::ProcessTable::OS_get_table!\n"); */
		ppt_warn("cannot allocate memory in Proc::ProcessTable::OS_get_table!");
		return;
	}

	/* get current time of day */
	gettimeofday(&now_tval, 0);
	now_time = TVALU_TO_SEC(now_tval);

	/* keep on grabbing chunks of processes until getprocs returns a smaller
       block than we asked for */
	while( (fetched = getprocs(procs, sizeof(struct procsinfo64),
                               NULL, 0, &index, PROCS_TO_FETCH))
           >= PROCS_TO_FETCH) {

		bless_procs(procs, fetched);
	}

	/* bless the last block of procs */
	bless_procs(procs, fetched);

	/* release the memory */
	Safefree(procs);

	return;

}


void bless_procs(struct procsinfo64 *procs, int count)
{

	int index;
	char cmndline[ARG_MAX];
	char comm[ARG_MAX];
	char pctmem[PCT_LENGTH];
	char pctcpu[PCT_LENGTH];
	char state[STATE_LENGTH];
	char *c = NULL;
	struct procsinfo64 *curproc = NULL;
    struct procsinfo curproc_for_getargs;
	int zombie;
	int done;
	long utime, stime, cutime, cstime;

    /* initialize */
    Zero(&curproc_for_getargs, 1, struct procsinfo);

	for( index = 0, curproc = procs; index < count; index++, curproc = procs+index ) {

		/* reset char fields */
		memset(cmndline, 0, ARG_MAX);
		memset(comm, 0, ARG_MAX);
		memset(pctmem, 0, PCT_LENGTH);
		memset(pctcpu, 0, PCT_LENGTH);
		memset(state, 0, STATE_LENGTH);

		/* skip procs with a state of NONE */
		if( curproc->pi_state == SNONE ) {
			continue;
		}

		/* copy the format into something we can use */
		strcpy(format, Defaultformat);

		/* presume we are not a zombie */
		zombie = 0;

		/* set a descriptive name for the process state */
		switch( curproc->pi_state ) {

			case SSLEEP:
					strcpy(state, SLEEP);
					break;

			case SRUN:
					strcpy(state, RUN);
					break;

			case SIDL:
					strcpy(state, IDLE);
					break;

			case SZOMB:
					strcpy(state, ZOMBIE);
					zombie = 1;
					break;

			case SSTOP:
					strcpy(state, STOP);
					break;

			case SACTIVE:
					strcpy(state, ACTIVE);
					break;

			default:
					format[F_STAT] = 'S';
					break;

		}

		/* calc utime, stime, cutime, cstime */
		if( zombie ) {
			utime = curproc->pi_utime;
			stime = curproc->pi_stime;
		} else {
			utime = TVALN_TO_SEC(curproc->pi_ru.ru_utime);
			stime = TVALN_TO_SEC(curproc->pi_ru.ru_stime);
			cutime = TVALN_TO_SEC(curproc->pi_cru.ru_utime);
			cstime = TVALN_TO_SEC(curproc->pi_cru.ru_stime);
		}

		/* calc pctcpu */
		sprintf(pctcpu, "%3.2f", ((utime + stime) * 100 /
                                  ( now_time - curproc->pi_start )) / ncpus );

		/* calc pctmem */
		if( memory == 0 ) {
			format[F_PCTMEM] = 'S';
		} else {
			sprintf(pctmem, "%3.2f", (curproc->pi_drss + curproc->pi_trss) * pagesize * 100 / (float)memory);
		}

		/* determine comm & cmndline */
		if( (curproc->pi_flags & SKPROC) == SKPROC ) {
			if( curproc->pi_pid == 0 ) {
				snprintf(comm, ARG_MAX, "kproc (swapper)");
				snprintf(cmndline, ARG_MAX, "kproc (swapper)");
			} else {
				snprintf(comm, ARG_MAX, "kproc (%s)", curproc->pi_comm);
				snprintf(cmndline, ARG_MAX, "kproc (%s)", curproc->pi_comm);
			}
		} else {
			snprintf(comm, ARG_MAX, "%s", curproc->pi_comm);
            curproc_for_getargs.pi_pid = curproc->pi_pid;
			if( getargs(&curproc_for_getargs, sizeof(struct procsinfo), cmndline, ARG_MAX) < 0 ) {
				snprintf(cmndline, ARG_MAX, "%s", curproc->pi_comm);
			} else {
				/* replace NUL characters in command line with spaces */
				c = cmndline;
				done = 0;
				while( ! done ) {
					if( *c == '\0' ) {
						if( *(c+1) == '\0' ) {
							done = 1;
						} else {
							*c++ = ' ';
						}
					} else {
						c++;
					}
				}
			}
		}

		/* sanity check: bless_into_procs doesn't like negative ints */
		if( curproc->pi_cred.cr_uid == -1 ) {
			format[F_EUID] = 'I';
			curproc->pi_cred.cr_uid = 0;
		}
		if( curproc->pi_ttyd == -1 ) {
			format[F_TTYNUM] = 'I';
			curproc->pi_ttyd = 0;
		}
		if( curproc->pi_ttympx == -1 ) {
			format[F_TTYMPX] = 'I';
			curproc->pi_ttympx = 0;
		}
		
		/* give it to Perl */
		bless_into_proc(format, Fields,
			curproc->pi_pid,		/* pid */
			curproc->pi_ppid,		/* ppid */
			curproc->pi_sid,		/* sess */
			curproc->pi_pgrp,		/* pgrp */
			curproc->pi_uid,		/* uid */
			curproc->pi_suid,		/* suid */
			curproc->pi_cred.cr_luid,	/* luid */
			curproc->pi_cred.cr_uid,	/* euid */
			curproc->pi_cred.cr_rgid,	/* gid */
			curproc->pi_cred.cr_gid,	/* egid */
			curproc->pi_pri,		/* priority */
			curproc->pi_nice,		/* nice */
			curproc->pi_thcount,	/* thcount */
			state,
			curproc->pi_flags,		/* flags */
			curproc->pi_flags2,		/* flags2 */
			curproc->pi_adspace,	/* adspace */
			curproc->pi_majflt,		/* majflt */
			curproc->pi_minflt,		/* minflt */
			(long)(utime * 100),	/* utime */
			(long)(stime * 100),	/* stime */
			(long)(cutime * 100),	/* cutime */
			(long)(cstime * 100),	/* cstime */
			curproc->pi_start,		/* start */
			curproc->pi_size,		/* size */
			curproc->pi_tsize,		/* tsize */
			curproc->pi_ttyp,		/* ttyp */
			curproc->pi_ttyd,		/* ttynum */
			curproc->pi_ttympx,		/* ttympx */
			curproc->pi_drss,		/* drss */
			curproc->pi_trss,		/* trss */
			curproc->pi_dvm,		/* dvm */
			pctmem,					/* pctmem */
			pctcpu,					/* pctcpu */
			comm,					/* comm */
			cmndline				/* cmndline */
		);
	}

}


/*
 * EOF
 */