Back about mid-January I posted a query about running top under Solaris
2.1/SunOS 5.1. I wasn't able to get it working initially but I have it
working now and thought I'd share what I did. Sorry for the lateness
and hope this info is still worthwhile.
First I'd like to thank Robert Boucher for writing m_svr4.c and
David J. MacKenzie who sent me a copy of it.
To build top I started with the top 3.0 distribution and Robert's
m_svr4.c (a copy is included below). I used the unbundled Sun C.
To get it to build I had to:
o remove /usr/ucb from my path so I wouldn't get Berkeley
compatibilty stuff when I compiled.
o run configure and choose "svr4" as the system type.
o edit the Makefile so the "CDEFS" and "LIBS" lines look like:
CDEFS = -O -DPRIO_MIN=-20 -DPRIO_MAX=20
LIBS = -lelf -lc -L/usr/ucblib -lucb
o install it as setuid root so it can read stuff in /proc.
-Marc
-----
m_svr4.c:
/*
* top - a top users display for Unix
*
* SYNOPSIS: For System V Release 4
*
* DESCRIPTION:
*
* The following must be added to CFLAGS in the Makefile:
* [From <ucbinclude/sys/resource.h>]
* -DPRIO_MIN=-20 -DPRIO_MAX=20
*
* NOTES: The memory sizes displayed for every process is greater by
* a factor of 2 from DELL's version. The original port was using the
* pagesize (bytes per click) from the UCB compatibility package (2048
* bytes) instead of the native pagesize (4096 bytes). To anybody who
* can read the source, is this correct? I've somewhat confirmed it by
* summing the in-core size of all processes and comparing the results to
* vmtotal->t_rm.
*
* SECURITY: The end of this module contains the patch to commands.c and
* top.c to add some sort of security. Apply the patches, add -DSECURITY_OPTION
* to CFLAGS and recompile. This patch is essentialy a verbatim port of
* DELL's security mechanism from top 2.1.
*
* LIBS: -lelf -lc -lucb
*
* AUTHOR: Robert Boucher <boucher@ncs.dnd.ca>
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/fault.h>
#include <sys/sysinfo.h>
#include <sys/sysmacros.h>
#include <sys/syscall.h>
#include <dirent.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/procfs.h>
#include <sys/vm.h>
#include <sys/var.h>
#include <sys/file.h>
#include <dirent.h>
#include <stdio.h>
#include <nlist.h>
#include <fcntl.h>
#include <math.h>
#include "top.h"
#include "machine.h"
#ifdef sun
#define sysinfo cpu_sysinfo
#endif
#ifndef FSCALE
/*
* [From /usr/ucbinclude/sys/param.h]
* Scale factor for scaled integers used to count
* %cpu time and load averages.
*/
#define FSHIFT 8 /* bits to right of fixed binary point */
#define FSCALE (1<<FSHIFT)
#endif /* FSCALE */
/* [This is from <sys/vm/anon.h>, which run-time SVR4's don't have] */
struct anoninfo
{
u_int ani_max; /* maximum anon pages available */
u_int ani_free; /* number of anon pages currently free */
u_int ani_resv; /* number of anon pages reserved */
};
/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
struct prpsinfo **next_proc;/* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
#define CPUSTATES 5
/* declarations for load_avg */
typedef long load_avg;
#define loaddouble(la) ((double)(la) / FSCALE)
/* define what weighted cpu is. */
#define percent_cpu(pp) ((double)(pp)->pr_cpu / FSCALE)
#define weighted_cpu(pct, pp) ( ((pp)->pr_time.tv_sec) == 0 ? 0.0 : \
((pp)->pr_cpu) / ((pp)->pr_time.tv_sec) )
#ifdef sun
#undef ctob
#define ctob(x) ((x) << 12)
#endif
/* Convert clicks (kernel pages) to kbytes ... */
#define pagetok(size) ctob(size) >> LOG1024
/* definitions for indices in the nlist array */
#define X_AVENRUN 0
#define X_MPID 1
#define X_SYSINFO 2
#define X_V 3
#define X_NPROC 4
#define X_ANONINFO 5
#define X_TOTAL 6
static struct nlist nlst[] = {
{ "avenrun" }, /* 0 */
{ "mpid" }, /* 1 */
{ "sysinfo" }, /* 2 */
{ "v" }, /* 3 */
{ "nproc" }, /* 4 */
{ "anoninfo"}, /* 5 */
#ifndef sun
{ "total" }, /* 6 */
#endif
{ 0 }
};
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %3d.0%% %5.2f%% %.16s"
/* process state names for the "STATE" column of the display */
/* the extra nulls in the string "run" are for adding a slash and
the processor number when needed */
char *state_abbrev[] =
{
"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"
};
#ifdef sun
#define UNIX "/kernel/unix"
#else
#define UNIX "/stand/unix"
#endif
#define KMEM "/dev/kmem"
#define PROCFS "/proc"
static int kmem = -1;
static int mem = -1;
#ifndef sun
struct vmtotal total;
#endif
struct anoninfo anoninfo;
/* these are retrieved from the kernel in _init */
static int nproc;
/* these are offsets obtained via nlist and used in the get_ functions */
static unsigned long mpid_offset;
static unsigned long nproc_offset;
static unsigned long avenrun_offset;
static unsigned long total_offset;
static unsigned long sysinfo_offset;
static unsigned long anoninfo_offset;
/* these are for calculating cpu state percentages */
static long cp_time[CPUSTATES];
static long cp_old[CPUSTATES];
static long cp_diff[CPUSTATES];
/* these are for detailing the process states */
int process_states[8];
char *procstatenames[] = {
"", " sleeping, ", " running, ", " zombie, ", " stopped, ",
" starting, ", " on cpu, ", " swapped, ",
NULL
};
/* these are for detailing the cpu states */
int cpu_states[CPUSTATES];
char *cpustatenames[] = {
"idle", "user", "kernel", "wait", "swap",
NULL
};
/* these are for detailing the memory statistics */
int memory_stats[5];
char *memorynames[] = {
"K real, ", "K active, ", "K free, ", "K swap, " , "K free swap", NULL
};
/* these are for keeping track of the proc array */
static int bytes;
static struct prpsinfo *pbase;
static struct prpsinfo **pref;
/* useful externals */
extern int errno;
extern char *sys_errlist[];
extern char *myname;
long lseek();
long percentages();
machine_init(statics)
struct statics *statics;
{
register int i;
static struct var v;
/* open kernel memory */
if ((kmem = open(KMEM, 0)) < 0)
{
perror(KMEM);
exit(20);
}
/* get the list of symbols we want to access in the kernel */
if ((i = nlist(UNIX, nlst)) < 0)
{
fprintf(stderr, "%s: nlist failed\n",myname);
return(-1);
}
/* make sure they were all found */
if (i > 0 && check_nlist(nlst) > 0)
{
return(-1);
}
/* get the symbol values out of kmem */
/* NPROC Tuning parameter for max number of processes */
(void) getkval(nlst[X_V].n_value, &v, sizeof(struct var), nlst[X_V].n_name);
nproc = v.v_proc;
/* stash away certain offsets for later use */
mpid_offset = nlst[X_MPID].n_value;
nproc_offset = nlst[X_NPROC].n_value;
avenrun_offset = nlst[X_AVENRUN].n_value;
anoninfo_offset = nlst[X_ANONINFO].n_value;
#ifndef sun
total_offset = nlst[X_TOTAL].n_value;
#else
total_offset = 0;
#endif
sysinfo_offset = nlst[X_SYSINFO].n_value;
/* allocate space for proc structure array and array of pointers */
bytes = nproc * sizeof(struct prpsinfo);
pbase = (struct prpsinfo *)malloc(bytes);
pref = (struct prpsinfo **)malloc(nproc * sizeof(struct prpsinfo *));
/* Just in case ... */
if (pbase == (struct prpsinfo *)NULL || pref == (struct prpsinfo **)NULL)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
return(-1);
}
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
/* all done! */
return(0);
}
char *format_header(uname_field)
register char *uname_field;
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
{
*ptr++ = *uname_field++;
}
return(header);
}
get_system_info(si)
struct system_info *si;
{
load_avg avenrun[3];
struct sysinfo sysinfo;
register int j;
/* get the cp_time array */
for (j = 0; j < CPUSTATES; j++) cp_time[j] = 0L;
(void) getkval(sysinfo_offset, &sysinfo, sizeof(struct sysinfo), "sysinfo");
for (j = 0; j < CPUSTATES; j++) cp_time[j] += (long)sysinfo.cpu[j];
/* get load average array */
(void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
"avenrun");
/* get mpid -- process id of last process */
(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
"mpid");
/* convert load averages to doubles */
{
register int i;
register double *infoloadp;
register load_avg *sysloadp;
infoloadp = si->load_avg;
sysloadp = avenrun;
for (i = 0; i < 3; i++)
{
*infoloadp++ = loaddouble(*sysloadp++);
}
}
/* convert cp_time counts to percentages */
(void) percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
#ifndef sun
/* get total -- systemwide main memory usage structure */
(void) getkval(total_offset, (int *)(&total), sizeof(total), "total");
/* convert memory stats to Kbytes */
memory_stats[0] = pagetok(total.t_rm);
memory_stats[1] = pagetok(total.t_arm);
memory_stats[2] = pagetok(total.t_free);
#else
memory_stats[0] = 0;
memory_stats[1] = 0;
memory_stats[2] = 0;
#endif
(void) getkval(anoninfo_offset, (int *)(&anoninfo), sizeof(anoninfo),
"anoninfo");
memory_stats[3] = pagetok(anoninfo.ani_max - anoninfo.ani_free);
memory_stats[4] = pagetok(anoninfo.ani_max - anoninfo.ani_resv);
/* set arrays and strings */
si->cpustates = cpu_states;
si->memory = memory_stats;
}
static struct handle handle;
caddr_t get_process_info(si, sel, compare)
struct system_info *si;
struct process_select *sel;
int (*compare)();
{
register int i;
register int total_procs;
register int active_procs;
register struct prpsinfo **prefp;
register struct prpsinfo *pp;
/* these are copied out of sel for speed */
int show_idle;
int show_system;
int show_uid;
/* Get current number of processes */
(void) getkval(nproc_offset, (int *)(&nproc), sizeof(nproc), "nproc");
/* read all the proc structures */
getptable(pbase);
/* 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;
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
bzero((char *)process_states, sizeof(process_states));
prefp = pref;
for (pp = pbase, i = 0; i < nproc; pp++, i++)
{
/*
* 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.
*/
if (pp->pr_state != 0 &&
(show_system || ((pp->pr_flag & SSYS) == 0)))
{
total_procs++;
process_states[pp->pr_state]++;
if ( (!pp->pr_zomb) &&
(show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC) ) &&
(!show_uid || pp->pr_uid == (uid_t)sel->uid))
{
*prefp++ = pp;
active_procs++;
}
}
}
/* if requested, sort the "interesting" processes */
if (compare != NULL)
{
qsort((char *)pref, active_procs, sizeof(struct prpsinfo *), compare);
}
/* remember active and total counts */
si->p_total = total_procs;
si->p_active = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return((caddr_t)&handle);
}
char fmt[128]; /* static area where result is built */
char *format_next_process(handle, get_userid)
caddr_t handle;
char *(*get_userid)();
{
register struct prpsinfo *pp;
struct handle *hp;
register long cputime;
register double pctcpu;
/* find and remember the next proc structure */
hp = (struct handle *)handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* get the cpu usage and calculate the cpu percentages */
cputime = pp->pr_time.tv_sec;
pctcpu = percent_cpu(pp);
/* format this entry */
sprintf(fmt,
Proc_format,
pp->pr_pid,
(*get_userid)(pp->pr_uid),
pp->pr_pri - PZERO,
pp->pr_nice - NZERO,
pagetok(pp->pr_size),
pagetok(pp->pr_rssize),
state_abbrev[pp->pr_state],
cputime / 60l,
cputime % 60l,
(pp->pr_cpu & 0377),
100.0 * pctcpu,
pp->pr_fname);
/* pp->pr_fname ); */
/* 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 != NULL)
{
if (nlst->n_type == 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;
{
if (lseek(kmem, (long)offset, 0) == -1)
{
if (*refstr == '!')
{
refstr++;
}
fprintf(stderr, "%s: lseek to %s: %s\n",
KMEM, refstr, sys_errlist[errno]);
quit(22);
}
if (read(kmem, (char *)ptr, size) == -1)
{
if (*refstr == '!')
{
/* we lost the race with the kernel, process isn't in memory */
return(0);
}
else
{
fprintf(stderr, "%s: reading %s: %s\n",
KMEM, refstr, sys_errlist[errno]);
quit(23);
}
}
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.
*/
unsigned char sorted_state[] =
{
0, /* not used */
3, /* sleep */
6, /* run */
2, /* zombie */
4, /* stop */
5, /* start */
7, /* run on a processor */
1 /* being swapped (WAIT) */
};
proc_compare(pp1, pp2)
struct prpsinfo **pp1;
struct prpsinfo **pp2;
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/* compare percent cpu (pctcpu) */
if ((result = (long)(p2->pr_cpu - p1->pr_cpu)) == 0)
{
/* use cpticks to break the tie */
if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec ) == 0)
{
/* use process state to break the tie */
if ((result = (long) (sorted_state[p2->pr_state] -
sorted_state[p1->pr_state])) == 0)
{
/* use priority to break the tie */
if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
{
/* use resident set size (rssize) to break the tie */
if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
{
/* use total memory to break the tie */
result = (p2->pr_size - p1->pr_size);
}
}
}
}
}
return(result);
}
/*
get process table
V.4 only has a linked list of processes so we want to follow that
linked list, get all the process structures, and put them in our own
table
*/
getptable(baseptr)
struct prpsinfo *baseptr;
{
struct prpsinfo *currproc; /* pointer to current proc structure */
int numprocs=0;
DIR *procdir;
struct dirent *direntp;
int fd;
char fname[40];
if ( (procdir = opendir(PROCFS)) == NULL ) {
fprintf(stderr,"%s: Unable to open %s\n", myname, PROCFS);
exit(1);
}
while (((direntp = readdir(procdir))!=NULL) && (numprocs<nproc)) {
if( (!strcmp(direntp->d_name,"."))||(!strcmp(direntp->d_name,"..")) ) {
continue;
}
strcpy(fname,PROCFS);
strcat(fname,"/");
strcat(fname,direntp->d_name);
if ( (fd = open(fname, O_RDONLY)) < 0 ) {
/* fprintf(stderr,"%s: Unable to open %s\n",myname, direntp->d_name); */
close(fd);
continue;
}
currproc = &baseptr[numprocs];
if ( ioctl(fd, PIOCPSINFO, currproc) < 0 ) {
/* fprintf(stderr,"%s: Unable to get info from %s\n", myname, direntp->d_name); */
close(fd);
continue;
}
numprocs++;
close(fd);
}
closedir(procdir);
if (nproc!=numprocs) nproc=numprocs;
}
#ifdef SECURITY_OPTION
#include <sys/resource.h> /* for PRIO_PROCESS */
#define ROOT_ID 0
#define NICE_VALUE 10
#define TRUE 1
#define FALSE 0
int not_su;
int real_uid;
/* if su return ok, else check to see if it's our process */
/* if it's ours ok, else -1 */
check_secure(procnum)
int procnum;
{
register struct prpsinfo *p;
register int i;
if (not_su) {
for (i=0,p=pbase;i<nproc;i++,p++)
if ( p->pr_pid == procnum )
return( (p->pr_uid == real_uid) ? 0 : -1);
} else return(0);
}
/* find out if we're really the superuser or just pretending */
init_secure()
{
/* get real uid and find out if it's really root */
real_uid=getuid();
not_su = (real_uid != ROOT_ID) ? TRUE : FALSE;
}
nice_us()
{
setpriority(PRIO_PROCESS, getpid(), NICE_VALUE);
}
#endif /* SECURITY_OPTION */
/* Begin of SECURITY PATCH
diff orig/commands.c secure/commands.c
23a24
> #include <sys/errno.h>
392a394,397
> else if (check_secure(procnum) == -1)
> {
> ERROR(str, EPERM);
> }
453a459,462
> }
> else if (check_secure(procnum) == -1)
> {
> ERROR(str, EPERM);
diff orig/top.c secure/top.c
309a310,313
> init_secure();
>
> nice_us();
>
End of SECURITY PATCH */
This archive was generated by hypermail 2.1.2 : Fri Sep 28 2001 - 23:07:33 CDT