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
168 static 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
187 static unsigned long avenrun_offset;
188 static unsigned long mpid_offset;
189 #ifdef USE_KSTAT
190 static kstat_ctl_t *kc = NULL;
191 static kid_t kcid = 0;
192 #else
193 static unsigned long *cpu_offset;
194 #endif
195 static unsigned long nproc_offset;
196 static unsigned long freemem_offset;
197 static unsigned long maxmem_offset;
198 static unsigned long anoninfo_offset;
199 static int maxfiles = 256;
200 #define MAXFILES 2048
201 static int *display_fields;
202 static int show_threads = 0;
203 static int show_fullcmd;
204
205 /* get_process_info passes back a handle. This is what it looks like: */
206 struct 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 */
216 struct 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
230 hash_table *prochash;
231 hash_table *threadhash;
232
233 /*
234 * Structure for tracking per-cpu information
235 */
236 struct 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
260 struct 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 */
270 char *state_abbrev[] =
271 {"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
272
273 int process_states[PROCSTATES];
274 char *procstatenames[] =
275 {
276 "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
277 " starting, ", " on cpu, ", " swapped, ",
278 NULL
279 };
280
281 int cpu_states[CPUSTATES];
282 char *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 */
289 long memory_stats[5];
290 char *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 */
298 int kernel_stats[8];
299 char *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 */
312 char *ordernames[] =
313 {"cpu", "size", "res", "time", "pid", NULL};
314
315 /* forward definitions for comparison functions */
316 int compare_cpu();
317 int compare_size();
318 int compare_res();
319 int compare_time();
320 int compare_pid();
321
322 int (*proc_compares[])() = {
323 compare_cpu,
324 compare_size,
325 compare_res,
326 compare_time,
327 compare_pid,
328 NULL };
329
330 kvm_t *kd;
331 static 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 */
336 static int cpucount;
337
338 /* pagetok function is really a pointer to an appropriate function */
339 static int pageshift;
340 static long (*p_pagetok) ();
341 #define pagetok(size) ((*p_pagetok)(size))
342
343 /* useful externals */
344 extern char *myname;
345 extern void perror ();
346 extern int getptable ();
347 extern void quit ();
348
349 /* process formatting functions and data */
350
351 int
fmt_pid(char * buf,int sz,struct prpsinfo * pp)352 fmt_pid(char *buf, int sz, struct prpsinfo *pp)
353
354 {
355 return snprintf(buf, sz, "%6d", (int)pp->pr_pid);
356 }
357
358 int
fmt_username(char * buf,int sz,struct prpsinfo * pp)359 fmt_username(char *buf, int sz, struct prpsinfo *pp)
360
361 {
362 return snprintf(buf, sz, "%-8.8s", username(pp->pr_uid));
363 }
364
365 int
fmt_uid(char * buf,int sz,struct prpsinfo * pp)366 fmt_uid(char *buf, int sz, struct prpsinfo *pp)
367
368 {
369 return snprintf(buf, sz, "%6d", (int)pp->pr_uid);
370 }
371
372 int
fmt_nlwp(char * buf,int sz,struct prpsinfo * pp)373 fmt_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
379 int
fmt_pri(char * buf,int sz,struct prpsinfo * pp)380 fmt_pri(char *buf, int sz, struct prpsinfo *pp)
381
382 {
383 return snprintf(buf, sz, "%3d", pp->px_pri);
384 }
385
386 int
fmt_nice(char * buf,int sz,struct prpsinfo * pp)387 fmt_nice(char *buf, int sz, struct prpsinfo *pp)
388
389 {
390 return snprintf(buf, sz, "%4d", pp->px_nice - NZERO);
391 }
392
393 int
fmt_size(char * buf,int sz,struct prpsinfo * pp)394 fmt_size(char *buf, int sz, struct prpsinfo *pp)
395
396 {
397 return snprintf(buf, sz, "%5s", format_k(SIZE_K(pp)));
398 }
399
400 int
fmt_res(char * buf,int sz,struct prpsinfo * pp)401 fmt_res(char *buf, int sz, struct prpsinfo *pp)
402
403 {
404 return snprintf(buf, sz, "%5s", format_k(RSS_K(pp)));
405 }
406
407 int
fmt_state(char * buf,int sz,struct prpsinfo * pp)408 fmt_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
424 int
fmt_time(char * buf,int sz,struct prpsinfo * pp)425 fmt_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
431 int
fmt_cpu(char * buf,int sz,struct prpsinfo * pp)432 fmt_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
439 int
fmt_command(char * buf,int sz,struct prpsinfo * pp)440 fmt_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
447 int
fmt_lwp(char * buf,int sz,struct prpsinfo * pp)448 fmt_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
455 struct 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
474 static int proc_display[MAX_FIELDS];
475 static int thr_display[MAX_FIELDS];
476
477 int
field_index(char * col)478 field_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
498 void
field_subst(int * fp,int old,int new)499 field_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
pagetok_none(long size)515 long pagetok_none(long size)
516
517 {
518 return(size);
519 }
520
pagetok_left(long size)521 long pagetok_left(long size)
522
523 {
524 return(size << pageshift);
525 }
526
pagetok_right(long size)527 long 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 */
544 int
getkval(unsigned long offset,int * ptr,int size,char * refstr)545 getkval (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
574 static struct prpsinfo **allprocs = NULL;
575 static struct prpsinfo **nextproc = NULL;
576 static int maxprocs = 0;
577 static 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
586 void
procs_prealloc(int max)587 procs_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
621 struct prpsinfo *
procs_next()622 procs_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
634 struct prpsinfo *
procs_dup(struct prpsinfo * p)635 procs_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
651 struct prpsinfo *
procs_start()652 procs_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
666 int
procs_max()667 procs_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 */
679 int
check_nlist(register struct nlist * nlst)680 check_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
702 char *
format_header(register char * uname_field)703 format_header (register char *uname_field)
704 {
705 return ("");
706 }
707
708 #ifdef USE_KSTAT
709
710 long
kstat_data_value_l(kstat_named_t * kn)711 kstat_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
732 int
kstat_safe_retrieve(kstat_t ** ksp,char * module,int instance,char * name,void * buf)733 kstat_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
786 int
kstat_safe_namematch(int num,kstat_t ** ksparg,char * name,void * buf,int size)787 kstat_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
847 static kstat_t *ks_system_misc = NULL;
848
849 #endif /* USE_KSTAT */
850
851
852 int
get_avenrun(int avenrun[3])853 get_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
890 int
get_ncpus()891 get_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
912 int
get_nproc()913 get_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
934 struct cpustats *
get_cpustats(int * cnt,struct cpustats * cpustats)935 get_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
1060 int
get_meminfo(long * total,long * fr)1061 get_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
1116 int
get_swapinfo(long * total,long * fr)1117 get_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
1185 int
machine_init(struct statics * statics)1186 machine_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
1397 void
get_system_info(struct system_info * si)1398 get_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
1489 static struct handle handle;
1490
1491 caddr_t
get_process_info(struct system_info * si,struct process_select * sel,int compare_index)1492 get_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
1609 static char p_header[MAX_COLS];
1610
1611 char *
format_process_header(struct process_select * sel,caddr_t handle,int count)1612 format_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
1661 static char fmt[MAX_COLS]; /* static area where result is built */
1662
1663 char *
format_next_process(caddr_t handle,char * (* get_userid)(int))1664 format_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
1750 unsigned 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
1765 int
compare_cpu(struct prpsinfo ** pp1,struct prpsinfo ** pp2)1766 compare_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
1793 int
compare_size(struct prpsinfo ** pp1,struct prpsinfo ** pp2)1794 compare_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
1821 int
compare_res(struct prpsinfo ** pp1,struct prpsinfo ** pp2)1822 compare_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
1849 int
compare_time(struct prpsinfo ** pp1,struct prpsinfo ** pp2)1850 compare_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
1877 int
compare_pid(struct prpsinfo ** pp1,struct prpsinfo ** pp2)1878 compare_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 */
1897 int
getptable(struct prpsinfo * baseptr)1898 getptable (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 */
2224 int
proc_owner(int pid)2225 proc_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)
2240 int
setpriority(int dummy,int who,int niceval)2241 setpriority (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