/*-
 * This code relies heavily on the Darwin "ps" command, which is available
 * from Apple in the adv_cmds portion of the Darwin distribution. The portions
 * of this code which were included from that source are:
 *
 * Copyright (c) 1990, 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, 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.
 *
 * The portions of this code which were necessary to tie into the Perl
 * Proc::ProcessTable module are:
 *
 * Copyright (c) 2003, 2004 by Thomas R. Wyant, III
 *
 * and may be reused under the same terms as Perl itself.
 */
 
#include "os/darwin.h"

/*
 * static void getproclline (KINFO *k, char ** command_name, int *cmdlen, int eflg);
 */
static void getproclline (KINFO *k, char ** command_name, int *cmdlen,
	int eflg, int show_args);
static int get_task_info (KINFO *ki);

int mempages = 0;

#ifdef TESTING

void OS_get_table (void);
char *OS_initialize (void);

int main (int argc, char **argv) {
OS_initialize ();
OS_get_table ();
exit (0);
}

#endif


char* OS_initialize(void) {

	size_t oldlen;
	int mib[2];

	oldlen = sizeof(mempages);
	mib[0] = CTL_HW;
	mib[1] = HW_PHYSMEM;

	sysctl(mib, 2, &mempages, &oldlen, NULL, 0);

	return NULL;
}

/* The appropriate FREE_BUFFERS definition for OS_get_table */

#define FREE_BUFFERS \
	{	if (kprocbuf != NULL) free (kprocbuf); \
	}

void OS_get_table(void) {

	size_t bufSize = 0;
	char *command_name;
	int cmdlen;
	int i;
	struct kinfo_proc *kp;
	struct kinfo_proc *kprocbuf = NULL;
	int local_error=0;
	int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
	int nentries;
	size_t orig_bufSize = 0;
	char pctcpu[6];
	char pctmem[6];
	int retry_count = 0;
	int state;

	if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0)
		DIE_HORRIBLY ("Failure calling sysctl")

	if ((kprocbuf= kp = (struct kinfo_proc *)malloc(bufSize)) == NULL)
		DIE_HORRIBLY ("Memory allocation failure")

	retry_count = 0;
	orig_bufSize = bufSize;
	for(retry_count=0; ; retry_count++) {
	/* retry for transient errors due to load in the system */
		local_error = 0;
		bufSize = orig_bufSize;
		if ((local_error = sysctl(mib, 4, kp, &bufSize, NULL, 0)) < 0) {
			if (retry_count < 1000) {
			/* 1 sec back off */
				sleep(1);
				continue;
			}
			DIE_HORRIBLY ("Failure calling sysctl")
		} else if (local_error == 0) {
			break;
		}
	/* 1 sec back off */
	sleep(1);
	}

	/* This has to be after the second sysctl since the bufSize
		may have changed.  */
	nentries = bufSize/ sizeof(struct kinfo_proc);

	/* the loop was stolen from ps - but it backs through the data.
	 * We're going through forward, which means we need to play
	 * slightly different games.
	 */

