/*
 * ps.c - a program to print MiNT process statuses
 *
 * Based on the original ps by Tony Reynolds, cctony@sgisci1.ocis.olemiss.edu,
 * and updated by Eric Smith and Allan Pratt.
 * For use with MiNT release 0.9.
 * Compiles with my port of the MiNTlib to Sozobon 2.0, as well as the GCC.
 *
 * $Log: ps.c,v $
 * Revision 0.9  1992/01/15  21:48:22  sozobon
 * dpg's first version.
 * Uses opendir rather than Fsfirst/next.
 * Print owner information, rather than parent process id.
 *
 */

static char *proc =
"$Id: ps.c,v 0.9 1992/01/15 21:48:22 sozobon Exp sozobon $";

#include <basepage.h>
#include <dirent.h>
#include <errno.h>
#include <ioctl.h>
#include <pwd.h>
#include <stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PROCDIR "u:/proc"

#define ulong unsigned long
#define ushort unsigned short

extern int __mint;

/* The following structures are taken from proc.h in the MiNT kernel. */

/* a process context consists, for now, of its registers */

typedef struct _context {
	long	regs[15];	/* registers d0-d7, a0-a6 */
	long	usp;		/* user stack pointer (a7) */
	short	sr;		/* status register */
	long	pc;		/* program counter */
	long	ssp;		/* supervisor stack pointer */
	long	term_vec;	/* GEMDOS terminate vector (0x102) */
	char	fstate[216];	/* FPU internal state */
	char	fregs[12*8];	/* registers fp0-fp7 */
	long	fctrl[3];		/* FPCR/FPSR/FPIAR */
	short	sfmt;			/* stack frame format identifier */
	long	iar;			/* instruction address */
	short	internal[4];	/* four words of internal info */
} CONTEXT;

#define PROC_CTXTS	2

typedef struct proc {
/* this is stuff that the public can know about */
/* NOTE: some assembly code (in intr.s and contxt.s) makes
 * assumptions about the offsets of sysstack, ctxt[0], systime,
 * and usrtime.
 */
	long	sysstack;		/* must be first		*/
	CONTEXT	ctxt[PROC_CTXTS];	/* must be second		*/

	long	magic;			/* validation for proc struct	*/

	char	*base;			/* process base page		*/
	short	pid, ppid, pgrp;
	short	ruid;			/* process real user id 	*/
	short	rgid;			/* process real group id 	*/
	short	euid, egid;		/* effective user and group ids */

	ushort	memflags;		/* e.g. malloc from alternate ram */
	short	pri;			/* base process priority 	*/
	short	wait_q;			/* current process queue	*/
	long	wait_cond;		/* condition we're waiting on	*/
					/* (also return code from wait) */

	/* (all times are in milliseconds) */
	ulong	systime;		/* time spent in kernel		*/
	ulong	usrtime;		/* time spent out of kernel	*/
	ulong	chldstime;		/* children's kernel time 	*/
	ulong	chldutime;		/* children's user time		*/

	ulong	maxmem;			/* max. amount of memory to use */
	ulong	maxdata;		/* max. data region for process */
	ulong	maxcore;		/* max. core memory for process */
	ulong	maxcpu;			/* max. cpu time to use 	*/

	short	domain;			/* process domain (TOS or UNIX)	*/

	short	curpri;			/* current process priority	*/
#define MIN_NICE -20
#define MAX_NICE 20
} PROC;

/* different process queues */

#define CURPROC_Q	0
#define READY_Q		1
#define WAIT_Q		2
#define IO_Q		3
#define ZOMBIE_Q	4
#define TSR_Q		5
#define STOP_Q		6
#define SELECT_Q	7

#define NUM_QUEUES	8

struct status {
    char mint;
    char desc;
}   proc_stat[NUM_QUEUES + 1] = {
    CURPROC_Q,	'R',
    READY_Q,	'R',
    WAIT_Q,	'S',
    IO_Q,	'S',
    SELECT_Q,	'S',
    STOP_Q,	'T',
    ZOMBIE_Q,	'Z',
    TSR_Q,	'X',
    -1, '?'
};				/* initialized from data in procfs.c */

void
main(argc, argv)
    int argc;
    char **argv;
{
    DIR *dirp;
    struct dirent *ent;
    struct stat info;
    int fd;
    int fserror;
    long place;
    short pid,
        ppid,
        pri,
        curpri;
    char *s,
	username[9];
    struct status *statp;
    BASEPAGE bpage;		/* process basepage read in here if possible */
    PROC proc;			/* process info read in here */
    long ptime;
    long hour,
        min,
        sec,
        frac;
    struct passwd *pwent;

    if (chdir(PROCDIR) || !(dirp = opendir("."))) {
	perror(PROCDIR);
	return 1;
    }

    printf("USER     PID PRI/CUR STAT SIZE   TIME   COMMAND\n");
    username[8] = '\0';
    
    while (ent = readdir(dirp)) {
	if ((fd = open(ent->d_name, 0)) < 0 || fstat(fd, &info))
	    goto next;

	/* Should really copy the name elsewhere. */
	for (s = ent->d_name; *s && *s != '.'; s++);
	*s = '\0';

	ioctl(fd, PPROCADDR, &place);
	lseek(fd, place, 0);
	read(fd, &proc, sizeof(proc));

	if (pwent = getpwuid(proc.ruid))
	    strncpy(username, pwent->pw_name, 8);
	else
	    strcpy(username, "??user??");

	ptime = proc.systime + proc.usrtime;
	hour = (ptime / 1000 / 60 / 60);
	min = (ptime / 1000 / 60) % 60;
	sec = (ptime / 1000) % 60;
	frac = (ptime % 1000) / 10;	/* (never anything in .00x digit) */

	ioctl(fd, PBASEADDR, &place);
	lseek(fd, place, 0);
	read(fd, &bpage, sizeof(bpage));
	if (*bpage.p_cmdlin)
	    *bpage.p_cmdlin = ' ';
	close(fd);

	statp = proc_stat;	/* hunt down string referring to process
				 * status */
	while (statp->mint != proc.wait_q &&
	       statp->mint >= 0)
	    statp++;

	printf("%-8s %03d %3d/%3d %c %c  ", username, proc.pid,
	       proc.pri, proc.curpri, statp->desc,
	       proc.pri < 0 ? 'N' : proc.pri > 0 ? '<' : ' ');
	if (info.st_size > 1000000L)
	    printf("%3ldM ", info.st_size / 1000000L);
	else if (info.st_size > 1000L)
	    printf("%3ldk ", info.st_size / 1000L);
	else
	    printf("%3ld  ", info.st_size);
	if (hour)
	    printf("%02ld:%02ld:%02ld", hour, min, sec);
	else
	    printf("%02ld:%02ld.%02ld", min, sec, frac);
	printf(" %s%s\n", ent->d_name, bpage.p_cmdlin);
next:
    }
    closedir(dirp);
    endpwent();
    return 0;
}
