1/*
2 * top - a top users display for Unix
3 *
4 * SYNOPSIS:  Any Sun running SunOS 5.x (Solaris 2.x)
5 *
6 * DESCRIPTION:
7 * This is the machine-dependent module for SunOS 5.x (Solaris 2).
8 * There is some support for MP architectures.
9 * This makes top work on all revisions of SunOS 5 from 5.0
10 * through 5.9 (otherwise known as Solaris 9).  It has not been
11 * tested on SunOS 5.10.
12 *
13 * AUTHORS:      Torsten Kasch 		<torsten@techfak.uni-bielefeld.de>
14 *               Robert Boucher		<boucher@sofkin.ca>
15 * CONTRIBUTORS: Marc Cohen 		<marc@aai.com>
16 *               Charles Hedrick 	<hedrick@geneva.rutgers.edu>
17 *	         William L. Jones 	<jones@chpc>
18 *               Petri Kutvonen         <kutvonen@cs.helsinki.fi>
19 *	         Casper Dik             <casper.dik@sun.com>
20 *               Tim Pugh               <tpugh@oce.orst.edu>
21 */
22
23#define _KMEMUSER
24
25#include "os.h"
26#include "utils.h"
27#include "username.h"
28#include "display.h"
29
30#if (OSREV == 551)
31#undef OSREV
32#define OSREV 55
33#endif
34
35/*
36 * Starting with SunOS 5.6 the data in /proc changed along with the
37 * means by which it is accessed.  In this case we define USE_NEW_PROC.
38 * Note that with USE_NEW_PROC defined the structure named "prpsinfo"
39 * is redefined to be "psinfo".  This will be confusing as you read
40 * the code.
41 */
42
43#if OSREV >= 56
44#define USE_NEW_PROC
45#endif
46
47#if defined(USE_NEW_PROC)
48#define _STRUCTURED_PROC 1
49#define prpsinfo psinfo
50#include <sys/procfs.h>
51#define pr_fill pr_nlwp
52/* the "px" macros are used where the actual member could be in a substructure */
53#define px_state pr_lwp.pr_state
54#define px_nice pr_lwp.pr_nice
55#define px_pri pr_lwp.pr_pri
56#define px_onpro pr_lwp.pr_onpro
57#define ZOMBIE(p)	((p)->pr_nlwp == 0)
58#define SIZE_K(p)	(long)((p)->pr_size)
59#define RSS_K(p)	(long)((p)->pr_rssize)
60#else
61#define px_state pr_state
62#define px_oldpri pr_oldpri
63#define px_nice pr_nice
64#define px_pri pr_pri
65#define px_onpro pr_filler[5]
66#define ZOMBIE(p)	((p)->pr_zomb)
67#define SIZE_K(p)	(long)((p)->pr_bysize/1024)
68#define RSS_K(p)	(long)((p)->pr_byrssize/1024)
69#endif
70
71#include "top.h"
72#include "machine.h"
73#include <limits.h>
74#include <stdio.h>
75#include <fcntl.h>
76#include <unistd.h>
77#include <stdlib.h>
78#include <errno.h>
79#include <dirent.h>
80#include <nlist.h>
81#include <string.h>
82#include <kvm.h>
83#include <signal.h>
84#include <sys/types.h>
85#include <sys/param.h>
86#include <sys/signal.h>
87#include <sys/fault.h>
88#include <sys/sysinfo.h>
89#include <sys/sysmacros.h>
90#include <sys/syscall.h>
91#include <sys/user.h>
92#include <sys/proc.h>
93#include <sys/procfs.h>
94#include <sys/vm.h>
95#include <sys/var.h>
96#include <sys/cpuvar.h>
97#include <sys/file.h>
98#include <sys/time.h>
99#include <sys/priocntl.h>
100#include <sys/tspriocntl.h>
101#include <sys/processor.h>
102#include <sys/resource.h>
103#include <sys/swap.h>
104#include <sys/stat.h>
105#include <vm/anon.h>
106#include <math.h>
107#include <utmpx.h>
108#include "utils.h"
109#include "hash.h"
110
111#if OSREV >= 53
112#define USE_KSTAT
113#endif
114#ifdef USE_KSTAT
115#include <kstat.h>
116/*
117 * Some kstats are fixed at 32 bits, these will be specified as ui32; some
118 * are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris
119 * we'll make those unsigned long)
120 * Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit.
121 */
122# ifndef KSTAT_DATA_UINT32
123#  define ui32 ul
124# endif
125#endif
126
127#define UNIX "/dev/ksyms"
128#define KMEM "/dev/kmem"
129#define PROCFS "/proc"
130#define CPUSTATES     5
131#ifndef PRIO_MIN
132#define PRIO_MIN	-20
133#endif
134#ifndef PRIO_MAX
135#define PRIO_MAX	20
136#endif
137
138#ifndef FSCALE
139#define FSHIFT  8		/* bits to right of fixed binary point */
140#define FSCALE  (1<<FSHIFT)
141#endif /* FSCALE */
142
143#define loaddouble(la) ((double)(la) / FSCALE)
144#define dbl_align(x)	(((unsigned long)(x)+(sizeof(double)-1)) & \
145						~(sizeof(double)-1))
146
147/*
148 * SunOS 5.4 and above track pctcpu in the proc structure as pr_pctcpu.
149 * These values are weighted over one minute whereas top output prefers
150 * a near-instantaneous measure of cpu utilization.  So we choose to
151 * ignore pr_pctcpu: we calculate our own cpu percentage and store it in
152 * one of the spare slots in the prinfo structure.
153 */
154
155#define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0]))
156
157/* definitions for indices in the nlist array */
158#define X_V			 0
159#define X_MPID			 1
160#define X_ANONINFO		 2
161#define X_MAXMEM		 3
162#define X_FREEMEM		 4
163#define X_AVENRUN		 5
164#define X_CPU			 6
165#define X_NPROC			 7
166#define X_NCPUS		   	 8
167
168static struct nlist nlst[] =
169{
170  {"v"},			/* 0 */	/* replaced by dynamic allocation */
171  {"mpid"},			/* 1 */
172#if OSREV >= 56
173  /* this structure really has some extra fields, but the first three match */
174  {"k_anoninfo"},		/* 2 */
175#else
176  {"anoninfo"},			/* 2 */
177#endif
178  {"maxmem"},			/* 3 */ /* use sysconf */
179  {"freemem"},			/* 4 */	/* available from kstat >= 2.5 */
180  {"avenrun"},			/* 5 */ /* available from kstat */
181  {"cpu"},			/* 6 */ /* available from kstat */
182  {"nproc"},			/* 7 */ /* available from kstat */
183  {"ncpus"},			/* 8 */ /* available from kstat */
184  {0}
185};
186
187static unsigned long avenrun_offset;
188static unsigned long mpid_offset;
189#ifdef USE_KSTAT
190static kstat_ctl_t *kc = NULL;
191static kid_t kcid = 0;
192#else
193static unsigned long *cpu_offset;
194#endif
195static unsigned long nproc_offset;
196static unsigned long freemem_offset;
197static unsigned long maxmem_offset;
198static unsigned long anoninfo_offset;
199static int maxfiles = 256;
200#define MAXFILES 2048
201static int *display_fields;
202static int show_threads = 0;
203static int show_fullcmd;
204
205/* get_process_info passes back a handle.  This is what it looks like: */
206struct handle
207{
208    struct prpsinfo **next_proc;/* points to next valid proc pointer */
209    int remaining;		/* number of pointers remaining */
210};
211
212/*
213 * Structure for keeping track processes between updates.
214 * We keep these things in a hash table, which is updated at every cycle.
215 */
216struct oldproc
217{
218    pid_t pid;
219    id_t lwpid;
220    double oldtime;
221    double oldpct;
222    uid_t  owner_uid;
223    int fd_psinfo;
224    int fd_lpsinfo;
225    int seen;
226};
227
228#define TIMESPEC_TO_DOUBLE(ts) ((ts).tv_sec * 1.0e9 + (ts).tv_nsec)
229
230hash_table *prochash;
231hash_table *threadhash;
232
233/*
234 * Structure for tracking per-cpu information
235 */
236struct cpustats
237{
238    unsigned int states[CPUSTATES];
239    uint_t pswitch;
240    uint_t trap;
241    uint_t intr;
242    uint_t syscall;
243    uint_t sysfork;
244    uint_t sysvfork;
245    uint_t pfault;
246    uint_t pgin;
247    uint_t pgout;
248};
249
250/*
251 * GCC assumes that all doubles are aligned.  Unfortunately it
252 * doesn't round up the structure size to be a multiple of 8.
253 * Thus we'll get a coredump when going through array.  The
254 * following is a size rounded up to 8.
255 */
256#define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo))
257
258/* this defines one field (or column) in the process display */
259
260struct proc_field {
261    char *name;
262    int width;
263    int rjust;
264    int min_screenwidth;
265    int (*format)(char *, int, struct prpsinfo *);
266};
267
268#define PROCSTATES 8
269/* process state names for the "STATE" column of the display */
270char *state_abbrev[] =
271{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
272
273int process_states[PROCSTATES];
274char *procstatenames[] =
275{
276  "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
277  " starting, ", " on cpu, ", " swapped, ",
278  NULL
279};
280
281int cpu_states[CPUSTATES];
282char *cpustatenames[] =
283{"idle", "user", "kernel", "iowait", "swap", NULL};
284#define CPUSTATE_IOWAIT 3
285#define CPUSTATE_SWAP   4
286
287
288/* these are for detailing the memory statistics */
289long memory_stats[5];
290char *memorynames[] =
291{"K phys mem, ", "K free mem, ", "K total swap, ", "K free swap", NULL};
292#define MEMORY_TOTALMEM  0
293#define MEMORY_FREEMEM   1
294#define MEMORY_TOTALSWAP 2
295#define MEMORY_FREESWAP  3
296
297/* these are for detailing kernel statistics */
298int kernel_stats[8];
299char *kernelnames[] =
300{" ctxsw, ", " trap, ", " intr, ", " syscall, ", " fork, ",
301 " flt, ", " pgin, ", " pgout, ", NULL};
302#define KERNEL_CSWITCH 0
303#define KERNEL_TRAP 1
304#define KERNEL_INTR 2
305#define KERNEL_SYSCALL 3
306#define KERNEL_FORK 4
307#define KERNEL_PFAULT 5
308#define KERNEL_PGIN 6
309#define KERNEL_PGOUT 7
310
311/* these are names given to allowed sorting orders -- first is default */
312char *ordernames[] =
313{"cpu", "size", "res", "time", "pid", NULL};
314
315/* forward definitions for comparison functions */
316int compare_cpu();
317int compare_size();
318int compare_res();
319int compare_time();
320int compare_pid();
321
322int (*proc_compares[])() = {
323    compare_cpu,
324    compare_size,
325    compare_res,
326    compare_time,
327    compare_pid,
328    NULL };
329
330kvm_t *kd;
331static DIR *procdir;
332
333/* "cpucount" is used to store the value for the kernel variable "ncpus".
334   But since <sys/cpuvar.h> actually defines a variable "ncpus" we need
335   to use a different name here.   --wnl */
336static int cpucount;
337
338/* pagetok function is really a pointer to an appropriate function */
339static int pageshift;
340static long (*p_pagetok) ();
341#define pagetok(size) ((*p_pagetok)(size))
342
343/* useful externals */
344extern char *myname;
345extern void perror ();
346extern int getptable ();
347extern void quit ();
348
349/* process formatting functions and data */
350
351int
352fmt_pid(char *buf, int sz, struct prpsinfo *pp)
353
354{
355    return snprintf(buf, sz, "%6d", (int)pp->pr_pid);
356}
357
358int
359fmt_username(char *buf, int sz, struct prpsinfo *pp)
360
361{
362    return snprintf(buf, sz, "%-8.8s", username(pp->pr_uid));
363}
364
365int
366fmt_uid(char *buf, int sz, struct prpsinfo *pp)
367
368{
369    return snprintf(buf, sz, "%6d", (int)pp->pr_uid);
370}
371
372int
373fmt_nlwp(char *buf, int sz, struct prpsinfo *pp)
374
375{
376    return snprintf(buf, sz, "%4d", pp->pr_fill < 999 ? pp->pr_fill: 999);
377}
378
379int
380fmt_pri(char *buf, int sz, struct prpsinfo *pp)
381
382{
383    return snprintf(buf, sz, "%3d", pp->px_pri);
384}
385
386int
387fmt_nice(char *buf, int sz, struct prpsinfo *pp)
388
389{
390    return snprintf(buf, sz, "%4d", pp->px_nice - NZERO);
391}
392
393int
394fmt_size(char *buf, int sz, struct prpsinfo *pp)
395
396{
397    return snprintf(buf, sz, "%5s", format_k(SIZE_K(pp)));
398}
399
400int
401fmt_res(char *buf, int sz, struct prpsinfo *pp)
402
403{
404    return snprintf(buf, sz, "%5s", format_k(RSS_K(pp)));
405}
406
407int
408fmt_state(char *buf, int sz, struct prpsinfo *pp)
409
410{
411    if (pp->px_state == SONPROC && cpucount > 1)
412    {
413	/* large #s may overflow colums */
414	if (pp->px_onpro < 100)
415	{
416	    return snprintf(buf, sz, "cpu/%-2d", pp->px_onpro);
417	}
418	return snprintf(buf, sz, "cpu/**");
419    }
420
421    return snprintf(buf, sz, "%-6s", state_abbrev[(int)pp->px_state]);
422}
423
424int
425fmt_time(char *buf, int sz, struct prpsinfo *pp)
426
427{
428    return snprintf(buf, sz, "%6s", format_time(pp->pr_time.tv_sec));
429}
430
431int
432fmt_cpu(char *buf, int sz, struct prpsinfo *pp)
433
434{
435    return snprintf(buf, sz, "%5s%%",
436		    format_percent(percent_cpu(pp) / cpucount));
437}
438
439int
440fmt_command(char *buf, int sz, struct prpsinfo *pp)
441
442{
443    return snprintf(buf, sz, "%s",
444		    printable(show_fullcmd ? pp->pr_psargs : pp->pr_fname));
445}
446
447int
448fmt_lwp(char *buf, int sz, struct prpsinfo *pp)
449
450{
451    return snprintf(buf, sz, "%4d", ((int)pp->pr_lwp.pr_lwpid < 10000 ?
452		(int)pp->pr_lwp.pr_lwpid : 9999));
453}
454
455struct proc_field proc_field[] = {
456    { "PID", 6, 1, 0, fmt_pid },
457    { "USERNAME", 8, 0, 0, fmt_username },
458#define FIELD_USERNAME 1
459    { "UID", 6, 1, 0, fmt_uid },
460#define FIELD_UID 2
461    { "NLWP", 4, 1, 0, fmt_nlwp },
462    { "PRI", 3, 1, 0, fmt_pri },
463    { "NICE", 4, 1, 0, fmt_nice },
464    { "SIZE", 5, 1, 0, fmt_size },
465    { "RES", 5, 1, 0, fmt_res },
466    { "STATE", 6, 0, 0, fmt_state },
467    { "TIME", 6, 1, 0, fmt_time },
468    { "CPU", 6, 1, 0, fmt_cpu },
469    { "COMMAND", 7, 0, 0, fmt_command },
470    { "LWP", 4, 1, 0, fmt_lwp },
471};
472#define MAX_FIELDS 13
473
474static int proc_display[MAX_FIELDS];
475static int thr_display[MAX_FIELDS];
476
477int
478field_index(char *col)
479
480{
481    struct proc_field *fp;
482    int i = 0;
483
484    fp = proc_field;
485    while (fp->name != NULL)
486    {
487	if (strcmp(col, fp->name) == 0)
488	{
489	    return i;
490	}
491	fp++;
492	i++;
493    }
494
495    return -1;
496}
497
498void
499field_subst(int *fp, int old, int new)
500
501{
502    while (*fp != -1)
503    {
504	if (*fp == old)
505	{
506	    *fp = new;
507	}
508	fp++;
509    }
510}
511
512/* p_pagetok points to one of the following, depending on which
513   direction data has to be shifted: */
514
515long pagetok_none(long size)
516
517{
518    return(size);
519}
520
521long pagetok_left(long size)
522
523{
524    return(size << pageshift);
525}
526
527long pagetok_right(long size)
528
529{
530    return(size >> pageshift);
531}
532
533/*
534 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
535 *	"offset" is the byte offset into the kernel for the desired value,
536 *  	"ptr" points to a buffer into which the value is retrieved,
537 *  	"size" is the size of the buffer (and the object to retrieve),
538 *  	"refstr" is a reference string used when printing error meessages,
539 *	    if "refstr" starts with a '!', then a failure on read will not
540 *  	    be fatal (this may seem like a silly way to do things, but I
541 *  	    really didn't want the overhead of another argument).
542 *
543 */
544int
545getkval (unsigned long offset,
546	 int *ptr,
547	 int size,
548	 char *refstr)
549{
550    dprintf("getkval(%08x, %08x, %d, %s)\n", offset, ptr, size, refstr);
551
552    if (kvm_read (kd, offset, (char *) ptr, size) != size)
553    {
554	dprintf("getkval: read failed\n");
555	if (*refstr == '!')
556	{
557	    return (0);
558	}
559	else
560	{
561	    fprintf (stderr, "top: kvm_read for %s: %s\n", refstr, strerror(errno));
562	    quit (23);
563	}
564    }
565
566    dprintf("getkval read %d (%08x)\n", *ptr);
567
568    return (1);
569
570}
571
572/* procs structure memory management */
573
574static struct prpsinfo **allprocs = NULL;
575static struct prpsinfo **nextproc = NULL;
576static int maxprocs = 0;
577static int idxprocs = 0;
578
579/*
580 * void procs_prealloc(int cnt)
581 *
582 * Preallocate "cnt" procs structures.  If "cnt" is less than or equal
583 * to procs_max() then this function has no effect.
584 */
585
586void
587procs_prealloc(int max)
588
589{
590    int cnt;
591    struct prpsinfo *new;
592    struct prpsinfo **pp;
593
594    cnt = max - maxprocs;
595    if (cnt > 0)
596    {
597	dprintf("procs_prealloc: need %d, deficit %d\n", max, cnt);
598	allprocs = (struct prpsinfo **)
599	    realloc((void *)allprocs, max * sizeof(struct prpsinfo *));
600	pp = nextproc = allprocs + idxprocs;
601	new = (struct prpsinfo *)malloc(cnt * PRPSINFOSIZE);
602	dprintf("procs_prealloc: idxprocs %d, allprocs %08x, nextproc %08x, new %08x\n",
603		idxprocs, allprocs, nextproc, new);
604	while (--cnt >= 0)
605	{
606	    *pp++ = new;
607	    new = (struct prpsinfo *) ((char *)new + PRPSINFOSIZE);
608	}
609	dprintf("procs_prealloc: done filling at %08x\n", new);
610	maxprocs = max;
611    }
612}
613
614/*
615 * struct prpsinfo *procs_next()
616 *
617 * Return the next available procs structure, allocating a new one
618 * if needed.
619 */
620
621struct prpsinfo *
622procs_next()
623
624{
625    if (idxprocs >= maxprocs)
626    {
627	/* allocate some more */
628	procs_prealloc(maxprocs + 128);
629    }
630    idxprocs++;
631    return *nextproc++;
632}
633
634struct prpsinfo *
635procs_dup(struct prpsinfo *p)
636
637{
638    struct prpsinfo *n;
639
640    n = procs_next();
641    memcpy(n, p, PRPSINFOSIZE);
642    return n;
643}
644
645/*
646 * struct prpsinfo *procs_start()
647 *
648 * Return the first procs structure.
649 */
650
651struct prpsinfo *
652procs_start()
653
654{
655    idxprocs = 0;
656    nextproc = allprocs;
657    return procs_next();
658}
659
660/*
661 * int procs_max()
662 *
663 * Return the maximum number of procs structures currently allocated.
664 */
665
666int
667procs_max()
668
669{
670    return maxprocs;
671}
672
673/*
674 * check_nlist(nlst) - checks the nlist to see if any symbols were not
675 *		found.  For every symbol that was not found, a one-line
676 *		message is printed to stderr.  The routine returns the
677 *		number of symbols NOT found.
678 */
679int
680check_nlist (register struct nlist *nlst)
681{
682    register int i;
683
684    /* check to see if we got ALL the symbols we requested */
685    /* this will write one line to stderr for every symbol not found */
686
687    i = 0;
688    while (nlst->n_name != NULL)
689    {
690	if (nlst->n_type == 0)
691	{
692	    /* this one wasn't found */
693	    fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
694	    i = 1;
695	}
696	nlst++;
697    }
698    return (i);
699}
700
701
702char *
703format_header (register char *uname_field)
704{
705  return ("");
706}
707
708#ifdef USE_KSTAT
709
710long
711kstat_data_value_l(kstat_named_t *kn)
712
713{
714#ifdef KSTAT_DATA_UINT32
715    switch(kn->data_type)
716    {
717    case KSTAT_DATA_INT32:
718	return ((long)(kn->value.i32));
719    case KSTAT_DATA_UINT32:
720	return ((long)(kn->value.ui32));
721    case KSTAT_DATA_INT64:
722	return ((long)(kn->value.i64));
723    case KSTAT_DATA_UINT64:
724	return ((long)(kn->value.ui64));
725    }
726    return 0;
727#else
728    return ((long)(kn->value.ui32));
729#endif
730}
731
732int
733kstat_safe_retrieve(kstat_t **ksp,
734		    char *module, int instance, char *name, void *buf)
735
736{
737    kstat_t *ks;
738    kid_t new_kcid;
739    int changed;
740
741    dprintf("kstat_safe_retrieve(%08x -> %08x, %s, %d, %s, %08x)\n",
742	    ksp, *ksp, module, instance, name, buf);
743
744    ks = *ksp;
745    do {
746	changed = 0;
747	/* if we dont already have the kstat, retrieve it */
748	if (ks == NULL)
749	{
750	    if ((ks = kstat_lookup(kc, module, instance, name)) == NULL)
751	    {
752		return (-1);
753	    }
754	    *ksp = ks;
755	}
756
757	/* attempt to read it */
758	new_kcid = kstat_read(kc, ks, buf);
759	/* chance for an infinite loop here if kstat_read keeps
760	   returning -1 */
761
762	/* if the chain changed, update it */
763	if (new_kcid != kcid)
764	{
765	    dprintf("kstat_safe_retrieve: chain changed to %d...updating\n",
766		    new_kcid);
767	    changed = 1;
768	    kcid = kstat_chain_update(kc);
769	}
770    } while (changed);
771
772    return (0);
773}
774
775/*
776 * int kstat_safe_namematch(int num, kstat_t *ksp, char *name, void *buf)
777 *
778 * Safe scan of kstat chain for names starting with "name".  Matches
779 * are copied in to "ksp", and kstat_read is called on each match using
780 * "buf" as a buffer of length "size".  The actual number of records
781 * found is returned.  Up to "num" kstats are copied in to "ksp", but
782 * no more.  If any kstat_read indicates that the chain has changed, then
783 * the whole process is restarted.
784 */
785
786int
787kstat_safe_namematch(int num, kstat_t **ksparg, char *name, void *buf, int size)
788
789{
790    kstat_t *ks;
791    kstat_t **ksp;
792    kid_t new_kcid;
793    int namelen;
794    int count;
795    int changed;
796    char *cbuf;
797
798    dprintf("kstat_safe_namematch(%d, %08x, %s, %08x, %d)\n",
799	    num, ksparg, name, buf, size);
800
801    namelen = strlen(name);
802
803    do {
804	/* initialize before the scan */
805	cbuf = (char *)buf;
806	ksp = ksparg;
807	count = 0;
808	changed = 0;
809
810	/* scan the chain for matching kstats */
811	for (ks = kc->kc_chain; ks != NULL; ks = ks->ks_next)
812	{
813	    if (strncmp(ks->ks_name, name, namelen) == 0)
814	    {
815		/* this kstat matches: save it if there is room */
816		if (count++ < num)
817		{
818		    /* read the kstat */
819		    new_kcid = kstat_read(kc, ks, cbuf);
820
821		    /* if the chain changed, update it */
822		    if (new_kcid != kcid)
823		    {
824			dprintf("kstat_safe_namematch: chain changed to %d...updating\n",
825				new_kcid);
826			changed = 1;
827			kcid = kstat_chain_update(kc);
828
829			/* there's no sense in continuing the scan */
830			/* so break out of the for loop */
831			break;
832		    }
833
834		    /* move to the next buffers */
835		    cbuf += size;
836		    *ksp++ = ks;
837		}
838	    }
839	}
840    } while(changed);
841
842    dprintf("kstat_safe_namematch returns %d\n", count);
843
844    return count;
845}
846
847static kstat_t *ks_system_misc = NULL;
848
849#endif /* USE_KSTAT */
850
851
852int
853get_avenrun(int avenrun[3])
854
855{
856#ifdef USE_KSTAT
857    int status;
858    kstat_named_t *kn;
859
860    dprintf("get_avenrun(%08x)\n", avenrun);
861
862    if ((status = kstat_safe_retrieve(&ks_system_misc,
863				      "unix", 0, "system_misc", NULL)) == 0)
864    {
865	if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_1min")) != NULL)
866	{
867	    avenrun[0] = kn->value.ui32;
868	}
869	if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_5min")) != NULL)
870	{
871	    avenrun[1] = kn->value.ui32;
872	}
873	if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_15min")) != NULL)
874	{
875	    avenrun[2] = kn->value.ui32;
876	}
877    }
878    dprintf("get_avenrun returns %d\n", status);
879    return (status);
880
881#else /* !USE_KSTAT */
882
883    (void) getkval (avenrun_offset, (int *) avenrun, sizeof (int [3]), "avenrun");
884
885    return 0;
886
887#endif /* USE_KSTAT */
888}
889
890int
891get_ncpus()
892
893{
894#ifdef USE_KSTAT
895    kstat_named_t *kn;
896    int ret = -1;
897
898    if ((kn = kstat_data_lookup(ks_system_misc, "ncpus")) != NULL)
899    {
900	ret = (int)(kn->value.ui32);
901    }
902
903    return ret;
904#else
905    int ret;
906
907    (void) getkval(nlst[X_NCPUS].n_value, (int *)(&ret), sizeof(ret), "ncpus");
908    return ret;
909#endif
910}
911
912int
913get_nproc()
914
915{
916#ifdef USE_KSTAT
917    kstat_named_t *kn;
918    int ret = -1;
919
920    if ((kn = kstat_data_lookup(ks_system_misc, "nproc")) != NULL)
921    {
922	ret = (int)(kn->value.ui32);
923    }
924#else
925    int ret;
926
927    (void) getkval (nproc_offset, (int *) (&ret), sizeof (ret), "nproc");
928#endif
929
930    dprintf("get_nproc returns %d\n", ret);
931    return ret;
932}
933
934struct cpustats *
935get_cpustats(int *cnt, struct cpustats *cpustats)
936
937{
938#ifdef USE_KSTAT
939    static kstat_t **cpu_ks = NULL;
940    static cpu_stat_t *cpu_stat = NULL;
941    static unsigned int nelems = 0;
942    cpu_stat_t *cpu_stat_p;
943    int i, cpu_num;
944    struct cpustats *cpustats_p;
945
946    dprintf("get_cpustats(%d -> %d, %08x)\n", cnt, *cnt, cpustats);
947
948    while (nelems > 0 ?
949	   (cpu_num = kstat_safe_namematch(nelems,
950					   cpu_ks,
951					   "cpu_stat",
952					   cpu_stat,
953					   sizeof(cpu_stat_t))) > nelems :
954	   (cpu_num = get_ncpus()) > 0)
955    {
956	/* reallocate the arrays */
957	dprintf("realloc from %d to %d\n", nelems, cpu_num);
958	nelems = cpu_num;
959	if (cpu_ks != NULL)
960	{
961	    free(cpu_ks);
962	}
963	cpu_ks = (kstat_t **)calloc(nelems, sizeof(kstat_t *));
964	if (cpu_stat != NULL)
965	{
966	    free(cpu_stat);
967	}
968	cpu_stat = (cpu_stat_t *)malloc(nelems * sizeof(cpu_stat_t));
969    }
970
971    /* do we have more cpus than our caller? */
972    if (cpu_num > *cnt)
973    {
974	/* yes, so realloc their array, too */
975	dprintf("realloc array from %d to %d\n", *cnt, cpu_num);
976	*cnt = cpu_num;
977	cpustats = (struct cpustats *)realloc(cpustats,
978					      cpu_num * sizeof(struct cpustats));
979    }
980
981    cpu_stat_p = cpu_stat;
982    cpustats_p = cpustats;
983    for (i = 0; i < cpu_num; i++)
984    {
985	dprintf("cpu %d %08x: idle %u, user %u, syscall %u\n", i, cpu_stat_p,
986		cpu_stat_p->cpu_sysinfo.cpu[0],
987		cpu_stat_p->cpu_sysinfo.cpu[1],
988		cpu_stat_p->cpu_sysinfo.syscall);
989
990	cpustats_p->states[CPU_IDLE] = cpu_stat_p->cpu_sysinfo.cpu[CPU_IDLE];
991	cpustats_p->states[CPU_USER] = cpu_stat_p->cpu_sysinfo.cpu[CPU_USER];
992	cpustats_p->states[CPU_KERNEL] = cpu_stat_p->cpu_sysinfo.cpu[CPU_KERNEL];
993	cpustats_p->states[CPUSTATE_IOWAIT] = cpu_stat_p->cpu_sysinfo.wait[W_IO] +
994	    cpu_stat_p->cpu_sysinfo.wait[W_PIO];
995	cpustats_p->states[CPUSTATE_SWAP] = cpu_stat_p->cpu_sysinfo.wait[W_SWAP];
996	cpustats_p->pswitch = cpu_stat_p->cpu_sysinfo.pswitch;
997	cpustats_p->trap = cpu_stat_p->cpu_sysinfo.trap;
998	cpustats_p->intr = cpu_stat_p->cpu_sysinfo.intr;
999	cpustats_p->syscall = cpu_stat_p->cpu_sysinfo.syscall;
1000	cpustats_p->sysfork = cpu_stat_p->cpu_sysinfo.sysfork;
1001	cpustats_p->sysvfork = cpu_stat_p->cpu_sysinfo.sysvfork;
1002	cpustats_p->pfault = cpu_stat_p->cpu_vminfo.hat_fault +
1003	    cpu_stat_p->cpu_vminfo.as_fault;
1004	cpustats_p->pgin = cpu_stat_p->cpu_vminfo.pgin;
1005	cpustats_p->pgout = cpu_stat_p->cpu_vminfo.pgout;
1006	cpustats_p++;
1007	cpu_stat_p++;
1008    }
1009
1010    cpucount = cpu_num;
1011
1012    dprintf("get_cpustats sees %d cpus and returns %08x\n", cpucount, cpustats);
1013
1014    return (cpustats);
1015#else /* !USE_KSTAT */
1016    int i;
1017    struct cpu cpu;
1018    unsigned int (*cp_stats_p)[CPUSTATES];
1019
1020    /* do we have more cpus than our caller? */
1021    if (cpucount > *cnt)
1022    {
1023	/* yes, so realloc their array, too */
1024	dprintf("realloc array from %d to %d\n", *cnt, cpucount);
1025	*cnt = cpucount;
1026	cp_stats = (unsigned int (*)[CPUSTATES])realloc(cp_stats,
1027			 cpucount * sizeof(unsigned int) * CPUSTATES);
1028    }
1029
1030    cp_stats_p = cp_stats;
1031    for (i = 0; i < cpucount; i++)
1032    {
1033	if (cpu_offset[i] != 0)
1034	{
1035	    /* get struct cpu for this processor */
1036	    (void) getkval (cpu_offset[i], (int *)(&cpu), sizeof (struct cpu), "cpu");
1037
1038	    (*cp_stats_p)[CPU_IDLE] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_IDLE];
1039	    (*cp_stats_p)[CPU_USER] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_USER];
1040	    (*cp_stats_p)[CPU_KERNEL] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_KERNEL];
1041	    (*cp_stats_p)[CPUSTATE_IOWAIT] = cpu.cpu_stat.cpu_sysinfo.wait[W_IO] +
1042		cpu.cpu_stat.cpu_sysinfo.wait[W_PIO];
1043	    (*cp_stats_p)[CPUSTATE_SWAP] = cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP];
1044	    cp_stats_p++;
1045	}
1046    }
1047
1048    return (cp_stats);
1049#endif /* USE_KSTAT */
1050}
1051
1052/*
1053 * void get_meminfo(long *total, long *fr)
1054 *
1055 * Get information about the system's physical memory.  Pass back values
1056 * for total available and amount of memory that is free (in kilobytes).
1057 * It returns 0 on success and -1 on any kind of failure.
1058 */
1059
1060int
1061get_meminfo(long *total, long *fr)
1062
1063{
1064    long freemem;
1065    static kstat_t *ks = NULL;
1066    kstat_named_t *kn;
1067
1068    /* total comes from sysconf */
1069    *total = pagetok(sysconf(_SC_PHYS_PAGES));
1070
1071    /* free comes from the kernel's freemem or from kstat */
1072    /* prefer kmem for this because kstat unix:0:system_pages
1073       can be slow on systems with lots of memory */
1074    if (kd)
1075    {
1076	(void) getkval(freemem_offset, (int *)(&freemem), sizeof(freemem),
1077		       "freemem");
1078    }
1079    else
1080    {
1081#ifdef USE_KSTAT
1082	/* only need to grab kstat chain once */
1083	if (ks == NULL)
1084	{
1085	    ks = kstat_lookup(kc, "unix", 0, "system_pages");
1086	}
1087
1088	if (ks != NULL &&
1089	    kstat_read(kc, ks, 0) != -1 &&
1090	    (kn = kstat_data_lookup(ks, "freemem")) != NULL)
1091	{
1092	    freemem = kstat_data_value_l(kn);
1093	}
1094	else
1095	{
1096	    freemem = -1;
1097	}
1098#else
1099	freemem = -1;
1100#endif
1101    }
1102
1103    *fr = freemem == -1 ? -1 : pagetok(freemem);
1104
1105    return (0);
1106}
1107
1108/*
1109 * void get_swapinfo(long *total, long *fr)
1110 *
1111 * Get information about the system's swap.  Pass back values for
1112 * total swap available and amount of swap that is free (in kilobytes).
1113 * It returns 0 on success and -1 on any kind of failure.
1114 */
1115
1116int
1117get_swapinfo(long *total, long *fr)
1118
1119{
1120    register int cnt, i;
1121    register long t, f;
1122    struct swaptable *swt;
1123    struct swapent *ste;
1124    static char path[256];
1125
1126    /* preset values to 0 just in case we have to return early */
1127    *total = 0;
1128    *fr = 0;
1129
1130    /* get total number of swap entries */
1131    if ((cnt = swapctl(SC_GETNSWP, 0)) == -1)
1132    {
1133	return (-1);
1134    }
1135
1136    /* allocate enough space to hold count + n swapents */
1137    swt = (struct swaptable *)malloc(sizeof(int) +
1138				     cnt * sizeof(struct swapent));
1139    if (swt == NULL)
1140    {
1141	return (-1);
1142    }
1143    swt->swt_n = cnt;
1144
1145    /* fill in ste_path pointers: we don't care about the paths, so we point
1146       them all to the same buffer */
1147    ste = &(swt->swt_ent[0]);
1148    i = cnt;
1149    while (--i >= 0)
1150    {
1151	ste++->ste_path = path;
1152    }
1153
1154    /* grab all swap info */
1155    if (swapctl(SC_LIST, swt) == -1)
1156    {
1157	return (-1);
1158    }
1159
1160    /* walk thru the structs and sum up the fields */
1161    t = f = 0;
1162    ste = &(swt->swt_ent[0]);
1163    i = cnt;
1164    while (--i >= 0)
1165    {
1166	/* dont count slots being deleted */
1167	if (!(ste->ste_flags & ST_INDEL) &&
1168	    !(ste->ste_flags & ST_DOINGDEL))
1169	{
1170	    t += ste->ste_pages;
1171	    f += ste->ste_free;
1172	}
1173	ste++;
1174    }
1175
1176    /* fill in the results */
1177    *total = pagetok(t);
1178    *fr = pagetok(f);
1179    free(swt);
1180
1181    /* good to go */
1182    return (0);
1183}
1184
1185int
1186machine_init (struct statics *statics)
1187{
1188    struct utmpx ut;
1189    struct utmpx *up;
1190    struct rlimit rlim;
1191    int i;
1192    char *p;
1193    int *ip;
1194    int nproc;
1195#ifndef USE_KSTAT
1196    int offset;
1197#endif
1198
1199    /* There's a buffer overflow bug in curses that can be exploited when
1200       we run as root.  By making sure that TERMINFO is set to something
1201       this bug is avoided.  This code thanks to Casper */
1202    if ((p = getenv("TERMINFO")) == NULL || *p == '\0')
1203    {
1204        putenv("TERMINFO=/usr/share/lib/terminfo/");
1205    }
1206
1207    /* perform the kvm_open - suppress error here */
1208    if ((kd = kvm_open (NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
1209    {
1210	/* save the error message: we may need it later */
1211	p = strerror(errno);
1212    }
1213    dprintf("kvm_open: fd %d\n", kd);
1214
1215    /*
1216     * turn off super group/user privs - but beware; we might
1217     * want the privs back later and we still have a fd to
1218     * /dev/kmem open so we can't use setgid()/setuid() as that
1219     * would allow a debugger to attach to this process. CD
1220     */
1221    setegid(getgid());
1222    seteuid(getuid()); /* super user not needed for NEW_PROC */
1223
1224#ifdef USE_KSTAT
1225    /* open kstat */
1226    if ((kc = kstat_open()) == NULL)
1227    {
1228	fprintf(stderr, "Unable to open kstat.\n");
1229	return(-1);
1230    }
1231    kcid = kc->kc_chain_id;
1232    dprintf("kstat_open: chain %d\n", kcid);
1233#endif
1234
1235    /* fill in the statics information */
1236    statics->procstate_names = procstatenames;
1237    statics->cpustate_names = cpustatenames;
1238    statics->memory_names = memorynames;
1239    statics->kernel_names = kernelnames;
1240    statics->order_names = ordernames;
1241    statics->flags.fullcmds = 1;
1242    statics->flags.warmup = 1;
1243    statics->flags.threads = 1;
1244
1245    /* get boot time */
1246    ut.ut_type = BOOT_TIME;
1247    if ((up = getutxid(&ut)) != NULL)
1248    {
1249	statics->boottime = up->ut_tv.tv_sec;
1250    }
1251    endutxent();
1252
1253    /* if the kvm_open succeeded, get the nlist */
1254    if (kd)
1255    {
1256	if (kvm_nlist (kd, nlst) < 0)
1257        {
1258	    perror ("kvm_nlist");
1259	    return (-1);
1260        }
1261	if (check_nlist (nlst) != 0)
1262	    return (-1);
1263    }
1264#ifndef USE_KSTAT
1265    /* if KSTAT is not available to us and we can't open /dev/kmem,
1266       this is a serious problem.
1267    */
1268    else
1269    {
1270	/* Print the error message here */
1271	(void) fprintf(stderr, "kvm_open: %s\n", p);
1272	return (-1);
1273    }
1274#endif
1275
1276    /* stash away certain offsets for later use */
1277    mpid_offset = nlst[X_MPID].n_value;
1278    nproc_offset = nlst[X_NPROC].n_value;
1279    avenrun_offset = nlst[X_AVENRUN].n_value;
1280    anoninfo_offset = nlst[X_ANONINFO].n_value;
1281    freemem_offset = nlst[X_FREEMEM].n_value;
1282    maxmem_offset = nlst[X_MAXMEM].n_value;
1283
1284#ifndef USE_KSTAT
1285    (void) getkval (nlst[X_NCPUS].n_value, (int *) (&cpucount),
1286		    sizeof (cpucount), "ncpus");
1287
1288    cpu_offset = (unsigned long *) malloc (cpucount * sizeof (unsigned long));
1289    for (i = offset = 0; i < cpucount; offset += sizeof(unsigned long)) {
1290        (void) getkval (nlst[X_CPU].n_value + offset,
1291                        (int *)(&cpu_offset[i]), sizeof (unsigned long),
1292                        nlst[X_CPU].n_name );
1293        if (cpu_offset[i] != 0)
1294            i++;
1295    }
1296#endif
1297
1298    /* we need to get the current nproc */
1299#ifdef USE_KSTAT
1300    /* get_nproc assumes that the chain has already been retrieved,
1301       so we need to do that here */
1302    kstat_safe_retrieve(&ks_system_misc, "unix", 0, "system_misc", NULL);
1303#endif
1304    nproc = get_nproc();
1305    dprintf("machine_init: nproc=%d\n", nproc);
1306
1307    /* hash table for procs and threads sized based on current nproc*/
1308    prochash = hash_create(nproc > 100 ? nproc * 2 + 1 : 521);
1309    threadhash = hash_create(nproc > 100 ? nproc * 4 + 1 : 2053);
1310
1311    /* calculate pageshift value */
1312    i = sysconf(_SC_PAGESIZE);
1313    pageshift = 0;
1314    while ((i >>= 1) > 0)
1315    {
1316	pageshift++;
1317    }
1318
1319    /* calculate an amount to shift to K values */
1320    /* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */
1321    pageshift -= 10;
1322
1323    /* now determine which pageshift function is appropriate for the
1324       result (have to because x << y is undefined for y < 0) */
1325    if (pageshift > 0)
1326    {
1327	/* this is the most likely */
1328	p_pagetok = pagetok_left;
1329    }
1330    else if (pageshift == 0)
1331    {
1332	p_pagetok = pagetok_none;
1333    }
1334    else
1335    {
1336	p_pagetok = pagetok_right;
1337	pageshift = -pageshift;
1338    }
1339
1340    /* we cache open files to improve performance, so we need to up
1341       the NOFILE limit */
1342    if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
1343    {
1344	/* set a new soft limit */
1345	maxfiles = (int)(rlim.rlim_max < MAXFILES ? rlim.rlim_max : MAXFILES);
1346	rlim.rlim_cur = (rlim_t)maxfiles;
1347	(void)setrlimit(RLIMIT_NOFILE, &rlim);
1348
1349	/* now leave some wiggle room above the maximum */
1350	maxfiles -= 20;
1351    }
1352
1353    /* set up the display indices */
1354    ip = proc_display;
1355    *ip++ = field_index("PID");
1356    *ip++ = field_index("USERNAME");
1357    *ip++ = field_index("NLWP");
1358    *ip++ = field_index("PRI");
1359    *ip++ = field_index("NICE");
1360    *ip++ = field_index("SIZE");
1361    *ip++ = field_index("RES");
1362    *ip++ = field_index("STATE");
1363    *ip++ = field_index("TIME");
1364    *ip++ = field_index("CPU");
1365    *ip++ = field_index("COMMAND");
1366    *ip = -1;
1367    ip = thr_display;
1368    *ip++ = field_index("PID");
1369    *ip++ = field_index("LWP");
1370    *ip++ = field_index("USERNAME");
1371    *ip++ = field_index("PRI");
1372    *ip++ = field_index("NICE");
1373    *ip++ = field_index("SIZE");
1374    *ip++ = field_index("RES");
1375    *ip++ = field_index("STATE");
1376    *ip++ = field_index("TIME");
1377    *ip++ = field_index("CPU");
1378    *ip++ = field_index("COMMAND");
1379    *ip = -1;
1380
1381    if (!(procdir = opendir (PROCFS)))
1382    {
1383	(void) fprintf (stderr, "Unable to open %s\n", PROCFS);
1384	return (-1);
1385    }
1386
1387    if (chdir (PROCFS))
1388    {				/* handy for later on when we're reading it */
1389	(void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS);
1390	return (-1);
1391    }
1392
1393    /* all done! */
1394    return (0);
1395}
1396
1397void
1398get_system_info (struct system_info *si)
1399{
1400    int avenrun[3];
1401
1402    static long cp_time[CPUSTATES];
1403    static long cp_old[CPUSTATES];
1404    static long cp_diff[CPUSTATES];
1405    static struct cpustats *cpustats = NULL;
1406    static struct cpustats sum_current;
1407    static struct cpustats sum_old;
1408    static int cpus = 0;
1409    register int j, i;
1410
1411    /* remember the old values and zero out the current */
1412    memcpy(&sum_old, &sum_current, sizeof(sum_current));
1413    memset(&sum_current, 0, sizeof(sum_current));
1414
1415    /* get important information */
1416    get_avenrun(avenrun);
1417
1418    /* get the cpu statistics arrays */
1419    cpustats = get_cpustats(&cpus, cpustats);
1420
1421    /* zero the cp_time array */
1422    memset(cp_time, 0, sizeof(cp_time));
1423
1424    /* sum stats in to a single array and a single structure */
1425    for (i = 0; i < cpus; i++)
1426    {
1427	for (j = 0; j < CPUSTATES; j++)
1428	{
1429	    cp_time[j] += cpustats[i].states[j];
1430	}
1431	sum_current.pswitch += cpustats[i].pswitch;
1432	sum_current.trap += cpustats[i].trap;
1433	sum_current.intr += cpustats[i].intr;
1434	sum_current.syscall += cpustats[i].syscall;
1435	sum_current.sysfork += cpustats[i].sysfork;
1436	sum_current.sysvfork += cpustats[i].sysvfork;
1437	sum_current.pfault += cpustats[i].pfault;
1438	sum_current.pgin += cpustats[i].pgin;
1439	sum_current.pgout += cpustats[i].pgout;
1440    }
1441
1442    /* convert cp_time counts to percentages */
1443    (void) percentages (CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
1444
1445    /* get mpid -- process id of last process */
1446    if (kd)
1447	(void) getkval(mpid_offset, &(si->last_pid), sizeof (si->last_pid), "mpid");
1448    else
1449	si->last_pid = -1;
1450
1451    /* convert load averages to doubles */
1452    for (i = 0; i < 3; i++)
1453	si->load_avg[i] = loaddouble (avenrun[i]);
1454
1455    /* get physical memory data */
1456    if (get_meminfo(&(memory_stats[MEMORY_TOTALMEM]),
1457		    &(memory_stats[MEMORY_FREEMEM])) == -1)
1458    {
1459	memory_stats[MEMORY_TOTALMEM] = memory_stats[MEMORY_FREEMEM] = -1;
1460    }
1461
1462    /* get swap data */
1463    if (get_swapinfo(&(memory_stats[MEMORY_TOTALSWAP]),
1464		     &(memory_stats[MEMORY_FREESWAP])) == -1)
1465    {
1466	memory_stats[MEMORY_TOTALSWAP] = memory_stats[MEMORY_FREESWAP] = -1;
1467    }
1468
1469    /* get kernel data */
1470    kernel_stats[KERNEL_CSWITCH] = diff_per_second(sum_current.pswitch, sum_old.pswitch);
1471    kernel_stats[KERNEL_TRAP] = diff_per_second(sum_current.trap, sum_old.trap);
1472    kernel_stats[KERNEL_INTR] = diff_per_second(sum_current.intr, sum_old.intr);
1473    kernel_stats[KERNEL_SYSCALL] = diff_per_second(sum_current.syscall, sum_old.syscall);
1474    kernel_stats[KERNEL_FORK] = diff_per_second(sum_current.sysfork + sum_current.sysvfork,
1475						sum_old.sysfork + sum_old.sysvfork);
1476    kernel_stats[KERNEL_PFAULT] = diff_per_second(sum_current.pfault, sum_old.pfault);
1477    kernel_stats[KERNEL_PGIN] = pagetok(diff_per_second(sum_current.pgin, sum_old.pgin));
1478    kernel_stats[KERNEL_PGOUT] = pagetok(diff_per_second(sum_current.pgout, sum_old.pgout));
1479
1480
1481    /* set arrays and strings */
1482    si->cpustates = cpu_states;
1483    si->memory = memory_stats;
1484    si->kernel = kernel_stats;
1485
1486    dprintf("get_system_info returns\n");
1487}
1488
1489static struct handle handle;
1490
1491caddr_t
1492get_process_info (
1493		   struct system_info *si,
1494		   struct process_select *sel,
1495		   int compare_index)
1496{
1497    register int i;
1498    register int total_procs;
1499    register int active_procs;
1500    register struct prpsinfo **prefp;
1501    register struct prpsinfo *pp;
1502    int nproc;
1503    int state;
1504
1505    /* these are copied out of sel for speed */
1506    int show_idle;
1507    int show_system;
1508    int show_uid;
1509    char *show_command;
1510
1511    /* these persist across calls */
1512    static struct prpsinfo **pref = NULL;
1513    static int pref_size = 0;
1514
1515    /* set up flags which define what we are going to select */
1516    show_idle = sel->idle;
1517    show_system = sel->system;
1518    show_uid = sel->uid != -1;
1519    show_fullcmd = sel->fullcmd;
1520    show_command = sel->command;
1521    show_threads = sel->threads;
1522
1523    /* allocate enough space for twice our current needs */
1524    nproc = get_nproc();
1525    if (nproc > procs_max())
1526    {
1527	procs_prealloc(2 * nproc);
1528    }
1529
1530    /* read all the proc structures */
1531    nproc = getptable();
1532
1533    /* allocate pref[] */
1534    if (pref_size < nproc)
1535    {
1536	if (pref != NULL)
1537	{
1538	    free(pref);
1539	}
1540	pref = (struct prpsinfo **)malloc(nproc * sizeof(struct prpsinfo *));
1541	dprintf("get_process_info: allocated %d prinfo pointers at %08x\n",
1542		nproc, pref);
1543	pref_size = nproc;
1544    }
1545
1546    /* get a pointer to the states summary array */
1547    si->procstates = process_states;
1548
1549    /* count up process states and get pointers to interesting procs */
1550    total_procs = 0;
1551    active_procs = 0;
1552    (void) memset (process_states, 0, sizeof (process_states));
1553    prefp = pref;
1554
1555    for (pp = procs_start(), i = 0; i < nproc;
1556	 i++, pp = procs_next())
1557    {
1558	dprintf("looking at #%d: %d.%d\n", i,
1559		pp->pr_pid, pp->pr_lwp.pr_lwpid);
1560	/*
1561	 *  Place pointers to each valid proc structure in pref[].
1562	 *  Process slots that are actually in use have a non-zero
1563	 *  status field.  Processes with SSYS set are system
1564	 *  processes---these get ignored unless show_sysprocs is set.
1565	 */
1566	if (pp->px_state != 0 &&
1567	    (show_system || ((pp->pr_flag & SSYS) == 0)))
1568	{
1569	    total_procs++;
1570	    state = (int)pp->px_state;
1571	    if (state > 0 && state < PROCSTATES)
1572	    {
1573		process_states[state]++;
1574	    }
1575	    else
1576	    {
1577		dprintf("process %d.%d: state out of bounds %d\n",
1578			pp->pr_pid, pp->pr_lwp.pr_lwpid, state);
1579	    }
1580
1581	    if ((!ZOMBIE(pp)) &&
1582		(show_idle || percent_cpu (pp) || (pp->px_state == SRUN) || (pp->px_state == SONPROC)) &&
1583		(!show_uid || pp->pr_uid == (uid_t) sel->uid) &&
1584		(show_command == NULL ||
1585		 strstr(pp->pr_fname, show_command) != NULL))
1586	    {
1587		*prefp++ = pp;
1588		active_procs++;
1589	    }
1590	}
1591    }
1592
1593    dprintf("total_procs %d, active_procs %d\n", total_procs, active_procs);
1594
1595    /* if requested, sort the "interesting" processes */
1596    qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *),
1597	   proc_compares[compare_index]);
1598
1599    /* remember active and total counts */
1600    si->p_total = total_procs;
1601    si->p_active = active_procs;
1602
1603    /* pass back a handle */
1604    handle.next_proc = pref;
1605    handle.remaining = active_procs;
1606    return ((caddr_t) & handle);
1607}
1608
1609static char p_header[MAX_COLS];
1610
1611char *
1612format_process_header(struct process_select *sel, caddr_t handle, int count)
1613
1614{
1615    int cols;
1616    char *p;
1617    int *fi;
1618    struct proc_field *fp;
1619
1620    /* check for null handle */
1621    if (handle == NULL)
1622    {
1623	return("");
1624    }
1625
1626    /* remember how many columns there are on the display */
1627    cols = display_columns();
1628
1629    /* mode & threads dictate format */
1630    fi = display_fields = sel->threads ? thr_display : proc_display;
1631
1632    /* set username field correctly */
1633    if (!sel->usernames)
1634    {
1635	/* display uids */
1636	field_subst(fi, FIELD_USERNAME, FIELD_UID);
1637    }
1638    else
1639    {
1640	/* display usernames */
1641	field_subst(fi, FIELD_UID, FIELD_USERNAME);
1642    }
1643
1644    /* walk thru fields and construct header */
1645    /* are we worried about overflow??? */
1646    p = p_header;
1647    while (*fi != -1)
1648    {
1649	fp = &(proc_field[*fi++]);
1650	if (fp->min_screenwidth <= cols)
1651	{
1652	    p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name);
1653	    *p++ = ' ';
1654	}
1655    }
1656    *--p = '\0';
1657
1658    return p_header;
1659}
1660
1661static char fmt[MAX_COLS];		/* static area where result is built */
1662
1663char *
1664format_next_process(caddr_t handle, char *(*get_userid)(int))
1665
1666{
1667    struct prpsinfo *pp;
1668    struct handle *hp;
1669    struct proc_field *fp;
1670    int *fi;
1671    int i;
1672    int cols;
1673    char *p;
1674    int len;
1675    int x;
1676
1677    /* find and remember the next proc structure */
1678    hp = (struct handle *)handle;
1679    pp = *(hp->next_proc++);
1680    hp->remaining--;
1681
1682    /* grab format descriptor */
1683    fi = display_fields;
1684
1685    /* screen width is a consideration, too */
1686    cols = display_columns();
1687
1688    /* build output by field */
1689    p = fmt;
1690    len = MAX_COLS;
1691    while ((i = *fi++) != -1)
1692    {
1693	fp = &(proc_field[i]);
1694	if (len > 0 && fp->min_screenwidth <= cols)
1695	{
1696	    x = (*(fp->format))(p, len, pp);
1697	    if (x >= len)
1698	    {
1699		dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n",
1700			x, len, p, p + len, fmt, fmt + sizeof(fmt));
1701		p += len;
1702		len = 0;
1703	    }
1704	    else
1705	    {
1706		p += x;
1707		*p++ = ' ';
1708		len -= x + 1;
1709	    }
1710	}
1711    }
1712    *--p = '\0';
1713
1714    /* return the result */
1715    return(fmt);
1716}
1717
1718/* comparison routines for qsort */
1719
1720/*
1721 * There are currently four possible comparison routines.  main selects
1722 * one of these by indexing in to the array proc_compares.
1723 *
1724 * Possible keys are defined as macros below.  Currently these keys are
1725 * defined:  percent cpu, cpu ticks, process state, resident set size,
1726 * total virtual memory usage.  The process states are ordered as follows
1727 * (from least to most important):  WAIT, zombie, sleep, stop, start, run.
1728 * The array declaration below maps a process state index into a number
1729 * that reflects this ordering.
1730 */
1731
1732/* First, the possible comparison keys.  These are defined in such a way
1733   that they can be merely listed in the source code to define the actual
1734   desired ordering.
1735 */
1736
1737#define ORDERKEY_PCTCPU  if (dresult = percent_cpu (p2) - percent_cpu (p1),\
1738			     (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
1739#define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
1740#define ORDERKEY_STATE   if ((result = (long) (sorted_state[(int)p2->px_state] - \
1741			       sorted_state[(int)p1->px_state])) == 0)
1742#define ORDERKEY_PRIO    if ((result = p2->px_pri - p1->px_pri) == 0)
1743#define ORDERKEY_RSSIZE  if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
1744#define ORDERKEY_MEM     if ((result = (p2->pr_size - p1->pr_size)) == 0)
1745#define ORDERKEY_LWP     if ((result = (p1->pr_lwp.pr_lwpid - p2->pr_lwp.pr_lwpid)) == 0)
1746#define ORDERKEY_PID     if ((result = (p1->pr_pid - p2->pr_pid)) == 0)
1747
1748/* Now the array that maps process state to a weight */
1749
1750unsigned char sorted_state[] =
1751{
1752  0,				/* not used		*/
1753  3,				/* sleep		*/
1754  6,				/* run			*/
1755  2,				/* zombie		*/
1756  4,				/* stop			*/
1757  5,				/* start		*/
1758  7,				/* run on a processor   */
1759  1				/* being swapped (WAIT)	*/
1760};
1761
1762
1763/* compare_cpu - the comparison function for sorting by cpu percentage */
1764
1765int
1766compare_cpu (struct prpsinfo **pp1, struct prpsinfo **pp2)
1767
1768{
1769    register struct prpsinfo *p1;
1770    register struct prpsinfo *p2;
1771    register long result;
1772    double dresult;
1773
1774    /* remove one level of indirection */
1775    p1 = *pp1;
1776    p2 = *pp2;
1777
1778    ORDERKEY_PCTCPU
1779    ORDERKEY_CPTICKS
1780    ORDERKEY_STATE
1781    ORDERKEY_PRIO
1782    ORDERKEY_RSSIZE
1783    ORDERKEY_MEM
1784    ORDERKEY_PID
1785    ORDERKEY_LWP
1786    ;
1787
1788    return (result);
1789}
1790
1791/* compare_size - the comparison function for sorting by total memory usage */
1792
1793int
1794compare_size (struct prpsinfo **pp1, struct prpsinfo **pp2)
1795
1796{
1797    register struct prpsinfo *p1;
1798    register struct prpsinfo *p2;
1799    register long result;
1800    double dresult;
1801
1802    /* remove one level of indirection */
1803    p1 = *pp1;
1804    p2 = *pp2;
1805
1806    ORDERKEY_MEM
1807    ORDERKEY_RSSIZE
1808    ORDERKEY_PCTCPU
1809    ORDERKEY_CPTICKS
1810    ORDERKEY_STATE
1811    ORDERKEY_PRIO
1812    ORDERKEY_PID
1813    ORDERKEY_LWP
1814    ;
1815
1816    return (result);
1817}
1818
1819/* compare_res - the comparison function for sorting by resident set size */
1820
1821int
1822compare_res (struct prpsinfo **pp1, struct prpsinfo **pp2)
1823
1824{
1825    register struct prpsinfo *p1;
1826    register struct prpsinfo *p2;
1827    register long result;
1828    double dresult;
1829
1830    /* remove one level of indirection */
1831    p1 = *pp1;
1832    p2 = *pp2;
1833
1834    ORDERKEY_RSSIZE
1835    ORDERKEY_MEM
1836    ORDERKEY_PCTCPU
1837    ORDERKEY_CPTICKS
1838    ORDERKEY_STATE
1839    ORDERKEY_PRIO
1840    ORDERKEY_PID
1841    ORDERKEY_LWP
1842    ;
1843
1844    return (result);
1845}
1846
1847/* compare_time - the comparison function for sorting by total cpu time */
1848
1849int
1850compare_time (struct prpsinfo **pp1, struct prpsinfo **pp2)
1851
1852{
1853    register struct prpsinfo *p1;
1854    register struct prpsinfo *p2;
1855    register long result;
1856    double dresult;
1857
1858    /* remove one level of indirection */
1859    p1 = *pp1;
1860    p2 = *pp2;
1861
1862    ORDERKEY_CPTICKS
1863    ORDERKEY_PCTCPU
1864    ORDERKEY_STATE
1865    ORDERKEY_PRIO
1866    ORDERKEY_MEM
1867    ORDERKEY_RSSIZE
1868    ORDERKEY_PID
1869    ORDERKEY_LWP
1870    ;
1871
1872    return (result);
1873}
1874
1875/* compare_pid - the comparison function for sorting by process id */
1876
1877int
1878compare_pid (struct prpsinfo **pp1, struct prpsinfo **pp2)
1879
1880{
1881    register struct prpsinfo *p1;
1882    register struct prpsinfo *p2;
1883    register long result;
1884
1885    /* remove one level of indirection */
1886    p1 = *pp1;
1887    p2 = *pp2;
1888
1889    ORDERKEY_PID
1890    ORDERKEY_LWP
1891    ;
1892
1893    return (result);
1894}
1895
1896/* get process table */
1897int
1898getptable (struct prpsinfo *baseptr)
1899{
1900    struct prpsinfo *currproc;	/* pointer to current proc structure	*/
1901#ifndef USE_NEW_PROC
1902    struct prstatus prstatus;     /* for additional information */
1903#endif
1904    int numprocs = 0;
1905    struct dirent *direntp;
1906    struct oldproc *op;
1907    hash_pos pos;
1908    hash_item_pid *hi;
1909    hash_item_pidthr *hip;
1910    prheader_t *prp;
1911    lwpsinfo_t *lwpp;
1912    pidthr_t pidthr;
1913    static struct timeval lasttime =
1914	{0, 0};
1915    struct timeval thistime;
1916    struct stat st;
1917    double timediff;
1918
1919    gettimeofday (&thistime, NULL);
1920    /*
1921     * To avoid divides, we keep times in nanoseconds.  This is
1922     * scaled by 1e7 rather than 1e9 so that when we divide we
1923     * get percent.
1924     */
1925    if (lasttime.tv_sec)
1926	timediff = ((double) thistime.tv_sec * 1.0e7 +
1927		    ((double) thistime.tv_usec * 10.0)) -
1928	    ((double) lasttime.tv_sec * 1.0e7 +
1929	     ((double) lasttime.tv_usec * 10.0));
1930    else
1931	timediff = 1.0e7;
1932
1933    /* get our first procs pointer */
1934    currproc = procs_start();
1935
1936    /* before reading /proc files, turn on root privs */
1937    /* (we don't care if this fails since it will be caught later) */
1938#ifndef USE_NEW_PROC
1939    seteuid(0);
1940#endif
1941
1942    for (rewinddir (procdir); (direntp = readdir (procdir));)
1943    {
1944	int fd;
1945	int pid;
1946	char buf[40];
1947
1948	/* skip dot files */
1949	if (direntp->d_name[0] == '.')
1950	    continue;
1951
1952	/* convert pid to a number (and make sure its valid) */
1953	pid = atoi(direntp->d_name);
1954	if (pid <= 0)
1955	    continue;
1956
1957	/* fetch the old proc data */
1958	op = (struct oldproc *)hash_lookup_pid(prochash, pid);
1959	if (op == NULL)
1960	{
1961	    /* new proc: create an entry for it */
1962	    op = (struct oldproc *)malloc(sizeof(struct oldproc));
1963	    hash_add_pid(prochash, pid, (void *)op);
1964	    op->pid = pid;
1965	    op->fd_psinfo = -1;
1966	    op->fd_lpsinfo = -1;
1967	    op->oldtime = 0.0;
1968	}
1969
1970	/* do we have a cached file? */
1971	fd = op->fd_psinfo;
1972	if (fd == -1)
1973	{
1974	    /* no: open the psinfo file */
1975	    snprintf(buf, sizeof(buf), "%s/psinfo", direntp->d_name);
1976	    if ((fd = open(buf, O_RDONLY)) < 0)
1977	    {
1978		/* cleanup??? */
1979		continue;
1980	    }
1981	}
1982
1983	/* read data from the file */
1984#ifdef USE_NEW_PROC
1985	if (pread(fd, currproc, sizeof(psinfo_t), 0) != sizeof(psinfo_t))
1986	{
1987	    (void) close (fd);
1988	    op->fd_psinfo = -1;
1989	    continue;
1990	}
1991#else
1992	if (ioctl(fd, PIOCPSINFO, currproc) < 0)
1993	{
1994	    (void) close(fd);
1995	    op->fd_psinfo = -1;
1996	    continue;
1997	}
1998
1999	if (ioctl(fd, PIOCSTATUS, &prstatus) < 0)
2000	{
2001	    /* not a show stopper -- just fill in the needed values */
2002	    currproc->pr_fill = 0;
2003	    currproc->px_onpro = 0;
2004	}
2005	else
2006	{
2007	    /* copy over the values we need from prstatus */
2008	    currproc->pr_fill = (short)prstatus.pr_nlwp;
2009	    currproc->px_onpro = prstatus.pr_processor;
2010	}
2011#endif
2012
2013	/*
2014	 * We track our own cpu% usage.
2015	 * We compute it based on CPU since the last update by calculating
2016	 * the difference in cumulative cpu time and dividing by the amount
2017	 * of time we measured between updates (timediff).
2018	 * NOTE:  Solaris 2.4 and higher do maintain CPU% in psinfo,
2019	 * but it does not produce the kind of results we really want,
2020	 * so we don't use it even though its there.
2021	 */
2022	if (lasttime.tv_sec > 0)
2023	{
2024	    percent_cpu(currproc) =
2025		(TIMESPEC_TO_DOUBLE(currproc->pr_time) - op->oldtime) / timediff;
2026	}
2027	else
2028	{
2029	    /* first screen -- no difference is possible */
2030	    percent_cpu(currproc) = 0.0;
2031	}
2032
2033	/* save data for next time */
2034	op->pid = currproc->pr_pid;
2035	op->oldtime = TIMESPEC_TO_DOUBLE(currproc->pr_time);
2036	op->owner_uid = currproc->pr_uid;
2037	op->seen = 1;
2038
2039	/* cache the file descriptor if we can */
2040	if (fd < maxfiles)
2041	{
2042	    op->fd_psinfo = fd;
2043	}
2044	else
2045	{
2046	    (void) close(fd);
2047	}
2048
2049#ifdef USE_NEW_PROC
2050	/* collect up the threads */
2051	/* use cached lps file if it's there */
2052	fd = op->fd_lpsinfo;
2053	if (fd == -1)
2054	{
2055	    snprintf(buf, sizeof(buf), "%s/lpsinfo", direntp->d_name);
2056	    fd = open(buf, O_RDONLY);
2057	}
2058
2059	/* make sure we have a valid descriptor and the file's current size */
2060	if (fd >= 0 && fstat(fd, &st) != -1)
2061	{
2062	    char *p;
2063	    int i;
2064
2065	    /* read the whole file */
2066	    p = malloc(st.st_size);
2067	    (void)pread(fd, p, st.st_size, 0);
2068
2069	    /* cache the file descriptor if we can */
2070	    if (fd < maxfiles)
2071	    {
2072		op->fd_lpsinfo = fd;
2073	    }
2074	    else
2075	    {
2076		(void)close(fd);
2077	    }
2078
2079	    /* the file starts with a struct prheader */
2080	    prp = (prheader_t *)p;
2081	    p += sizeof(prheader_t);
2082
2083	    /* there are prp->pr_nent entries in the file */
2084	    for (i = 0; i < prp->pr_nent; i++)
2085	    {
2086		/* process this entry */
2087		lwpp = (lwpsinfo_t *)p;
2088		p += prp->pr_entsize;
2089
2090		/* fetch the old thread data */
2091		/* this hash is indexed by both pid and lwpid */
2092		pidthr.k_pid = currproc->pr_pid;
2093		pidthr.k_thr = lwpp->pr_lwpid;
2094		dprintf("getptable: processing %d.%d\n",
2095			pidthr.k_pid, pidthr.k_thr);
2096		op = (struct oldproc *)hash_lookup_pidthr(threadhash, pidthr);
2097		if (op == NULL)
2098		{
2099		    /* new thread: create an entry for it */
2100		    op = (struct oldproc *)malloc(sizeof(struct oldproc));
2101		    hash_add_pidthr(threadhash, pidthr, (void *)op);
2102		    op->pid = pid;
2103		    op->lwpid = lwpp->pr_lwpid;
2104		    op->oldtime = 0.0;
2105		    dprintf("getptable: %d.%d: new thread\n",
2106			    pidthr.k_pid, pidthr.k_thr);
2107		}
2108
2109		/* are we showing individual threads? */
2110		if (show_threads)
2111		{
2112		    /* yes: if this is the first thread we reuse the proc
2113		       entry we have, otherwise we create a new one by
2114		       duping the current one */
2115		    if (i > 0)
2116		    {
2117			currproc = procs_dup(currproc);
2118			numprocs++;
2119		    }
2120
2121		    /* yes: copy over thread-specific data */
2122		    currproc->pr_time = lwpp->pr_time;
2123		    currproc->px_state = lwpp->pr_state;
2124		    currproc->px_pri = lwpp->pr_pri;
2125		    currproc->px_onpro = lwpp->pr_onpro;
2126		    currproc->pr_lwp.pr_lwpid = lwpp->pr_lwpid;
2127
2128		    /* calculate percent cpu for just this thread */
2129		    if (lasttime.tv_sec > 0)
2130		    {
2131			percent_cpu(currproc) =
2132			    (TIMESPEC_TO_DOUBLE(lwpp->pr_time) - op->oldtime) /
2133			    timediff;
2134		    }
2135		    else
2136		    {
2137			/* first screen -- no difference is possible */
2138			percent_cpu(currproc) = 0.0;
2139		    }
2140
2141		    dprintf("getptable: %d.%d: time %.0f, state %d, pctcpu %.2f\n",
2142			    currproc->pr_pid, lwpp->pr_lwpid,
2143			    TIMESPEC_TO_DOUBLE(currproc->pr_time),
2144			    currproc->px_state, percent_cpu(currproc));
2145		}
2146
2147		/* save data for next time */
2148		op->oldtime = TIMESPEC_TO_DOUBLE(lwpp->pr_time);
2149		op->seen = 1;
2150	    }
2151	    free(p);
2152	}
2153#endif
2154
2155	/* move to next */
2156	numprocs++;
2157	currproc = procs_next();
2158    }
2159
2160#ifndef USE_NEW_PROC
2161    /* turn off root privs */
2162    seteuid(getuid());
2163#endif
2164
2165    dprintf("getptable saw %d procs\n", numprocs);
2166
2167    /* scan the hash tables and remove dead entries */
2168    hi = hash_first_pid(prochash, &pos);
2169    while (hi != NULL)
2170    {
2171	op = (struct oldproc *)(hi->value);
2172	if (op->seen)
2173	{
2174	    op->seen = 0;
2175	}
2176	else
2177	{
2178	    dprintf("removing %d from prochash\n", op->pid);
2179	    if (op->fd_psinfo >= 0)
2180	    {
2181		(void)close(op->fd_psinfo);
2182	    }
2183	    if (op->fd_lpsinfo >= 0)
2184	    {
2185		(void)close(op->fd_lpsinfo);
2186	    }
2187	    hash_remove_pos_pid(&pos);
2188	    free(op);
2189	}
2190	hi = hash_next_pid(&pos);
2191    }
2192
2193    hip = hash_first_pidthr(threadhash, &pos);
2194    while (hip != NULL)
2195    {
2196	op = (struct oldproc *)(hip->value);
2197	if (op->seen)
2198	{
2199	    op->seen = 0;
2200	}
2201	else
2202	{
2203	    dprintf("removing %d from threadhash\n", op->pid);
2204	    hash_remove_pos_pidthr(&pos);
2205	    free(op);
2206	}
2207	hip = hash_next_pidthr(&pos);
2208    }
2209
2210    lasttime = thistime;
2211
2212    return numprocs;
2213}
2214
2215/*
2216 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
2217 *              the process does not exist.
2218 *              It is EXTREMLY IMPORTANT that this function work correctly.
2219 *              If top runs setuid root (as in SVR4), then this function
2220 *              is the only thing that stands in the way of a serious
2221 *              security problem.  It validates requests for the "kill"
2222 *              and "renice" commands.
2223 */
2224int
2225proc_owner (int pid)
2226{
2227    struct oldproc *op;
2228
2229    /* we keep this information in the hash table */
2230    op = (struct oldproc *)hash_lookup_pid(prochash, (pid_t)pid);
2231    if (op != NULL)
2232    {
2233	return((int)(op->owner_uid));
2234    }
2235    return(-1);
2236}
2237
2238/* older revisions don't supply a setpriority */
2239#if (OSREV < 55)
2240int
2241setpriority (int dummy, int who, int niceval)
2242{
2243    int scale;
2244    int prio;
2245    pcinfo_t pcinfo;
2246    pcparms_t pcparms;
2247    tsparms_t *tsparms;
2248
2249    strcpy (pcinfo.pc_clname, "TS");
2250    if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
2251	return (-1);
2252
2253    prio = niceval;
2254    if (prio > PRIO_MAX)
2255	prio = PRIO_MAX;
2256    else if (prio < PRIO_MIN)
2257	prio = PRIO_MIN;
2258
2259    tsparms = (tsparms_t *) pcparms.pc_clparms;
2260    scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
2261    tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
2262    pcparms.pc_cid = pcinfo.pc_cid;
2263
2264    if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
2265	return (-1);
2266
2267    return (0);
2268}
2269#endif
2270
2271