#if 1
	kp += nentries - 1;
	for (i = 1; i <= nentries; i++, --kp) {
#else
	for (i = nentries; --i >= 0; ++kp) {
#endif
		struct extern_proc *p;
		struct eproc *e;
		KINFO kinfo;
		KINFO *ki = &kinfo;
		memset(ki, 0, sizeof(*ki));
#ifdef TESTING
		char *ttname = NULL;
#endif /* def TESTING */

		time_value_t total_time, system_time, user_time;

		ki->ki_p = kp;

		get_task_info(ki);

		p = KI_PROC (ki);
		e = KI_EPROC (ki);
		state = p->p_stat == SZOMB ? SZOMB : ki->state;
		getproclline (ki, &command_name, &cmdlen, 0, 1);

		user_time = ki->tasks_info.user_time;
		time_value_add (&user_time, &ki->times.user_time);
		system_time = ki->tasks_info.system_time;
		time_value_add (&system_time, &ki->times.system_time);
		total_time = user_time;
		time_value_add (&total_time, &system_time);

#ifndef	TH_USAGE_SCALE
#define	TH_USAGE_SCALE	1000
#endif	TH_USAGE_SCALE
#define usage_to_percent(u)	((u*100)/TH_USAGE_SCALE)
#define usage_to_tenths(u)	(((u*1000)/TH_USAGE_SCALE) % 10)
		sprintf (pctcpu, "%d.%01d", usage_to_percent (ki->cpu_usage),
			usage_to_tenths (ki->cpu_usage));
		sprintf (pctmem, "%.1f", ((float) ki->tasks_info.resident_size)
			* 100 / mempages);

#ifdef TIME_IN_MICROSECONDS
#define time_value_to_ticks(x) 1000000LL * (x).seconds + (x).microseconds
#else /* def TIME_IN_MICROSECONDS */
#define time_value_to_ticks(x) ((long) (x).seconds * 100 + \
	((long) (x).microseconds + 5000) / 10000)
#endif /* def TIME_IN_MICROSECONDS */

#ifdef TESTING

		/* Since we're testing stand-alone, we'll provide a ttydev value
		 * for the look of the thing. We don't have to do this when live,
		 * since the .xs module does this automagically when it sees the
		 * ttynum value. This means ttydev must be passed BEFORE
		 * ttynum.
		 */

		if (e->e_tdev != NODEV) ttname = devname (e->e_tdev, S_IFCHR);
		if (ttname == NULL) ttname = "";

#ifdef DEBUGGING
		printf ("\nPid: %d; i:%d; kp: %p\n", p->p_pid, i, kp);
#else /* def DEBUGGING */
		printf ("\nPid: %d\n", p->p_pid);
#endif /* def DEBUGGING */
		printf ("    ppid: %d\n", e->e_ppid);
		printf ("    pgid: %d\n", e->e_pgid);
		printf ("     uid: %d\n", e->e_pcred.p_ruid);
		printf ("     gid: %d\n", e->e_pcred.p_rgid);
		printf ("    euid: %d\n", e->e_ucred.cr_uid);
		printf ("    egid: %d\n", e->e_ucred.cr_gid);
		printf ("    suid: %d\n", e->e_pcred.p_svuid);
		printf ("    sgid: %d\n", e->e_pcred.p_svgid);
		printf ("priority: %u\n", ki->curpri);
		printf ("    size: %lu Kb\n", (u_long)ki->tasks_info.virtual_size/1024);
		printf ("     rss: %lu Kb\n", (u_long)ki->tasks_info.resident_size/1024);
		printf ("   flags: %#0x\n", p->p_flag);
		printf ("    nice: %d\n", p->p_nice);
		printf (" session: %p\n", e->e_sess);
#ifdef TIME_IN_MICROSECONDS
		printf ("    time: %lld microseconds\n", time_value_to_ticks (total_time));
		printf ("   stime: %lld microseconds\n", time_value_to_ticks (system_time));
		printf ("   utime: %lld microseconds\n", time_value_to_ticks (user_time));
#else /* def TIME_IN_MICROSECONDS */
		printf ("    time: %ld centiseconds\n", time_value_to_ticks (total_time));
		printf ("   stime: %ld centiseconds\n", time_value_to_ticks (system_time));
		printf ("   utime: %ld centiseconds\n", time_value_to_ticks (user_time));
#endif /* def TIME_IN_MICROSECONDS */
		printf ("   start: %ld\n", (unsigned long) p->p_starttime.tv_sec);
		printf ("   wchan: %p\n", p->p_wchan);
		printf ("  ttydev: %s\n", ttname);
		printf ("  ttynum: %ld\n", (long) e->e_tdev);
		printf ("    %%cpu: %s\n", pctcpu);
		printf ("    %%mem: %s\n", pctmem);
		printf ("   state: %d (%s)\n", state, States[state]);
		printf ("     cmd: %s\n", cmdlen ? command_name : " (none available)");
		printf ("   fname: %s\n", p->p_comm);

#else /* def TESTING */

		/* Send if off to Perl */

		bless_into_proc (Format, Fields,
			p->p_pid,
			e->e_ppid,
			e->e_pgid,
			e->e_pcred.p_ruid,
			e->e_pcred.p_rgid,
			e->e_ucred.cr_uid,
			e->e_ucred.cr_gid,
			e->e_pcred.p_svuid,
			e->e_pcred.p_svgid,
			ki->curpri,
			ki->tasks_info.virtual_size/1024,
			ki->tasks_info.resident_size/1024,
			p->p_flag,
			p->p_nice,
			e->e_sess,
			time_value_to_ticks (total_time),
			time_value_to_ticks (system_time),
			time_value_to_ticks (user_time),
			p->p_starttime.tv_sec,
			p->p_wchan,
			"",		/* The .xs code provides ttydev automagically */
			e->e_tdev,
			pctcpu,
			pctmem,
			States[state],
			cmdlen ? command_name : "",
			p->p_comm
			);

#endif /* def TESTING */

		if (command_name != NULL) free (command_name);
	}

	FREE_BUFFERS

}

