ftp.nice.ch/pub/next/unix/admin/top.0.5.NI.b.tar.gz#/top-v0.5/machine/m_aux31.c

This is m_aux31.c in view mode; [Download] [Up]

/*
 * top - a top users display for Unix
 *
 * SYNOPSIS:  a Mac running A/UX version 3.1
 *
 * DESCRIPTION:
 * This is the machine-dependent module for A/UX 3.1.
 * It might work on A/UX 3.0.
 * ==
 * Although AUX does not generally have a renice systemcall, it can be
 * implemented by tweeking kernel memory.  While such a simple hack should
 * not be difficult to get right, USE THIS FEATURE AT YOUR OWN RISK!
 * To turn on setpriority emulation, add "-DIMPLEMENT_SETPRIORITY" to
 * the CFLAGS when prompted in the configure script.
 *
 * CFLAGS: -Dclear=clear_scr -DPRIO_PROCESS=0
 *
 * LIBS:
 *
 * AUTHOR:  Richard Henderson <richard@atheist.tamu.edu>
 */


#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <a.out.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/sysinfo.h>
#include <sys/var.h>

#define FSCALE	65536.0

#include "top.h"
#include "machine.h"
#include "loadavg.h"

/*=NLIST INFO===========================================================*/

#define X_V		0
#define X_SYSINFO	1
#define X_AVENRUN	2
#define X_MAXMEM	3
#define X_FREEMEM	4
#define X_AVAILRMEM	5
#define X_AVAILSMEM	6

static struct nlist nlst[] = {
    {"v"},
    {"sysinfo"},
    {"avenrun"},
    {"maxmem"},
    {"freemem"},
    {0},		/* "availrmem" */
    {0},		/* "availsmem" */
    {0}
};

static int kmem;
static int mem;

static struct var v;
static int maxmem;

#define V_OFS		(nlst[X_V].n_value)
#define SYSINFO_OFS	(nlst[X_SYSINFO].n_value)
#define AVENRUN_OFS	(nlst[X_AVENRUN].n_value)
#define MAXMEM_OFS	(nlst[X_MAXMEM].n_value)
#define FREEMEM_OFS	(nlst[X_FREEMEM].n_value)
#define AVAILRMEM_OFS	(nlst[X_AVAILRMEM].n_value)
#define AVAILSMEM_OFS	(nlst[X_AVAILSMEM].n_value)

/*=SYSTEM STATE INFO====================================================*/

/* these are for calculating cpu state percentages */

static long cp_time[NCPUSTATES];
static long cp_old[NCPUSTATES];
static long cp_diff[NCPUSTATES];

/* these are for keeping track of the proc array */

static struct proc *pbase;	/* the current proc structures */
static struct proc *obase;	/* the old proc structures */

static struct proc **pref;	/* list of active structures */
static struct proc **nextactive; /* for iterating through the processes */

/* these are for passing data back to the mach. ind. portion */

static int cpu_states[NCPUSTATES];
static int process_states[8];
static int memory_stats[5];

/* a few useful macros... */

#define pagetok(pg)		((pg) << (v.v_pageshift - LOG1024))
#define PROCSIZE(pp)		((pp)->p_size)

#define proc_name(pp)		((char *)&(pp)->p_compatflags)
#define percent_cpu(pp)		(*(double *)&(pp)->p_spare[0])
#define weighted_cpu(pp)	(*(double *)&(pp)->p_spare[2])

/*=STATE IDENT STRINGS==================================================*/

static char *state_abbrev[] =
{
    "", "sleep", "run", "zomb", "stop", "start", "cpu", "swap",
    NULL
};

static char *procstatenames[] =
{
    "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
    " starting, ", " on cpu, ", " swapping, ",
    NULL
};

static char *cpustatenames[] =
{
    "idle", "user", "kernel", "wait", "nice",
    NULL
};

static char *memorynames[] = 
{
    "K real, ", "K free, ", "K free swap, ", "K locked",
    NULL
};

/*======================================================================*/

machine_init(statics)
    struct statics *statics;
{
    /* access kernel memory */
#ifndef IMPLEMENT_SETPRIORITY
    if ((kmem = open("/dev/kmem", O_RDONLY)) < 0)
#else
    if ((kmem = open("/dev/kmem", O_RDWR)) < 0 &&
	(kmem = open("/dev/kmem", O_RDONLY)) < 0)
#endif
    {
	perror("/dev/kmem");
	return -1;
    }
    if ((mem = open("/dev/mem", O_RDONLY)) < 0)
    {
	perror("/dev/mem");
	return -1;
    }

    /* get the list of symbols we want to access in the kernel */
    nlst[X_AVAILRMEM].n_nptr = "availrmem";
    nlst[X_AVAILSMEM].n_nptr = "availsmem";

