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.