/* We're done with this definition of FREE_BUFFERS. Get rid of it in
 * the hope that using it below (as a consequence of an inappropriate
 * use of DIE_HORRIBLY, for example) will generate a more obvious
 * error message.
 */

#undef FREE_BUFFERS


/*
 * The interface used to fetch the command arguments changed
 * drastically between Jaguar (MacOS 10.2, Darwin 6.something) and
 * Panther (MacOS 10.3, Darwin version unknown to me at this time).
 * The corresponding module of the ps command changed likewise.
 * Unfortunately, we have to either keep both interfaces around,
 * or abandon Panther (stupid!), or abandon Jaguar (which I'm reluctant
 * to do, since I have a not-so-sneaking sympathy for those who upgrade
 * no software before its time). -- TRW
 */

#ifdef KERN_PROCARGS2

/*
 * The following is pretty much verbatim from module print.c of the
 * Panther version of the ps command. Specifically, it's from
 * adv_cmds-63. But the calling sequence has been modified for our
 * convenience. Specifically, the global variable eflg has been made
 * into an argument. -- TRW
 */

/*
 * Get command and arguments.
 *
 * If the global variable eflg is non-zero and the user has permission to view
 * the process's environment, the environment is included.
 */
static void
getproclline(KINFO *k, char **command_name, int *cmdlen, int eflg, int show_args)
{
	int		mib[3], argmax, nargs, c = 0;
	size_t		size;
	char		*procargs, *sp, *np, *cp;
/*  Made into a command argument. -- TRW
 *	extern int	eflg;
 */

	/* Get the maximum process arguments size. */
	mib[0] = CTL_KERN;
	mib[1] = KERN_ARGMAX;

	size = sizeof(argmax);
	if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) {
		goto ERROR_A;
	}

	/* Allocate space for the arguments. */
	procargs = (char *)malloc(argmax);
	if (procargs == NULL) {
		goto ERROR_A;
	}

	/*
	 * Make a sysctl() call to get the raw argument space of the process.
	 * The layout is documented in start.s, which is part of the Csu
	 * project.  In summary, it looks like:
	 *
	 * /---------------\ 0x00000000
	 * :               :
	 * :               :
	 * |---------------|
	 * | argc          |
	 * |---------------|
	 * | arg[0]        |
	 * |---------------|
	 * :               :
	 * :               :
	 * |---------------|
	 * | arg[argc - 1] |
	 * |---------------|
	 * | 0             |
	 * |---------------|
	 * | env[0]        |
	 * |---------------|
	 * :               :
	 * :               :
	 * |---------------|
	 * | env[n]        |
	 * |---------------|
	 * | 0             |
	 * |---------------| <-- Beginning of data returned by sysctl() is here.
	 * | argc          |
	 * |---------------|
	 * | exec_path     |
	 * |:::::::::::::::|
	 * |               |
	 * | String area.  |
	 * |               |
	 * |---------------| <-- Top of stack.
	 * :               :
	 * :               :
	 * \---------------/ 0xffffffff
	 */
	mib[0] = CTL_KERN;
	mib[1] = KERN_PROCARGS2;
	mib[2] = KI_PROC(k)->p_pid;

	size = (size_t)argmax;
	if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
		goto ERROR_B;
	}

	memcpy(&nargs, procargs, sizeof(nargs));
	cp = procargs + sizeof(nargs);

	/* Skip the saved exec_path. */
	for (; cp < &procargs[size]; cp++) {
		if (*cp == '\0') {
			/* End of exec_path reached. */
			break;
		}
	}
	if (cp == &procargs[size]) {
		goto ERROR_B;
	}

	/* Skip trailing '\0' characters. */
	for (; cp < &procargs[size]; cp++) {
		if (*cp != '\0') {
			/* Beginning of first argument reached. */
			break;
		}
	}
	if (cp == &procargs[size]) {
		goto ERROR_B;
	}
	/* Save where the argv[0] string starts. */
	sp = cp;

	/*
	 * Iterate through the '\0'-terminated strings and convert '\0' to ' '
	 * until a string is found that has a '=' character in it (or there are
	 * no more strings in procargs).  There is no way to deterministically
	 * know where the command arguments end and the environment strings
	 * start, which is why the '=' character is searched for as a heuristic.
	 */
	for (np = NULL; c < nargs && cp < &procargs[size]; cp++) {
		if (*cp == '\0') {
			c++;
			if (np != NULL) {
				/* Convert previous '\0'. */
				*np = ' ';
			}
			/* Note location of current '\0'. */
			np = cp;

			if (!show_args) {
				/*
				 * Don't convert '\0' characters to ' '.
				 * However, we needed to know that the
				 * command name was terminated, which we
				 * now know.
				 */
				break;
			}
		}
	}

	/*
	 * If eflg is non-zero, continue converting '\0' characters to ' '
	 * characters until no more strings that look like environment settings
	 * follow.
	 */
	if ( (eflg != 0) && ( (getuid() == 0) || (KI_EPROC(k)->e_pcred.p_ruid == getuid()) ) ) {
		for (; cp < &procargs[size]; cp++) {
			if (*cp == '\0') {
				if (np != NULL) {
					if (&np[1] == cp) {
						/*
						 * Two '\0' characters in a row.
						 * This should normally only
						 * happen after all the strings
						 * have been seen, but in any
						 * case, stop parsing.
						 */
						break;
					}
					/* Convert previous '\0'. */
					*np = ' ';
				}
				/* Note location of current '\0'. */
				np = cp;
			}
		}
	}

	/*
	 * sp points to the beginning of the arguments/environment string, and
	 * np should point to the '\0' terminator for the string.
	 */
	if (np == NULL || np == sp) {
		/* Empty or unterminated string. */
		goto ERROR_B;
	}

	/* Make a copy of the string. */
	*cmdlen = asprintf(command_name, "%s", sp);

	/* Clean up. */
	free(procargs);
	return;

	ERROR_B:
	free(procargs);
	ERROR_A:
	*cmdlen = asprintf(command_name, "(%s)", KI_PROC(k)->p_comm);
}


