home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / zip / mint / mntutl95.lzh / MNTUTL95 / TOP.C < prev    next >
C/C++ Source or Header  |  1993-08-03  |  14KB  |  576 lines

  1. /*
  2.  
  3.   ps.c: a program to print MiNT process statuses
  4.  
  5.   
  6.   Coded: 3/24/1991 by Tony Reynolds, cctony@sgisci1.ocis.olemiss.edu
  7.   For use with: MiNT release 0.7
  8.   Updated: 3/29/1991 by Eric Smith for MiNT version 0.8
  9.  
  10.   Updated and completely revised from 'ps' into 'top' by AKP 8/91.
  11.   Link with -lcurses and -ltermcap.  Beware!  Bammi's GNU curses
  12.   makefile actually links the termcap object files into curses.olb,
  13.   so changing them and recompiling only termcap.olb will have no effect!
  14.  
  15.   Updated some more 11/91 by Allan Pratt for MiNT version 0.9
  16. */
  17.  
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <osbind.h>
  21. #include <errno.h>
  22. #include <basepage.h>
  23. #include <ioctl.h>
  24. #include <time.h>
  25. #include <curses.h>
  26. #include <signal.h>
  27. #include <mintbind.h>
  28. #include <ctype.h>
  29. #include <string.h>
  30. #include <unistd.h>
  31.  
  32. #ifndef PCTXTSIZE
  33. #define PCTXTSIZE (('P'<<8)|3)
  34. #endif
  35.  
  36. #define MINWIDTH 36    /* this is the width of everything before the command */
  37. #define DEF_PTABSIZ 99    /* this is how many process' worth of table to have */
  38.  
  39. struct status {        /* defines values for internal->external process states */
  40.   int mint;        /* what MiNT knows */
  41.   char desc[6];        /* a textual description for above */
  42. } proc_stat[] = {
  43.   0,    "Run  ",    /* really "run" but "top" is always running! */
  44.   0x01, "Run  ",
  45.   0x20, "Wait ",
  46.   0x21, "Sleep",
  47.   0x22, "Exit ",
  48.   0x02, "TSR  ",
  49.   0x24, "Stop ",
  50.   -1,    "?????"
  51. };
  52.  
  53. typedef struct _context {
  54.     long    regs[15];    /* registers d0-d7, a0-a6 */
  55.     long    usp;        /* user stack pointer (a7) */
  56.     short    sr;        /* status register */
  57.     long    pc;        /* program counter */
  58.     long    ssp;        /* supervisor stack pointer */
  59.     long    term_vec;    /* GEMDOS terminate vector (0x102) */
  60.  
  61. /* these fields were added in MiNT 0.9 -- ERS */
  62.     char    fstate[216];    /* FPU internal state */
  63.     char    fregs[12*8];    /* registers fp0-fp7 */
  64.     long    fctrl[3];        /* FPCR/FPSR/FPIAR */
  65.     short    sfmt;            /* stack frame format identifier */
  66.     long    iar;            /* instruction address */
  67.     short    internal[4];    /* four words of internal info */
  68. } CONTEXT;
  69.  
  70. struct pinfo {
  71.     long    magic;
  72.     char    *base;
  73.     short    pid, ppid, pgrp;
  74.     short    ruid, rgid;
  75.     short    euid, egid;
  76.     short    flags;
  77.     short    pri;
  78.     short    wait_q;
  79.     long    wait_cond;
  80.     unsigned long systime, usrtime, chldstime, chldutime;
  81.     unsigned long maxmem, maxdata, maxcore, maxcpu;
  82.     short    domain;
  83.     short    curpri;
  84. };
  85.  
  86. /* open the process named "name" */
  87. int
  88. open_proc(name)
  89. char *name;
  90. {
  91.   static char pname[32] = "u:\\proc\\";
  92.  
  93.   strcpy(pname+8, name);
  94.   return open(pname, 0);
  95. }
  96.  
  97. struct ptab {
  98.     short pid;
  99.     short used;
  100.     long lasttime;
  101.     char line[2];    /* make sure sizeof(struct ptab) is even */
  102. } *ptab;        /* an array of these gets malloc'ed */
  103.  
  104. #define INCPTAB(p) ((struct ptab *)(((char *)p) + sizeof(struct ptab) + width))
  105.  
  106. int garbage = 0;    /* when set, screen needs total update */
  107.  
  108. void
  109. quit(sig)
  110. long sig;
  111. {
  112.     /* move to bottom of screen */
  113.     mvcur(0, COLS - 1, LINES - 1, 0);
  114.     endwin();
  115.     exit(0);
  116. }
  117.  
  118. void
  119. usage()
  120. {
  121.     puts("Usage: top [-s sleeptime] [-w width] [-n nlines] [-r]");
  122.     puts("-r sorts procs in decreasing PID order; usually increasing PID.");
  123.     puts("TOP ONLY RUNS UNDER MiNT.  Use '?' to get help.");
  124.     exit(1);
  125. }
  126.  
  127. void
  128. dokill(s)
  129. char *s;
  130. {
  131.     int signum = SIGTERM;
  132.     int pid;
  133.     char *p;
  134.     char hold;
  135.  
  136.     while (isspace(*s)) s++;
  137.     if (*s == '-') {
  138.     p = ++s;
  139.     while (*p && !isspace(*p)) p++;
  140.     hold = *p;
  141.     *p = '\0';
  142.     if (p != s) signum = atoi(s);
  143.     *p = hold;
  144.     if (!signum) signum = SIGTERM;
  145.     s = p;
  146.     }
  147.     while (isspace(*s)) s++;
  148.  
  149.     while (*s) {
  150.     p = s;
  151.     while (*p && !isspace(*p)) p++;
  152.     hold = *p;
  153.     *p = '\0';
  154.     pid = atoi(s);
  155.     *p = hold;
  156.     s = p;
  157.     if (pid) (void)Pkill(pid,signum);
  158.     while (isspace(*s)) s++;
  159.     }
  160. }
  161.  
  162. void
  163. dorenice(s)
  164. char *s;
  165. {
  166.     int niceval;
  167.     int pid;
  168.     char *p;
  169.     char hold;
  170.  
  171.     while (isspace(*s)) s++;
  172.     p = s;
  173.     while (*p && !isspace(*p)) p++;
  174.     hold = *p;
  175.     *p = '\0';
  176.     niceval = atoi(s);
  177.     *p = hold;
  178.     s = p;
  179.  
  180.     while (isspace(*s)) s++;
  181.  
  182.     while (*s) {
  183.     p = s;
  184.     while (*p && !isspace(*p)) p++;
  185.     hold = *p;
  186.     *p = '\0';
  187.     pid = atoi(s);
  188.     *p = hold;
  189.     s = p;
  190.     if (pid) (void)Prenice(pid,niceval);
  191.     while (isspace(*s)) s++;
  192.     }
  193. }
  194.  
  195. int
  196. cmp_incr_pid(void *p1, void *p2)
  197. {
  198.     struct ptab *pt1 = p1, *pt2 = p2;
  199.     int pid1 = pt1->pid, pid2 = pt2->pid;
  200.     if (pid1 == -1 && pid2 == -1) return 0;
  201.     if (pid1 == -1) return 1;    /* send invalid procs to bottom */
  202.     if (pid2 == -1) return -1;
  203.     return pid1 - pid2;
  204. }
  205.  
  206. int
  207. cmp_decr_pid(void *p1, void *p2)
  208. {
  209.     struct ptab *pt1 = p1, *pt2 = p2;
  210.     int pid1 = pt1->pid, pid2 = pt2->pid;
  211.     if (pid1 == -1 && pid2 == -1) return 0;
  212.     if (pid1 == -1) return 1;    /* send invalid procs to bottom */
  213.     if (pid2 == -1) return -1;
  214.     return pid2 - pid1;
  215. }
  216.  
  217. char helpstr[] = 
  218.   "    s[leeptime] w[idth] n[lines] k[ill] r[enice] h[elp] p[riority] q[uit]\r";
  219.  
  220. void
  221. main(argc, argv)
  222. int argc;
  223. char **argv;
  224. {
  225.     int repeat_time = 0;
  226.     _DTA mydta;
  227.     int fd;
  228.     int fserror=0;
  229.     long place, procaddr;
  230.     int pid, ppid;
  231.     char *cmdline;
  232.     struct status *statp;
  233.     char bpage[sizeof(BASEPAGE)];
  234.     struct pinfo proc;
  235.     int thispid;
  236.     struct ptab *pt;
  237.     char *c;
  238.     unsigned long ptime, hour, min, sec, frac;
  239.     long realtime;
  240.     long lastclock = clock();
  241.     long thisclock;
  242.     int i;
  243.     long deltime;
  244.     long ctxtsize;
  245.     int nprocs, nrunning;
  246.     long cummemory, idletime;
  247.     int width = 0;
  248.     long readfds;
  249.     char ibuf[80];
  250.     int ichar;
  251.     int taillen, cmdlen;
  252.     int tailstart;
  253.     int nlines = 0;
  254.     int helpline = 0;
  255.     int ptabsiz = DEF_PTABSIZ;
  256.     int len;
  257.     int (*cmpfunc)();
  258.     extern int __mint;
  259.     int ppid_mode = 1;
  260.  
  261.     cmpfunc = cmp_incr_pid;
  262.  
  263.     if (!__mint) usage();
  264.     --argc, ++argv;
  265.     while (argc) {
  266.     if (**argv != '-') usage();
  267.     switch(argv[0][1]) {
  268.         case 'r': cmpfunc = cmp_decr_pid;
  269.               break;
  270.         case 's': if (argc == 0) usage();
  271.               if ((repeat_time = atoi(argv[1])) == 0) usage();
  272.               ++argv, --argc;
  273.               break;
  274.         case 'w': if (argc == 0) usage();
  275.               if ((width = atoi(argv[1])) <= 0) usage();
  276.               if (width < MINWIDTH) width = MINWIDTH;
  277.               ++argv, --argc;
  278.               break;
  279.         case 'n': if (argc == 0) usage();
  280.               if ((nlines = atoi(argv[1])) <= 0) usage();
  281.               ++argv, --argc;
  282.               break;
  283.  
  284.         default: usage();
  285.     }
  286.     --argc, ++argv;
  287.     }
  288.  
  289.     Fsetdta(&mydta);          /* give the OS my new DTA */
  290.  
  291.     initscr();
  292.  
  293.     if (!width) width = COLS-1;    /* default if it's unset */
  294.     width &= ~1;        /* make sure width is even, too */
  295.  
  296.     if (!repeat_time) repeat_time = 5;
  297.     if (LINES < 4) {
  298.     puts("top: can't run on a screen with < 4 lines");
  299.     exit(1);
  300.     }
  301.     if (nlines == 0 || nlines > (LINES-3)) nlines = LINES-3;
  302.  
  303.     /* struct ptab ends with char line[2] so there's already padding for '\0' */
  304.     if ((ptab = malloc((sizeof(struct ptab) + width) * ptabsiz)) == NULL) {
  305.     puts("top: can't get memory for process structures");
  306.     exit(1);
  307.     }
  308.  
  309.     cbreak();
  310.     noecho();
  311.  
  312.     signal(SIGINT,quit);
  313.     signal(SIGQUIT,quit);
  314.     signal(SIGHUP,quit);
  315.     signal(SIGTERM,quit);
  316.  
  317.     for (i = ptabsiz, pt = ptab; i; i--, pt = INCPTAB(pt)) {
  318.     pt->pid = -1;
  319.     pt->used = 0;
  320.     }
  321.  
  322.     while (1) {
  323.     fserror=Fsfirst("u:\\proc\\*.*", -1);
  324.     if (fserror < 0) {
  325.         fprintf(stderr, "top: cannot list processes ?!");
  326.         exit(1);
  327.     }
  328.  
  329.     erase();
  330.  
  331.     /* top line is printed later */
  332.  
  333.     if (ppid_mode) {
  334.         printw("\n\nPID PPID STATUS   SIZE   TIME     %% COMMAND\n");
  335.     }
  336.     else {
  337.         printw("\n\nPID PRI CUR STATUS   SIZE   TIME     %% COMMAND\n");
  338.     }
  339.  
  340.     nprocs = 0;
  341.     nrunning = 0;
  342.     idletime = 0;
  343.     cummemory = 0;
  344.  
  345.     thisclock = clock();
  346.     realtime = (thisclock - lastclock) * (1000/CLOCKS_PER_SEC);
  347.     lastclock = thisclock;
  348.  
  349.     /* okay, now run the Fsnext bit through the wringer */
  350.  
  351.     fserror = E_OK;       /* set up for the loop */
  352.  
  353.     while (fserror == E_OK ) {
  354.         fd = open_proc(mydta.dta_name);
  355.         if (fd < 0) goto getnext;
  356.  
  357.         /* cut name off at '.' */
  358.         for (cmdline = mydta.dta_name;
  359.         *cmdline && *cmdline != '.';
  360.         cmdline++)
  361.             /* do nothing */ ;
  362.         *cmdline = 0;
  363.  
  364.         ioctl(fd, PPROCADDR, &procaddr);
  365.         if (ioctl(fd, PCTXTSIZE, &ctxtsize) < 0)
  366.         lseek(fd, procaddr+4+2*sizeof(CONTEXT), 0);
  367.         else
  368.         lseek(fd, procaddr, 0);
  369.         read(fd, &proc, sizeof(proc));
  370.         pid = proc.pid;
  371.         ppid = proc.ppid;
  372.         ioctl(fd, PBASEADDR, &place);
  373.         lseek(fd, place, 0);
  374.         read(fd, bpage, sizeof(bpage));
  375.         close(fd);
  376.         cmdline = bpage+128;
  377.         if (*cmdline) *cmdline = ' ';
  378.  
  379.         /* span cmdline turning control chars into question marks */
  380.         for (c = cmdline; *c; c++) if (*c < ' ') *c = '?';
  381.  
  382.         taillen = c - cmdline;
  383.  
  384.         thispid = proc.pid;
  385.         if (ppid < 0) ppid = 0;
  386.  
  387.         statp = proc_stat;      /* hunt down string referring to
  388.                     process status */
  389.         while (statp->mint != mydta.dta_attribute &&
  390.         statp->mint >= 0) statp++;
  391.  
  392.         ptime = proc.systime + proc.usrtime;
  393.  
  394.         /* set deltime to the time used since last time we checked */
  395.         for (i = ptabsiz, pt = ptab; i; i--, pt = INCPTAB(pt)) {
  396.         if (pt->pid == thispid) {
  397.             pt->used = 1;
  398.             deltime = ptime - pt->lasttime;
  399.             pt->lasttime = ptime;
  400.             goto found;
  401.         }
  402.         }
  403.  
  404.         /* fell out: didn't find the process */
  405.         for (i = ptabsiz, pt = ptab; i; i--, pt = INCPTAB(pt)) {
  406.         if (pt->pid == -1) {
  407.             /* use this slot */
  408.             pt->used = 1;
  409.             pt->pid = thispid;
  410.             pt->lasttime = ptime;
  411.             deltime = (ptime > realtime ? realtime : ptime);
  412.             goto found;
  413.         }
  414.         }
  415.         /* fell out again - can't keep track of this process (sorry!) */
  416.         deltime = 0;
  417.         pt = NULL;
  418.  
  419. found:
  420.  
  421.         if (thispid == 0) {
  422.         /* idle time is charged to process zero */
  423.         idletime = deltime;
  424.         strcpy(mydta.dta_name,"(idle)");
  425.         }
  426.  
  427.         hour = (ptime/1000/60/60);
  428.         min = (ptime/1000/60)%60;
  429.         sec = (ptime/1000)%60;
  430.         frac = (ptime%1000) / 10;    /* (never anything in .001 digit) */
  431.  
  432.         if (ppid_mode) {
  433.         len = sprintf(pt->line,"%03d %03d %-5s %8ld ",
  434.             pid, ppid, statp->desc, mydta.dta_size);
  435.         }
  436.         else {
  437.         len = sprintf(pt->line,"%03d %3d %3d %-5s %8ld ",
  438.             pid, proc.pri, proc.curpri, statp->desc, mydta.dta_size);
  439.         }
  440.  
  441.         if (hour)
  442.         len += sprintf(pt->line + len,"%2ld:%02ld:%02ld",hour,min,sec);
  443.         else
  444.         len += sprintf(pt->line + len,"%2ld:%02ld.%02ld",min,sec,frac);
  445.  
  446.         len += sprintf(pt->line + len," %3ld ",(deltime * 100) / realtime);
  447.  
  448.         /* if the line's too long, cut off the tail so the total is width */
  449.         cmdlen = strlen(mydta.dta_name);
  450.         tailstart = len+cmdlen;
  451.         if (width < tailstart) cmdline[0] = 0;
  452.         else if (tailstart+taillen > width) {
  453.         cmdline[width-tailstart] = '\0';
  454.         }
  455.         sprintf(pt->line + len,"%s%s",mydta.dta_name,cmdline);
  456.  
  457.         nprocs++;
  458.  
  459.         cummemory += mydta.dta_size;
  460.         if (deltime) nrunning++;
  461.  
  462. getnext:
  463.         fserror = Fsnext();
  464.     }
  465.  
  466.     /*
  467.     * Now zero out those slots which weren't used in the last pass, and zero
  468.     * all "used" fields
  469.     */
  470.     for (i = nlines, pt = ptab; i; i--, pt = INCPTAB(pt)) {
  471.         if (!pt->used) pt->pid = -1;
  472.         else pt->used = 0;
  473.     }
  474.  
  475.     /*
  476.     * Now all processes are in the ptab array, their lines stored. Sort them
  477.     * into printing order and print nlines of them.
  478.     */
  479.  
  480.     qsort(ptab,nlines,sizeof(struct ptab) + width,cmpfunc);
  481.     if (nprocs < nlines) i = nprocs;
  482.     else i = nlines;
  483.     for (pt = ptab; i; i--, pt = INCPTAB(pt)) {
  484.         printw("%s\n",pt->line);
  485.     }
  486.  
  487.     /* compute idle percentage into idletime */
  488.     idletime = (idletime * 100) / realtime;
  489.  
  490.     move(0,0);
  491.     printw("Sample time: %5ld; %3ld%% idle; %3d processes, %3d running"
  492.         "; %ld bytes used\n",
  493.         realtime,idletime,nprocs,nrunning,cummemory);
  494.  
  495.     if (helpline) printw(helpstr);
  496.  
  497.     if (garbage) wrefresh(curscr);
  498.     else refresh();
  499.     garbage = 0;
  500.  
  501.     /* Fall asleep for repeat_time seconds or until a key comes in. */
  502.     /* We want zero to mean "no time" not "forever" so add one. */
  503.     readfds = 1;
  504.     if (Fselect(repeat_time * 1000 + 1,&readfds,0L,0L)) {
  505.         /* input waiting */
  506.         if (helpline) {
  507.         clrtoeol();
  508.         refresh();
  509.         helpline = 0;
  510.         }
  511.         ichar = getch();
  512.         switch(ichar) {
  513.         case 'L'-'@': 
  514.             garbage = 1;
  515.             break;
  516.         case 's':
  517.             printw("sleep time: ");
  518.             refresh();
  519.             echo();
  520.             getstr(ibuf);
  521.             noecho();
  522.             if (*ibuf) repeat_time = atoi(ibuf);
  523.             if (repeat_time < 0) repeat_time = 0;
  524.             break;
  525.         case 'w':
  526.             printw("width: ");
  527.             refresh();
  528.             echo();
  529.             getstr(ibuf);
  530.             noecho();
  531.             if (*ibuf) width = atoi(ibuf);
  532.             if (width < MINWIDTH) width = MINWIDTH;
  533.             width &= ~1;    /* make sure width is even */
  534.             break;
  535.         case 'n':
  536.             printw("nlines: ");
  537.             refresh();
  538.             echo();
  539.             getstr(ibuf);
  540.             noecho();
  541.             if (*ibuf) nlines = atoi(ibuf);
  542.             if (nlines == 0 || nlines > (LINES-3)) nlines = (LINES-3);
  543.             break;
  544.         case 'k':
  545.             printw("kill ");
  546.             refresh();
  547.             echo();
  548.             getstr(ibuf);
  549.             noecho();
  550.             if (*ibuf) dokill(ibuf);
  551.             break;
  552.         case 'r':
  553.             printw("renice ");
  554.             refresh();
  555.             echo();
  556.             getstr(ibuf);
  557.             noecho();
  558.             if (*ibuf) dorenice(ibuf);
  559.             break;
  560.         case 'p':
  561.             /* toggle ppid_mode */
  562.             ppid_mode ^= 1;
  563.             break;
  564.         case 'h':
  565.         case '?':
  566.             helpline = 1;
  567.             break;
  568.         case 'q': quit();
  569.         default: ;
  570.         }
  571.     }
  572.     }
  573. }
  574.  
  575.  
  576.