    if (nlist("/unix", nlst) < 0)
    {
	fprintf(stderr, "top: nlist failed\n");
	return -1;
    }

    /* make sure they were all found */
    if (check_nlist(nlst) > 0)
    {
	return (-1);
    }

    /* grab the kernel configuration information */
    (void)getkval(V_OFS, (char *)&v, sizeof(v), "v");
    (void)getkval(MAXMEM_OFS, (char *)&maxmem, sizeof(maxmem), "maxmem");

    /* allocate space for process related info */
    pbase = (struct proc *)calloc(v.v_proc, sizeof(struct proc));
    obase = (struct proc *)calloc(v.v_proc, sizeof(struct proc));
    pref = (struct proc **)calloc(v.v_proc, sizeof(struct proc *));

    /* Just in case ... */
    if (!pbase || !obase || !pref)
    {
	fprintf(stderr, "top: can't allocate sufficient memory\n");
	return -1;
    }

    /* fill in the statics information */
    statics->procstate_names = procstatenames;
    statics->cpustate_names = cpustatenames;
    statics->memory_names = memorynames;

    /* all done! */
    return 0;
}

get_system_info(info)
    struct system_info *info;
{
    /* convert load averages */
    {
	load_avg ar[3];

	(void)getkval(AVENRUN_OFS, (char *)&ar, sizeof(ar), "avenrun");

	/* convert load averages to doubles */
	info->load_avg[0] = loaddouble(ar[0]);
	info->load_avg[1] = loaddouble(ar[1]);
	info->load_avg[2] = loaddouble(ar[2]);
    }

    /* get cpu time counts */
    {
	struct sysinfo si;

	(void)getkval(SYSINFO_OFS, (char *)&si, sizeof(si), "sysinfo");

	memcpy(cp_time, si.cpu, sizeof(cp_time));
	percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
    }

    /* get memory usage information */
    {
	int freemem, availrmem, availsmem;

	(void)getkval(FREEMEM_OFS, (char *)&freemem, sizeof(freemem),
		      "freemem");
	(void)getkval(AVAILRMEM_OFS, (char *)&availrmem, sizeof(availrmem),
		      "availrmem");
	(void)getkval(AVAILSMEM_OFS, (char *)&availsmem, sizeof(availsmem),
		      "availsmem");

	memory_stats[0] = pagetok(availrmem - freemem);
	memory_stats[1] = pagetok(freemem);
	memory_stats[2] = pagetok(availsmem - availrmem);
	memory_stats[3] = pagetok(maxmem - availrmem);
    }

    info->last_pid = -1;

    /* set arrays and strings */
    info->cpustates = cpu_states;
    info->memory = memory_stats;
}