#else	/* #ifdef KERN_PROCARGS2 */

/*
 * The following code is pretty much verbatim from module print.c of
 * the ps command. The version of print.c is unknown. It is identical
 * to, but certainly earlier than, the version in adv_cmds-46, which
 * is the most recent version that goes with Jaguar. The calling
 * sequence has been modified to pass eflg as an argument (rather than
 * a global variable), and to be the same as the Panther version. Also,
 * some band-aid code has been inserted late in the module to cover an
 * apparent bug. The Panther version of this subroutine was completely
 * rewritten, and uses a different sysctl funtion. -- TRW.
 */
static void getproclline (KINFO *k, char ** command_name, int *cmdlen,
	int eflg, int show_args)
/* show_args = 1, display environment; 0 = don't. */
{

	/*
	 *	Get command and arguments.
	 */
	int		command_length;
	char * cmdpath;
	volatile int 	*ip, *savedip;
	volatile char	*cp;
	int		nbad;
	char		c;
	char		*end_argc;
	int 		mib[4];
	char *		arguments;
	size_t		arguments_size = 4096;
	int			len=0;
	volatile unsigned int *valuep;
	unsigned int value;
	int blahlen=0, skiplen=0;

	/* A sysctl() is made to find out the full path that the command
	   was called with.
	*/

	*command_name = NULL;
	*cmdlen = 0;

	mib[0] = CTL_KERN;
	mib[1] = KERN_PROCARGS;
	mib[2] = KI_PROC(k)->p_pid;
	mib[3] = 0;

	arguments = (char *) malloc(arguments_size);
	if (sysctl(mib, 3, arguments, &arguments_size, NULL, 0) < 0) {
		goto retucomm;
	}
	end_argc = &arguments[arguments_size];

	ip = (int *)end_argc;
	ip -= 2;		/* last arg word and .long 0 */
	while (*--ip)
		if (ip == (int *)arguments)
		goto retucomm;

		savedip = ip;
		savedip++;
		cp = (char *)savedip;
	while (*--ip)
	    if (ip == (int *)arguments)
		goto retucomm;
        ip++;
        
        valuep = (unsigned int *)ip;
        value = *valuep;
        if ((value & 0xbfff0000) == 0xbfff0000) {
                ip++;ip++;
		valuep = ip;
               blahlen = strlen((char *) ip);
                skiplen = (blahlen +3 ) /4 ;
                valuep += skiplen;
                cp = (char *)valuep;
                while (!*cp) {
                    cp++;
                }
                savedip = (int *) cp;
        }

	nbad = 0;

	for (cp = (char *)savedip; cp < (end_argc-1); cp++) {
	    c = *cp & 0177;
	    if (c == 0)
		*cp = ' ';
	    else if (c < ' ' || c > 0176) {
		if (++nbad >= 5*(eflg+1)) {
		    *cp++ = ' ';
		    break;
		}
		*cp = '?';
	    }
		else if (eflg == 0 && c == '=') {
		while (*--cp != ' ')
			if (cp <= (char *)ip)
			break;
		break;
		}
	}

	*cp = 0;
#if 0
	while (*--cp == ' ')
	    *cp = 0;
#endif
	cp = (char *)savedip;
	command_length = end_argc - cp;	/* <= MAX_COMMAND_SIZE */

	if (cp[0] == '-' || cp[0] == '?' || cp[0] <= ' ') {
		/*
		 *	Not enough information - add short command name
		 */
		/*
		 *	I have a report of this section of the code failing under
		 *	Panther because command_length < 0. When Jaguar hits this
		 *  code it typically has a largeish value of command_length
		 *  (200 bytes plus). Both are clearly bogus, though in the
		 *  case of Panther, the problem is benign. The problem is
		 *  that Jaguar uses a completely different sysctl call to
		 *  get the data, so I can't simply use that code. The
		 *  print.c module of the ps command from adv_cmds_43 (the
		 *  latest Jaguar version) is identical to the one I based
		 *  this code on originally. So no help there. Until I can
		 *  figure something better, we'll just have to rely on this
		 *  band-aid. A possible way to proceed, once I upgrade to
		 *  Panther myself, is to conditionalize on the existence of
		 *  KERN_PROCARGS2 to decide which version of this subroutine
		 *  to use. Sigh. -- TRW
		 */
#ifdef DEBUGGING
		fprintf (stdout, "Debug - getproclline found short cmd; pid %d command_length %d\n",
			KI_PROC(k)->p_pid, command_length);
#endif
		if (command_length > 0) {
			len = ((unsigned)command_length + MAXCOMLEN + 5);
			cmdpath = (char *)malloc(len);
			(void) strncpy(cmdpath, (const char *) cp, command_length);
			(void) strcat(cmdpath, " (");
			(void) strncat(cmdpath, KI_PROC(k)->p_comm,
				MAXCOMLEN+1);
			(void) strcat(cmdpath, ")");
			*command_name = cmdpath;
			*cmdlen = len;
			}
		  else {
		  	cmdpath = (char *)malloc(2);
		  	strncpy (cmdpath, "", 2);
		  	*command_name = cmdpath;
			*cmdlen = 0;
			}
		free(arguments);
		return;
	}
	else {
		cmdpath = (char *)malloc((unsigned)command_length + 1);
		(void) strncpy(cmdpath, (const char *) cp, command_length);
		cmdpath[command_length] = '\0';
		*command_name = cmdpath;
		*cmdlen = command_length;
		free(arguments);
		return;
	}

retucomm:
	len = (MAXCOMLEN + 5);
	cmdpath = (char *)malloc(len);
	(void) strcpy(cmdpath, " (");
	(void) strncat(cmdpath, KI_PROC(k)->p_comm,
			MAXCOMLEN+1);
	(void) strcat(cmdpath, ")");
	*cmdlen = len;
	*command_name = cmdpath;
	free(arguments);
	return;
}

