xref: /minix/external/bsd/top/dist/machine/m_decosf1.c (revision b89261ba)
1 /*
2  * Copyright (c) 1984 through 2008, William LeFebvre
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  *     * Neither the name of William LeFebvre nor the names of other
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * top - a top users display for Unix
35  *
36  * SYNOPSIS:  OSF/1, Digital Unix 4.0, Compaq Tru64 5.0
37  *
38  * DESCRIPTION:
39  * This is the machine-dependent module for DEC OSF/1 and its descendents
40  * It is known to work on OSF/1 1.2, 1.3, 2.0-T3, 3.0, Digital Unix V4.0,
41  * Digital Unix 5.0, and Tru64 5.0.
42  * WARNING: if you use optimization with the standard "cc" compiler that
43  * .        comes with V3.0 the resulting executable may core dump.  If
44  * .        this happens, recompile without optimization.
45  *
46  * LIBS: -lmld -lmach
47  *
48  * CFLAGS: -DHAVE_GETOPT -DORDER
49  *
50  * AUTHOR:  Anthony Baxter, <anthony@aaii.oz.au>
51  * Derived originally from m_ultrix, by David S. Comay <dsc@seismo.css.gov>,
52  * although by now there is hardly any of the code from m_ultrix left.
53  * Helped a lot by having the source for syd(1), by Claus Kalle, and
54  * from several people at DEC who helped with providing information on
55  * some of the less-documented bits of the kernel interface.
56  *
57  * Modified: 31-Oct-94, Pat Welch, tpw@physics.orst.edu
58  *	changed _mpid to pidtab for compatibility with OSF/1 version 3.0
59  *
60  * Modified: 13-Dec-94, William LeFebvre, lefebvre@dis.anl.gov
61  *	removed used of pidtab (that was bogus) and changed things to
62  *	automatically detect the absence of _mpid in the nlist and
63  *	recover gracefully---this appears to be the only difference
64  *	with 3.0.
65  *
66  * Modified: 3-Mar-00, Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
67  *	added support for sort ordering.
68  */
69 /*
70  * Theory of operation:
71  *
72  * Use Mach calls to build up a structure that contains all the sorts
73  * of stuff normally found in a struct proc in a BSD system. Then
74  * everything else uses this structure. This has major performance wins,
75  * and also should work for future versions of the O/S.
76  */
77 
78 #include "config.h"
79 
80 #include <sys/types.h>
81 #include <sys/signal.h>
82 #include <sys/param.h>
83 
84 #include <string.h>
85 #include <sys/user.h>
86 #include <stdio.h>
87 #include <nlist.h>
88 #include <math.h>
89 #include <sys/dir.h>
90 #include <sys/user.h>
91 #include <sys/proc.h>
92 #include <sys/dk.h>
93 #include <sys/vm.h>
94 #include <sys/file.h>
95 #include <sys/time.h>
96 /* #include <machine/pte.h> */
97 /* forward declarations, needed by <net/if.h> included from <sys/table.h> */
98 struct rtentry;
99 struct mbuf;
100 #include <sys/table.h>
101 #include <mach.h>
102 #include <mach/mach_types.h>
103 #include <mach/vm_statistics.h>
104 #include <sys/syscall.h> /* for SYS_setpriority, in setpriority(), below */
105 
106 
107 #include "top.h"
108 #include "machine.h"
109 #include "utils.h"
110 
111 extern int errno, sys_nerr;
112 extern char *sys_errlist[];
113 #define strerror(e) (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error")
114 
115 #define VMUNIX	"/vmunix"
116 #define KMEM	"/dev/kmem"
117 #define MEM	"/dev/mem"
118 
119 /* get_process_info passes back a handle.  This is what it looks like: */
120 
121 struct handle
122 {
123     struct osf1_top_proc **next_proc;	/* points to next valid proc pointer */
124     int remaining;		/* number of pointers remaining */
125 };
126 
127 /* declarations for load_avg */
128 #include "loadavg.h"
129 
130 /* definitions for indices in the nlist array */
131 #define X_MPID		0
132 
133 static struct nlist nlst[] = {
134     { "_mpid" },		/* 0 */
135     { 0 }
136 };
137 
138 /* Some versions of OSF/1 don't support reporting of the last PID.
139    This flag indicates whether or not we are reporting the last PID. */
140 static int do_last_pid = 1;
141 
142 /*
143  *  These definitions control the format of the per-process area
144  */
145 
146 static char header[] =
147   "   PID X        PRI NICE  SIZE   RES STATE   TIME    CPU COMMAND";
148 /* 01234567   -- field to fill in starts at header+7 */
149 #define UNAME_START 7
150 
151 #define Proc_format \
152 	"%6d %-8.8s %3d %4d %5s %5s %-5s %-6s %5.2f%% %s"
153 
154 
155 /* process state names for the "STATE" column of the display */
156 /* the extra nulls in the string "run" are for adding a slash and
157  * the processor number when needed. Although OSF/1 doesnt support
158  * multiple processors yet, (and this module _certainly_ doesnt
159  * support it, either, we may as well plan for the future. :-)
160  */
161 
162 char *state_abbrev[] =
163 {
164     "", "run\0\0\0", "WAIT", "sleep", "sleep", "stop", "halt", "???", "zomb"
165 };
166 
167 
168 static int kmem, mem;
169 
170 /* values that we stash away in _init and use in later routines */
171 
172 static double logcpu;
173 
174 /* these are retrieved from the kernel in _init */
175 
176 static unsigned long proc;
177 static          int  nproc;
178 static load_avg  ccpu;
179 
180 typedef long mtime_t;
181 
182 /* these are offsets obtained via nlist and used in the get_ functions */
183 
184 static unsigned long mpid_offset;
185 
186 /* these are for detailing the process states */
187 
188 int process_states[9];
189 char *procstatenames[] = {
190     "", " running, ", " waiting, ", " sleeping, ", " idle, ",
191     " stopped, ", " halted, ", "", " zombie",
192     NULL
193 };
194 
195 /* these are for detailing the cpu states */
196 
197 int cpu_states[5];
198 char *cpustatenames[] = {
199     "user", "nice", "system", "wio", "idle", NULL
200 };
201 
202 long old_cpu_ticks[5];
203 
204 /* these are for detailing the memory statistics */
205 
206 long memory_stats[5];
207 char *memorynames[] = {
208     "K active, ", "K inactive, ", "K total, ", "K free", NULL
209 };
210 
211 long swap_stats[3];
212 char *swapnames[] = {
213     "K in use, ", "K total", NULL
214 };
215 
216 /* these are names given to allowed sorting orders -- first is default */
217 char *ordernames[] = {
218     "cpu", "size", "res", "time", NULL
219 };
220 
221 /* forward definitions for comparison functions */
222 int compare_cpu();
223 int compare_size();
224 int compare_res();
225 int compare_time();
226 
227 int (*proc_compares[])() = {
228     compare_cpu,
229     compare_size,
230     compare_res,
231     compare_time,
232     NULL
233 };
234 
235 /* these are for getting the memory statistics */
236 
237 static int pageshift;		/* log base 2 of the pagesize */
238 
239 /* define pagetok in terms of pageshift */
240 
241 #define pagetok(size) ((size) << pageshift)
242 
243 /* take a process, make it a mach task, and grab all the info out */
244 void do_threads_calculations();
245 
246 /*
247  * Because I dont feel like repeatedly grunging through the kernel with
248  * Mach calls, and I also dont want the horrid performance hit this
249  * would give, I read the stuff I need out, and put in into my own
250  * structure, for later use.
251  */
252 
253 struct osf1_top_proc {
254     size_t p_mach_virt_size;
255     char p_mach_state;
256     int p_flag;
257     fixpt_t p_mach_pct_cpu; /* aka p_pctcpu */
258     int used_ticks;
259     size_t process_size;
260     pid_t p_pid;
261     uid_t p_ruid;
262     char p_pri;
263     char p_nice;
264     size_t p_rssize;
265     char u_comm[PI_COMLEN + 1];
266 } ;
267 
268 /* these are for keeping track of the proc array */
269 
270 static int bytes;
271 static int pref_len;
272 static struct osf1_top_proc *pbase;
273 static struct osf1_top_proc **pref;
274 
275 /* useful externals */
276 extern int errno;
277 extern char *sys_errlist[];
278 
279 long percentages();
280 
281 machine_init(statics)
282 struct statics *statics;
283 {
284     register int i = 0;
285     register int pagesize;
286     struct tbl_sysinfo sibuf;
287 
288     if ((kmem = open(KMEM, O_RDONLY)) == -1) {
289 	perror(KMEM);
290 	return(-1);
291     }
292     if ((mem = open(MEM, O_RDONLY)) == -1) {
293 	perror(MEM);
294 	return(-1);
295     }
296 
297     /* get the list of symbols we want to access in the kernel */
298     if (nlist(VMUNIX, nlst) == -1)
299     {
300 	perror("TOP(nlist)");
301 	return (-1);
302     }
303 
304     if (nlst[X_MPID].n_type == 0)
305     {
306 	/* this kernel has no _mpid, so go without */
307 	do_last_pid = 0;
308     }
309     else
310     {
311 	/* stash away mpid pointer for later use */
312 	mpid_offset = nlst[X_MPID].n_value;
313     }
314 
315     /* get the symbol values out of kmem */
316     nproc  = table(TBL_PROCINFO, 0, (struct tbl_procinfo *)NULL, INT_MAX, 0);
317 
318     /* allocate space for proc structure array and array of pointers */
319     bytes = nproc * sizeof(struct osf1_top_proc);
320     pbase = (struct osf1_top_proc *)malloc(bytes);
321     pref  = (struct osf1_top_proc **)malloc(nproc *
322                                               sizeof(struct osf1_top_proc *));
323 
324     /* Just in case ... */
325     if (pbase == (struct osf1_top_proc *)NULL ||
326                                   pref == (struct osf1_top_proc **)NULL)
327     {
328 	fprintf(stderr, "top: cannot allocate sufficient memory\n");
329 	return(-1);
330     }
331 
332     /* get the page size with "getpagesize" and calculate pageshift from it */
333     pagesize = getpagesize();
334     pageshift = 0;
335     while (pagesize > 1)
336     {
337 	pageshift++;
338 	pagesize >>= 1;
339     }
340 
341     /* we only need the amount of log(2)1024 for our conversion */
342     pageshift -= LOG1024;
343 
344     /* fill in the statics information */
345     statics->procstate_names = procstatenames;
346     statics->cpustate_names = cpustatenames;
347     statics->memory_names = memorynames;
348     statics->order_names = ordernames;
349     statics->swap_names = swapnames;
350 
351     /* initialise this, for calculating cpu time */
352     if (table(TBL_SYSINFO,0,&sibuf,1,sizeof(struct tbl_sysinfo))<0) {
353 	perror("TBL_SYSINFO");
354 	return(-1);
355     }
356     old_cpu_ticks[0] = sibuf.si_user;
357     old_cpu_ticks[1] = sibuf.si_nice;
358     old_cpu_ticks[2] = sibuf.si_sys;
359     old_cpu_ticks[3] = sibuf.wait;
360     old_cpu_ticks[4] = sibuf.si_idle;
361 
362     /* all done! */
363     return(0);
364 }
365 
format_header(uname_field)366 char *format_header(uname_field)
367 register char *uname_field;
368 {
369     register char *ptr;
370 
371     ptr = header + UNAME_START;
372     while (*uname_field != '\0')
373     {
374 	*ptr++ = *uname_field++;
375     }
376 
377     return(header);
378 }
379 
get_system_info(si)380 void get_system_info(si)
381 struct system_info *si;
382 {
383     struct tbl_loadavg labuf;
384     struct tbl_sysinfo sibuf;
385     struct tbl_swapinfo swbuf;
386     vm_statistics_data_t vmstats;
387     int swap_pages=0,swap_free=0,i;
388     long new_ticks[5],diff_ticks[5];
389     long delta_ticks;
390 
391     if (do_last_pid)
392     {
393 	/* last pid assigned */
394 	(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
395 		       "_mpid");
396     }
397     else
398     {
399 	si->last_pid = -1;
400     }
401 
402     /* get load averages */
403     if (table(TBL_LOADAVG,0,&labuf,1,sizeof(struct tbl_loadavg))<0) {
404 	perror("TBL_LOADAVG");
405 	return;
406     }
407     if (labuf.tl_lscale)   /* scaled */
408 	for(i=0;i<3;i++)
409 	    si->load_avg[i] = ((double)labuf.tl_avenrun.l[i] /
410                                             (double)labuf.tl_lscale );
411     else                   /* not scaled */
412 	for(i=0;i<3;i++)
413 	    si->load_avg[i] = labuf.tl_avenrun.d[i];
414 
415     /* array of cpu state counters */
416     if (table(TBL_SYSINFO,0,&sibuf,1,sizeof(struct tbl_sysinfo))<0) {
417 	perror("TBL_SYSINFO");
418 	return;
419     }
420     new_ticks[0] = sibuf.si_user ; new_ticks[1] = sibuf.si_nice;
421     new_ticks[2] = sibuf.si_sys  ; new_ticks[3] = sibuf.wait;
422     new_ticks[4] = sibuf.si_idle;
423     delta_ticks=0;
424     for(i=0;i<5;i++) {
425 	diff_ticks[i] = new_ticks[i] - old_cpu_ticks[i];
426 	delta_ticks += diff_ticks[i];
427 	old_cpu_ticks[i] = new_ticks[i];
428     }
429     si->cpustates = cpu_states;
430     if(delta_ticks)
431 	for(i=0;i<5;i++)
432 	    si->cpustates[i] = (int)( ( (double)diff_ticks[i] /
433                                            (double)delta_ticks ) * 1000 );
434 
435     /* memory information */
436     /* this is possibly bogus - we work out total # pages by */
437     /* adding up the free, active, inactive, wired down, and */
438     /* zero filled. Anyone who knows a better way, TELL ME!  */
439     /* Change: dont use zero filled. */
440     (void) vm_statistics(task_self(),&vmstats);
441 
442     /* thanks DEC for the table() command. No thanks at all for   */
443     /* omitting the man page for it from OSF/1 1.2, and failing   */
444     /* to document SWAPINFO in the 1.3 man page. Lets hear it for */
445     /* include files. */
446     i=0;
447     while(table(TBL_SWAPINFO,i,&swbuf,1,sizeof(struct tbl_swapinfo))>0) {
448 	swap_pages += swbuf.size;
449 	swap_free  += swbuf.free;
450 	i++;
451     }
452     memory_stats[0] = pagetok(vmstats.active_count);
453     memory_stats[1] = pagetok(vmstats.inactive_count);
454     memory_stats[2] = pagetok((vmstats.free_count + vmstats.active_count +
455 	vmstats.inactive_count + vmstats.wire_count));
456     memory_stats[3] = pagetok(vmstats.free_count);
457     swap_stats[0] = pagetok(swap_pages - swap_free);
458     swap_stats[1] = pagetok(swap_pages);
459     si->memory = memory_stats;
460     si->swap = swap_stats;
461 }
462 
463 static struct handle handle;
464 
get_process_info(si,sel,compare_index)465 caddr_t get_process_info(si, sel, compare_index)
466 struct system_info *si;
467 struct process_select *sel;
468 int compare_index;
469 {
470     register int i;
471     register int total_procs;
472     register int active_procs;
473     register struct osf1_top_proc **prefp;
474     register struct osf1_top_proc *pp;
475     struct tbl_procinfo p_i[8];
476     int j,k,r;
477 
478     /* these are copied out of sel for speed */
479     int show_idle;
480     int show_uid;
481     int show_command;
482 
483     /* get a pointer to the states summary array */
484     si->procstates = process_states;
485 
486     /* set up flags which define what we are going to select */
487     show_idle = sel->idle;
488     show_uid = sel->uid != -1;
489     show_command = sel->command != NULL;
490 
491     /* count up process states and get pointers to interesting procs */
492     total_procs = 0;
493     active_procs = 0;
494     memset((char *)process_states, 0, sizeof(process_states));
495     prefp = pref;
496     pp=pbase;
497     for (j=0; j<nproc; j += 8)
498     {
499 	r = table(TBL_PROCINFO, j, (struct tbl_procinfo *)p_i, 8,
500                                                sizeof(struct tbl_procinfo));
501 	for (k=0; k < r; k++ , pp++)
502 	{
503 	    if(p_i[k].pi_pid == 0)
504 	    {
505 		pp->p_pid = 0;
506 	    }
507 	    else
508 	    {
509 		pp->p_pid = p_i[k].pi_pid;
510 		pp->p_ruid = p_i[k].pi_ruid;
511 		pp->p_flag = p_i[k].pi_flag;
512 		pp->p_nice = getpriority(PRIO_PROCESS,p_i[k].pi_pid);
513 		/* Load useful values into the proc structure */
514 		do_threads_calculations(pp);
515 		/*
516 		 *  Place pointers to each valid proc structure in pref[].
517 		 *  Process slots that are actually in use have a non-zero
518 		 *  status field.
519 		 */
520 #ifdef DEBUG
521 		/*
522 		 *  Emit debug info about all processes before selection.
523 		 */
524 		fprintf(stderr, "pid = %d ruid = %d comm = %s p_mach_state = %d p_stat = %d p_flag = 0x%x\n",
525 			pp->p_pid, pp->p_ruid, p_i[k].pi_comm,
526 			pp->p_mach_state, p_i[k].pi_status, pp->p_flag);
527 #endif
528 		if (pp->p_mach_state != 0)
529 		{
530 		    total_procs++;
531 		    process_states[pp->p_mach_state]++;
532 		    if ((pp->p_mach_state != 8) &&
533 			(show_idle || (pp->p_mach_pct_cpu != 0) ||
534 			 (pp->p_mach_state == 1)) &&
535 			(!show_uid || pp->p_ruid == (uid_t)sel->uid)) {
536 			*prefp++ = pp;
537 			active_procs++;
538 		    }
539 		}
540 	    }
541 	}
542     }
543 
544     /* if requested, sort the "interesting" processes */
545     if (proc_compares[compare_index] != NULL)
546     {
547 	qsort((char *)pref, active_procs, sizeof(struct osf1_top_proc *),
548 	      proc_compares[compare_index]);
549     }
550 
551     /* remember active and total counts */
552     si->p_total = total_procs;
553     si->p_active = pref_len = active_procs;
554 
555     /* pass back a handle */
556     handle.next_proc = pref;
557     handle.remaining = active_procs;
558     return((caddr_t)&handle);
559 }
560 
561 char fmt[MAX_COLS];		/* static area where result is built */
562 
format_next_process(handle,get_userid)563 char *format_next_process(handle, get_userid)
564 caddr_t handle;
565 char *(*get_userid)();
566 {
567     register struct osf1_top_proc *pp;
568     register long cputime;
569     register double pct;
570     struct user u;
571     struct handle *hp;
572 
573     /* find and remember the next proc structure */
574     hp = (struct handle *)handle;
575     pp = *(hp->next_proc++);
576     hp->remaining--;
577 
578     /* get the process's user struct and set cputime */
579 
580     if (table(TBL_UAREA,pp->p_pid,&u,1,sizeof(struct user))<0) {
581     /* whoops, it must have died between the read of the proc area
582      * and now. Oh well, lets just dump some meaningless thing out
583      * to keep the rest of the program happy
584      */
585 	sprintf(fmt,
586 		Proc_format,
587 		pp->p_pid,
588 		(*get_userid)(pp->p_ruid),
589 		0,
590 		0,
591 		"",
592 		"",
593 		"dead",
594 		"",
595 		0.0,
596 		"<dead>");
597 	    return(fmt);
598     }
599 
600     /* set u_comm for system processes */
601     if (u.u_comm[0] == '\0')
602     {
603 	if (pp->p_pid == 0)
604 	{
605 	    (void) strcpy(u.u_comm, "[idle]");
606 	}
607 	else if (pp->p_pid == 2)
608 	{
609 	    (void) strcpy(u.u_comm, "[execpt.hndlr]");
610 	}
611     }
612 
613     /* Check if process is in core */
614     if (!(pp->p_flag & SLOAD)) {
615 	/*
616 	 * Print swapped processes as <pname>
617 	 */
618 	char buf[sizeof(u.u_comm)];
619 	(void) strncpy(buf, u.u_comm, sizeof(u.u_comm));
620 	u.u_comm[0] = '<';
621 	(void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2);
622 	u.u_comm[sizeof(u.u_comm) - 2] = '\0';
623 	(void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1);
624 	u.u_comm[sizeof(u.u_comm) - 1] = '\0';
625     }
626 
627     cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec;
628 
629     /* calculate the base for cpu percentages */
630     pct = pctdouble(pp->p_mach_pct_cpu);
631 
632     /* format this entry */
633     sprintf(fmt,
634 	    Proc_format,
635 	    pp->p_pid,
636 	    (*get_userid)(pp->p_ruid),
637 	    pp->p_pri,
638 	    pp->p_nice,
639             format_k(pp->p_mach_virt_size/1024),
640             format_k(pp->p_rssize/1000),
641 	    state_abbrev[pp->p_mach_state],
642 	    format_time(cputime),
643 	    100.0 * ((double)pp->p_mach_pct_cpu / 10000.0),
644 	    printable(u.u_comm));
645 
646     /* return the result */
647     return(fmt);
648 }
649 
650 /*
651  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
652  *	"offset" is the byte offset into the kernel for the desired value,
653  *  	"ptr" points to a buffer into which the value is retrieved,
654  *  	"size" is the size of the buffer (and the object to retrieve),
655  *  	"refstr" is a reference string used when printing error meessages,
656  *	    if "refstr" starts with a '!', then a failure on read will not
657  *  	    be fatal (this may seem like a silly way to do things, but I
658  *  	    really didn't want the overhead of another argument).
659  *
660  */
661 
getkval(offset,ptr,size,refstr)662 getkval(offset, ptr, size, refstr)
663 
664 unsigned long offset;
665 int *ptr;
666 int size;
667 char *refstr;
668 
669 {
670     if (lseek(kmem, (long)offset, L_SET) == -1) {
671         if (*refstr == '!')
672             refstr++;
673         (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
674 		       refstr, strerror(errno));
675         quit(23);
676     }
677     if (read(kmem, (char *) ptr, size) == -1) {
678         if (*refstr == '!')
679             return(0);
680         else {
681             (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM,
682 			   refstr, strerror(errno));
683             quit(23);
684         }
685     }
686     return(1);
687 }
688 
689 /* comparison routines for qsort */
690 
691 /*
692  * There are currently four possible comparison routines.  main selects
693  * one of these by indexing in to the array proc_compares.
694  *
695  * Possible keys are defined as macros below.  Currently these keys are
696  * defined:  percent cpu, cpu ticks, process state, resident set size,
697  * total virtual memory usage.  The process states are ordered as follows
698  * (from least to most important):  WAIT, zomb, ???, halt, idle, sleep,
699  * stop, run.  The array declaration below maps a process state index into
700  * a number that reflects this ordering.
701  */
702 
703 /* First, the possible comparison keys.  These are defined in such a way
704    that they can be merely listed in the source code to define the actual
705    desired ordering.
706  */
707 
708 #define ORDERKEY_PCTCPU  if (lresult = p2->p_mach_pct_cpu - p1->p_mach_pct_cpu,\
709                            (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
710 #define ORDERKEY_CPTICKS if ((result = p2->used_ticks - p1->used_ticks) == 0)
711 #define ORDERKEY_STATE   if ((result = sorted_state[p2->p_mach_state] - \
712                             sorted_state[p1->p_mach_state])  == 0)
713 #define ORDERKEY_PRIO    if ((result = p2->p_pri - p1->p_pri) == 0)
714 #define ORDERKEY_RSSIZE  if ((result = p2->p_rssize - p1->p_rssize) == 0)
715 #define ORDERKEY_MEM     if ((result = p2->p_mach_virt_size - p1->p_mach_virt_size) == 0)
716 
717 /* Now the array that maps process state to a weight */
718 
719 static unsigned char sorted_state[] =
720 {
721    0, /*""*/
722    8, /*"run"*/
723    1, /*"WAIT"*/
724    6, /*"sleep"*/
725    5, /*"idle"*/
726    7, /*"stop"*/
727    4, /*"halt"*/
728    3, /*"???"*/
729    2, /*"zomb"*/
730 };
731 
732 /* compare_cpu - the comparison function for sorting by cpu percentage */
733 
734 compare_cpu(pp1, pp2)
735 
736 struct osf1_top_proc **pp1;
737 struct osf1_top_proc **pp2;
738 
739 {
740     register struct osf1_top_proc *p1;
741     register struct osf1_top_proc *p2;
742     register long result;
743     register pctcpu lresult;
744 
745     /* remove one level of indirection */
746     p1 = *pp1;
747     p2 = *pp2;
748 
749     ORDERKEY_PCTCPU
750     ORDERKEY_CPTICKS
751     ORDERKEY_STATE
752     ORDERKEY_PRIO
753     ORDERKEY_RSSIZE
754     ORDERKEY_MEM
755     ;
756 
757     return(result);
758 }
759 
760 /* compare_size - the comparison function for sorting by total memory usage */
761 
762 compare_size(pp1, pp2)
763 
764 struct osf1_top_proc **pp1;
765 struct osf1_top_proc **pp2;
766 
767 {
768     register struct osf1_top_proc *p1;
769     register struct osf1_top_proc *p2;
770     register long result;
771     register pctcpu lresult;
772 
773     /* remove one level of indirection */
774     p1 = *pp1;
775     p2 = *pp2;
776 
777     ORDERKEY_MEM
778     ORDERKEY_RSSIZE
779     ORDERKEY_PCTCPU
780     ORDERKEY_CPTICKS
781     ORDERKEY_STATE
782     ORDERKEY_PRIO
783     ;
784 
785     return(result);
786 }
787 
788 /* compare_res - the comparison function for sorting by resident set size */
789 
790 compare_res(pp1, pp2)
791 
792 struct osf1_top_proc **pp1;
793 struct osf1_top_proc **pp2;
794 
795 {
796     register struct osf1_top_proc *p1;
797     register struct osf1_top_proc *p2;
798     register long result;
799     register pctcpu lresult;
800 
801     /* remove one level of indirection */
802     p1 = *pp1;
803     p2 = *pp2;
804 
805     ORDERKEY_RSSIZE
806     ORDERKEY_MEM
807     ORDERKEY_PCTCPU
808     ORDERKEY_CPTICKS
809     ORDERKEY_STATE
810     ORDERKEY_PRIO
811     ;
812 
813     return(result);
814 }
815 
816 /* compare_time - the comparison function for sorting by total cpu time */
817 
818 compare_time(pp1, pp2)
819 
820 struct osf1_top_proc **pp1;
821 struct osf1_top_proc **pp2;
822 
823 {
824     register struct osf1_top_proc *p1;
825     register struct osf1_top_proc *p2;
826     register long result;
827     register pctcpu lresult;
828 
829     /* remove one level of indirection */
830     p1 = *pp1;
831     p2 = *pp2;
832 
833     ORDERKEY_CPTICKS
834     ORDERKEY_PCTCPU
835     ORDERKEY_STATE
836     ORDERKEY_PRIO
837     ORDERKEY_RSSIZE
838     ORDERKEY_MEM
839     ;
840 
841     return(result);
842 }
843 
844 /*
845  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
846  *		the process does not exist.
847  *		It is EXTREMLY IMPORTANT that this function work correctly.
848  *		If top runs setuid root (as in SVR4), then this function
849  *		is the only thing that stands in the way of a serious
850  *		security problem.  It validates requests for the "kill"
851  *		and "renice" commands.
852  */
853 
proc_owner(pid)854 int proc_owner(pid)
855 
856 int pid;
857 
858 {
859     register int cnt;
860     register struct osf1_top_proc **prefp;
861     register struct osf1_top_proc *pp;
862 
863     prefp = pref;
864     cnt = pref_len;
865     while (--cnt >= 0)
866     {
867 	if ((pp = *prefp++)->p_pid == (pid_t)pid)
868 	{
869 	    return((int)pp->p_ruid);
870 	}
871     }
872     return(-1);
873 }
874 
875 
876 /*
877  * We use the Mach interface, as well as the table(UAREA,,,) call to
878  * get some more information, then put it into unused fields in our
879  * copy of the proc structure, to make it faster and easier to get at
880  * later.
881  */
do_threads_calculations(thisproc)882 void do_threads_calculations(thisproc)
883 struct osf1_top_proc *thisproc;
884 {
885   int j;
886   task_t  thistask;
887   task_basic_info_data_t   taskinfo;
888   unsigned int taskinfo_l;
889   thread_array_t    threadarr;
890   unsigned int threadarr_l;
891   thread_basic_info_t     threadinfo;
892   thread_basic_info_data_t threadinfodata;
893   unsigned int threadinfo_l;
894   int task_tot_cpu=0;  /* total cpu usage of threads in a task */
895   struct user u;
896 
897   thisproc->p_pri=0;
898   thisproc->p_rssize=0;
899   thisproc->p_mach_virt_size=0;
900   thisproc->p_mach_state=0;
901   thisproc->p_mach_pct_cpu=0;
902 
903   if(task_by_unix_pid(task_self(), thisproc->p_pid, &thistask)
904                                                 != KERN_SUCCESS){
905       thisproc->p_mach_state=8; /* (zombie) */
906   } else {
907     taskinfo_l=TASK_BASIC_INFO_COUNT;
908     if(task_info(thistask, TASK_BASIC_INFO, (task_info_t) &taskinfo,
909                                       &taskinfo_l)
910        != KERN_SUCCESS) {
911       thisproc->p_mach_state=8; /* (zombie) */
912     } else {
913       int minim_state=99,mcurp=1000,mbasp=1000,mslpt=999;
914 
915       thisproc->p_rssize=taskinfo.resident_size;
916       thisproc->p_mach_virt_size=taskinfo.virtual_size;
917 
918       if (task_threads(thistask, &threadarr, &threadarr_l) != KERN_SUCCESS)
919 	  return;
920       threadinfo= &threadinfodata;
921       for(j=0; j < threadarr_l; j++) {
922 	threadinfo_l=THREAD_BASIC_INFO_COUNT;
923 	if(thread_info(threadarr[j],THREAD_BASIC_INFO,
924 	       (thread_info_t) threadinfo, &threadinfo_l) == KERN_SUCCESS) {
925 
926 	  task_tot_cpu += threadinfo->cpu_usage;
927 	  if(minim_state>threadinfo->run_state)
928               minim_state=threadinfo->run_state;
929 	  if(mcurp>threadinfo->cur_priority)
930               mcurp=threadinfo->cur_priority;
931 	  if(mbasp>threadinfo->base_priority)
932               mbasp=threadinfo->base_priority;
933 	  if(mslpt>threadinfo->sleep_time)
934               mslpt=threadinfo->sleep_time;
935 	}
936       }
937       switch (minim_state) {
938       case TH_STATE_RUNNING:
939 	    thisproc->p_mach_state=1;  break;
940       case TH_STATE_UNINTERRUPTIBLE:
941 	    thisproc->p_mach_state=2; break;
942       case TH_STATE_WAITING:
943 	    thisproc->p_mach_state=(threadinfo->sleep_time > 20) ? 4 : 3; break;
944       case TH_STATE_STOPPED:
945 	    thisproc->p_mach_state=5; break;
946       case TH_STATE_HALTED:
947 	    thisproc->p_mach_state=6; break;
948       default:
949 	    thisproc->p_mach_state=7; break;
950       }
951 
952       thisproc->p_pri=mcurp;
953       thisproc->p_mach_pct_cpu=(fixpt_t)(task_tot_cpu*10);
954       vm_deallocate(task_self(),(vm_address_t)threadarr,threadarr_l);
955     }
956   }
957   if (table(TBL_UAREA,thisproc->p_pid,&u,1,sizeof(struct user))>=0) {
958     thisproc->used_ticks=(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec);
959     thisproc->process_size=u.u_tsize + u.u_dsize + u.u_ssize;
960   }
961 }
962 
963 /* The reason for this function is that the system call will let
964  * someone lower their own processes priority (because top is setuid :-(
965  * Yes, using syscall() is a hack, if you can come up with something
966  * better, then I'd be thrilled to hear it. I'm not holding my breath,
967  * though.
968  *             Anthony.
969  */
setpriority(int dummy,int procnum,int niceval)970 int setpriority(int dummy, int procnum, int niceval)
971 {
972 
973     int uid, curprio;
974 
975     uid=getuid();
976     if ( (curprio=getpriority(PRIO_PROCESS,procnum) ) == -1)
977     {
978 	return(-1); /* errno goes back to renice_process() */
979     }
980     /* check for not-root - if so, dont allow users to decrease priority */
981     else if ( uid && (niceval<curprio) )
982     {
983 	errno=EACCES;
984 	return(-1);
985     }
986     return(syscall(SYS_setpriority,PRIO_PROCESS,procnum,niceval));
987 }
988 
989