get_process_info(si, sel, compare)
     struct system_info *si;
     struct process_select *sel;
     int (*compare)();
{
    int i, total_procs, active_procs;
    struct proc *pp1, *pp2, **a;
    static struct timeval lasttime;
    struct timeval thistime;
    double timediff, alpha, beta;

    /* these are copied out of sel for speed */
    int show_idle, show_system, show_uid, show_command;

    /* calculate the time difference since our last check */
    gettimeofday(&thistime);
    if (lasttime.tv_sec)
	timediff = ((thistime.tv_sec - lasttime.tv_sec) +
		    (thistime.tv_usec - lasttime.tv_usec) * 1e-6);
    else
	timediff = 1e9;
    lasttime = thistime;

    /* calculate constants for the exponental average */
    if (timediff < 30.0)
    {
	alpha = 0.5 * (timediff / 30.0);
	beta = 1.0 - alpha;
    }
    else
	alpha = beta = 0.5;

    /* read all the proc structures in one fell swoop */
    {
	struct proc *tmp = obase;
	obase = pbase;
	pbase = tmp;
	(void)getkval((long)v.ve_proctab, (char *)tmp,
		      sizeof(struct proc)*v.v_proc, "proc array");
    }

    /* get a pointer to the states summary array */
    si->procstates = process_states;

    /* set up flags which define what we are going to select */
    show_idle = sel->idle;
    show_system = sel->system;
    show_uid = sel->uid != -1;
    show_command = sel->command != NULL;

    /* count up process states and get pointers to interesting procs */
    total_procs = active_procs = 0;
    memset(process_states, 0, sizeof(process_states));

    pp1 = pbase; pp2 = obase; a = pref;

    for (i = 0; i < v.v_proc; i++, pp1++, pp2++)
    {
	/*
	 *  Place pointers to each valid proc structure in pref[].
	 *  Process slots that are actually in use have a non-zero
	 *  status field.  Processes with SSYS set are system
	 *  processes---these get ignored unless show_sysprocs is set.
	 */
	int state = pp1->p_stat;
	int flag = pp1->p_flag;
	if (state != 0 && (show_system || (flag & SSYS) == 0))
	{
	    struct user u;

	    /* load user struct -- the utime slot in proc is invalid.
	       stow away the two bits we need in parts of the proc
	       struct that we don't need */
	    if (lseek(mem, pp1->p_addr, 0) < 0 ||
		read(mem, &u, sizeof(u)) != sizeof(u))
	    {
		/* no user struct?? */
		pp1->p_utime = pp1->p_stime = 0;
		strncpy(proc_name(pp1), "<???>", COMMSIZ);
	    
		/* calculate relevant metrics */
		percent_cpu(pp1) = 0.0;
		if (pp1->p_pid == pp2->p_pid)
		    weighted_cpu(pp1) = percent_cpu(pp2) * beta;
		else
		    weighted_cpu(pp1) = 0.0;
	    }
	    else
	    {
		/* update the invalid info */
		pp1->p_utime = u.u_utime;
		pp1->p_stime = u.u_stime;

		/* tag swapped processes with brackets */
		u.u_comm[COMMSIZ] = 0;
		if (pp1->p_flag & SLOAD)
		    strcpy(proc_name(pp1), printable(u.u_comm));
		else
		    sprintf(proc_name(pp1), "<%s>", printable(u.u_comm));
		    
		/* calculate relevant metrics */
		if (pp1->p_pid == pp2->p_pid)
		{
		    percent_cpu(pp1) =
			(pp1->p_utime - pp2->p_utime +
			 pp1->p_stime - pp2->p_stime) / (v.v_hz * timediff);
		    weighted_cpu(pp1) = 
			percent_cpu(pp2) * beta + percent_cpu(pp1) * alpha;
		}
		else
		{
		    weighted_cpu(pp1) = percent_cpu(pp1) =
			(pp1->p_utime + pp1->p_stime) / (v.v_hz * timediff);
		}
	    }

	    total_procs++;
	    process_states[state]++;

	    if (state != SZOMB &&
 		/* use the same formula for determining active processes
 		   as the one used by the A/UX load average computation */
		(show_idle || state == SRUN || state == SIDL ||
		 state == SONPROC || ((state == SSLEEP || state == SSTOP) &&
				      (flag & (SINTR | SSYS)) == 0)) &&
		(!show_uid || pp1->p_uid == (uid_t)sel->uid))
	    {
		/* add it to our active list */
		*a++ = pp1;
		active_procs++;
	    }
	}
    }

    /* if requested, sort the "interesting" processes */
    if (compare != NULL)
	qsort((char *)pref, active_procs, sizeof(struct proc *), compare);

    /* remember active and total counts */
    si->p_total = total_procs;
    si->p_active = active_procs;

    /* set up to iterate though processes */
    nextactive = pref;
}


char *
format_header(uname_field)
    char *uname_field;
{
    static char header[132];
    sprintf(header,
    "  PID  PGRP %-8.8s PRI NICE  SIZE STATE   TIME    WCPU     CPU COMMAND",
	    uname_field);
    return header;
}

char *
format_next_process(handle, get_userid)
     caddr_t handle;
     char *(*get_userid)();
{
    static char fmt[128];	/* static area where result is built */
    struct proc *pp = *nextactive++;

    sprintf(fmt,
	    "%5d %5d %-8.8s %3d %4d %5s %-5s %6s %6.2f%% %6.2f%% %.14s",
	    pp->p_pid,
	    pp->p_pgrp,
	    (*get_userid)(pp->p_uid),
	    pp->p_pri - PZERO,
	    pp->p_nice - NZERO,
	    format_k(pagetok(PROCSIZE(pp))),
	    state_abbrev[pp->p_stat],
	    format_time((pp->p_utime + pp->p_stime) / v.v_hz),
	    weighted_cpu(pp) * 100.0,
	    percent_cpu(pp) * 100.0,
	    proc_name(pp));

    /* return the result */
    return (fmt);
}


/*
 * check_nlist(nlst) - checks the nlist to see if any symbols were not
 *              found.  For every symbol that was not found, a one-line
 *              message is printed to stderr.  The routine returns the
 *              number of symbols NOT found.
 */

int
check_nlist(nlst)
     register struct nlist *nlst;
{
    register int i;

    /* check to see if we got ALL the symbols we requested */
    /* this will write one line to stderr for every symbol not found */

    i = 0;
    while (nlst->n_name[0])
    {
	if (nlst->n_value == 0)
	{
	    /* this one wasn't found */
	    fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
	    i = 1;
	}
	nlst++;
    }

    return (i);
}


