source: trunk/third/top/machine/m_aux3.c @ 9084

Revision 9084, 16.8 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r9083, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * top - a top users display for Unix
3 *
4 * SYNOPSIS:  a Mac running A/UX version 3.x
5 *
6 * DESCRIPTION:
7 * This is the machine-dependent module for A/UX 3.x.
8 * ==
9 * Although AUX does not generally have a renice systemcall, it can be
10 * implemented by tweeking kernel memory.  While such a simple hack should
11 * not be difficult to get right, USE THIS FEATURE AT YOUR OWN RISK!
12 * To turn on setpriority emulation, add "-DIMPLEMENT_SETPRIORITY" to
13 * the CFLAGS when prompted in the configure script.
14 *
15 * CFLAGS: -Dclear=clear_scr -DPRIO_PROCESS=0
16 *
17 * LIBS:
18 *
19 * AUTHOR:  Richard Henderson <rth@tamu.edu>
20 */
21
22
23#include <stddef.h>
24#include <stdio.h>
25#include <string.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <a.out.h>
29#include <sys/types.h>
30#include <sys/signal.h>
31#include <sys/param.h>
32#include <sys/proc.h>
33#include <sys/user.h>
34#include <sys/sysinfo.h>
35#include <sys/var.h>
36#include <sys/swap.h>
37
38#define FSCALE  65536.0
39
40#include "top.h"
41#include "machine.h"
42#include "loadavg.h"
43
44/*=NLIST INFO===========================================================*/
45
46#define X_V             0
47#define X_SYSINFO       1
48#define X_AVENRUN       2
49#define X_MAXMEM        3
50#define X_FREEMEM       4
51#define X_SWAPTAB       5
52#define X_AVAILRMEM     6
53#define X_AVAILSMEM     7
54
55static struct nlist nlst[] = {
56    {"v"},
57    {"sysinfo"},
58    {"avenrun"},
59    {"maxmem"},
60    {"freemem"},
61    {"swaptab"},
62    {0},                /* "availrmem" */
63    {0},                /* "availsmem" */
64    {0}
65};
66
67static int kmem;
68static int mem;
69
70static struct var v;
71
72#define V_OFS           (nlst[X_V].n_value)
73#define SYSINFO_OFS     (nlst[X_SYSINFO].n_value)
74#define AVENRUN_OFS     (nlst[X_AVENRUN].n_value)
75#define MAXMEM_OFS      (nlst[X_MAXMEM].n_value)
76#define FREEMEM_OFS     (nlst[X_FREEMEM].n_value)
77#define SWAPTAB_OFS     (nlst[X_SWAPTAB].n_value)
78#define AVAILRMEM_OFS   (nlst[X_AVAILRMEM].n_value)
79#define AVAILSMEM_OFS   (nlst[X_AVAILSMEM].n_value)
80
81/*=SYSTEM STATE INFO====================================================*/
82
83/* these are for calculating cpu state percentages */
84
85static long cp_time[NCPUSTATES];
86static long cp_old[NCPUSTATES];
87static long cp_diff[NCPUSTATES];
88
89/* these are for keeping track of the proc array */
90
91struct top_proc
92{
93    pid_t p_pid;
94    pid_t p_pgrp;
95    uid_t p_uid;
96    int p_pri;
97    int p_nice;
98    int p_size;
99    int p_stat;
100    int p_flag;
101    int p_slot;
102    time_t p_start;
103    time_t p_time;
104    float p_pcpu;
105    float p_wcpu;
106    char p_name[COMMSIZ];
107};
108
109static int hash_size;
110static struct top_proc *ptable; /* the hash table of processes */
111static struct top_proc *eptable;
112static struct top_proc **pactive; /* list of active structures */
113static struct top_proc **nextactive; /* for iterating through the processes */
114static struct proc *preal;
115static struct proc *epreal;
116
117static pid_t last_pid;
118static struct timeval last_update;
119
120/* these are for passing data back to the mach. ind. portion */
121
122static int cpu_states[NCPUSTATES];
123static int process_states[8];
124static int memory_stats[6];
125
126/* a few useful macros... */
127
128#define blocktok(b)     ((b) >> 1)
129#define pagetok(pg)     ((pg) << (v.v_pageshift - LOG1024))
130#define HASH(x)         ((x) * 1686629713UL % hash_size)
131
132/*=STATE IDENT STRINGS==================================================*/
133
134static char *state_abbrev[] =
135{
136    "", "sleep", "run", "zomb", "stop", "start", "cpu", "swap",
137    NULL
138};
139
140static char *procstatenames[] =
141{
142    "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
143    " starting, ", " on cpu, ", " swapping, ",
144    NULL
145};
146
147static char *cpustatenames[] =
148{
149    "idle", "user", "kernel", "wait", "nice",
150    NULL
151};
152
153static char *memorynames[] =
154{
155    "K used, ", "K free, ", "K locked   Swap: ",
156    "K used, ", "K free",
157    NULL
158};
159
160static char fmt_header[] =
161  "  PID  PGRP X        PRI NICE  SIZE STATE   TIME    WCPU     CPU COMMAND";
162
163
164/*======================================================================*/
165
166int
167machine_init(statics)
168    struct statics *statics;
169{
170    /* access kernel memory */
171    if (
172#ifdef IMPLEMENT_SETPRIORITY
173        (kmem = open("/dev/kmem", O_RDWR)) < 0 &&
174#endif
175        (kmem = open("/dev/kmem", O_RDONLY)) < 0)
176    {
177        perror("/dev/kmem");
178        return -1;
179    }
180    if ((mem = open("/dev/mem", O_RDONLY)) < 0)
181    {
182        perror("/dev/mem");
183        return -1;
184    }
185
186    /* get the list of symbols we want to access in the kernel */
187    nlst[X_AVAILRMEM].n_nptr = "availrmem";
188    nlst[X_AVAILSMEM].n_nptr = "availsmem";
189
190    if (nlist("/unix", nlst) < 0)
191    {
192        fprintf(stderr, "top: nlist failed\n");
193        return -1;
194    }
195
196    /* make sure they were all found */
197    if (check_nlist(nlst) > 0)
198        return -1;
199
200    /* grab the kernel configuration information */
201    (void)getkval(V_OFS, (char *)&v, sizeof(v), "v");
202
203    /* allocate space for process related info */
204    hash_size = v.v_proc * 3 / 2;
205    ptable = (struct top_proc *)malloc(hash_size * sizeof(struct top_proc));
206    pactive = (struct top_proc **)malloc(v.v_proc * sizeof(struct top_proc *));
207
208    if (!ptable || !pactive)
209    {
210        fprintf(stderr, "top: can't allocate sufficient memory\n");
211        return -1;
212    }
213
214    eptable = ptable + hash_size;
215
216    {
217        struct top_proc *p;
218        for (p = ptable; p != eptable; ++p)
219            p->p_pid = -1;
220    }
221
222    /* fill in the statics information */
223    statics->procstate_names = procstatenames;
224    statics->cpustate_names = cpustatenames;
225    statics->memory_names = memorynames;
226
227    /* all done! */
228    return 0;
229}
230
231static struct top_proc *
232lookup_proc(id)
233    pid_t id;
234{
235    struct top_proc *p;
236
237    p = ptable+HASH(rp->p_pid);
238    while (p->p_pid != rp->p_pid && p->p_pid != -1)
239    {
240        if (++p == eptable)
241            p = ptable;
242    }
243
244    return p;
245}
246   
247
248static void
249update_proc_table()
250{
251    struct proc *rp;
252    struct top_proc *p;
253    float timediff, alpha, beta;
254
255    getkval((long)v.ve_proctab, (char *)preal,
256            sizeof(struct proc)*v.v_proc, "proc array");
257
258    /* calculate the time difference since our last proc read */
259    {
260        struct timeval thistime;
261        gettimeofday(&thistime, 0);
262        if (last_update.tv_sec)
263            timediff = ((thistime.tv_sec - last_update.tv_sec) +
264                        (thistime.tv_usec - last_update.tv_usec) * 1e-6);
265        else
266            timediff = 1e9;
267        last_update = thistime;
268    }
269
270    /* calculate constants for the exponental average */
271    if (timediff < 30.0)
272    {
273        alpha = 0.5 * (timediff / 30.0);
274        beta = 1.0 - alpha;
275    }
276    else
277        alpha = beta = 0.5;
278
279    timediff *= v.v_hz;
280
281    /* mark the hash table entries as not seen */
282    for (p = ptable; p != eptable; ++p)
283        p->p_stat = 0;
284
285    for (rp = preal; rp != epreal; ++rp)
286    {
287        struct user u;
288
289        if (rp->p_stat == 0)
290            continue;
291        else if (rp->p_stat == SZOMB ||
292                 lseek(mem, rp->p_addr, 0) < 0 ||
293                 read(mem, &u, sizeof(u)) != sizeof(u))
294        {
295            strcpy(u.u_comm, "???");
296            u.u_utime = u.u_stime = u.u_start = 0;
297        }
298
299        p = lookup_proc(rp->p_pid);
300
301        p->p_pgrp = rp->p_pgrp;
302        p->p_uid = rp->p_uid;
303        p->p_pri = rp->p_pri - PZERO;
304        p->p_nice = rp->p_nice - NZERO;
305        p->p_size = pagetok(rp->p_size);
306        p->p_stat = rp->p_stat;
307        p->p_flag = rp->p_flag;
308        if (p->p_pid != rp->p_pid)
309        {
310            /* new process */
311            p->p_pid = rp->p_pid;
312            p->p_slot = rp - preal;
313            p->p_start = u.u_start;
314            p->p_time = u.u_utime + u.u_stime;
315            p->p_pcpu = p->p_time / timediff;
316            p->p_wcpu = p->p_pcpu;
317            strncpy(p->p_name, u.u_comm, sizeof(u.u_comm));
318        }
319        else
320        {
321            time_t oldtime = p->p_time;
322            p->p_time = u.u_utime + u.u_stime;
323            p->p_pcpu = (p->p_time - oldtime) / timediff;
324            p->p_wcpu = alpha * p->p_pcpu + beta * p->p_wcpu;
325        }
326    }
327       
328    for (p = ptable; p != eptable; ++p)
329        if (p->p_stat == 0)
330            p->p_pid = -1;
331}
332
333void
334get_system_info(info)
335    struct system_info *info;
336{
337    /* convert load averages */
338    {
339        load_avg ar[3];
340
341        (void)getkval(AVENRUN_OFS, (char *)&ar, sizeof(ar), "avenrun");
342
343        /* convert load averages to doubles */
344        info->load_avg[0] = loaddouble(ar[0]);
345        info->load_avg[1] = loaddouble(ar[1]);
346        info->load_avg[2] = loaddouble(ar[2]);
347    }
348
349    /* get cpu time counts */
350    {
351        struct sysinfo si;
352
353        (void)getkval(SYSINFO_OFS, (char *)&si, sizeof(si), "sysinfo");
354
355        memcpy(cp_time, si.cpu, sizeof(cp_time));
356        percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
357    }
358
359    /* get memory usage information */
360    {
361        int freemem, availrmem, availsmem, maxmem;
362        struct swaptab swaptab[MSFILES];
363        int i, swaptot, swapfree;
364
365        (void)getkval(MAXMEM_OFS, (char *)&maxmem, sizeof(maxmem), "maxmem");
366        (void)getkval(FREEMEM_OFS, (char *)&freemem, sizeof(freemem),
367                      "freemem");
368        (void)getkval(AVAILRMEM_OFS, (char *)&availrmem, sizeof(availrmem),
369                      "availrmem");
370        (void)getkval(AVAILSMEM_OFS, (char *)&availsmem, sizeof(availsmem),
371                      "availsmem");
372        (void)getkval(SWAPTAB_OFS, (char *)&swaptab, sizeof(swaptab),
373                      "swaptab");
374
375        for (i = swaptot = swapfree = 0; i < MSFILES; ++i)
376            if (swaptab[i].st_dev)
377            {
378                swaptot += swaptab[i].st_npgs;
379                swapfree += swaptab[i].st_nfpgs;
380            }
381
382        memory_stats[0] = pagetok(availrmem - freemem);
383        memory_stats[1] = pagetok(freemem);
384        memory_stats[2] = pagetok(maxmem - availrmem);
385        memory_stats[3] = pagetok(swaptot - swapfree);
386        memory_stats[4] = pagetok(swapfree);
387    }
388
389    update_proc_table();
390
391    /* search proc structures for newest process id */
392    {
393        struct top_proc *p;
394        time_t t = 0;
395        pid_t id = 0;
396
397        for (p = ptable; p != eptable; ++p)
398        {
399            if (!p->p_stat)
400                continue;
401            if (p->p_start > t || p->p_start == t && p->p_pid > id)
402            {
403                t = p->p_start;
404                id = p->p_pid;
405            }
406        }
407
408        if (id > last_pid || id < last_pid - 10000)
409            last_pid = id;
410
411        info->last_pid = last_pid;
412    }
413
414    /* set arrays and strings */
415    info->cpustates = cpu_states;
416    info->memory = memory_stats;
417}
418
419caddr_t
420get_process_info(si, sel, compare)
421     struct system_info *si;
422     struct process_select *sel;
423     int (*compare)();
424{
425    int total_procs;
426    struct top_proc *p, **a;
427
428    /* these are copied out of sel for speed */
429    int show_idle, show_system, show_uid, show_command;
430
431    /* get a pointer to the states summary array */
432    si->procstates = process_states;
433
434    /* set up flags which define what we are going to select */
435    show_idle = sel->idle;
436    show_system = sel->system;
437    show_uid = sel->uid != -1;
438    show_command = sel->command != NULL;
439
440    /* count up process states and get pointers to interesting procs */
441    total_procs = 0;
442    memset(process_states, 0, sizeof(process_states));
443
444    for (p = ptable, a = pactive; p != eptable; ++p)
445    {
446        int stat = p->p_stat, flag = p->p_flag;
447
448        if (stat == 0 || (flag & SSYS) && !show_system)
449            continue;
450
451        total_procs++;
452        process_states[stat]++;
453
454        if (stat != SZOMB &&
455            (show_idle || stat == SRUN || stat == SIDL || stat == SONPROC ||
456             ((stat == SSLEEP || stat == SSTOP) &&
457              (flag & (SINTR | SSYS)) == 0)) &&
458             (!show_uid || p->p_uid == (uid_t)sel->uid))
459        {
460            /* add it to our active list */
461            *a++ = p;
462        }
463    }
464
465    /* remember active and total counts */
466    si->p_total = total_procs;
467    si->p_active = a - pactive;
468
469    /* if requested, sort the "interesting" processes */
470    if (compare != NULL)
471        qsort(pactive, si->p_active, sizeof(struct top_proc *), compare);
472
473    /* set up to iterate though processes */
474    nextactive = pactive;
475
476    /* don't even pretend the return value isn't bogus */
477    return 0;
478}
479
480
481char *
482format_header(uname_field)
483    char *uname_field;
484{
485    int len = strlen(uname_field);
486    if (len > 8)
487        len = 8;
488
489    memcpy(strchr(fmt_header, 'X'), uname_field, len);
490
491    return fmt_header;
492}
493
494char *
495format_next_process(handle, get_userid)
496     caddr_t handle;
497     char *(*get_userid)();
498{
499    static char fmt[128];       /* static area where result is built */
500    struct top_proc *pp = *nextactive++;
501
502    sprintf(fmt,
503            "%5d %5d %-8.8s %3d %4d %5s %-5s %6s %6.2f%% %6.2f%% %.14s",
504            pp->p_pid,
505            pp->p_pgrp,
506            (*get_userid)(pp->p_uid),
507            pp->p_pri,
508            pp->p_nice,
509            format_k(pp->p_size),
510            state_abbrev[pp->p_stat],
511            format_time((time_t)pp->p_time / v.v_hz),
512            pp->p_wcpu * 100.0,
513            pp->p_pcpu * 100.0,
514            pp->p_name);
515
516    /* return the result */
517    return (fmt);
518}
519
520
521/*
522 * check_nlist(nlst) - checks the nlist to see if any symbols were not
523 *              found.  For every symbol that was not found, a one-line
524 *              message is printed to stderr.  The routine returns the
525 *              number of symbols NOT found.
526 */
527
528int
529check_nlist(nlst)
530     register struct nlist *nlst;
531{
532    register int i;
533
534    /* check to see if we got ALL the symbols we requested */
535    /* this will write one line to stderr for every symbol not found */
536
537    i = 0;
538    while (nlst->n_name[0])
539    {
540        if (nlst->n_value == 0)
541        {
542            /* this one wasn't found */
543            fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
544            i = 1;
545        }
546        nlst++;
547    }
548
549    return (i);
550}
551
552
553/*
554 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
555 *      "offset" is the byte offset into the kernel for the desired value,
556 *      "ptr" points to a buffer into which the value is retrieved,
557 *      "size" is the size of the buffer (and the object to retrieve),
558 *      "refstr" is a reference string used when printing error meessages,
559 *          if "refstr" starts with a '!', then a failure on read will not
560 *          be fatal (this may seem like a silly way to do things, but I
561 *          really didn't want the overhead of another argument).
562 *     
563 */
564
565getkval(offset, ptr, size, refstr)
566     unsigned long offset;
567     int *ptr;
568     int size;
569     char *refstr;
570{
571    extern int errno;
572    extern char *sys_errlist[];
573
574    if (lseek(kmem, offset, 0) < 0 || read(kmem, ptr, size) != size)
575    {
576        if (*refstr == '!')
577        {
578            return (0);
579        }
580        else
581        {
582            fprintf(stderr, "top: getkval for %s: %s\n",
583                    refstr, sys_errlist[errno]);
584            quit(23);
585            /*NOTREACHED */
586        }
587    }
588    return (1);
589}
590
591/* comparison routine for qsort */
592
593/*
594 *  proc_compare - comparison function for "qsort"
595 *      Compares the resource consumption of two processes using five
596 *      distinct keys.  The keys (in descending order of importance) are:
597 *      percent cpu, cpu ticks, state, resident set size, total virtual
598 *      memory usage.  The process states are ordered as follows (from least
599 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
600 *      array declaration below maps a process state index into a number
601 *      that reflects this ordering.
602 */
603
604static unsigned char sorted_state[] =
605{
606    0,  /* not used             */
607    3,  /* sleep                */
608    6,  /* runable              */
609    1,  /* zombie               */
610    4,  /* stop                 */
611    5,  /* start                */
612    7,  /* running              */
613    2,  /* swapping             */
614};
615
616proc_compare(pp1, pp2)
617    struct top_proc **pp1, **pp2;
618{
619    struct top_proc *p1, *p2;
620    int result;
621    double dresult;
622
623    /* remove one level of indirection */
624    p1 = *pp1;
625    p2 = *pp2;
626
627    /* compare percent cpu */
628    dresult = p2->p_pcpu - p1->p_pcpu;
629    if (dresult != 0.0)
630        return dresult > 0.0 ? 1 : -1;
631
632    /* use process state to break the tie */
633    if ((result = (sorted_state[p2->p_stat] -
634                   sorted_state[p1->p_stat])) == 0)
635    {
636        /* use priority to break the tie */
637        if ((result = p2->p_pri - p1->p_pri) == 0)
638        {
639            /* use total memory to break the tie */
640            result = p2->p_size - p1->p_size;
641        }
642    }
643
644    return result;
645}
646
647/*
648 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
649 *              the process does not exist.
650 *              It is EXTREMLY IMPORTANT that this function work correctly.
651 *              If top runs setuid root (as in SVR4), then this function
652 *              is the only thing that stands in the way of a serious
653 *              security problem.  It validates requests for the "kill"
654 *              and "renice" commands.
655 */
656
657int
658proc_owner(pid)
659    int pid;
660{
661    struct top_proc *p;
662
663    for (p = ptable; p != eptable; ++p)
664        if (p->p_pid == pid)
665            return p->p_uid;
666
667    return -1;
668}
669
670/*
671 * setpriority(int which, pid_t pid, int val)
672 * This system does not have this system call -- fake it
673 */
674
675int
676setpriority(which, pid, val)
677    int which, pid, val;
678{
679#ifndef IMPLEMENT_SETPRIORITY
680    errno = ENOSYS;
681    return -1;
682#else
683    struct top_proc *p;
684    struct proc proc;
685    int uid;
686
687    /* sanity check arguments */
688    val += NZERO;
689    if (val < 0)
690        val = 0;
691    else if (val > 39)
692        val = 39;
693
694    p = lookup_proc(pid);
695    if (p->p_pid == -1)
696    {
697        errno = ESRCH;
698        return -1;
699    }
700
701    getkval((long)v.ve_proctab+p->p_slot*sizeof(proc),
702            (char *)&proc, sizeof(proc), "proc array");
703
704    if (proc.p_stat == 0 || proc.p_pid != pid)
705    {
706        errno = ESRCH;
707        return -1;
708    }
709
710    /* make sure we don't allow nasty people to do nasty things */
711    uid = getuid();
712    if (uid != 0)
713    {
714        if (uid != proc.p_uid || val < proc.p_nice)
715        {
716            errno = EACCES;
717            return -1;
718        }
719    }
720
721    /* renice */
722    proc.p_nice = val;
723    if (lseek(kmem, (v.ve_proctab + p->p_slot*sizeof(proc) +
724                     offsetof(struct proc, p_nice), 0) < 0 ||
725        write(kmem, &rp->p_nice, sizeof(rp->p_nice)) != sizeof(rp->p_nice))
726    {
727        return -1;
728    }
729
730    return 0;
731#endif
732}
Note: See TracBrowser for help on using the repository browser.