#endif	/* #ifdef KERN_PROCARGS2 */

static int mach_state_order (int s, long sleep_time);
static int thread_schedinfo (KINFO *ki, thread_port_t thread,
	policy_t pol, void * buf);

static int get_task_info (KINFO *ki) 
{
	kern_return_t   	error;
	unsigned int		info_count = TASK_BASIC_INFO_COUNT;
	unsigned int 		thread_info_count = THREAD_BASIC_INFO_COUNT;
	pid_t				pid;
	int j, err = 0;

	pid = KI_PROC(ki)->p_pid;
	if (task_for_pid(mach_task_self(), pid, &ki->task) != KERN_SUCCESS) {
		return(1);
	}
	info_count = TASK_BASIC_INFO_COUNT;
	error = task_info(ki->task, TASK_BASIC_INFO, (task_info_t) &ki->tasks_info, &info_count);
	if (error != KERN_SUCCESS) {
		ki->invalid_tinfo=1;
#ifdef DEBUG
		mach_error("Error calling task_info()", error);
#endif
		return(1);
	}
	{
		vm_region_basic_info_data_64_t	b_info;
		vm_address_t					address = GLOBAL_SHARED_TEXT_SEGMENT;
		vm_size_t					size;
		mach_port_t					object_name;

		/*
		 * try to determine if this task has the split libraries
		 * mapped in... if so, adjust its virtual size down by
		 * the 2 segments that are used for split libraries
		 */
		info_count = VM_REGION_BASIC_INFO_COUNT_64;
		error = vm_region_64(ki->task, &address, &size, VM_REGION_BASIC_INFO,
					(vm_region_info_t)&b_info, &info_count, &object_name);
		if (error == KERN_SUCCESS) {
			if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) &&
				ki->tasks_info.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE))
					ki->tasks_info.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE);
		}
	}
	info_count = TASK_THREAD_TIMES_INFO_COUNT;
	error = task_info(ki->task, TASK_THREAD_TIMES_INFO, (task_info_t) &ki->times, &info_count);
	if (error != KERN_SUCCESS) {
		ki->invalid_tinfo=1;
#ifdef DEBUG
		mach_error("Error calling task_info()", error);
#endif
		return(1);
	}

	switch(ki->tasks_info.policy) {

		case POLICY_TIMESHARE :
			info_count = POLICY_TIMESHARE_INFO_COUNT;
			error = task_info(ki->task, TASK_SCHED_TIMESHARE_INFO, (task_info_t) &ki->schedinfo.tshare, &info_count);
			if (error != KERN_SUCCESS) {
				ki->invalid_tinfo=1;
#ifdef DEBUG
				mach_error("Error calling task_info()", error);
#endif
				return(1);
			}

			ki->curpri = ki->schedinfo.tshare.cur_priority;
			ki->basepri = ki->schedinfo.tshare.base_priority;
			break;

		case POLICY_RR :
	 		info_count = POLICY_RR_INFO_COUNT;
			error = task_info(ki->task, TASK_SCHED_RR_INFO, (task_info_t) &ki->schedinfo.rr, &info_count);
			if (error != KERN_SUCCESS) {
				ki->invalid_tinfo=1;
#ifdef DEBUG
				mach_error("Error calling task_info()", error);
#endif
				return(1);
			}

			ki->curpri = ki->schedinfo.rr.base_priority;
			ki->basepri = ki->schedinfo.rr.base_priority;
			break;

		case POLICY_FIFO :
			info_count = POLICY_FIFO_INFO_COUNT;
			error = task_info(ki->task, TASK_SCHED_FIFO_INFO, (task_info_t) &ki->schedinfo.fifo, &info_count);
			if (error != KERN_SUCCESS) {
				ki->invalid_tinfo=1;
#ifdef DEBUG
				mach_error("Error calling task_info()", error);
#endif
				return(1);
			}

			ki->curpri = ki->schedinfo.fifo.base_priority;
			ki->basepri = ki->schedinfo.fifo.base_priority;
			break;
	}

	ki->invalid_tinfo=0;

	ki->cpu_usage=0;
	error = task_threads(ki->task, &ki->thread_list, &ki->thread_count);
	if (error != KERN_SUCCESS) {
		mach_port_deallocate(mach_task_self(),ki->task);
#ifdef DEBUG
		mach_error("Call to task_threads() failed", error);
#endif
		return(1);
	}
	err=0;
	ki->state = STATE_MAX;
	//ki->curpri = 255;
	//ki->basepri = 255;
	ki->swapped = 1;
	ki->thval = malloc(ki->thread_count * sizeof(struct thread_values));
	if (ki->thval != NULL) {
		for (j = 0; j < ki->thread_count; j++) {
			int tstate;
			thread_info_count = THREAD_BASIC_INFO_COUNT;
			error = thread_info(ki->thread_list[j], THREAD_BASIC_INFO,
				(thread_info_t)&ki->thval[j].tb,
				&thread_info_count);
			if (error != KERN_SUCCESS) {
#ifdef DEBUG
				mach_error("Call to thread_info() failed", error);
#endif
				err=1;
			}
			error = thread_schedinfo(ki, ki->thread_list[j],
				ki->thval[j].tb.policy, &ki->thval[j].schedinfo);
			if (error != KERN_SUCCESS) {
#ifdef DEBUG
				mach_error("Call to thread_info() failed", error);
#endif
				err=1;
			}
			ki->cpu_usage += ki->thval[j].tb.cpu_usage;
			tstate = mach_state_order(ki->thval[j].tb.run_state,
					ki->thval[j].tb.sleep_time);
			if (tstate < ki->state)
				ki->state = tstate;
			if ((ki->thval[j].tb.flags & TH_FLAGS_SWAPPED ) == 0)
				ki->swapped = 0;
			mach_port_deallocate(mach_task_self(),
				ki->thread_list[j]);
		}
		free (ki->thval);
		ki->thval = NULL;
	}
	ki->invalid_thinfo = err;
	/* Deallocate the list of threads. */
	error = vm_deallocate(mach_task_self(), 
		(vm_address_t)(ki->thread_list),
		 sizeof(thread_port_array_t) * ki->thread_count);
	if (error != KERN_SUCCESS) {
#ifdef DEBUG
		mach_error("Trouble freeing thread_list", error);
#endif
	}

	mach_port_deallocate(mach_task_self(),ki->task);
	return(0);
}