/*
 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *      "offset" is the byte offset into the kernel for the desired value,
 *      "ptr" points to a buffer into which the value is retrieved,
 *      "size" is the size of the buffer (and the object to retrieve),
 *      "refstr" is a reference string used when printing error meessages,
 *          if "refstr" starts with a '!', then a failure on read will not
 *          be fatal (this may seem like a silly way to do things, but I
 *          really didn't want the overhead of another argument).
 *      
 */

getkval(offset, ptr, size, refstr)
     unsigned long offset;
     int *ptr;
     int size;
     char *refstr;
{
    extern int errno;
    extern char *sys_errlist[];

    if (lseek(kmem, offset, 0) < 0 || read(kmem, ptr, size) != size)
    {
	if (*refstr == '!')
	{
	    return (0);
	}
	else
	{
	    fprintf(stderr, "top: getkval for %s: %s\n",
		    refstr, sys_errlist[errno]);
	    quit(23);
	    /*NOTREACHED */
	}
    }
    return (1);
}

/* comparison routine for qsort */

/*
 *  proc_compare - comparison function for "qsort"
 *      Compares the resource consumption of two processes using five
 *      distinct keys.  The keys (in descending order of importance) are:
 *      percent cpu, cpu ticks, state, resident set size, total virtual
 *      memory usage.  The process states are ordered as follows (from least
 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
 *      array declaration below maps a process state index into a number
 *      that reflects this ordering.
 */

static unsigned char sorted_state[] =
{
    0,	/* not used             */
    3,	/* sleep                */
    6,	/* runable              */
    1,	/* zombie               */
    4,	/* stop                 */
    5,	/* start                */
    7,	/* running              */
    2,	/* swapping             */
};

proc_compare(pp1, pp2)
    struct proc **pp1, **pp2;
{
    struct proc *p1, *p2;
    int result;
    double dresult;

    /* remove one level of indirection */
    p1 = *pp1;
    p2 = *pp2;

    /* compare percent cpu */
    dresult = percent_cpu(p2) - percent_cpu(p1);
    if (dresult != 0.0)
	return (dresult > 0.0 ? 1 : -1);

    /* compare cpu scheduling ticks */
    if ((result = p2->p_cpu - p1->p_cpu) == 0)
    {
	/* use resident time to break the tie */
	if ((result = p1->p_time - p2->p_time) == 0)
	{
	    /* use process state to break the tie */
	    if ((result = (sorted_state[p2->p_stat] -
			   sorted_state[p1->p_stat])) == 0)
	    {
		/* use priority to break the tie */
		if ((result = p2->p_pri - p1->p_pri) == 0)
		{
		    /* use total memory to break the tie */
		    result = PROCSIZE(p2) - PROCSIZE(p1);
		}
	    }
	}
    }

    return (result);
}

/*
 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
 *              the process does not exist.
 *              It is EXTREMLY IMPORTANT that this function work correctly.
 *              If top runs setuid root (as in SVR4), then this function
 *              is the only thing that stands in the way of a serious
 *              security problem.  It validates requests for the "kill"
 *              and "renice" commands.
 */

int
proc_owner(pid)
    int pid;
{
    struct proc *pp;
    int i;

    for (pp = pbase, i = 0; i < v.v_proc; pp++, i++)
	if (pp->p_stat != 0 && pp->p_pid == pid)
	    return pp->p_uid;

    return -1;
}

/* 
 * setpriority(int which, pid_t pid, int val)
 * This system does not have this system call -- fake it
 */

int
setpriority(which, pid, val)
    int which, pid, val;
{
#ifndef IMPLEMENT_SETPRIORITY
    errno = ENOSYS;
    return -1;
#else
    int ofs, uid;
    struct proc *pp;

    /* sanity check arguments */
    val += NZERO;
    if (val < 0)
	val = 0;
    else if (val > 39)
	val = 39;

    /* locate the process */
    for (ofs = 0, pp = pbase; ofs < v.v_proc; ofs++, pp++)
	if (pp->p_stat != 0 && pp->p_pid == pid)
	    break;
    if (ofs == v.v_proc)
    {
	errno = ESRCH;
	return -1;
    }

    /* make sure we don't allow nasty people to do nasty things */
    uid = getuid();
    if (uid != 0)
    {
	if (uid != pp->p_uid || val < pp->p_nice)
	{
	    errno = EACCES;
	    return -1;
	}
    }

    /* renice */
    pp->p_nice = val;
    if (lseek(kmem, v.ve_proctab+((char*)&pp->p_nice-(char*)pbase), 0) < 0 ||
	write(kmem, &pp->p_nice, sizeof(pp->p_nice)) != sizeof(pp->p_nice))
    {
	return -1;
    }

    return 0;
#endif
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.