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:  SCO UNIX OpenServer5
37 *
38 * DESCRIPTION:
39 * This is the machine-dependent module for SCO OpenServer5.
40 * Originally written for BSD4.3 system by Christos Zoulas.
41 * Modified to m_sco.c (3.2v4.2)  by Gregory Shilin <shilin@onyx.co.il>
42 * Modified to m_sco5.c (3.2v5.*) by Mike Hopkirk <hops@sco.com>
43 * Works for:
44 * SCO UNIX 3.2v5.*
45 *
46 * CFLAGS: -DHAVE_GETOPT -DORDER
47 *
48 * AUTHOR: Mike Hopkirk (hops@sco.com)
49 * hops 10-Jul-98 - added sort fields
50 *      17-Jul-98 - add philiph's chopped cmd string support
51 *                    (define NO_COMMAND_ARGS to enable )
52 *      09-Dec-98 - provide RSS calculation
53 *      15-Mar-2000 - Fix broken lines and cleanup sysinfo access w macros
54 */
55
56#include <sys/types.h>
57#include <sys/param.h>
58
59#include <stdio.h>
60#include <unistd.h>
61#include <fcntl.h>
62#include <nlist.h>
63#include <math.h>
64#include <signal.h>
65#include <string.h>
66
67#include <sys/dir.h>
68#include <sys/immu.h>
69#include <sys/region.h>
70#include <sys/proc.h>
71#include <sys/user.h>
72#include <sys/sysinfo.h>
73#include <sys/systm.h>
74#include <sys/sysmacros.h>
75#include <sys/var.h>
76#include <sys/sysi86.h>
77
78#include "top.h"
79#include "machine.h"
80#include "utils.h"
81#include "loadavg.h"
82
83/*
84typedef unsigned long  ulong;
85typedef unsigned int   uint;
86typedef unsigned short ushort;
87*/
88typedef unsigned char  uchar;
89
90#define VMUNIX  "/unix"
91#define KMEM    "/dev/kmem"
92#define MEM     "/dev/mem"
93
94#define SI_ACTIVE(p)   p->p_active
95#define SI_TOTAL(p)    p->p_total
96
97/* get_process_info passes back a handle. This is what it looks like: */
98struct handle {
99   struct proc **next_proc; /* points to next valid proc pointer */
100   int           remaining; /* number of pointers remaining */
101};
102
103/* define what weighted cpu is */
104#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
105			 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
106
107#define bytetok(bytes) ((bytes) >> 10)
108
109/* what we consider to be process size: */
110#define PROCSIZE(up) bytetok(ctob((up)->u_tsize + (up)->u_dsize+(up)->u_ssize))
111
112/* definitions for indices in the nlist array */
113#define X_V             0  /* System configuration information */
114#define X_PROC          1  /* process tables */
115#define X_FREEMEM       2  /* current free memory */
116#define X_AVAILRMEM     3  /* available resident (not swappable) mem in pages
117*/
118#define X_AVAILSMEM     4  /* available swappable memory in pages */
119#define X_MAXMEM        5  /* maximum available free memory in clicks */
120#define X_PHYSMEM       6  /* physical memory in clicks */
121#define X_NSWAP         7  /* size of swap space in blocks */
122#define X_HZ            8  /* ticks/second of the clock */
123#define X_MPID          9  /* last process id */
124#define X_SYSINFO       10 /* system information (cpu states) */
125#define X_CUR_CPU       11
126
127static struct nlist nlst[] = {
128   { "v" },             /* 0 */
129   { "proc" },          /* 1 */
130   { "freemem" },       /* 2 */
131   { "availrmem" },     /* 3 */
132   { "availsmem" },     /* 4 */
133   { "maxmem" },        /* 5 */
134   { "physmem" },       /* 6 */
135   { "nswap" },         /* 7 */
136   { "Hz" },            /* 8 */
137   { "mpid" },          /* 9 */
138   { "sysinfo" },       /* 10 */
139   { "cur_cpu" },       /* 11 */
140   { NULL }
141};
142
143/*
144 *  These definitions control the format of the per-process area
145 */
146
147static char header[] =
148  "  PID X        PRI NICE   SIZE   RES  STATE   TIME  COMMAND";
149/* 0123456   -- field to fill in starts at header+6 */
150#define UNAME_START 6
151
152#define Proc_format \
153	"%5d %-8.8s %3d %4d  %5s %5dK %-5s %6s  %.28s"
154
155static int kmem, mem;
156
157static double logcpu;
158
159/* these are retrieved from the kernel in _init */
160static int   Hz;
161static struct var   v;
162static ulong proca;
163static load_avg  cur_cpu;
164
165/* these are for detailing the process states */
166int process_states[8];
167char *procstatenames[] = {
168    "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
169    " created, ", " onproc, ", " xswapped, ",
170    NULL
171};
172
173/* process state names for the "STATE" column of the display */
174char *state_abbrev[] = {
175   "", "sleep", "run", "zomb", "stop", "create", "onpr", "swap"
176};
177
178/* these are for calculating cpu state percentages */
179#define CPUSTATES       5    /* definition from struct sysinfo */
180static time_t cp_time[CPUSTATES];
181static time_t cp_old[CPUSTATES];
182static time_t cp_diff[CPUSTATES];
183
184/* these are for detailing the cpu states */
185int cpu_states[CPUSTATES];
186char *cpustatenames[] = {
187    "idle", "user", "system", "wait", "sxbrk",
188    NULL
189};
190
191/* these are for detailing the memory statistics */
192unsigned long memory_stats[6];
193char *memorynames[] = {
194    "K phys, ", "K max, ", "K free, ", "K lck, ", "K unlck, ",
195    "K swap,", NULL
196};
197
198/* these are for keeping track of the proc array */
199static int bytes;
200static int pref_len;
201static struct proc *pbase;
202static struct proc **pref;
203
204/* forward definitions for comparison functions */
205int proc_compare();
206int compare_cpu();
207int compare_size();
208int compare_time();
209
210int (*proc_compares[])() = {
211    proc_compare,   /* state, pri, time, size */
212    compare_cpu,    /* cpu, time, state, pri, size */
213    compare_size,   /* size, cpu, time, state pri  */
214    compare_time,   /* time, cpu, state, pri, size */
215/* compare_res,     /* res,  cpu, time, state pri  */
216    NULL };
217
218/* these are names given to allowed sorting orders -- first is default */
219char *ordernames[]={"state", "cpu", "size", "time", NULL}; /*hops*/
220
221/* useful externals */
222extern int errno;
223extern char *sys_errlist[];
224
225long time();
226long percentages();
227
228int
229machine_init(struct statics *statics)
230
231{
232ulong ptr;
233
234   if ((kmem = open(KMEM, O_RDONLY)) == -1) {
235      perror(KMEM);
236      return -1;
237   }
238   if ((mem = open(MEM, O_RDONLY)) == -1) {
239      perror(MEM);
240      return -1;
241   }
242
243   /* get the list of symbols we want to access in the kernel */
244   if (nlist(VMUNIX, nlst) == -1) {
245      fprintf(stderr, "top: nlist failed\n");
246      return -1;
247   }
248   /* make sure they were all found */
249   /*ZZ
250   if (check_nlist(nlst) > 0)
251      return -1;
252   */
253
254   proca = nlst[X_PROC].n_value;
255
256   /* get the symbol values out of kmem */
257   (void) getkval(nlst[X_CUR_CPU].n_value, (int *)(&cur_cpu), sizeof(cur_cpu),
258		  nlst[X_CUR_CPU].n_name);
259   (void) getkval(nlst[X_HZ].n_value,      (int *)(&Hz),      sizeof(Hz),
260		  nlst[X_HZ].n_name);
261   (void) getkval(nlst[X_V].n_value,       (int *)(&v),       sizeof(v),
262		  nlst[X_V].n_name);
263
264   /* this is used in calculating WCPU -- calculate it ahead of time */
265   logcpu = log(fabs(loaddouble(cur_cpu)));
266
267   /* allocate space for proc structure array and array of pointers */
268   bytes = v.v_proc * sizeof(struct proc);
269   pbase = (struct proc *)malloc(bytes);
270   pref  = (struct proc **)malloc(v.v_proc * sizeof(struct proc *));
271   if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL) {
272      fprintf(stderr, "top: cannot allocate sufficient memory\n");
273      return -1;
274   }
275
276   /* fill in the statics information */
277   statics->procstate_names = procstatenames;
278   statics->cpustate_names = cpustatenames;
279   statics->memory_names = memorynames;
280   statics->order_names = ordernames ;  /* hops */
281
282   return 0;
283}
284
285char *
286format_header(register char *uname_field)
287
288{
289    register char *ptr;
290
291    ptr = header + UNAME_START;
292    while (*uname_field != '\0')
293    {
294	*ptr++ = *uname_field++;
295    }
296
297    return(header);
298}
299
300
301/* philiph - get run ave fm /dev/table info */
302static int
303tab_avenrun(double runave[])
304{
305   FILE *fp = fopen("/dev/table/avenrun", "r");
306   int i;
307
308   for (i=0; i<3; i++)
309      runave[i] = -1.0;
310
311   if (fp==NULL)
312      return -1;
313   else
314   {
315      short rawave[3];
316
317	if (fread(rawave, sizeof(short), 3, fp) !=3 )
318	{
319	    fclose(fp);
320	    return -1;
321	}
322	else
323	{
324	    int i;
325
326	    for (i=0; i<3; i++)
327		runave[i] = (double) (rawave[i] / 256.0);
328
329	    fclose(fp);
330	    return 0;
331	}
332    }
333}
334
335struct pregion *
336get_pregion(void *ptr)
337{
338    static struct pregion preg;
339    long addr = (long)ptr;
340
341   (void) getkval(addr , (struct pregion *)(&preg),
342		    sizeof(struct pregion), "pregion" );
343    return &preg;
344}
345
346struct region *
347get_region(void *ptr)
348{
349    static struct region reg;
350    long addr = (long)ptr;
351
352   (void) getkval( addr , (struct region *)(&reg),
353		    sizeof(struct region), "region" );
354    return &reg;
355}
356
357static unsigned char shareable[RT_VM86 + 1];     /* 1 if shareable */
358
359/*
360 * sum private referenced pages,
361 * treat shared pages depending on value of TREAT_SHARABLE_PAGES macro
362 *      undefined : ignore (don't account for - default)
363 *      1:  divide among # of references
364 *      2:  accumulate as if private
365 */
366/* #define TREAT_SHAREABLE_PAGES 1 */
367static long
368proc_residentsize(struct proc *pp)
369{
370    struct pregion *prp;
371    struct region *rp;
372    long rtot = 0;
373    long stot = 0;
374    long s1tot = 0;
375
376    /* init shareable region array */
377    if (shareable[RT_STEXT] == 0 )
378	shareable[RT_STEXT] = shareable[RT_SHMEM] = shareable[RT_MAPFILE] = 1
379	;
380
381    prp = pp->p_region;
382
383    if ( prp == 0)
384	return 0;
385
386    for( ; prp && (prp = get_pregion((void *)(prp))) &&
387	   prp->p_reg && (rp = get_region((void*)(prp->p_reg)));
388	   prp = prp->p_next)
389    {
390	if (shareable[rp->r_type] )     /* account for shared pgs separately
391	*/
392	{
393	    stot  += (rp->r_nvalid / rp->r_refcnt);
394	    s1tot += rp->r_nvalid;
395	}
396	else
397	    rtot += rp->r_nvalid;
398
399    }
400#if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
401	rtot += stot;           /* accumulate and spread over users */
402#endif
403
404#if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
405	rtot += s1tot;          /* accumulate as if private */
406#endif
407
408    return rtot * NBPP/1024; ;
409}
410
411void
412get_system_info(struct system_info *si)
413
414{
415long total;
416
417   /* get process id of the last process */
418   (void) getkval(nlst[X_MPID].n_value,  &(si->last_pid),
419   sizeof(si->last_pid),
420		  nlst[X_MPID].n_name);
421   /* get the cp_time array */
422   (void) getkval(nlst[X_SYSINFO].n_value, (int *)cp_time, sizeof(cp_time),
423		  nlst[X_SYSINFO].n_name);
424
425   /* convert cp_time counts to persentages */
426   total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
427
428   /* sum memory statistics */
429   (void) getkval(nlst[X_PHYSMEM].n_value, &memory_stats[0],
430		  sizeof(memory_stats[0]), nlst[X_PHYSMEM].n_name);
431   (void) getkval(nlst[X_MAXMEM].n_value, &memory_stats[1],
432		  sizeof(memory_stats[1]), nlst[X_MAXMEM].n_name);
433   (void) getkval(nlst[X_FREEMEM].n_value, &memory_stats[2],
434		  sizeof(memory_stats[2]), nlst[X_FREEMEM].n_name);
435   (void) getkval(nlst[X_AVAILRMEM].n_value, &memory_stats[3],
436		  sizeof(memory_stats[3]), nlst[X_AVAILRMEM].n_name);
437   (void) getkval(nlst[X_AVAILSMEM].n_value, &memory_stats[4],
438		  sizeof(memory_stats[4]), nlst[X_AVAILSMEM].n_name);
439   (void) getkval(nlst[X_NSWAP].n_value, &memory_stats[5],
440		  sizeof(memory_stats[5]), nlst[X_NSWAP].n_name);
441   memory_stats[0] = bytetok(ctob(memory_stats[0]));    /* clicks -> bytes
442   */
443   memory_stats[1] = bytetok(ctob(memory_stats[1]));    /* clicks -> bytes
444   */
445   memory_stats[2] = bytetok(ctob(memory_stats[2]));    /* clicks -> bytes
446   */
447   memory_stats[3] = bytetok(memory_stats[3] * NBPP);   /* # bytes per page
448   */
449   memory_stats[4] = bytetok(memory_stats[4] * NBPP);   /* # bytes per page
450   */
451   memory_stats[5] = bytetok(memory_stats[5] * NBPSCTR);/* # bytes per sector
452   */
453
454   /* set arrays and strings */
455   /* Note: we keep memory_stats as an unsigned long to avoid sign
456      extension problems when shifting in bytetok. But the module
457      interface requires an array of signed longs. So we just cast
458      the pointer here and hope for the best.   --wnl
459   */
460   si->cpustates = cpu_states;
461   si->memory = (long *)memory_stats;
462
463   tab_avenrun(si->load_avg);   /* philiph */
464}
465
466static struct handle handle;
467
468caddr_t
469get_process_info(struct system_info *si,
470		 struct process_select *sel,
471		 int idx)
472
473{
474register int i;
475register int total_procs;
476register int active_procs;
477register struct proc **prefp;
478register struct proc *pp;
479
480/* set up flags of what we are going to select */
481/* these are copied out of sel for simplicity */
482int show_idle = sel->idle;
483int show_system = sel->system;
484int show_uid = sel->uid != -1;
485int show_command = sel->command != NULL;
486
487   /* read all the proc structures in one fell swoop */
488   (void) getkval(proca, (int *)pbase, bytes, "proc array");
489
490   /* get a pointer to the states summary array */
491   si->procstates = process_states;
492
493   /* count up process states and get pointers to interesting procs */
494   total_procs = active_procs = 0;
495   memset((char *)process_states, 0, sizeof(process_states));
496   prefp = pref;
497   for (pp = pbase, i = 0; i < v.v_proc; pp++, i++) {
498      /*
499       * Place pointers to each valid proc structure in pref[].
500       * Process slots that are actually in use have a non-zero
501       * status field. Processes with SSYS set are system processes --
502       * these are ignored unless show_system is set.
503       */
504      if (pp->p_stat && (show_system || ((pp->p_flag & SSYS) == 0))) {
505	 total_procs++;
506	 process_states[pp->p_stat]++;
507	 if ((pp->p_stat != SZOMB) &&
508	     (show_idle || (pp->p_stat == SRUN) || (pp->p_stat == SONPROC)) &&
509	     (!show_uid || pp->p_uid == (ushort)sel->uid)) {
510		*prefp++ = pp;
511		active_procs++;
512	 }
513      }
514   }
515
516   /* if requested, sort the "interesting" processes */
517   qsort((char *)pref, active_procs, sizeof(struct proc *), proc_compares[idx]);
518
519   /* remember active and total counts */
520   SI_TOTAL(si)  = total_procs;
521   SI_ACTIVE(si) = pref_len = active_procs;
522
523   /* pass back a handle */
524   handle.next_proc = pref;
525   handle.remaining = active_procs;
526   return((caddr_t)&handle);
527}
528
529char fmt[128];          /* static area where result is built */
530
531char *
532format_next_process(caddr_t handle, char *(*get_userid)())
533
534{
535register struct proc *pp;
536register time_t cputime;
537register double pct;
538int where;
539struct user u;
540struct handle *hp;
541char command[29];
542char * process;
543char * process2;
544
545   /* find and remember the next proc structure */
546   hp = (struct handle *)handle;
547   pp = *(hp->next_proc++);
548   hp->remaining--;
549
550   /* get the process's user struct and set cputime */
551   if ((where = sysi86(RDUBLK, pp->p_pid, &u, sizeof(struct user))) != -1)
552      where = (pp->p_flag & SLOAD) ? 0 : 1;
553   if (where == -1) {
554      strcpy(command, "<swapped>");
555      cputime = 0;
556   } else {
557      /* set u_comm for system processes */
558      if (u.u_comm[0] == '\0') {
559	 if (pp->p_pid == 0)
560	    strcpy(command, "Swapper");
561	 else if (pp->p_pid == 2)
562	    strcpy(command, "Pager");
563	 else if (pp->p_pid == 3)
564	    strcpy(command, "Sync'er");
565      } else if (where == 1) {
566	 /* print swapped processes as <pname> */
567	 register char *s1;
568
569	 u.u_psargs[28 - 3] = '\0';
570	 strcpy(command, "<");
571	 strcat(command, strtok(u.u_psargs, " "));
572	 strcat(command, ">");
573	 while (s1 = (char *)strtok(NULL, " "))
574	    strcat(command, s1);
575      } else {
576	 sprintf(command, "%s", u.u_psargs);
577      }
578    cputime = u.u_utime + u.u_stime;
579/*     cputime = pp->p_utime + pp->p_stime;  */
580   }
581   /* calculate the base for cpu percentages */
582   pct = pctdouble(pp->p_cpu);
583
584   /*
585    * psargs gives the absolute path of the process... strip it to only the
586    * command - [Changes by D. Currie & M. Muldner Aitt NS Canada]
587    */
588    process = printable(command);
589#if NO_COMMAND_ARGS
590    strtok(process," ");
591#endif
592    process2 = strrchr(process,'/');
593    if(process2)
594    {
595	process = process2;
596	process++;
597    }
598
599
600   /* format this entry */
601   sprintf(fmt,
602	   Proc_format,
603	   pp->p_pid,
604	   (*get_userid)(pp->p_uid),
605	   pp->p_pri - PZERO,
606	   pp->p_nice - NZERO,
607	   format_k(PROCSIZE(&u)),  /* same as  pp->p_size * 4 */
608	   proc_residentsize(pp),
609	   state_abbrev[pp->p_stat],
610	   format_time(cputime / Hz),
611	   printable(process) );
612
613   return(fmt);
614}
615
616/*
617 * Checks the nlist to see if any symbols were not found.
618 * For every symbol that was not found, a one-line message
619 * is printed to stderr. The routine returns the number of
620 * symbols NOT founded.
621 */
622
623int check_nlist(register struct nlist *nlst)
624
625{
626register int i = 0;
627
628   while (nlst->n_name) {
629      if (nlst->n_type == 0) {
630	 fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
631	 i++;
632      }
633      nlst++;
634   }
635   return i;
636}
637
638/*
639 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
640 *      "offset" is the byte offset into the kernel for the desired value,
641 *      "ptr" points to a buffer into which the value is retrieved,
642 *      "size" is the size of the buffer (and the object to retrieve),
643 *      "refstr" is a reference string used when printing error meessages,
644 *          if "refstr" starts with a '!', then a failure on read will not
645 *          be fatal (this may seem like a silly way to do things, but I
646 *          really didn't want the overhead of another argument).
647 *
648 */
649
650int
651getkval(unsigned long offset, int *ptr, int size, char *refstr)
652
653{
654   if (lseek(kmem, (long)offset, SEEK_SET) == -1) {
655      if (*refstr == '!')
656	 refstr++;
657      fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
658	       refstr, errmsg(errno));
659      quit(23);
660   }
661   if (read(kmem, (char *)ptr, size) == -1) {
662      if (*refstr == '!')
663	 return 0;
664      fprintf(stderr, "%s: reading %s: %s\n", KMEM,
665	       refstr, errmsg(errno));
666      quit(23);
667   }
668   return(1);
669}
670
671/* comparison routine for qsort */
672/* NOTE: this is specific to the BSD proc structure, but it should
673   give you a good place to start. */
674
675/*
676 *  proc_compare - comparison function for "qsort"
677 *      Compares the resource consumption of two processes using five
678 *      distinct keys.  The keys (in descending order of importance) are:
679 *      percent cpu, cpu ticks, state, resident set size, total virtual
680 *      memory usage.  The process states are ordered as follows (from least
681 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
682 *      array declaration below maps a process state index into a number
683 *      that reflects this ordering.
684 */
685
686static unsigned char sorted_state[] =
687{
688    0,  /* not used             */
689    5,  /* sleep                */
690    6,  /* run                  */
691    2,  /* zombie               */
692    4,  /* stop                 */
693    1,  /* start                */
694    7,  /* onpr                 */
695    3,  /* swap                 */
696};
697
698int
699proc_compare(struct proc **pp1, struct proc **pp2)
700
701{
702register struct proc *p1;
703register struct proc *p2;
704register int result;
705register ulong lresult;
706
707   /* remove one level of indirection */
708   p1 = *pp1;
709   p2 = *pp2;
710
711   /* use process state to break the tie */
712   if ((result = sorted_state[p2->p_stat] -
713		 sorted_state[p1->p_stat])  == 0)
714   {
715      /* use priority to break the tie */
716      if ((result = p2->p_pri - p1->p_pri) == 0)
717      {
718	 /* use time to break the tie */
719	 if ((result = (p2->p_utime + p2->p_stime) -
720		       (p1->p_utime + p1->p_stime)) == 0)
721	 {
722	    /* use resident set size (rssize) to break the tie */
723	    if ((result = p2->p_size - p1->p_size) == 0)
724	    {
725	       result = 0;
726	    }
727	 }
728      }
729   }
730
731    return(result);
732}
733
734/* returns uid of owner of process pid */
735int
736proc_owner(int pid)
737
738{
739register int cnt;
740register struct proc **prefp;
741register struct proc  *pp;
742
743   prefp = pref;
744   cnt = pref_len;
745   while (--cnt >= 0) {
746      if ((pp = *prefp++)->p_pid == (short)pid)
747	 return ((int)pp->p_uid);
748   }
749   return(-1);
750}
751
752#if 0
753int setpriority(int dummy, int who, int nicewal)
754{
755   errno = 1;
756   return -1;
757}
758#endif
759
760/* sigblock is not POSIX conformant */
761sigset_t sigblock (sigset_t mask)
762{
763sigset_t oset;
764
765   sigemptyset(&oset);
766   sigprocmask(SIG_BLOCK, &mask, &oset);
767   return oset;
768}
769
770/* sigsetmask is not POSIX conformant */
771sigsetmask(sigset_t mask)
772{
773sigset_t oset;
774
775   sigemptyset(&oset);
776   sigprocmask(SIG_SETMASK, &mask, &oset);
777   return oset;
778}
779
780
781/* ---------------- hops - comparison/ordering support ---------------- */
782
783#define ORDERKEY_PCTCPU  if (dresult = pctdouble(p2->p_cpu) - pctdouble(p1->p_cpu),\
784			     (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
785#define ORDERKEY_MEMSIZE if ((result = (p2->p_size - p1->p_size)) == 0)
786#define ORDERKEY_CPTIME  if ((result = (long)(p2->p_utime + p2->p_stime) -\
787				       (long)(p1->p_utime + p1->p_stime)) == 0)
788
789#define ORDERKEY_STATE   if ((result = (sorted_state[p2->p_stat] - \
790			       sorted_state[p1->p_stat])) == 0)
791#define ORDERKEY_PRIO    if ((result = p2->p_pri - p1->p_pri) == 0)
792
793
794int
795compare_cpu (   struct proc **pp1, struct proc **pp2)
796{
797    register struct proc *p1;
798    register struct proc *p2;
799    register int result;
800    double dresult;
801
802    /* remove one level of indirection */
803    p1 = *pp1;
804    p2 = *pp2;
805
806    ORDERKEY_PCTCPU
807    ORDERKEY_CPTIME
808    ORDERKEY_STATE
809    ORDERKEY_PRIO
810    ORDERKEY_MEMSIZE
811    ;
812
813    return (result);
814}
815
816
817
818/* compare_size - the comparison function for sorting by process size */
819int
820compare_size ( struct proc **pp1, struct proc **pp2)
821{
822    register struct proc *p1;
823    register struct proc *p2;
824    register int result;
825    double dresult;
826
827    /* remove one level of indirection */
828    p1 = *pp1;
829    p2 = *pp2;
830
831
832    ORDERKEY_MEMSIZE
833    ORDERKEY_PCTCPU
834    ORDERKEY_CPTIME
835    ORDERKEY_STATE
836    ORDERKEY_PRIO
837    ;
838
839    return (result);
840}
841
842/* compare_res - the comparison function for sorting by resident set size */
843/* TODO: add shadow proc struct updating usr + sys times and RSS for use
844 * in comparison rtns, implement compare_res rtn as per compare_size()
845 */
846
847/* compare_time - the comparison function for sorting by total cpu time */
848/* This is giving wrong results since its using the proc structure vals not
849 * the u struct vals we display above
850 * TODO: add shadow proc struct updating usr + sys times and RSS for use
851 * in comparison rtns
852 */
853int
854compare_time ( struct proc **pp1, struct proc **pp2)
855{
856    register struct proc *p1;
857    register struct proc *p2;
858    register int result;
859    double dresult;
860
861    /* remove one level of indirection */
862    p1 = *pp1;
863    p2 = *pp2;
864
865    ORDERKEY_CPTIME
866    ORDERKEY_PCTCPU
867    ORDERKEY_STATE
868    ORDERKEY_PRIO
869    ORDERKEY_MEMSIZE
870    ;
871
872    return (result);
873}
874
875