static int mach_state_order (int s, long sleep_time)
{      
	switch (s) {
	case TH_STATE_RUNNING:		return(1);
	case TH_STATE_UNINTERRUPTIBLE:
								return(2);
	case TH_STATE_WAITING:		return((sleep_time > 20) ? 4 : 3);
	case TH_STATE_STOPPED:		return(5);
	case TH_STATE_HALTED:		return(6);  
	default:					return(7); 
	}
}

static int thread_schedinfo (KINFO *ki, thread_port_t thread,
	policy_t pol, void * buf)
{
	unsigned int		count;
	int ret = KERN_FAILURE;

	switch (pol) {

	case POLICY_TIMESHARE:
		count = POLICY_TIMESHARE_INFO_COUNT;
		ret = thread_info(thread, THREAD_SCHED_TIMESHARE_INFO,
					(thread_info_t)buf, &count);
		if((ret == KERN_SUCCESS) && (ki->curpri < (((struct policy_timeshare_info *)buf)->cur_priority)))
			ki->curpri  = ((struct policy_timeshare_info *)buf)->cur_priority;
		break;

	case POLICY_FIFO:
		count = POLICY_FIFO_INFO_COUNT;
		ret = thread_info(thread, THREAD_SCHED_FIFO_INFO,
					buf, &count);
		if((ret == KERN_SUCCESS) && (ki->curpri < (((struct policy_fifo_info *)buf)->base_priority)))
			ki->curpri  = ((struct policy_fifo_info *)buf)->base_priority;
		break;

	case POLICY_RR:
		count = POLICY_RR_INFO_COUNT;
		ret = thread_info(thread, THREAD_SCHED_RR_INFO,
					buf, &count);
		if((ret == KERN_SUCCESS) && (ki->curpri < (((struct policy_rr_info *)buf)->base_priority)))
			ki->curpri  = ((struct policy_rr_info *)buf)->base_priority;
		break;
	}
	return(ret);
}