home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume41 / rperf / part03 / rperf.c < prev   
Encoding:
C/C++ Source or Header  |  1993-12-19  |  46.5 KB  |  1,719 lines

  1. /**
  2.  * This program may be copied, redistributed in any form,
  3.  * source or binary, and used for any purpose, provided
  4.  * this copyright notice is retained.
  5.  **/
  6. static char    *copyright[] = {
  7.     "@(#)rperf.c    3.1 12/15/93 (c) Copyright Brian P. Fitzgerald",
  8.     "Rensselaer Polytechnic Institute",
  9.     0
  10. };
  11.  
  12. /**
  13.  
  14. rperf.c -- print system performance statistics.
  15.  
  16. by Brian P. Fitzgerald
  17. Mechanics of Materials Laboratory
  18. Rensselaer Polytechnic Institute
  19. Troy, New York
  20.  
  21. usage: rperf [ options ] [ interval [ count ] ] [ +sortkey ] [ host ... ]
  22.        rup [ options ] [ interval ] [ host ... ]
  23.  
  24. Send comments and bug fixes to fitz@rpi.edu.  Please indicate the
  25. manufacturer and model of your computer, the name and version of your
  26. operating system and the version of rperf that you are using.
  27.  
  28. Tested:
  29. 386BSD
  30. Alliant FX/2800
  31. Amiga 4.0 2.1c
  32. BSDi
  33. DEC Ultrix 4.2
  34. DG/UX v5.4.2
  35. EISA PC NetBSD-0.9
  36. ESIX 4.0.4    (configure script in development)
  37. HP-UX 8.0, 9.01
  38. IBM AIX 3.1, 3.2
  39. Linux 0.99p14    (minimum required)
  40. MIPS RISCos
  41. Motorola R40V4.1, FH32.31
  42. NeXT Mach 20
  43. SCO Unix 3.2.2
  44. SGI PI Iris 4.0.1, 4.0.2, 4.0.5
  45. SunOS 4.1.1, 4.1.2, 4.1.3, 5.2
  46.  
  47. Not supported:
  48. AIX 1.2.1 on aix370
  49.  
  50. Broadcast mode not supported:
  51. Linux 0.99p13
  52.  
  53. Send reports of successful compilation on other platforms to
  54. fitz@rpi.edu
  55.  
  56. History:
  57.  
  58. 5/6/92    beta    posted to alt.sources
  59.  
  60. 5/18/92    1.1    Added multiple hosts, uptime display.
  61.         Improved output format.
  62.         posted to alt.sources
  63.  
  64. 5/25/92    1.2    Added signal handler.
  65.  
  66. 1/6/93    1.3    Added broadcast mode
  67.  
  68. 2/6/93    1.4    Added output sorting options.
  69.         Added rup alias.
  70.  
  71. 2/10/93    1.5    Corrected rpc version mismatch.  Now if_opackets right.
  72.         Plugged some memory leaks.
  73.  
  74. 3/5/93    1.6    Variable number of disks and cpu states.
  75.         Fixed a line wrap problem.  Added termcap routine.
  76.         posted to alt.sources
  77.  
  78. 8/26/93    2.1    posted to comp.sources.misc
  79.  
  80. 9/13/93    2.2    Solaris 2 port
  81.  
  82. 9/26/93    2.3    verbose help, setrlimit
  83.         mailed to comp.sources.testers volunteers
  84.  
  85. 10/26/93 2.4    fixed dk_xfer array problem
  86.         fixed cp_time problem on AIX
  87.         split up rperf.c
  88.         broadcast over multiple versions
  89.         use hash table for addresses
  90.         used comments from testers -- thanks:
  91.          hostname trim
  92.          screen clear
  93.          print '-' for unreported statistics
  94.          eliminate duplication for routers
  95.  
  96. 10/28/93 2.5    broadcast interval algorithm
  97.  
  98. 11/2/93     2.6    debugging version to check each_result
  99.  
  100. 11/10/93 2.7    mailed out to testers for review
  101.  
  102. 11/17/93 2.8    always try to adjust lines longer than ruler
  103.  
  104. 11/24/93 2.9    report when server has rebooted
  105.  
  106. 12/15/93 3.1    posted to comp.sources.misc
  107.  
  108. **/
  109.  
  110. #include "common.h"
  111. #include "rperf.h"
  112. #include "term.h"
  113.  
  114. key             keys[] = {
  115.     {"avg", sn_offset(avenrun[AV1]), sn_offset(avenrun[AV1]), 0, 0},
  116.     {"ave", sn_offset(avenrun[AV1]), sn_offset(avenrun[AV1]), 0, 0},
  117.     {"loadavg", sn_offset(avenrun[AV1]), sn_offset(avenrun[AV1]), 0, 0},
  118.     {"loadave", sn_offset(avenrun[AV1]), sn_offset(avenrun[AV1]), 0, 0},
  119.     {"loadav", sn_offset(avenrun[AV1]), sn_offset(avenrun[AV1]), 0, 0},
  120.     {"load", sn_offset(avenrun[AV1]), sn_offset(avenrun[AV1]), 0, 0},
  121.     {"avenrun", sn_offset(avenrun[AV1]), sn_offset(avenrun[AV1]), 0, 0},
  122.     {"av1", sn_offset(avenrun[AV1]), sn_offset(avenrun[AV1]), 0, 0},
  123.     {"av5", sn_offset(avenrun[AV5]), sn_offset(avenrun[AV5]), 0, 0},
  124.     {"av15", sn_offset(avenrun[AV15]), sn_offset(avenrun[AV15]), 0, 0},
  125.  
  126.     {"boottime", sn_offset(boottime.tv_sec), sn_offset(boottime.tv_sec), K_NZERO, 0},
  127.     {"boot", sn_offset(boottime.tv_sec), sn_offset(boottime.tv_sec), K_NZERO, 0},
  128.     {"uptime", sn_offset(boottime.tv_sec), sd_offset(boottime.tv_sec), K_NZERO, 0},
  129.     {"up", sn_offset(boottime.tv_sec), sd_offset(boottime.tv_sec), K_NZERO, 0},
  130.  
  131.     {"curtime", sn_offset(curtime.tv_sec), sn_offset(curtime.tv_sec), K_NZERO, 0},
  132.     {"time", sn_offset(curtime.tv_sec), sn_offset(curtime.tv_sec), K_NZERO, 0},
  133.     {"clock", sn_offset(curtime.tv_sec), sn_offset(curtime.tv_sec), K_NZERO, 0},
  134.  
  135.     {"user", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 0},
  136.     {"us", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 0},
  137.     {"nice", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 1},
  138.     {"ni", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 1},
  139.     {"sys", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 2},
  140.     {"system", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 2},
  141.     {"sy", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 2},
  142.     {"idle", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 3},
  143.     {"id", sn_offset(cp_time), sd_offset(cp_time), K_DELTA | K_ARRAY, 3},
  144.  
  145.     {"intr", sn_offset(v_intr), sd_offset(v_intr), K_DELTA, 0},
  146.     {"swtch", sn_offset(v_swtch), sd_offset(v_swtch), K_DELTA, 0},
  147.     {"cxsw", sn_offset(v_swtch), sd_offset(v_swtch), K_DELTA, 0},
  148.     {"csw", sn_offset(v_swtch), sd_offset(v_swtch), K_DELTA, 0},
  149.  
  150.     {"pgpgin", sn_offset(v_pgpgin), sd_offset(v_pgpgin), K_DELTA, 0},
  151.     {"pgin", sn_offset(v_pgpgin), sd_offset(v_pgpgin), K_DELTA, 0},
  152.     {"pgpgout", sn_offset(v_pgpgout), sd_offset(v_pgpgout), K_DELTA, 0},
  153.     {"pgout", sn_offset(v_pgpgout), sd_offset(v_pgpgout), K_DELTA, 0},
  154.     {"pgo", sn_offset(v_pgpgout), sd_offset(v_pgpgout), K_DELTA, 0},
  155.  
  156.     {"pswpin", sn_offset(v_pswpin), sd_offset(v_pswpin), K_DELTA, 0},
  157.     {"swpin", sn_offset(v_pswpin), sd_offset(v_pswpin), K_DELTA, 0},
  158.     {"swin", sn_offset(v_pswpin), sd_offset(v_pswpin), K_DELTA, 0},
  159.     {"pswpout", sn_offset(v_pswpout), sd_offset(v_pswpout), K_DELTA, 0},
  160.     {"swpout", sn_offset(v_pswpout), sd_offset(v_pswpout), K_DELTA, 0},
  161.     {"swo", sn_offset(v_pswpout), sd_offset(v_pswpout), K_DELTA, 0},
  162.  
  163.     {"dk_xfer", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 0},
  164.     {"sd0", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 0},
  165.     {"disk0", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 0},
  166.     {"d0", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 0},
  167.     {"sd1", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 1},
  168.     {"disk1", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 1},
  169.     {"d1", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 1},
  170.     {"sd2", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 2},
  171.     {"disk2", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 2},
  172.     {"d2", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 2},
  173.     {"sd3", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 3},
  174.     {"disk3", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 3},
  175.     {"d3", sn_offset(dk_xfer), sd_offset(dk_xfer), K_DELTA | K_ARRAY, 3},
  176.  
  177.     {"ipackets", sn_offset(if_ipackets), sd_offset(if_ipackets), K_DELTA, 0},
  178.     {"ipk", sn_offset(if_ipackets), sd_offset(if_ipackets), K_DELTA, 0},
  179.     {"ierrors", sn_offset(if_ierrors), sd_offset(if_ierrors), K_DELTA, 0},
  180.     {"ier", sn_offset(if_ierrors), sd_offset(if_ierrors), K_DELTA, 0},
  181.     {"oerrors", sn_offset(if_oerrors), sd_offset(if_oerrors), K_DELTA, 0},
  182.     {"oer", sn_offset(if_oerrors), sd_offset(if_oerrors), K_DELTA, 0},
  183.     {"opackets", sn_offset(if_opackets), sd_offset(if_opackets), K_DELTA, 0},
  184.     {"opk", sn_offset(if_opackets), sd_offset(if_opackets), K_DELTA, 0},
  185.     {"collisions", sn_offset(if_collisions), sd_offset(if_collisions), K_DELTA, 0},
  186.     {"coll", sn_offset(if_collisions), sd_offset(if_collisions), K_DELTA, 0},
  187.     {0, 0, 0, 0, 0}
  188. };
  189.  
  190. key            *key_ptr = NULL;
  191.  
  192. /* xx keep up to date */
  193. char           *rperfusagev[] = {
  194.     "usage: rperf [ options ] [ interval [ count ]] [ +sortkey ] [ host ... ]",
  195.     "rperf options:",
  196.     "-a    almost all (-cdiv)    -S    seconds in unix time",
  197.     "-c    cpu time, load average    -T    date and local time",
  198.     "-d    disk activity        -s    elapsed time, seconds",
  199.     "-i    ethernet activity    -u    uptime",
  200.     "-v    virtual memory        -A    all (-aSTsu)",
  201.     "-1,-2,-3,-4 rstat version    -b    broadcast continuously",
  202.     "-h    sort by hostname    -D    debug (also -DD, -DDD, etc.)",
  203.     "-n    don't resolve hostnames    -r    reverse sort",
  204.     "-N    never clear the screen    -B    bare display.  no headers",
  205.     "-?    verbose help (this message)",
  206.     0
  207. };
  208.  
  209. /* xx keep up to date */
  210. char           *rupusagev[] = {
  211.     "rup [ options ] [ interval ] [ host ... ]",
  212.     "-h    sort by hostname    -l    sort by 1 min load avg",
  213.     "-t    sort by uptime        -n    don't resolve host names",
  214.     "-D    debug (also -DD, -DDD, etc.)",
  215.     0
  216. };
  217.  
  218. /* xx keep up to date */
  219. char           *keyhelp[] = {
  220.     "The sort keys are:",
  221.     "loadavg     user      pgpgin      disk0      boottime    ipackets",
  222.     "av1         nice      pgpgout     disk1      uptime      ierrors",
  223.     "av5         sys       pswpin      disk2      curtime     oerrors",
  224.     "av15        idle      pswpout     disk3                  opackets",
  225.     "                      intr                               collisions",
  226.     "                      swtch",
  227.     0
  228. };
  229.  
  230. u_long          nhosts = 0;    /* multiple hosts idea from Larry McVoy */
  231. u_long          mode = MODE_BCST;    /* assume */
  232. u_long          opts = 0;
  233. u_long          thiscount = 0;
  234. u_long          nresp = 0;
  235. u_long          nprint = 0;
  236. u_long          dbg_lvl = 0;
  237.  
  238. struct timeval  starttime;
  239.  
  240. char           *f1_cp = "%-15s ";
  241. char           *f2_cp = "%3s %3s %3s %3s ";
  242. char           *fr_cp = "%3s %3s %3s %3s ";
  243.  
  244. char           *f1_av = "%-14s ";
  245. char           *f2_av = "%4s %4s %4s ";
  246. char           *fo_av = "%4.2f %4.2f %4.2f ";
  247. char           *fo_a0 = "%4.1f %4.1f %4.1f ";
  248.  
  249. char           *f1_dk = "%-12s";
  250. char           *f2_dk = "%2s %2s %2s %2s ";
  251. char           *fr_dk = "%2s %2s %2s %2s ";
  252.  
  253. char           *f1_vm = "%2s %2s %2s %2s %3s %3s ";
  254. char           *f2_vm = "%2s %2s %2s %2s %3s %3s ";
  255. char           *fr_vm = "%2s %2s %2s %2s %3s %3s ";
  256.  
  257. char           *f1_if = " %2s %2s %2s %2s %2s";
  258. char           *f2_if = " %2s %2s %2s %2s %2s";
  259. char           *fr_if = " %2s %2s %2s %2s %2s";
  260.  
  261. char           *f_time = "%-9.9s ";
  262. char           *fo_time = "%9d ";
  263.  
  264. char           *f_date = "%-15.15s ";
  265.  
  266. char           *f_secs = "%-5.5s ";
  267. char           *fo_secs = "%5.2f ";
  268.  
  269. char           *f2_up = "%15.15s ";
  270. char           *f_up = "%-15.15s ";
  271.  
  272. char           *f_haddr = "%-15.15s ";
  273. char            f_h[20] = "%12.12s ";
  274.  
  275. char            title1[160];
  276. char            title2[160];
  277. char            ruler[160];
  278.  
  279. void
  280. msg(s, fp)
  281.     char          **s;
  282.     FILE           *fp;
  283. {
  284.     while (*s) {
  285.     (void) fputs(*s++, fp);
  286.     (void) fputc('\n', fp);
  287.     }
  288. }
  289.  
  290. void
  291. verbosehelp()
  292. {
  293.     msg(copyright, stderr);
  294.     msg(rperfusagev, stderr);
  295.     msg(keyhelp, stderr);
  296.     msg(rupusagev, stderr);
  297.     exit(0);
  298. }
  299.  
  300. /**
  301.  * handle termination signals
  302. **/
  303. RETSIGTYPE
  304. cleanup()
  305. {
  306.     exit(0);            /* flush and close stdout */
  307. }
  308.  
  309. /**
  310.  * Check whether the data line is longer than the ruler.
  311.  * Try to adjust.
  312.  * Return the adjusted string.
  313. **/
  314. char           *
  315. adjust_line(obuf)
  316.     char           *obuf;
  317. {
  318.     int             olen = strlen(obuf);
  319.     int             rlen = strlen(ruler);
  320.     int             rpos = 0, opos;
  321.     int             nsp, err = 0;
  322.     char           *rp, *op, *tp, *sp, *cp;
  323.  
  324.     if (olen <= rlen) {
  325.     if (olen < rlen && dbg_lvl >= 2) {
  326.         (void) fprintf(stderr, "short line ruler=%d output%d\n",
  327.                rlen, olen);
  328.         (void) fputs(ruler, stderr);
  329.         (void) putc('\n', stderr);
  330.     }
  331.     return obuf;        /* leave alone */
  332.     }
  333.     if (dbg_lvl >= 2) {
  334.     (void) fprintf(stderr, "line too long ruler=%d output%d\n",
  335.                rlen, olen);
  336.     (void) fputs(ruler, stderr);
  337.     (void) putc('\n', stderr);
  338.     }
  339.     op = obuf;
  340.     for (rp = ruler; *rp;) {
  341.     tp = op;
  342.     for (; *rp && *rp == ' '; rp++);
  343.     for (; *op && *op == ' '; op++);
  344.     nsp = op - tp;
  345.     for (; *rp && *rp != ' '; rp++);
  346.     for (; *op && *op != ' '; op++);
  347.  
  348.     if (!*rp || nsp > 1) {
  349.         rpos = rp - ruler;
  350.         opos = op - obuf;
  351.         err = opos - rpos;
  352.     }
  353.     if (err > 0) {
  354.         if (dbg_lvl >= 2) {
  355.         (void) fputs(obuf, stderr);
  356.         (void) putc('\n', stderr);
  357.         (void) fprintf(stderr, "rpos=%d err=%d\n", rpos, err);
  358.         }
  359.         for (sp = op; sp != obuf; sp--) {
  360.         if (*sp == ' ')
  361.             sp--;
  362.         if (*sp == ' ') {    /* shift left one char */
  363.             sp++;
  364.             for (cp = sp; *cp; cp++)
  365.             *cp = *(cp + 1);
  366.             op--;
  367.             if (!--err)
  368.             break;
  369.         }
  370.         }
  371.     }
  372.     rpos = rp - ruler;
  373.     opos = op - obuf;
  374.     err = opos - rpos;
  375.     }
  376.  
  377.     if (dbg_lvl >= 2)
  378.     (void) fputs(" . . . . 1 . . . . 2 . . . . 3 . . . . 4 . . . . 5 . . . . 6 . . . . 7 . . . .\n", stderr);
  379.     return obuf;
  380. }
  381.  
  382. /**
  383.  * qsort comparison routine
  384.  *
  385.  * if i is to precede j, return a value < 0
  386.  * otherwise, return a value > 0
  387. **/
  388. static int
  389. datacompare(i, j)
  390.     struct datatbl *i, *j;
  391. {
  392.     return opts & O_RVRS
  393.     ? i->val - j->val
  394.     : j->val - i->val;
  395. }
  396.  
  397. /**
  398.  * Calculate deltas and other items.
  399.  * Prepare array of keys and pointers.
  400.  * Sort if required.
  401.  * Return a pointer to the sorted array.
  402. **/
  403. void
  404. compute_data()
  405. {
  406.     long            dt, hdt;    /* msec */
  407.     int             i;
  408.     int             dcpu, hdcpu;
  409.     struct data   **dpp;
  410.     enum cp_time_kind kind_tmp;
  411.  
  412.     if (dbg_lvl >= 4) {
  413.     (void) fprintf(stderr, "in compute_data\n");
  414.     }
  415.     nresp = 0;
  416.     for (dpp = &dp; *dpp; dpp = &(*dpp)->datap) {
  417.     /* this data is current */
  418.     if ((*dpp)->nn == thiscount) {
  419.  
  420.         (*dpp)->sd.boottime.tv_sec =    /* uptime */
  421.         (*dpp)->sn.boottime.tv_sec ?    /* if boottime reported */
  422.         (*dpp)->sn.curtime.tv_sec
  423.         - (*dpp)->sn.boottime.tv_sec : 0;
  424.  
  425.         nresp++;
  426.         if ((*dpp)->no) {
  427.         /* this data is not the first response from that host */
  428.         int             rebooted;
  429.  
  430.         /* time in milliseconds, since last call */
  431.         dt = (((*dpp)->sn.curtime.tv_sec
  432.                - (*dpp)->so.curtime.tv_sec) * 1000000
  433.               + ((*dpp)->sn.curtime.tv_usec
  434.              - (*dpp)->so.curtime.tv_usec) + 500) / 1000;
  435.         hdt = (dt + 1) / 2;
  436.  
  437.         if (!(*dpp)->sd.cp_val)
  438.             create_array((array *) & (*dpp)->sd.cp_time,
  439.                  (*dpp)->sn.cp_len);
  440.  
  441.         /**
  442.          * calculate cp_time deltas
  443.          *
  444.          * if cp_time is reckoned cumulatively, then a negative delta
  445.          * is impossible.  Therefore, if delta is negative the remote
  446.          * hosts's rpc.rstatd must be reporting cp_time as an
  447.          * increment since the previous call.  IBM AIX does it that
  448.          * way.
  449.         **/
  450.         kind_tmp = (*dpp)->cp_kind;
  451.         /**
  452.          * All counters are reset on reboot.
  453.          * Arbitrarily pick if_ipackets as an indicator
  454.          * Can't use: t_n(i).boottime.tv_sec > t_o(i).boottime.tv_sec
  455.          * because on some machines it is a derived quantity
  456.          * (curtime - _lbolt * _hz), and therefore fluctuates a bit.
  457.         **/
  458.         rebooted = (*dpp)->sn.if_ipackets < (*dpp)->so.if_ipackets;
  459.         for (i = 0; i < (*dpp)->sn.cp_len; i++) {
  460.             (*dpp)->sd.cp_val[i] = delta(cp_val[i]);
  461.             if ((*dpp)->sd.cp_val[i] < 0 && !rebooted)
  462.             (*dpp)->cp_kind = CP_TIME_INC;
  463.         }
  464.         if (dbg_lvl >= 1 && kind_tmp == CP_TIME_CUM
  465.             && (*dpp)->cp_kind == CP_TIME_INC) {
  466.             (void) fprintf(stderr,
  467.                  "%s: rpc.rstatd reports incremental cp_time\n",
  468.                    (*dpp)->host);
  469.         }
  470.         /**
  471.              * From vmstat.c 5.31 (Berkeley)
  472.              * We round upward to avoid losing low-frequency events
  473.              * (i.e., >= 1 per interval but < 1 per second).
  474.              **/
  475.         dcpu = 0;
  476.         switch ((*dpp)->cp_kind) {
  477.  
  478.         case CP_TIME_CUM:
  479.             for (i = 0; i < (*dpp)->sn.cp_len; i++) {
  480.             dcpu += (*dpp)->sd.cp_val[i];
  481.             }
  482.             hdcpu = dcpu / 2;
  483.             for (i = 0; i < (*dpp)->sn.cp_len; i++)
  484.             (*dpp)->sd.cp_val[i] = cpu((*dpp)->sd.cp_val[i]);
  485.             break;
  486.  
  487.         case CP_TIME_INC:    /* incremental.  disregard old data */
  488.             for (i = 0; i < (*dpp)->sn.cp_len; i++) {
  489.             dcpu += (*dpp)->sn.cp_val[i];
  490.             }
  491.             hdcpu = dcpu / 2;
  492.             for (i = 0; i < (*dpp)->sn.cp_len; i++)
  493.             /* store it in sd anyway because sd is printed */
  494.             (*dpp)->sd.cp_val[i] = cpu((*dpp)->sn.cp_val[i]);
  495.             break;
  496.  
  497.         }        /* switch cp_kind */
  498.  
  499.         /* if different RSTAT versions for so and sn */
  500.         if ((*dpp)->sn.dk_len != (*dpp)->so.dk_len
  501.         /* or sd.dk_xfer is uninitialized */
  502.             || !(*dpp)->sd.dk_len) {
  503.             if ((*dpp)->sd.dk_val)
  504.             destroy_array((array *) & (*dpp)->sd.dk_xfer);
  505.             create_array((array *) & (*dpp)->sd.dk_xfer,
  506.                  Max((*dpp)->sn.dk_len, (*dpp)->so.dk_len));
  507.         }
  508.         /**
  509.          * calculate the deltas for dk_xfer
  510.         **/
  511.         for (i = 0; i < Min((*dpp)->sn.dk_len, (*dpp)->so.dk_len); i++)
  512.             (*dpp)->sd.dk_val[i] = rate(delta(dk_val[i]));
  513.  
  514.         (*dpp)->sd.v_pgpgin = rate(delta(v_pgpgin));
  515.         (*dpp)->sd.v_pgpgout = rate(delta(v_pgpgout));
  516.         (*dpp)->sd.v_pswpin = rate(delta(v_pswpin));
  517.         (*dpp)->sd.v_pswpout = rate(delta(v_pswpout));
  518.         (*dpp)->sd.v_swtch = rate(delta(v_swtch));
  519.         /* Why does v_intr count down on aix370? */
  520.         (*dpp)->sd.v_intr = rate(Abs((int) (delta(v_intr))));
  521.  
  522.         (*dpp)->sd.if_ipackets = rate(delta(if_ipackets));
  523.         (*dpp)->sd.if_ierrors = rate(delta(if_ierrors));
  524.         (*dpp)->sd.if_opackets = rate(delta(if_opackets));
  525.         (*dpp)->sd.if_oerrors = rate(delta(if_oerrors));
  526.         (*dpp)->sd.if_collisions = rate(delta(if_collisions));
  527.         }            /* (*dpp)->no */
  528.     }            /* (*dpp)->nn == thiscount */
  529.     }
  530.     if (dbg_lvl >= 4) {
  531.     (void) fprintf(stderr, "returning from compute_data\n");
  532.     }
  533. }
  534.  
  535. struct datatbl *
  536. sort_data(tbl)
  537.     struct datatbl *tbl;
  538. {
  539.     int             sort_needs_delta;
  540.     int             will_sort;
  541.     int             i;
  542.     struct data   **dpp;
  543.  
  544.     if (dbg_lvl >= 4) {
  545.     (void) fprintf(stderr, "in sort_data\n");
  546.     }
  547.     will_sort = opts & O_SORT;
  548.     sort_needs_delta = will_sort && key_ptr->k_flag & K_DELTA;
  549.  
  550.     if (tbl)
  551.     free((char *) tbl);
  552.     tbl = (struct datatbl *) malloc((unsigned) nresp * sizeof(struct datatbl));
  553.     if (tbl == NULL) {
  554.     perror("malloc");
  555.     exit(1);
  556.     }
  557.     i = 0;
  558.     for (dpp = &dp; *dpp; dpp = &(*dpp)->datap) {
  559.     if ((*dpp)->nn == thiscount) {
  560.  
  561.         tbl[i].dp = *dpp;
  562.         if (will_sort && (!sort_needs_delta || (*dpp)->no)) {
  563.         if (key_ptr->k_flag & K_ARRAY) {
  564.             array           arr;
  565.  
  566.             arr = *(array *) ((char *) (*dpp) + key_ptr->k_doffset);
  567.             tbl[i].val = arr.val[key_ptr->k_index];
  568.         } else {
  569.             tbl[i].val = *(int *) ((char *) (*dpp) + key_ptr->k_doffset);
  570.         }
  571.         } else {
  572.         /* this val can be sorted but will not be printed */
  573.         tbl[i].val = 0;
  574.         }
  575.         i++;
  576.     }
  577.     }
  578.  
  579.     if (will_sort)
  580.     qsort((char *) tbl, (int) nresp, sizeof(struct datatbl), datacompare);
  581.  
  582.     if (dbg_lvl >= 4) {
  583.     (void) fprintf(stderr, "returning from sort_data\n");
  584.     }
  585.     return tbl;
  586. }
  587.  
  588. /**
  589.  * make the titles for the columns, depending on the options selected
  590. **/
  591. void
  592. build_title()
  593. {
  594.     if (dbg_lvl >= 4) {
  595.     (void) fprintf(stderr, "in build_title\n");
  596.     }
  597.     title1[0] = '\0';
  598.     title2[0] = '\0';
  599.     ruler[0] = '\0';
  600.     if (opts & O_BARE) {
  601.     if (dbg_lvl >= 4) {
  602.         (void) fprintf(stderr,
  603.                "returning from build_title (opts & O_BARE)\n");
  604.     }
  605.     return;
  606.     }
  607.     if (opts & O_TIME) {
  608.     (void) sprintf(title1 + strlen(title1), f_time, "unix time");
  609.     (void) sprintf(title2 + strlen(title2), f_time, "(seconds)");
  610.     (void) sprintf(ruler + strlen(ruler), fo_time, 100000000);
  611.     }
  612.     if (opts & O_DATE) {
  613.     (void) sprintf(title1 + strlen(title1), f_date, "Date   Time");
  614.     (void) sprintf(title2 + strlen(title2), f_date, "       (local)");
  615.     (void) sprintf(ruler + strlen(ruler), f_date,
  616.                "700101 00:00:00");
  617.     }
  618.     if (opts & O_SECS) {
  619.     (void) sprintf(title1 + strlen(title1), f_secs, "time");
  620.     (void) sprintf(title2 + strlen(title2), f_secs, "(sec)");
  621.     (void) sprintf(ruler + strlen(ruler), fo_secs, 10.01);
  622.     }
  623.     if (nhosts > 1) {
  624.     (void) sprintf(title1 + strlen(title1), f_h, "");
  625.     (void) sprintf(title2 + strlen(title2), f_h, "host");
  626.     (void) sprintf(ruler + strlen(ruler), f_h,
  627.                "127.000.000.001");
  628.     }
  629.     if (opts & O_UP) {
  630.     (void) sprintf(title1 + strlen(title1), f_up, "");
  631.     (void) sprintf(title2 + strlen(title2), f2_up, "uptime");
  632.     (void) sprintf(ruler + strlen(ruler), f_up, "365 days, 23:59 ");
  633.     }
  634.     if (opts & O_CP) {
  635.     (void) sprintf(title1 + strlen(title1), f1_cp, "%cpu");
  636.     (void) sprintf(title1 + strlen(title1), f1_av, "loadavg (nrun)");
  637.     (void) sprintf(title2 + strlen(title2), f2_cp, "us", "ni", "sy", "id");
  638.     (void) sprintf(title2 + strlen(title2), f2_av, "1m", "5m", "15m");
  639.     (void) sprintf(ruler + strlen(ruler), fr_cp, "100", "100", "100", "100");
  640.     (void) sprintf(ruler + strlen(ruler), fo_a0, 10.00, 10.00, 10.00);
  641.     }
  642.     if (opts & O_DK) {
  643.     (void) sprintf(title1 + strlen(title1), f1_dk, "disk xfers");
  644.     (void) sprintf(title2 + strlen(title2), f2_dk,
  645.                "d0", "d1", "d2", "d3");
  646.     (void) sprintf(ruler + strlen(ruler), fr_dk, "10", "10", "10", "10");
  647.     }
  648.     if (opts & O_VM) {
  649.     (void) sprintf(title1 + strlen(title1), f1_vm,
  650.                "pg", "pg", "sw", "sw", "cx", "in");
  651.     (void) sprintf(title2 + strlen(title2), f2_vm,
  652.                "in", "o", "in", "o", "sw", "tr");
  653.     (void) sprintf(ruler + strlen(ruler), fr_vm, "10", "10", "10", "10", "100", "100");
  654.     }
  655.     if (opts & O_IF) {
  656.     (void) sprintf(title1 + strlen(title1), f1_if,
  657.                "i", "i", "o", "o", "co");
  658.     (void) sprintf(title2 + strlen(title2), f2_if,
  659.                "pk", "er", "pk", "er", "ll");
  660.     (void) sprintf(ruler + strlen(ruler), fr_if, "10", "10", "10", "10", "10");
  661.     }
  662.     (void) sprintf(title1 + strlen(title1), "\n");
  663.     (void) sprintf(title2 + strlen(title2), "\n");
  664.     if (dbg_lvl >= 4) {
  665.     (void) fprintf(stderr, "returning from build_title\n");
  666.     }
  667.     return;
  668. }
  669.  
  670. char           *
  671. common_suffix(tbl)
  672.     struct datatbl *tbl;
  673. {
  674.     int             i;
  675.     int             suflen = 0;
  676.     char           *dotp, *hp, *sp;
  677.     char           *hname;
  678.     static char     suffix[256];
  679.  
  680.     /* determine the longest common suffix starting with a '.' */
  681.     if (dbg_lvl >= 4) {
  682.     (void) fprintf(stderr, "in common_suffix\n");
  683.     }
  684.     suffix[0] = '\0';
  685.     for (i = 0; i < nprint; ++i) {
  686.     hname = tbl[i].dp->host_dup;
  687.  
  688.     if (strcmp(hname, "localhost") == 0)
  689.         continue;
  690.  
  691.     if (!isalpha(*hname))
  692.         continue;
  693.     if (!suffix[0]) {
  694.         dotp = strchr(hname, '.');
  695.         if (dotp) {
  696.         (void) strcpy(suffix, dotp);
  697.         suflen = strlen(suffix);
  698.         }
  699.     }
  700.     sp = suffix + suflen - 1;
  701.     hp = hname + strlen(hname) - 1;
  702.     while (sp >= suffix && hp >= hname && *sp == *hp)
  703.         --sp, --hp;
  704.  
  705.     dotp = strchr(hp + 1, '.');
  706.     if (!dotp) {
  707.         *suffix = '\0';
  708.         if (dbg_lvl >= 4) {
  709.         (void) fprintf(stderr,
  710.                    "returning from common_suffix (!dotp)\n");
  711.         }
  712.         return suffix;
  713.     }
  714.     if (sp > suffix) {
  715.         (void) strcpy(suffix, dotp);
  716.         suflen = strlen(suffix);
  717.     }
  718.     }
  719.     if (dbg_lvl >= 4) {
  720.     (void) fprintf(stderr, "returning from common_suffix\n");
  721.     }
  722.     return suffix;
  723. }
  724.  
  725. /**
  726.  * remove common suffix from hostnames
  727.  * by sreiz@aie.nl (Steven Reiz)
  728. **/
  729. void
  730. strip_hostnames(tbl)
  731.     struct datatbl *tbl;
  732. {
  733.     int             i, suflen, maxlen;
  734.     char           *p;
  735.     char           *suffix;
  736.  
  737.     static int      oldmaxlen;
  738.  
  739.     if (dbg_lvl >= 4) {
  740.     (void) fprintf(stderr, "in strip_hostnames\n");
  741.     }
  742.     if (opts & O_NHOST) {
  743.     if (dbg_lvl >= 4) {
  744.         (void) fprintf(stderr,
  745.                "returning from strip_hostnames (opts & O_NHOST)\n");
  746.     }
  747.     return;
  748.     }
  749.     suffix = common_suffix(tbl);
  750.     suflen = strlen(suffix);
  751.     if (dbg_lvl >= 1)
  752.     (void) fprintf(stderr, "suffix %s\n", suffix);
  753.     /* remove the suffix */
  754.     maxlen = 4;            /* length of the "host" header string */
  755.     for (i = 0; i < nprint; ++i) {
  756.     int             oldplen, newplen;
  757.  
  758.     (void) strcpy(tbl[i].dp->host, tbl[i].dp->host_dup);
  759.     p = tbl[i].dp->host;
  760.     if (strcmp(p, "localhost") == 0)
  761.         continue;
  762.     if (!isalpha(p[0]))
  763.         continue;
  764.     oldplen = strlen(p);
  765.     newplen = oldplen - suflen;
  766.     p[newplen] = '\0';
  767.     maxlen = Max(maxlen, newplen);
  768.     }
  769.     /* set f_h */
  770.     maxlen = Min(maxlen, 12);    /* never longer than default */
  771.     if (oldmaxlen != maxlen) {
  772.     oldmaxlen = maxlen;
  773.     (void) sprintf(f_h, "%%%d.%ds ", maxlen, maxlen);
  774.     /* rebuild title */
  775.     build_title();
  776.     }
  777.     if (dbg_lvl >= 4) {
  778.     (void) fprintf(stderr, "returning from strip_hostnames\n");
  779.     }
  780. }
  781.  
  782. /**
  783.  * print the statistics or the uptime
  784. **/
  785. void
  786. print_data(tbl, flag)
  787.     struct datatbl *tbl;
  788.     int             flag;
  789. {
  790.     long            up, upd, uph, upm;
  791.     float           s = 0.0;
  792.     int             i;
  793.     char           *out;
  794.     char            obuf[160];
  795.     char            fo_bf[32];
  796.  
  797.     if (dbg_lvl >= 4) {
  798.     (void) fprintf(stderr, "in print_data\n");
  799.     }
  800.     if (opts & O_SECS && flag == P_DATA) {
  801.     struct timeval  now;
  802.     if (gettimeofday(&now, (struct timezone *) NULL) == -1) {
  803.         perror("gettimeofday");
  804.         exit(1);
  805.     }
  806.     s = (now.tv_sec - starttime.tv_sec)
  807.         + ((now.tv_usec - starttime.tv_usec) + 5000)
  808.         / 10000 / 100.;
  809.     }
  810.     for (i = 0; i < nprint; i++) {
  811.     if (!tbl[i].dp->no && flag == P_DATA)    /* do we have delta? */
  812.         continue;
  813.     if (tbl[i].dp->no && flag == P_UP)    /* do we have delta? */
  814.         continue;
  815.     if (tbl[i].dp->no
  816.         && t_n(i).if_ipackets < t_o(i).if_ipackets) {
  817.         if (dbg_lvl >= 1) {
  818.         (void) fprintf(stderr, "%s was rebooted.\n", tbl[i].dp->host);
  819.         }
  820.         continue;
  821.     }
  822.     obuf[0] = '\0';
  823.     if (opts & O_TIME && flag == P_DATA)
  824.         (void) sprintf(obuf + strlen(obuf), fo_time,
  825.                t_n(i).curtime.tv_sec);
  826.     if (opts & O_DATE && flag == P_DATA) {
  827.         char            buf[30];
  828.         struct tm      *tp;
  829.  
  830.         tp = localtime((long *) &t_n(i).curtime.tv_sec);
  831.         if (!strftime(buf, 30, "%y%m%d %H:%M:%S ", tp)) {
  832.         (void) fputs("can't convert date\n", stderr);
  833.         exit(1);
  834.         }
  835.         (void) sprintf(obuf + strlen(obuf), f_date, buf);
  836.     }
  837.     if (opts & O_SECS && flag == P_DATA)
  838.         (void) sprintf(obuf + strlen(obuf), fo_secs, s);
  839.  
  840.     if (nhosts > 1 || mode == MODE_BCST || flag == P_UP)
  841.         (void) sprintf(obuf + strlen(obuf),
  842.                (isalpha(*tbl[i].dp->host) ? f_h : f_haddr),
  843.                tbl[i].dp->host);
  844.  
  845.     if (opts & O_UP || flag == P_UP) {
  846.         if (t_n(i).boottime.tv_sec) {    /* > vers 1 ) */
  847.         up = t_d(i).boottime.tv_sec;    /* uptime stored here */
  848.         upd = up / 86400;
  849.         up = Abs(up - upd * 86400);
  850.         uph = up / 3600;
  851.         up -= uph * 3600;
  852.         upm = up / 60;
  853.         if (flag == P_UP)
  854.             (void) sprintf(obuf + strlen(obuf), "up ");
  855.         switch (upd) {
  856.         case 0:
  857.             (void) sprintf(obuf + strlen(obuf), "          ");
  858.             break;
  859.         case 1:
  860.             (void) sprintf(obuf + strlen(obuf), "%3d day,  ", upd);
  861.             break;
  862.         default:
  863.             (void) sprintf(obuf + strlen(obuf), "%3d days, ", upd);
  864.             break;
  865.         }        /* switch upd */
  866.         (void) sprintf(obuf + strlen(obuf), "%2d:%02d", uph, upm);
  867.         if (flag == P_UP)
  868.             (void) sprintf(obuf + strlen(obuf), ",");
  869.         (void) sprintf(obuf + strlen(obuf), " ");
  870.         } else {        /* vers 1 ) */
  871.         (void) sprintf(obuf + strlen(obuf), f_up, "-");
  872.         }
  873.     }
  874.     /**
  875.      * macros to control what to print:
  876.      * data or '-'
  877.      *
  878.      * use these macros with care
  879.     **/
  880. #define on_nzero(a) (t_o(i).a && t_n(i).a)    /* old and new nonzero */
  881. #define f_nzero2(a) (on_nzero(a) ? "%2d" : " %c")
  882. #define f_nzero3(a) (on_nzero(a) ? "%3d" : "  %c")
  883. #define d_nzero(a) (on_nzero(a) ? t_d(i).a : '-')
  884.  
  885.     if (opts & O_CP && flag == P_DATA) {
  886.         (void) sprintf(fo_bf, fr_cp,
  887.                f_nzero3(cp_val[CP_USER]),
  888.                f_nzero3(cp_val[CP_NICE]),
  889.                f_nzero3(cp_val[CP_SYS]),
  890.                f_nzero3(cp_val[CP_IDLE]));
  891.         (void) sprintf(obuf + strlen(obuf), fo_bf,
  892.                d_nzero(cp_val[CP_USER]),
  893.                d_nzero(cp_val[CP_NICE]),
  894.                d_nzero(cp_val[CP_SYS]),
  895.                d_nzero(cp_val[CP_IDLE]));
  896.     }
  897.     if (opts & O_CP || flag == P_UP) {
  898.         /* average run queue lengths */
  899.         if (t_n(i).boottime.tv_sec) {    /* > vers 1 */
  900.         if (flag == P_UP)
  901.             (void) sprintf(obuf + strlen(obuf),
  902.                    "     load average: ");
  903.         /* average run queue lengths */
  904.         (void) sprintf(obuf + strlen(obuf),
  905.                    flag == P_UP ? fo_av : fo_a0,
  906.                    loadav(t_n(i).avenrun[AV1]),
  907.                    loadav(t_n(i).avenrun[AV5]),
  908.                    loadav(t_n(i).avenrun[AV15]));
  909.         } else {        /* vers 1 */
  910.         (void) sprintf(obuf + strlen(obuf),
  911.                    f2_av, "-", "-", "-");
  912.         }
  913.     }
  914.     if (opts & O_DK && flag == P_DATA) {
  915.         (void) sprintf(fo_bf, fr_dk,
  916.                f_nzero2(dk_val[0]),
  917.                f_nzero2(dk_val[1]),
  918.                f_nzero2(dk_val[2]),
  919.                f_nzero2(dk_val[3]));
  920.         (void) sprintf(obuf + strlen(obuf), fo_bf,
  921.                d_nzero(dk_val[0]),
  922.                d_nzero(dk_val[1]),
  923.                d_nzero(dk_val[2]),
  924.                d_nzero(dk_val[3]));
  925.     }
  926.     if (opts & O_VM && flag == P_DATA) {
  927.         (void) sprintf(fo_bf, fr_vm,
  928.                f_nzero2(v_pgpgin),
  929.                f_nzero2(v_pgpgout),
  930.                f_nzero2(v_pswpin),
  931.                f_nzero2(v_pswpout),
  932.                f_nzero3(v_swtch),
  933.                f_nzero3(v_intr));
  934.         (void) sprintf(obuf + strlen(obuf), fo_bf,
  935.                d_nzero(v_pgpgin),
  936.                d_nzero(v_pgpgout),
  937.                d_nzero(v_pswpin),
  938.                d_nzero(v_pswpout),
  939.                d_nzero(v_swtch),
  940.                d_nzero(v_intr));
  941.     }
  942.     if (opts & O_IF && flag == P_DATA) {
  943.         (void) sprintf(fo_bf, fr_if,
  944.                f_nzero2(if_ipackets),
  945.                f_nzero2(if_ierrors),
  946.                f_nzero2(if_opackets),
  947.                f_nzero2(if_oerrors),
  948.                f_nzero2(if_collisions));
  949.         (void) sprintf(obuf + strlen(obuf), fo_bf,
  950.                d_nzero(if_ipackets),
  951.                d_nzero(if_ierrors),
  952.                d_nzero(if_opackets),
  953.                d_nzero(if_oerrors),
  954.                d_nzero(if_collisions));
  955.     }
  956.     if (flag == P_DATA) {
  957.         out = adjust_line(obuf);
  958.     } else {
  959.         out = obuf;
  960.     }            /* switch flag */
  961.  
  962.     (void) fputs(out, stdout);
  963.     (void) putc('\n', stdout);
  964.  
  965.     if (opts & O_CP && flag == P_DATA) {
  966.         int             c, chigh = 0, morestates = 0;
  967.  
  968.         for (c = CPUSTATES;
  969.          c < Min(t_n(i).cp_len, t_o(i).cp_len); c++) {
  970.         static          cp_len_flag;
  971.         if (dbg_lvl >= 1 && !cp_len_flag) {
  972.             (void) fprintf(stderr,
  973.             "%s: cp_len > 4.  rperf cannot handle this case yet.\n",
  974.                    tbl[i].dp->host);
  975.             cp_len_flag = 1;
  976.         }
  977.         if (on_nzero(cp_val[c])) {
  978.             chigh = c;
  979.             morestates++;
  980.         }
  981.         }
  982.         if (morestates > 0) {
  983.         (void) fprintf(stdout, "%d more cpu states%s...",
  984.                    morestates, morestates == 1 ? "" : "s");
  985.         for (c = CPUSTATES; c <= chigh; c++)
  986.             if (on_nzero(cp_val[c]))
  987.             (void) fprintf(stdout,
  988.                   "  cpu state%d: %d", c, t_d(i).cp_val[c]);
  989.         (void) putc('\n', stdout);
  990.         }
  991.     }
  992.     if (opts & O_DK && flag == P_DATA) {
  993.         int             d, dhigh = 0, moredisks = 0;
  994.  
  995.         for (d = DK_NDRIVE;
  996.          d < Min(t_n(i).dk_len, t_o(i).dk_len); d++) {
  997.         if (on_nzero(dk_val[d])) {
  998.             dhigh = d;
  999.             moredisks++;
  1000.         }
  1001.         }
  1002.         if (moredisks > 0) {
  1003.         (void) fprintf(stdout, "%d more disk%s...",
  1004.                    moredisks, moredisks == 1 ? "" : "s");
  1005.         for (d = DK_NDRIVE; d <= dhigh; d++)
  1006.             if (on_nzero(dk_val[d]))
  1007.             (void) fprintf(stdout,
  1008.                        "  disk%d: %d", d, t_d(i).dk_val[d]);
  1009.         (void) putc('\n', stdout);
  1010.         }
  1011.     }
  1012.     }
  1013.     if (dbg_lvl >= 4) {
  1014.     (void) fprintf(stderr, "returning from print_data\n");
  1015.     }
  1016. }
  1017.  
  1018. /**
  1019.  * If we are sorting, delete hosts which have had zero counts since boot
  1020.  * time.  They may not be capable of reporting that statistic
  1021. **/
  1022. void
  1023. delete_zeros()
  1024. {
  1025.     struct data   **dpp;
  1026.  
  1027.     if (dbg_lvl >= 4) {
  1028.     (void) fprintf(stderr, "in delete_zeros\n");
  1029.     }
  1030.     if (!key_ptr) {
  1031.     if (dbg_lvl >= 4) {
  1032.         (void) fprintf(stderr, "returning from delete_zeros (!key_ptr)\n");
  1033.     }
  1034.     return;
  1035.     }
  1036.     for (dpp = &dp; *dpp; dpp = &(*dpp)->datap) {
  1037.  
  1038.     if (!(*dpp)->nn)
  1039.         continue;
  1040.  
  1041.     if (key_ptr->k_noffset == sn_offset(cp_time)
  1042.         && (*dpp)->cp_kind == CP_TIME_INC)
  1043.         continue;
  1044.  
  1045.     if (key_ptr->k_flag & K_NZERO) {
  1046.         int             val;
  1047.         val = *(int *) ((char *) (*dpp) + key_ptr->k_noffset);
  1048.         if (!val) {
  1049.         if (dbg_lvl >= 1) {
  1050.             (void) fprintf(stderr,
  1051.                   "%s: rpc.rstatd vers %d does not report %s\n",
  1052.               (*dpp)->host, (*dpp)->bcst_vers, key_ptr->k_name);
  1053.         }
  1054.         (*dpp)->nn = 0;    /* mark as not received */
  1055.         continue;
  1056.         }
  1057.     }            /* K_NZERO */
  1058.     if (key_ptr->k_flag & K_DELTA) {
  1059.         int             sinceboot;
  1060.         if (key_ptr->k_flag & K_ARRAY) {
  1061.         array           arr;
  1062.  
  1063.         arr = *(array *) ((char *) (*dpp) + key_ptr->k_noffset);
  1064.         sinceboot = arr.val[key_ptr->k_index];
  1065.         } else {
  1066.         sinceboot = *(int *) ((char *) (*dpp) + key_ptr->k_noffset);
  1067.         }
  1068.         if (!sinceboot) {
  1069.         if (dbg_lvl >= 1)
  1070.             (void) fprintf(stderr,
  1071.                 " %s zero %s events since boot.  ignore.\n",
  1072.                    (*dpp)->host, key_ptr->k_name);
  1073.         (*dpp)->nn = 0;    /* mark as not received */
  1074.         }
  1075.     }            /* K_DELTA */
  1076.     }                /* for dpp */
  1077.     if (dbg_lvl >= 4) {
  1078.     (void) fprintf(stderr, "returning from delete_zeros\n");
  1079.     }
  1080. }
  1081.  
  1082. /**
  1083.  * If possible, increase the user's file descriptor table size to the maximum
  1084.  * allowable by the system, or to a very large number.
  1085.  *
  1086.  * In any case, return the file descriptor table size.
  1087. **/
  1088. int
  1089. set_max_nofiles()
  1090. {
  1091.     static struct Rlimit nofiles;
  1092.  
  1093. #ifdef RLIMIT_NOFILE
  1094.     if (getrlimit(RLIMIT_NOFILE, &nofiles) == -1) {
  1095.     (void) perror("getrlimit");
  1096.     exit(1);
  1097.     }
  1098.     nofiles.rlim_cur = nofiles.rlim_max;
  1099.     if (setrlimit(RLIMIT_NOFILE, &nofiles) == -1) {
  1100.     (void) perror("setrlimit");
  1101.     /* don't exit -- just use existing limit */
  1102.     }
  1103.     if (getrlimit(RLIMIT_NOFILE, &nofiles) == -1) {
  1104.     (void) perror("getrlimit");
  1105.     exit(1);
  1106.     }
  1107. #else                /* !RLIMIT_NOFILE */
  1108. #ifdef HAVE_SETDTABLESIZE
  1109.     /* thanks -- Ric Anderson <ric@cs.arizona.edu> */
  1110.     nofiles.rlim_cur = setdtablesize(1000 /* huge */ );
  1111.     if (nofiles.rlim_cur == -1) {
  1112.     (void) perror("setdtablesize");
  1113.     /* don't exit -- just use existing limit */
  1114.     }
  1115. #else                /* !HAVE_SETDTABLESIZE */
  1116. #ifdef HAVE_UNISTD_H
  1117.     /* thanks -- Colin M. Clark <cmc@srg-ssr.ch> */
  1118.     nofiles.rlim_cur = sysconf(_SC_OPEN_MAX);
  1119.     if (nofiles.rlim_cur == -1) {
  1120.     (void) perror("sysconf");
  1121.     exit(1);
  1122.     }
  1123. #else                /* !HAVE_UNISTD_H */
  1124.     nofiles.rlim_cur = getdtablesize();
  1125. #endif                /* !HAVE_UNISTD_H */
  1126. #endif                /* !HAVE_SETDTABLESIZE */
  1127. #endif                /* !RLIMIT_NOFILE */
  1128.  
  1129.     if (dbg_lvl >= 1)
  1130.     (void) fprintf(stderr, "Open file descriptor limit now %d\n",
  1131.                nofiles.rlim_cur);
  1132.     return nofiles.rlim_cur;
  1133. }
  1134.  
  1135. void
  1136. dumplist()
  1137. {
  1138.     int             i = 0;
  1139.     int             d;
  1140.     struct data   **dpp;
  1141.     char           *dotquad;
  1142.  
  1143.     (void) fprintf(stdout, "\nexpecting %d hosts\n", nhosts);
  1144.  
  1145.     for (dpp = &dp; *dpp; dpp = &(*dpp)->datap) {
  1146.     (void) fprintf(stdout, "%2d/%2d ", ++i, nhosts);
  1147.     (void) fprintf(stdout, "%s %s\n", (*dpp)->host_dup, (*dpp)->host);
  1148.     dotquad = inet_ntoa((*dpp)->addr);
  1149.     (void) fputs(dotquad, stdout);
  1150.     (void) fprintf(stdout, "\t*dpp=0x%08x (*dpp)->datap=0x%08x\n",
  1151.                (u_long) (*dpp), (u_long) (*dpp)->datap);
  1152.     (void) fputs(dotquad, stdout);
  1153.     (void) fprintf(stdout,
  1154.           "\tno=%d, nn=%d, clnt_vers=%d, bcst_vers=%d, ok_to_call=%d\n",
  1155.                (*dpp)->no,
  1156.                (*dpp)->nn,
  1157.                (*dpp)->clnt_vers,
  1158.                (*dpp)->bcst_vers,
  1159.                (*dpp)->ok_to_call);
  1160.  
  1161.     (void) fputs(dotquad, stdout);
  1162.     if ((*dpp)->sn.cp_val) {
  1163.         (void) fprintf(stdout, "\tcp_time\t%08x %08x %08x %08x\n",
  1164.                (*dpp)->sn.cp_val[CP_USER],
  1165.                (*dpp)->sn.cp_val[CP_NICE],
  1166.                (*dpp)->sn.cp_val[CP_SYS],
  1167.                (*dpp)->sn.cp_val[CP_IDLE]);
  1168.     } else {
  1169.         (void) fprintf(stdout, "\tcp_val = NULL\n");
  1170.     }
  1171.  
  1172.     (void) fputs(dotquad, stdout);
  1173.     (void) fprintf(stdout, "\tavenrun\t%08x %08x %08x\n",
  1174.                (*dpp)->sn.avenrun[AV1],
  1175.                (*dpp)->sn.avenrun[AV5],
  1176.                (*dpp)->sn.avenrun[AV15]);
  1177.  
  1178.     (void) fputs(dotquad, stdout);
  1179.     if ((*dpp)->sn.dk_val) {
  1180.         (void) fprintf(stdout, "\tdk_xfer\t%08x %08x %08x %08x\n",
  1181.                (*dpp)->sn.dk_val[0],
  1182.                (*dpp)->sn.dk_val[1],
  1183.                (*dpp)->sn.dk_val[2],
  1184.                (*dpp)->sn.dk_val[3]);
  1185.     } else {
  1186.         (void) fprintf(stdout, "\tdk_val = NULL\n");
  1187.     }
  1188.  
  1189.     for (d = DK_NDRIVE; d < (*dpp)->sn.dk_len; d += 4) {
  1190.         int             e;
  1191.  
  1192.         (void) fputs(dotquad, stdout);
  1193.         (void) fprintf(stdout, "\t.....dk\t");
  1194.         for (e = 0; e < Min((*dpp)->sn.dk_len - d, 4); e++) {
  1195.         (void) fprintf(stdout, " %08x", (*dpp)->sn.dk_val[d + e]);
  1196.         }
  1197.         (void) putc('\n', stdout);
  1198.     }
  1199.  
  1200.     (void) fputs(dotquad, stdout);
  1201.     (void) fprintf(stdout, "\tvm\t%08x %08x %08x %08x %08x %08x\n",
  1202.                (*dpp)->sn.v_pgpgin,
  1203.                (*dpp)->sn.v_pgpgout,
  1204.                (*dpp)->sn.v_pswpin,
  1205.                (*dpp)->sn.v_pswpout,
  1206.                (*dpp)->sn.v_swtch,
  1207.                (*dpp)->sn.v_intr);
  1208.     (void) fputs(dotquad, stdout);
  1209.     (void) fprintf(stdout, "\tif\t%08x %08x %08x %08x %08x\n",
  1210.                (*dpp)->sn.if_ipackets,
  1211.                (*dpp)->sn.if_ierrors,
  1212.                (*dpp)->sn.if_opackets,
  1213.                (*dpp)->sn.if_oerrors,
  1214.                (*dpp)->sn.if_collisions);
  1215.     }
  1216.     (void) fprintf(stdout, "end of dump\n");
  1217. }
  1218.  
  1219. RETSIGTYPE
  1220. illhdlr()
  1221. {
  1222.     (void) fprintf(stderr, "Illegal instruction\n");
  1223.     dumplist();
  1224.     (void) fprintf(stderr, "exiting from illhdlr\n");
  1225.     exit(1);
  1226. }
  1227.  
  1228. RETSIGTYPE
  1229. segvhdlr()
  1230. {
  1231.     (void) fprintf(stderr, "Segmentation fault\n");
  1232.     dumplist();
  1233.     (void) fprintf(stderr, "exiting from segvhdlr\n");
  1234.     exit(1);
  1235. }
  1236.  
  1237. /**
  1238.  * line-buffer the output
  1239.  * permits "tail -f" of a growing log file or a pipe
  1240.  **/
  1241. void
  1242. linebuf_stdout()
  1243. {
  1244. #ifdef HAVE_SETLINEBUF
  1245.     (void) setlinebuf(stdout);
  1246. #else                /* !HAVE_SETLINEBUF */
  1247.     errno = 0;
  1248. #ifdef SETVBUF_REVERSED
  1249.     if (setvbuf(stdout, _IOLBF, (char *) NULL, BUFSIZ))
  1250. #else                /* !SETVBUF_REVERSED */
  1251.     if (setvbuf(stdout, (char *) NULL, _IOLBF, BUFSIZ))
  1252. #endif                /* !SETVBUF_REVERSED */
  1253.     {
  1254.     if (errno)
  1255.         perror("setvbuf");
  1256.     else
  1257.         (void) fprintf(stderr, "setvbuf error\n");
  1258.     exit(1);
  1259.     }
  1260. #endif                /* !HAVE_SETLINEBUF */
  1261. }
  1262.  
  1263. /**
  1264.  * initialize signal handlers
  1265.  * parse options
  1266.  * resolve host names
  1267.  * loop endlessly. either:
  1268.  *    do client calls
  1269.  *    or do broadcasts
  1270. **/
  1271. int
  1272. main(argc, argv)
  1273.     int             argc;
  1274.     char           *argv[];
  1275. {
  1276.     u_long          vers_max;
  1277.     u_long          nbcst_vers;
  1278.     int             stdout_is_a_tty;
  1279.     int             c_seen = 0;
  1280.     int             do_uptime = 0;
  1281.     int             n_ofiles;
  1282.     int             opti;
  1283.     int             c;
  1284.     int             i_seen = 0;
  1285.     unsigned        count = 0;
  1286.     unsigned        interval = 10;
  1287.     unsigned        bcst_interval;
  1288.     unsigned        hdrcnt;
  1289.     unsigned        telapse;
  1290.     unsigned short  term_rows;
  1291.     char           *optstring;
  1292.     char           *basename;
  1293.     struct data   **dpp;
  1294.     struct datatbl *tbl;
  1295.     struct timeval  time1, time2;
  1296.  
  1297.     stdout_is_a_tty = isatty(STDOUT_FILENO);
  1298.  
  1299.     tbl = NULL;
  1300.     if (gettimeofday(&starttime, (struct timezone *) NULL) == -1) {
  1301.     perror("gettimeofday");
  1302.     exit(1);
  1303.     }
  1304.     (void) signal(SIGHUP, cleanup);
  1305.     (void) signal(SIGINT, cleanup);
  1306.     (void) signal(SIGTERM, cleanup);
  1307.     (void) signal(SIGILL, illhdlr);
  1308.     (void) signal(SIGSEGV, segvhdlr);
  1309.  
  1310.     linebuf_stdout();
  1311.     basename = strrchr(argv[0], '/');
  1312.     basename = basename ? ++basename : argv[0];
  1313.  
  1314.     opterr = 0;
  1315.     if (strcmp(basename, "rup") == 0) {
  1316.     vers_max = RSTATVERS_TIME;
  1317.     optstring = "hltnD";
  1318.     while ((c = getopt(argc, argv, optstring)) != -1) {
  1319.         switch (c) {
  1320.         case 'h':
  1321.         opts |= O_SHOST;
  1322.         break;
  1323.         case 'l':
  1324.         opts |= O_SORT | O_RVRS;
  1325.         key_ptr = (key *) calloc(1, sizeof(key));
  1326.         key_ptr->k_doffset = sn_offset(avenrun[AV1]);
  1327.         break;
  1328.         case 't':
  1329.         opts |= O_SORT;
  1330.         key_ptr = (key *) calloc(1, sizeof(key));
  1331.         /* sd.boottime is uptime */
  1332.         key_ptr->k_doffset = sd_offset(boottime.tv_sec);
  1333.         break;
  1334.         case 'n':
  1335.         opts |= O_NHOST;
  1336.         break;
  1337.         case 'D':
  1338.         opts |= O_DBG;
  1339.         dbg_lvl++;
  1340.         break;
  1341.         case '?':
  1342.         msg(rupusagev, stderr);
  1343.         exit(1);
  1344.         }
  1345.     }
  1346.     c_seen = 1;
  1347.     count = 1;
  1348.     do_uptime = 1;
  1349.     opts |= O_NCLR;        /* never clear the screen in rup mode */
  1350.     } else {            /* rperf */
  1351.     vers_max = RSTATVERS_VAR;
  1352.     optstring = "aAcdivhnSTsub1234BDrN";
  1353.     while ((c = getopt(argc, argv, optstring)) != -1)
  1354.         switch (c) {
  1355.         case 'a':
  1356.         opts |= O_MOST;
  1357.         break;
  1358.         case 'A':
  1359.         opts |= O_ALL;    /* everything! */
  1360.         break;
  1361.         case 'c':
  1362.         opts |= O_CP;
  1363.         break;
  1364.         case 'd':
  1365.         opts |= O_DK;
  1366.         break;
  1367.         case 'i':
  1368.         opts |= O_IF;
  1369.         break;
  1370.         case 'S':
  1371.         opts |= O_TIME;
  1372.         break;
  1373.         case 'T':
  1374.         opts |= O_DATE;
  1375.         break;
  1376.         case 's':
  1377.         opts |= O_SECS;
  1378.         break;
  1379.         case 'h':
  1380.         opts |= O_SHOST;
  1381.         break;
  1382.         case 'u':
  1383.         opts |= O_UP;
  1384.         break;
  1385.         case 'n':
  1386.         opts |= O_NHOST;
  1387.         break;
  1388.         case 'v':
  1389.         opts |= O_VM;
  1390.         break;
  1391.         case 'b':
  1392.         opts |= O_BCST;
  1393.         break;
  1394.         case '1':
  1395.         vers_max = RSTATVERS_ORIG;
  1396.         break;
  1397.         case '2':
  1398.         vers_max = RSTATVERS_SWTCH;
  1399.         break;
  1400.         case '3':
  1401.         vers_max = RSTATVERS_TIME;
  1402.         break;
  1403.         case '4':
  1404.         vers_max = RSTATVERS_VAR;
  1405.         break;
  1406.         case 'B':
  1407.         opts |= O_BARE | O_NCLR;
  1408.         break;
  1409.         case 'r':
  1410.         opts |= O_RVRS;
  1411.         break;
  1412.         case 'D':
  1413.         opts |= O_DBG;
  1414.         dbg_lvl++;
  1415.         break;
  1416.         case 'N':
  1417.         opts |= O_NCLR;
  1418.         break;
  1419.         case '?':
  1420.         verbosehelp();
  1421.         }
  1422.  
  1423.     if (!(opts & O_BARE))
  1424.         do_uptime = 1;
  1425.     }
  1426.  
  1427.     init_termcap();
  1428.  
  1429.     n_ofiles = set_max_nofiles();
  1430.     {
  1431.     /**
  1432.      * Estimate size of hash table.
  1433.      *
  1434.      * The hash table gets filled with the ascii host address, the official
  1435.      * host name, and alias host names.
  1436.      *
  1437.      * Machines with more than one interface get each address stored in the
  1438.      * table, as replies are received from them.
  1439.      *
  1440.      * The table is used to for quick lookup when a broadcast reply is
  1441.      * received, and to eliminate duplication for hosts with more than
  1442.      * one network interface
  1443.     **/
  1444.     errno = 0;
  1445.     if (!hcreate((unsigned) Max(2 * n_ofiles, 1031 /* big */ ))) {
  1446.         if (errno)
  1447.         perror("hcreate");
  1448.         else
  1449.         (void) fprintf(stderr, "Can't hcreate\n");
  1450.         exit(1);
  1451.     }
  1452.     }
  1453.  
  1454.     if (opts & O_NHOST)
  1455.     (void) strcpy(f_h, f_haddr);
  1456.  
  1457.     opti = optind;
  1458.     while (argv[opti]) {
  1459.     char           *oa;
  1460.     long            l;
  1461.  
  1462.     l = strtol(argv[opti], &oa, 0);
  1463.     if (argv[opti] == oa || *oa) {
  1464.         /* "mml0.meche.rpi.edu" or "128.113.14.20" or "+idle" */
  1465.         if (*argv[opti] == '+') {
  1466.         char           *sortkey;
  1467.         key            *kp;
  1468.  
  1469.         sortkey = argv[opti] + sizeof(char);
  1470.         for (kp = keys; kp->k_name != NULL; kp++) {
  1471.             if (strcmp(sortkey, kp->k_name) == 0) {
  1472.             key_ptr = kp;
  1473.             break;
  1474.             }
  1475.         }
  1476.         if (!key_ptr) {
  1477.             (void) fprintf(stderr, "unknown sort key: %s\n", sortkey);
  1478.             msg(keyhelp, stderr);
  1479.             exit(1);
  1480.         }
  1481.         if (vers_max < RSTATVERS_TIME) {
  1482.             if (key_ptr->k_noffset == sn_offset(curtime.tv_sec)) {
  1483.             (void) fprintf(stderr,
  1484.                   "Cannot sort by %s in version %d of rstatd\n",
  1485.                        sortkey, vers_max);
  1486.             exit(1);
  1487.             }
  1488.         }
  1489.         if (vers_max == RSTATVERS_ORIG) {
  1490.             if (key_ptr->k_noffset == sn_offset(boottime.tv_sec)
  1491.             || key_ptr->k_noffset == sn_offset(v_swtch)
  1492.             || key_ptr->k_noffset == sn_offset(avenrun[AV1])
  1493.             || key_ptr->k_noffset == sn_offset(avenrun[AV5])
  1494.             || key_ptr->k_noffset == sn_offset(avenrun[AV15])) {
  1495.             (void) fprintf(stderr,
  1496.                   "Cannot sort by %s in version %d of rstatd\n",
  1497.                        sortkey, vers_max);
  1498.             exit(1);
  1499.             }
  1500.         }
  1501.         opts |= O_SORT;
  1502.         } else {
  1503.         /* not a sort key.  Must be a host name or address */
  1504.         mode = MODE_CALL;
  1505.         }
  1506.     } else {
  1507.         /* arg is a decimal number */
  1508.         if (!i_seen) {
  1509.         if (l <= 0) {
  1510.             (void) fprintf(stderr, "%s interval invalid\n",
  1511.                    l ? "Negative" : "Zero");
  1512.             exit(1);
  1513.         }
  1514.         interval = l;
  1515.         i_seen = 1;
  1516.         } else if (!c_seen) {
  1517.         count = l + 1;    /* the user wants l data displays */
  1518.         c_seen = 1;
  1519.         } else {
  1520.         /* third decimal argument */
  1521.         (void) fprintf(stderr, "Unknown option %s\n", argv[opti]);
  1522.         verbosehelp();
  1523.         }
  1524.     }
  1525.     opti++;
  1526.     }
  1527.  
  1528.  
  1529.     if (mode == MODE_CALL) {
  1530.     if (opts & O_BCST) {
  1531.         (void) fprintf(stderr,
  1532.              "Can't use -b and hostname arguments together.\n");
  1533.         exit(1);
  1534.     }
  1535.     if (opts & O_NHOST) {
  1536.         (void) fprintf(stderr,
  1537.              "Can't use -n and hostname arguments together.\n");
  1538.         exit(1);
  1539.     }
  1540.     if (opts & O_SHOST) {
  1541.         (void) fprintf(stderr,
  1542.              "Can't use -h and hostname arguments together.\n");
  1543.         exit(1);
  1544.     }
  1545.     if (dbg_lvl >= 1)
  1546.         (void) fputs("resolving host names\n", stderr);
  1547.     opti = optind;
  1548.     dpp = &dp;
  1549.  
  1550.     while (argv[opti]) {
  1551.         char           *oa;
  1552.         u_long          host_address;
  1553.  
  1554.         (void) strtol(argv[opti], &oa, 0);
  1555.         if ((argv[opti] == oa || *oa) && *argv[opti] != '+') {
  1556.         /* "mml0.meche.rpi.edu" or "128.113.14.20" */
  1557.         struct in_addr  addr;
  1558.         char           *hostname;
  1559.         struct hostent *host_entry;
  1560.  
  1561.         hostname = argv[opti];
  1562.         host_entry = gethostbyname(hostname);
  1563.         if (!host_entry) {    /* maybe he typed an address */
  1564.             if (dbg_lvl >= 1)
  1565.             (void) fputs("byname failed\n", stderr);
  1566. #ifdef STRUCT_IN_ADDR_INET_ADDR
  1567.             /**
  1568.              * Data General.
  1569.              * thanks mpc@mbs.linet.org (Mark Clements)
  1570.             **/
  1571.             {
  1572.             struct in_addr  host_in_addr;
  1573.             host_in_addr = inet_addr(hostname);
  1574.             host_address = host_in_addr.s_addr;
  1575.             }
  1576. #else                /* u_long inet_addr(); */
  1577.             host_address = inet_addr(hostname);
  1578. #endif                /* u_long inet_addr(); */
  1579.             if (host_address != -1) {
  1580.             host_entry =
  1581.                 gethostbyaddr((char *) &host_address, 4, AF_INET);
  1582.             if (!host_entry && dbg_lvl >= 1)
  1583.                 (void) fputs("byaddr failed\n", stderr);
  1584.             } else if (dbg_lvl >= 1)
  1585.             (void) fputs("inet_addr failed\n", stderr);
  1586.         }
  1587.         if (host_entry) {
  1588.             bcopy(host_entry->h_addr,
  1589.               (char *) &addr.s_addr,
  1590.               host_entry->h_length);
  1591.         }
  1592.         /**
  1593.          * maybe he typed a valid address anyway --
  1594.         **/
  1595.         if (!host_entry && (host_address != -1))
  1596.             addr.s_addr = host_address;
  1597.  
  1598.         if (host_entry || (host_address != -1)) {
  1599.             /* host ok.  add to list */
  1600.             if (dbg_lvl >= 1)
  1601.             (void) fprintf(stderr, "%s\n", inet_ntoa(addr));
  1602.             (*dpp) = new_host(&addr, hostname);
  1603.             (*dpp)->datap = NULL;
  1604.             dpp = &(*dpp)->datap;
  1605.         } else {
  1606.             (void) fprintf(stderr, "can't get address for %s\n",
  1607.                    hostname);
  1608.         }
  1609.         }            /* a host argument */
  1610.         opti++;
  1611.     }
  1612.     if (dbg_lvl >= 1)
  1613.         (void) fprintf(stderr, "%d hosts\n", nhosts);
  1614.     if (!nhosts)
  1615.         exit(0);
  1616.     }
  1617.     switch (mode) {
  1618.     case MODE_CALL:
  1619.     if (!(opts & O_ALL)) {
  1620.         if (nhosts == 1)
  1621.         opts |= O_DEFL1;
  1622.         else
  1623.         opts |= O_DEFL;
  1624.     }
  1625.     build_title();
  1626.     break;
  1627.     case MODE_BCST:
  1628.     if (!(opts & O_ALL)) {
  1629.         opts |= O_DEFL;
  1630.     }
  1631.     nhosts = 2;
  1632.     build_title();
  1633.     nhosts = 0;
  1634.     break;
  1635.     }
  1636.  
  1637.     if (vers_max == RSTATVERS_ORIG) {
  1638.     if (opts & O_UP)
  1639.         (void) fprintf(stderr,
  1640.            "Warning, no uptime available in version 1 of rstatd\n");
  1641.     if (opts & O_CP)
  1642.         (void) fprintf(stderr,
  1643.          "Warning, no load average available in version 1 of rstatd\n");
  1644.     if (opts & O_VM)
  1645.         (void) fprintf(stderr,
  1646.           "Warning, no v_swtch available in version 1 of rstatd\n");
  1647.     }
  1648.     nbcst_vers = Max(0, (int) vers_max - (int) RSTATVERS_TIME) + 1;
  1649.     bcst_interval = interval / nbcst_vers;
  1650.  
  1651.     for (hdrcnt = 1;;) {    /* main loop */
  1652.     thiscount++;
  1653.  
  1654.     if (gettimeofday(&time1, (struct timezone *) NULL) == -1) {
  1655.         perror("gettimeofday");
  1656.         exit(1);
  1657.     }
  1658.     if (dbg_lvl >= 1)
  1659.         (void) fprintf(stderr, "thiscount %d\n", thiscount);
  1660.  
  1661.     if (mode == MODE_BCST && (thiscount == 1 || opts & O_BCST)) {
  1662.         do_broadcast(vers_max, bcst_interval);
  1663.     }
  1664.     if (stdout_is_a_tty && nhosts > 1 && !(opts & O_NCLR))
  1665.         clear();
  1666.  
  1667.     doclnt_calls(vers_max);
  1668.  
  1669.     if (dbg_lvl >= 3) {
  1670.         dumplist();
  1671.         (void) fprintf(stderr, "end of debug dump\n");
  1672.     }
  1673.     delete_zeros();
  1674.     compute_data();
  1675.     tbl = sort_data(tbl);
  1676.     if (stdout_is_a_tty
  1677.         && (!(opts & O_NCLR) || mode == MODE_CALL)) {
  1678.         check_term(&term_rows, (unsigned short *) NULL);
  1679.         if (term_rows)
  1680.         nprint = Min(nresp, term_rows - 3);
  1681.     } else
  1682.         nprint = nresp;
  1683.  
  1684.     strip_hostnames(tbl);
  1685.  
  1686.     if (dbg_lvl >= 1)
  1687.         (void) fprintf(stderr, "%d host%s\n",
  1688.                nhosts, nhosts == 1 ? "" : "s");
  1689.  
  1690.     if (do_uptime && vers_max > RSTATVERS_ORIG) {
  1691.         print_data(tbl, P_UP);
  1692.     }
  1693.     if (thiscount > 1
  1694.         && ((mode == MODE_CALL && (!--hdrcnt || nhosts > 1))
  1695.         || (mode == MODE_BCST))) {
  1696.         (void) fputs(title1, stdout);
  1697.         (void) fputs(title2, stdout);
  1698.         hdrcnt = Max(1, term_rows ? term_rows - 2 : 20);
  1699.     }
  1700.     if (thiscount > 1) {
  1701.         print_data(tbl, P_DATA);
  1702.     }
  1703.     if (c_seen && !--count)
  1704.         exit(0);
  1705.  
  1706.     if (gettimeofday(&time2, (struct timezone *) NULL) == -1) {
  1707.         perror("gettimeofday");
  1708.         exit(1);
  1709.     }
  1710.     telapse = time2.tv_sec - time1.tv_sec;
  1711.  
  1712.     if (interval > telapse)
  1713.         (void) sleep(interval - telapse);
  1714.  
  1715.     if (!(opts & O_BCST) && (mode == MODE_BCST) && thiscount == 1)
  1716.         (void) sleep(interval);
  1717.     }                /* main loop */
  1718. }
  1719.