1/*
2 * Copyright (c) 1984 through 2008, William LeFebvre
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *
16 *     * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * top - a top users display for Unix
35 *
36 * SYNOPSIS:  any hp9000 running hpux version 7 or earlier
37 *
38 * DESCRIPTION:
39 * This is the machine-dependent module for Hpux 6.5 and 7.0.
40 * This makes top work on the following systems:
41 *	hp9000s300
42 *	hp9000s700
43 *	hp9000s800
44 *
45 * LIBS:
46 *
47 * AUTHOR:  Christos Zoulas <christos@ee.cornell.edu>
48 */
49
50#include "config.h"
51#include <sys/types.h>
52#include <sys/signal.h>
53#include <sys/param.h>
54
55#include <stdio.h>
56#include <nlist.h>
57#include <math.h>
58#include <sys/dir.h>
59#include <sys/user.h>
60#include <sys/proc.h>
61#include <sys/dk.h>
62#include <sys/vm.h>
63#include <sys/file.h>
64#include <sys/time.h>
65
66#include "top.h"
67#include "machine.h"
68#include "utils.h"
69
70#define VMUNIX	"/hp-ux"
71#define KMEM	"/dev/kmem"
72#define MEM	"/dev/mem"
73#ifdef DOSWAP
74#define SWAP	"/dev/swap"
75#endif
76
77/* get_process_info passes back a handle.  This is what it looks like: */
78
79struct handle
80{
81    struct proc **next_proc;	/* points to next valid proc pointer */
82    int remaining;		/* number of pointers remaining */
83};
84
85/* declarations for load_avg */
86#include "loadavg.h"
87
88/* define what weighted cpu is.  */
89#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
90			 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
91
92/* what we consider to be process size: */
93#define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize)
94
95/* definitions for indices in the nlist array */
96#define X_AVENRUN	0
97#define X_CCPU		1
98#define X_NPROC		2
99#define X_PROC		3
100#define X_TOTAL		4
101#define X_CP_TIME	5
102#ifdef hp9000s300
103# define X_USRPTMAP	6
104# define X_USRPT	7
105#else
106# define X_MPID		6
107# define X_HZ		7
108#endif
109#ifdef hp9000s800
110# define X_NPIDS	8
111# define X_UBASE	9
112#endif
113
114static struct nlist nlst[] = {
115    { "_avenrun" },		/* 0 */
116    { "_ccpu" },		/* 1 */
117    { "_nproc" },		/* 2 */
118    { "_proc" },		/* 3 */
119    { "_total" },		/* 4 */
120    { "_cp_time" },		/* 5 */
121#ifdef hp9000s300
122    { "_Usrptmap" },		/* 6 */
123    { "_usrpt" },		/* 7 */
124#else
125    { "_mpid" },		/* 6 */
126    { "_hz" },			/* 7 */
127#endif
128#ifdef hp9000s800
129    { "_npids" }, 		/* 8 */
130    { "_ubase" },		/* 9 */
131#endif
132    { 0 }
133};
134
135/*
136 *  These definitions control the format of the per-process area
137 */
138
139static char header[] =
140  "  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
141/* 0123456   -- field to fill in starts at header+6 */
142#define UNAME_START 6
143
144#define Proc_format \
145	"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %s"
146
147
148/* process state names for the "STATE" column of the display */
149/* the extra nulls in the string "run" are for adding a slash and
150   the processor number when needed */
151
152char *state_abbrev[] =
153{
154    "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
155};
156
157
158static int kmem, mem;
159#ifdef DOSWAP
160static int swap;
161#endif
162
163/* values that we stash away in _init and use in later routines */
164
165static double logcpu;
166
167/* these are retrieved from the kernel in _init */
168
169static unsigned long proc;
170static          int  nproc;
171static          long hz;
172static load_avg  ccpu;
173static          int  ncpu = 0;
174
175/* these are offsets obtained via nlist and used in the get_ functions */
176
177#ifndef hp9000s300
178static unsigned long mpid_offset;
179#endif
180#ifdef hp9000s300
181static struct pte *Usrptmap, *usrpt;
182#endif
183#ifdef hp9000s800
184static int npids;
185char *ubase;
186#endif
187static unsigned long avenrun_offset;
188static unsigned long total_offset;
189static unsigned long cp_time_offset;
190
191/* these are for calculating cpu state percentages */
192
193static long cp_time[CPUSTATES];
194static long cp_old[CPUSTATES];
195static long cp_diff[CPUSTATES];
196
197/* these are for detailing the process states */
198
199int process_states[7];
200char *procstatenames[] = {
201    "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
202    " zombie, ", " stopped, ",
203    NULL
204};
205
206/* these are for detailing the cpu states */
207
208#ifdef hp9000s300
209int cpu_states[9];
210#endif
211#ifdef hp9000s800
212int cpu_states[5];
213#endif
214char *cpustatenames[] = {
215#ifdef hp9000s300
216    "usr", "nice", "sys", "idle", "", "", "", "intr", "ker",
217#endif
218#ifdef hp9000s800
219    "user", "nice", "system", "idle", "wait",
220#endif
221    NULL
222};
223
224/* these are for detailing the memory statistics */
225
226long memory_stats[8];
227char *memorynames[] = {
228    "Real: ", "K active, ", "K total  ", "Virtual: ", "K active, ",
229    "K total, ", "K free", NULL
230};
231
232/* these are for keeping track of the proc array */
233
234static int bytes;
235static int pref_len;
236static struct proc *pbase;
237static struct proc **pref;
238
239/* these are for getting the memory statistics */
240
241static int pageshift;		/* log base 2 of the pagesize */
242
243/* define pagetok in terms of pageshift */
244
245#define pagetok(size) ((size) << pageshift)
246
247/* useful externals */
248extern int errno;
249extern char *sys_errlist[];
250
251long lseek();
252long time();
253
254machine_init(statics)
255
256struct statics *statics;
257
258{
259    register int i = 0;
260    register int pagesize;
261
262    if ((kmem = open(KMEM, O_RDONLY)) == -1) {
263	perror(KMEM);
264	return(-1);
265    }
266    if ((mem = open(MEM, O_RDONLY)) == -1) {
267	perror(MEM);
268	return(-1);
269    }
270
271#ifdef DOSWAP
272    if ((swap = open(SWAP, O_RDONLY)) == -1) {
273	perror(SWAP);
274	return(-1);
275    }
276#endif
277
278#ifdef hp9000s800
279    /* 800 names don't have leading underscores */
280    for (i = 0; nlst[i].n_name; nlst[i++].n_name++)
281	continue;
282#endif
283
284    /* get the list of symbols we want to access in the kernel */
285    (void) nlist(VMUNIX, nlst);
286    if (nlst[0].n_type == 0)
287    {
288	fprintf(stderr, "top: nlist failed\n");
289	return(-1);
290    }
291
292    /* make sure they were all found */
293    if (i > 0 && check_nlist(nlst) > 0)
294    {
295	return(-1);
296    }
297
298    /* get the symbol values out of kmem */
299    (void) getkval(nlst[X_PROC].n_value,   (int *)(&proc),	sizeof(proc),
300	    nlst[X_PROC].n_name);
301    (void) getkval(nlst[X_NPROC].n_value,  &nproc,		sizeof(nproc),
302	    nlst[X_NPROC].n_name);
303#ifndef hp9000s300
304    (void) getkval(nlst[X_HZ].n_value,     (int *)(&hz),	sizeof(hz),
305	    nlst[X_HZ].n_name);
306#else
307    hz = HZ;
308#endif
309    (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),	sizeof(ccpu),
310	    nlst[X_CCPU].n_name);
311#ifdef  hp9000s800
312    (void) getkval(nlst[X_NPIDS].n_value,   (int *)(&npids),	sizeof(npids),
313	    nlst[X_NPIDS].n_name);
314#endif
315
316    /* stash away certain offsets for later use */
317#ifdef hp9000s800
318# ifndef UAREA
319    ubase = nlst[X_UBASE].n_value;
320# else
321    ubase = UAREA;
322# endif
323#endif
324#ifdef hp9000s300
325    Usrptmap = (struct pte *) nlst[X_USRPTMAP].n_value;
326    usrpt = (struct pte *) nlst[X_USRPT].n_value;
327#endif
328#ifndef hp9000s300
329    mpid_offset = nlst[X_MPID].n_value;
330#endif
331    avenrun_offset = nlst[X_AVENRUN].n_value;
332    total_offset = nlst[X_TOTAL].n_value;
333    cp_time_offset = nlst[X_CP_TIME].n_value;
334
335    /* this is used in calculating WCPU -- calculate it ahead of time */
336    logcpu = log(loaddouble(ccpu));
337
338    /* allocate space for proc structure array and array of pointers */
339    bytes = nproc * sizeof(struct proc);
340    pbase = (struct proc *)malloc(bytes);
341    pref  = (struct proc **)malloc(nproc * sizeof(struct proc *));
342
343    /* Just in case ... */
344    if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
345    {
346	fprintf(stderr, "top: can't allocate sufficient memory\n");
347	return(-1);
348    }
349
350    /* get the page size with "getpagesize" and calculate pageshift from it */
351    pagesize = getpagesize();
352    pageshift = 0;
353    while (pagesize > 1)
354    {
355	pageshift++;
356	pagesize >>= 1;
357    }
358
359    /* we only need the amount of log(2)1024 for our conversion */
360    pageshift -= LOG1024;
361
362    /* fill in the statics information */
363    statics->procstate_names = procstatenames;
364    statics->cpustate_names = cpustatenames;
365    statics->memory_names = memorynames;
366
367    /* all done! */
368    return(0);
369}
370
371char *format_header(uname_field)
372
373register char *uname_field;
374
375{
376    register char *ptr;
377
378    ptr = header + UNAME_START;
379    while (*uname_field != '\0')
380    {
381	*ptr++ = *uname_field++;
382    }
383
384    return(header);
385}
386
387void
388get_system_info(si)
389
390struct system_info *si;
391
392{
393    load_avg avenrun[3];
394    long total;
395
396    /* get the cp_time array */
397    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
398		   "_cp_time");
399
400    /* get load average array */
401    (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
402		   "_avenrun");
403
404#ifndef hp9000s300
405    /* get mpid -- process id of last process */
406    (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
407		   "_mpid");
408#else
409    si->last_pid = -1;
410#endif
411
412    /* convert load averages to doubles */
413    {
414	register int i;
415	register double *infoloadp;
416	register load_avg *sysloadp;
417
418	infoloadp = si->load_avg;
419	sysloadp = avenrun;
420	for (i = 0; i < 3; i++)
421	{
422	    *infoloadp++ = loaddouble(*sysloadp++);
423	}
424    }
425
426    /* convert cp_time counts to percentages */
427    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
428
429    /* sum memory statistics */
430    {
431	struct vmtotal total;
432
433	/* get total -- systemwide main memory usage structure */
434	(void) getkval(total_offset, (int *)(&total), sizeof(total),
435		       "_total");
436	/* convert memory stats to Kbytes */
437	memory_stats[0] = -1;
438	memory_stats[1] = pagetok(total.t_arm);
439	memory_stats[2] = pagetok(total.t_rm);
440	memory_stats[3] = -1;
441	memory_stats[4] = pagetok(total.t_avm);
442	memory_stats[5] = pagetok(total.t_vm);
443	memory_stats[6] = pagetok(total.t_free);
444    }
445
446    /* set arrays and strings */
447    si->cpustates = cpu_states;
448    si->memory = memory_stats;
449}
450
451static struct handle handle;
452
453caddr_t get_process_info(si, sel, i)
454
455struct system_info *si;
456struct process_select *sel;
457int i;
458
459{
460    register int i;
461    register int total_procs;
462    register int active_procs;
463    register struct proc **prefp;
464    register struct proc *pp;
465
466    /* these are copied out of sel for speed */
467    int show_idle;
468    int show_system;
469    int show_uid;
470    int show_command;
471
472    /* read all the proc structures in one fell swoop */
473    (void) getkval(proc, (int *)pbase, bytes, "proc array");
474
475    /* get a pointer to the states summary array */
476    si->procstates = process_states;
477
478    /* set up flags which define what we are going to select */
479    show_idle = sel->idle;
480    show_system = sel->system;
481    show_uid = sel->uid != -1;
482    show_command = sel->command != NULL;
483
484    /* count up process states and get pointers to interesting procs */
485    total_procs = 0;
486    active_procs = 0;
487    memset((char *)process_states, 0, sizeof(process_states));
488    prefp = pref;
489    for (pp = pbase, i = 0; i < nproc; pp++, i++)
490    {
491	/*
492	 *  Place pointers to each valid proc structure in pref[].
493	 *  Process slots that are actually in use have a non-zero
494	 *  status field.  Processes with SSYS set are system
495	 *  processes---these get ignored unless show_sysprocs is set.
496	 */
497	if (pp->p_stat != 0 &&
498	    (show_system || ((pp->p_flag & SSYS) == 0)))
499	{
500	    total_procs++;
501	    process_states[pp->p_stat]++;
502	    if ((pp->p_stat != SZOMB) &&
503		(show_idle || (pp->p_pctcpu != 0) || (pp->p_stat == SRUN)) &&
504		(!show_uid || pp->p_uid == (uid_t)sel->uid))
505	    {
506		*prefp++ = pp;
507		active_procs++;
508	    }
509	}
510    }
511
512    /* if requested, sort the "interesting" processes */
513    if (compare != NULL)
514    {
515	qsort((char *)pref, active_procs, sizeof(struct proc *), proc_compare);
516    }
517
518    /* remember active and total counts */
519    si->p_total = total_procs;
520    si->p_active = pref_len = active_procs;
521
522    /* pass back a handle */
523    handle.next_proc = pref;
524    handle.remaining = active_procs;
525    return((caddr_t)&handle);
526}
527
528char fmt[MAX_COLS];		/* static area where result is built */
529
530char *format_next_process(handle, get_userid)
531
532caddr_t handle;
533char *(*get_userid)();
534
535{
536    register struct proc *pp;
537    register long cputime;
538    register double pct;
539    int where;
540    struct user u;
541    struct handle *hp;
542
543    /* find and remember the next proc structure */
544    hp = (struct handle *)handle;
545    pp = *(hp->next_proc++);
546    hp->remaining--;
547
548
549    /* get the process's user struct and set cputime */
550    where = getu(pp, &u);
551    if (where == -1)
552    {
553	(void) strcpy(u.u_comm, "<swapped>");
554	cputime = 0;
555    }
556    else
557    {
558
559
560	/* set u_comm for system processes */
561	if (u.u_comm[0] == '\0')
562	{
563	    if (pp->p_pid == 0)
564	    {
565		(void) strcpy(u.u_comm, "Swapper");
566	    }
567	    else if (pp->p_pid == 2)
568	    {
569		(void) strcpy(u.u_comm, "Pager");
570	    }
571	}
572	if (where == 1) {
573	    /*
574	     * Print swapped processes as <pname>
575	     */
576	    char buf[sizeof(u.u_comm)];
577	    (void) strncpy(buf, u.u_comm, sizeof(u.u_comm));
578	    u.u_comm[0] = '<';
579	    (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2);
580	    u.u_comm[sizeof(u.u_comm) - 2] = '\0';
581	    (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1);
582	    u.u_comm[sizeof(u.u_comm) - 1] = '\0';
583	}
584
585	cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec;
586    }
587
588    /* calculate the base for cpu percentages */
589    pct = pctdouble(pp->p_pctcpu);
590
591    /* format this entry */
592    sprintf(fmt,
593	    Proc_format,
594	    pp->p_pid,
595	    (*get_userid)(pp->p_uid),
596	    pp->p_pri - PZERO,
597	    pp->p_nice - NZERO,
598	    format_k(pagetok(PROCSIZE(pp))),
599	    format_k(pagetok(pp->p_rssize)),
600	    state_abbrev[pp->p_stat],
601	    format_time(cputime),
602	    100.0 * weighted_cpu(pct, pp),
603	    100.0 * pct,
604	    printable(u.u_comm));
605
606    /* return the result */
607    return(fmt);
608}
609
610/*
611 *  getu(p, u) - get the user structure for the process whose proc structure
612 *	is pointed to by p.  The user structure is put in the buffer pointed
613 *	to by u.  Return 0 if successful, -1 on failure (such as the process
614 *	being swapped out).
615 */
616
617#define USERSIZE sizeof(struct user)
618
619getu(p, u)
620
621register struct proc *p;
622struct user *u;
623
624{
625    struct pte uptes[UPAGES];
626    register caddr_t upage;
627    register struct pte *pte;
628    register nbytes, n;
629
630    /*
631     *  Check if the process is currently loaded or swapped out.  The way we
632     *  get the u area is totally different for the two cases.  For this
633     *  application, we just don't bother if the process is swapped out.
634     */
635    if ((p->p_flag & SLOAD) == 0) {
636#ifdef DOSWAP
637	if (lseek(swap, (long)dtob(p->p_swaddr), 0) == -1) {
638	    perror("lseek(swap)");
639	    return(-1);
640	}
641	if (read(swap, (char *) u, USERSIZE) != USERSIZE)  {
642	    perror("read(swap)");
643	    return(-1);
644	}
645	return (1);
646#else
647	return(-1);
648#endif
649    }
650
651    /*
652     *  Process is currently in memory, we hope!
653     */
654    if (!getkval((unsigned long)p->p_addr, (int *)uptes, sizeof(uptes),
655                "!p->p_addr"))
656    {
657#ifdef DEBUG
658	perror("getkval(uptes)");
659#endif
660	/* we can't seem to get to it, so pretend it's swapped out */
661	return(-1);
662    }
663    upage = (caddr_t) u;
664    pte = uptes;
665    for (nbytes = USERSIZE; nbytes > 0; nbytes -= NBPG) {
666    	(void) lseek(mem, (long)(pte++->pg_pfnum * NBPG), 0);
667#ifdef DEBUG
668	perror("lseek(mem)");
669#endif
670	n = MIN(nbytes, NBPG);
671	if (read(mem, upage, n) != n) {
672#ifdef DEBUG
673	perror("read(mem)");
674#endif
675	    /* we can't seem to get to it, so pretend it's swapped out */
676	    return(-1);
677	}
678	upage += n;
679    }
680    return(0);
681}
682
683/*
684 * check_nlist(nlst) - checks the nlist to see if any symbols were not
685 *		found.  For every symbol that was not found, a one-line
686 *		message is printed to stderr.  The routine returns the
687 *		number of symbols NOT found.
688 */
689
690int check_nlist(nlst)
691
692register struct nlist *nlst;
693
694{
695    register int i;
696
697    /* check to see if we got ALL the symbols we requested */
698    /* this will write one line to stderr for every symbol not found */
699
700    i = 0;
701    while (nlst->n_name != NULL)
702    {
703	if (nlst->n_type == 0)
704	{
705	    /* this one wasn't found */
706	    fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
707	    i = 1;
708	}
709	nlst++;
710    }
711
712    return(i);
713}
714
715
716/*
717 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
718 *	"offset" is the byte offset into the kernel for the desired value,
719 *  	"ptr" points to a buffer into which the value is retrieved,
720 *  	"size" is the size of the buffer (and the object to retrieve),
721 *  	"refstr" is a reference string used when printing error meessages,
722 *	    if "refstr" starts with a '!', then a failure on read will not
723 *  	    be fatal (this may seem like a silly way to do things, but I
724 *  	    really didn't want the overhead of another argument).
725 *
726 */
727
728getkval(offset, ptr, size, refstr)
729
730unsigned long offset;
731int *ptr;
732int size;
733char *refstr;
734
735{
736    if (lseek(kmem, (long)offset, L_SET) == -1) {
737        if (*refstr == '!')
738            refstr++;
739        (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
740		       refstr, strerror(errno));
741        quit(23);
742    }
743    if (read(kmem, (char *) ptr, size) == -1) {
744        if (*refstr == '!')
745            return(0);
746        else {
747            (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM,
748			   refstr, strerror(errno));
749            quit(23);
750        }
751    }
752    return(1);
753}
754
755/* comparison routine for qsort */
756
757/*
758 *  proc_compare - comparison function for "qsort"
759 *	Compares the resource consumption of two processes using five
760 *  	distinct keys.  The keys (in descending order of importance) are:
761 *  	percent cpu, cpu ticks, state, resident set size, total virtual
762 *  	memory usage.  The process states are ordered as follows (from least
763 *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
764 *  	array declaration below maps a process state index into a number
765 *  	that reflects this ordering.
766 */
767
768static unsigned char sorted_state[] =
769{
770    0,	/* not used		*/
771    3,	/* sleep		*/
772    1,	/* ABANDONED (WAIT)	*/
773    6,	/* run			*/
774    5,	/* start		*/
775    2,	/* zombie		*/
776    4	/* stop			*/
777};
778
779proc_compare(pp1, pp2)
780
781struct proc **pp1;
782struct proc **pp2;
783
784{
785    register struct proc *p1;
786    register struct proc *p2;
787    register int result;
788    register pctcpu lresult;
789
790    /* remove one level of indirection */
791    p1 = *pp1;
792    p2 = *pp2;
793
794    /* compare percent cpu (pctcpu) */
795    if ((lresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
796    {
797	/* use cpticks to break the tie */
798	if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
799	{
800	    /* use process state to break the tie */
801	    if ((result = sorted_state[p2->p_stat] -
802			  sorted_state[p1->p_stat])  == 0)
803	    {
804		/* use priority to break the tie */
805		if ((result = p2->p_pri - p1->p_pri) == 0)
806		{
807		    /* use resident set size (rssize) to break the tie */
808		    if ((result = p2->p_rssize - p1->p_rssize) == 0)
809		    {
810			/* use total memory to break the tie */
811			result = PROCSIZE(p2) - PROCSIZE(p1);
812		    }
813		}
814	    }
815	}
816    }
817    else
818    {
819	result = lresult < 0 ? -1 : 1;
820    }
821
822    return(result);
823}
824
825
826void (*signal(sig, func))()
827    int sig;
828    void (*func)();
829{
830    struct sigvec osv, sv;
831
832    /*
833     * XXX: we should block the signal we are playing with,
834     *	    in case we get interrupted in here.
835     */
836    if (sigvector(sig, NULL, &osv) == -1)
837	return BADSIG;
838    sv = osv;
839    sv.sv_handler = func;
840#ifdef SV_BSDSIG
841    sv.sv_flags |= SV_BSDSIG;
842#endif
843    if (sigvector(sig, &sv, NULL) == -1)
844	return BADSIG;
845    return osv.sv_handler;
846}
847
848int getpagesize() { return 1 << PGSHIFT; }
849
850int setpriority(a, b, c) { errno = ENOSYS; return -1; }
851
852/*
853 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
854 *		the process does not exist.
855 *		It is EXTREMLY IMPORTANT that this function work correctly.
856 *		If top runs setuid root (as in SVR4), then this function
857 *		is the only thing that stands in the way of a serious
858 *		security problem.  It validates requests for the "kill"
859 *		and "renice" commands.
860 */
861
862int proc_owner(pid)
863
864int pid;
865
866{
867    register int cnt;
868    register struct proc **prefp;
869    register struct proc *pp;
870
871    prefp = pref;
872    cnt = pref_len;
873    while (--cnt >= 0)
874    {
875	if ((pp = *prefp++)->p_pid == (pid_t)pid)
876	{
877	    return((int)pp->p_uid);
878	}
879    }
880    return(-1);
881}
882