nfsstat: Obviate and remove copy_stats().
[nfs-utils.git] / utils / nfsstat / nfsstat.c
1 /*
2  * nfsstat.c            Output NFS statistics
3  *
4  * Copyright (C) 1995-2005 Olaf Kirch <okir@suse.de>
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #define NFSSRVSTAT      "/proc/net/rpc/nfsd"
12 #define NFSCLTSTAT      "/proc/net/rpc/nfs"
13
14 #define MOUNTSFILE      "/proc/mounts"
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <getopt.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <signal.h>
24 #include <time.h>
25
26 #define MAXNRVALS       32
27
28 static unsigned int     srvproc2info[20], srvproc2info_old[20]; /* NFSv2 call counts ([0] == 18) */
29 static unsigned int     cltproc2info[20], cltproc2info_old[20]; /* NFSv2 call counts ([0] == 18) */
30 static unsigned int     srvproc3info[24], srvproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */
31 static unsigned int     cltproc3info[24], cltproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */
32 static unsigned int     srvproc4info[4], srvproc4info_old[4];   /* NFSv4 call counts ([0] == 2) */
33 static unsigned int     cltproc4info[37], cltproc4info_old[37]; /* NFSv4 call counts ([0] == 35) */
34 static unsigned int     srvproc4opsinfo[42], srvproc4opsinfo_old[42];   /* NFSv4 call counts ([0] == 40) */
35 static unsigned int     srvnetinfo[5], srvnetinfo_old[5];       /* 0  # of received packets
36                                                                  * 1  UDP packets
37                                                                  * 2  TCP packets
38                                                                  * 3  TCP connections
39                                                                  */
40 static unsigned int     cltnetinfo[5], cltnetinfo_old[5];       /* 0  # of received packets
41                                                                  * 1  UDP packets
42                                                                  * 2  TCP packets
43                                                                  * 3  TCP connections
44                                                                  */
45
46 static unsigned int     srvrpcinfo[6], srvrpcinfo_old[6];       /* 0  total # of RPC calls
47                                                                  * 1  total # of bad calls
48                                                                  * 2  bad format
49                                                                  * 3  authentication failed
50                                                                  * 4  unknown client
51                                                                  */
52 static unsigned int     cltrpcinfo[4], cltrpcinfo_old[4];       /* 0  total # of RPC calls
53                                                                  * 1  retransmitted calls
54                                                                  * 2  cred refreshs
55                                                                  */
56
57 static unsigned int     srvrcinfo[9], srvrcinfo_old[9];         /* 0  repcache hits
58                                                                  * 1  repcache hits
59                                                                  * 2  uncached reqs
60                                                                  * (for pre-2.4 kernels:)
61                                                                  * 3  FH lookups
62                                                                  * 4  'anon' FHs
63                                                                  * 5  noncached non-directories
64                                                                  * 6  noncached directories
65                                                                  * 7  stale
66                                                                  */
67
68 static unsigned int     srvfhinfo[7], srvfhinfo_old[7];         /* (for kernels >= 2.4.0)
69                                                                  * 0  stale
70                                                                  * 1  FH lookups
71                                                                  * 2  'anon' FHs
72                                                                  * 3  noncached directories
73                                                                  * 4  noncached non-directories
74                                                                  * leave hole to relocate stale for order
75                                                                  *    compatability.
76                                                                  */
77
78 static const char *     nfsv2name[18] = {
79         "null", "getattr", "setattr", "root",   "lookup",  "readlink",
80         "read", "wrcache", "write",   "create", "remove",  "rename",
81         "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"
82 };
83
84 static const char *     nfsv3name[22] = {
85         "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
86         "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
87         "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
88         "fsstat", "fsinfo",  "pathconf", "commit"
89 };
90
91 static const char *     nfssrvproc4name[2] = {
92         "null",
93         "compound",
94 };
95
96 static const char *     nfscltproc4name[35] = {
97         "null",      "read",      "write",   "commit",      "open",        "open_conf",
98         "open_noat", "open_dgrd", "close",   "setattr",     "fsinfo",      "renew",
99         "setclntid", "confirm",   "lock",
100         "lockt",     "locku",     "access",  "getattr",     "lookup",      "lookup_root",
101         "remove",    "rename",    "link",    "symlink",     "create",      "pathconf",
102         "statfs",    "readlink",  "readdir", "server_caps", "delegreturn", "getacl",
103         "setacl",    "fs_locations"
104 };
105
106 static const char *     nfssrvproc4opname[40] = {
107         "op0-unused",   "op1-unused", "op2-future",  "access",     "close",       "commit",
108         "create",       "delegpurge", "delegreturn", "getattr",    "getfh",       "link",
109         "lock",         "lockt",      "locku",       "lookup",     "lookup_root", "nverify",
110         "open",         "openattr",   "open_conf",   "open_dgrd",  "putfh",       "putpubfh",
111         "putrootfh",    "read",       "readdir",     "readlink",   "remove",      "rename",
112         "renew",        "restorefh",  "savefh",      "secinfo",    "setattr",     "setcltid",
113         "setcltidconf", "verify",     "write",       "rellockowner"
114 };
115
116 typedef struct statinfo {
117         char            *tag;
118         int             nrvals;
119         unsigned int *  valptr;
120 } statinfo;
121
122 /*
123  * We now build the arrays of statinfos using macros, which will make it easier
124  * to add new variables for --diff-stat.
125  * e.g., SRV(net) expands into the struct statinfo:  { "net", 5, srvnetinfo }
126  */
127 #define ARRAYSIZE(x)            sizeof(x)/sizeof(*x)
128 #define STATINFO(k, t, s...)    { #t, ARRAYSIZE(k##t##info##s), k##t##info##s }
129 #define SRV(t, s...)            STATINFO(srv, t, s)
130 #define CLT(t, s...)            STATINFO(clt, t, s)
131 #define DECLARE_SRV(n, s...)    static statinfo n##s[] = { \
132                                         SRV(net,s), \
133                                         SRV(rpc,s), \
134                                         SRV(rc,s), \
135                                         SRV(fh,s), \
136                                         SRV(proc2,s), \
137                                         SRV(proc3,s),\
138                                         SRV(proc4,s), \
139                                         SRV(proc4ops,s),\
140                                         { NULL, 0, NULL }\
141                                 }
142 #define DECLARE_CLT(n, s...)    static statinfo n##s[] = { \
143                                         CLT(net,s), \
144                                         CLT(rpc,s), \
145                                         CLT(proc2,s),\
146                                         CLT(proc3,s), \
147                                         CLT(proc4,s),\
148                                         { NULL, 0, NULL }\
149                                 }
150 DECLARE_SRV(srvinfo);
151 DECLARE_SRV(srvinfo, _old);
152 DECLARE_CLT(cltinfo);
153 DECLARE_CLT(cltinfo, _old);
154
155 static void             print_numbers(const char *, unsigned int *,
156                                         unsigned int);
157 static void             print_callstats(const char *, const char **,
158                                         unsigned int *, unsigned int);
159 static int              parse_statfile(const char *, struct statinfo *);
160
161 static statinfo         *get_stat_info(const char *, struct statinfo *);
162
163 static int              mounts(const char *);
164
165 static void             get_stats(const char *, statinfo *, int *, int, const char *);
166 static int              has_stats(const unsigned int *);
167 static void             diff_stats(statinfo *, statinfo *);
168 static void             unpause(int);
169
170 static time_t           starttime;
171
172 #define PRNT_CALLS      0x0001
173 #define PRNT_RPC        0x0002
174 #define PRNT_NET        0x0004
175 #define PRNT_FH         0x0008
176 #define PRNT_RC         0x0010
177 #define PRNT_AUTO       0x1000
178 #define PRNT_V2         0x2000
179 #define PRNT_V3         0x4000
180 #define PRNT_V4         0x8000
181 #define PRNT_ALL        0x0fff
182
183 int versions[] = {
184         PRNT_V2,
185         PRNT_V3,
186         PRNT_V4
187 };
188
189 void usage(char *name)
190 {
191         printf("Usage: %s [OPTION]...\n\
192 \n\
193   -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
194   -c, --client\t\tShow NFS client statistics\n\
195   -s, --server\t\tShow NFS server statistics\n\
196   -2\t\t\tShow NFS version 2 statistics\n\
197   -3\t\t\tShow NFS version 3 statistics\n\
198   -4\t\t\tShow NFS version 4 statistics\n\
199   -o [facility]\t\tShow statistics on particular facilities.\n\
200      nfs\tNFS protocol information\n\
201      rpc\tGeneral RPC information\n\
202      net\tNetwork layer statistics\n\
203      fh\t\tUsage information on the server's file handle cache\n\
204      rc\t\tUsage information on the server's request reply cache\n\
205      all\tSelect all of the above\n\
206   -v, --verbose, --all\tSame as '-o all'\n\
207   -r, --rpc\t\tShow RPC statistics\n\
208   -n, --nfs\t\tShow NFS statistics\n\
209   -Z, --sleep\tSaves stats, pauses, diffs current and saved\n\
210   --version\t\tShow program version\n\
211   --help\t\tWhat you just did\n\
212 \n", name);
213         exit(0);
214 }
215
216 static struct option longopts[] =
217 {
218         { "acl", 0, 0, 'a' },
219         { "all", 0, 0, 'v' },
220         { "auto", 0, 0, '\3' },
221         { "client", 0, 0, 'c' },
222         { "mounts", 0, 0, 'm' },
223         { "nfs", 0, 0, 'n' },
224         { "rpc", 0, 0, 'r' },
225         { "server", 0, 0, 's' },
226         { "verbose", 0, 0, 'v' },
227         { "zero", 0, 0, 'z' },
228         { "help", 0, 0, '\1' },
229         { "version", 0, 0, '\2' },
230         { "sleep", 0, 0, 'Z' },
231         { NULL, 0, 0, 0 }
232 };
233
234 int
235 main(int argc, char **argv)
236 {
237         int             opt_all = 0,
238                         opt_srv = 0,
239                         opt_clt = 0,
240                         opt_prt = 0,
241                         opt_sleep = 0;
242         int             c;
243         char           *progname;
244
245         struct statinfo *serverinfo = srvinfo,
246                         *serverinfo_tmp = srvinfo_old,
247                         *clientinfo = cltinfo,
248                         *clientinfo_tmp = cltinfo_old;
249
250         struct sigaction act = {
251                 .sa_handler = unpause,
252                 .sa_flags = SA_ONESHOT,
253         };
254
255         if ((progname = strrchr(argv[0], '/')))
256                 progname++;
257         else
258                 progname = argv[0];
259
260         while ((c = getopt_long(argc, argv, "234acmno:Zvrsz\1\2", longopts, NULL)) != EOF) {
261                 switch (c) {
262                 case 'a':
263                         fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
264                         return -1;
265                 case 'c':
266                         opt_clt = 1;
267                         break;
268                 case 'n':
269                         opt_prt |= PRNT_CALLS;
270                         break;
271                 case 'o':
272                         if (!strcmp(optarg, "nfs"))
273                                 opt_prt |= PRNT_CALLS;
274                         else if (!strcmp(optarg, "rpc"))
275                                 opt_prt |= PRNT_RPC;
276                         else if (!strcmp(optarg, "net"))
277                                 opt_prt |= PRNT_NET;
278                         else if (!strcmp(optarg, "rc"))
279                                 opt_prt |= PRNT_RC;
280                         else if (!strcmp(optarg, "fh"))
281                                 opt_prt |= PRNT_FH;
282                         else if (!strcmp(optarg, "all"))
283                                 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
284                         else {
285                                 fprintf(stderr, "nfsstat: unknown category: "
286                                                 "%s\n", optarg);
287                                 return 2;
288                         }
289                         break;
290                 case 'Z':
291                         opt_sleep = 1;
292                         break;
293                 case '2':
294                 case '3':
295                 case '4':
296                         opt_prt |= versions[c - '2'];
297                         break;
298                 case 'v':
299                         opt_all = 1;
300                         break;
301                 case '\3':
302                         opt_prt |= PRNT_AUTO;
303                         break;
304                 case 'r':
305                         opt_prt |= PRNT_RPC;
306                         break;
307                 case 's':
308                         opt_srv = 1;
309                         break;
310                 case 'z':
311                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
312                                         "not yet supported\n");
313                         return 2;
314                 case 'm':
315                         return mounts(MOUNTSFILE);
316                 case '\1':
317                         usage(progname);
318                         return 0;
319                 case '\2':
320                         fprintf(stdout, "nfsstat: " VERSION "\n");
321                         return 0;
322                 default:
323                         printf("Try `%s --help' for more information.\n", progname);
324                         return -1;
325                 }
326         }
327
328         if (opt_all) {
329                 opt_srv = opt_clt = 1;
330                 opt_prt |= PRNT_ALL;
331         }
332         if (!(opt_srv + opt_clt))
333                 opt_srv = opt_clt = 1;
334         if (!(opt_prt & 0xfff)) {
335                 opt_prt |= PRNT_CALLS + PRNT_RPC;
336         }
337         if (!(opt_prt & 0xe000)) {
338                 opt_prt |= PRNT_AUTO;
339         }
340         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
341                 fprintf(stderr,
342                         "You requested file handle or request cache "
343                         "statistics while using the -c option.\n"
344                         "This information is available only for the NFS "
345                         "server.\n");
346         }
347
348         if (opt_sleep) {
349                 serverinfo = srvinfo_old;
350                 serverinfo_tmp = srvinfo;
351                 clientinfo = cltinfo_old;
352                 clientinfo_tmp = cltinfo;
353         }
354
355         if (opt_srv)
356                 get_stats(NFSSRVSTAT, serverinfo, &opt_srv, opt_clt, "Server");
357         if (opt_clt)
358                 get_stats(NFSCLTSTAT, clientinfo, &opt_clt, opt_srv, "Client");
359
360         /* save stat snapshots; wait for signal; then diff current and saved stats */
361         if (opt_sleep) {
362                 starttime = time(NULL);
363                 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
364                 if (sigaction(SIGINT, &act, NULL) != 0) {
365                         fprintf(stderr, "Error: couldn't register for signal and pause.\n");
366                         return 1;
367                 }
368                 pause();
369                 if (opt_srv) {
370                         get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, "Server");
371                         diff_stats(serverinfo_tmp, serverinfo);
372                 }
373                 if (opt_clt) {
374                         get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, "Client");
375                         diff_stats(clientinfo_tmp, clientinfo);
376                 }
377         }
378
379         if (opt_srv) {
380                 if (opt_prt & PRNT_NET) {
381                         print_numbers(
382                         "Server packet stats:\n"
383                         "packets    udp        tcp        tcpconn\n",
384                         srvnetinfo, 4
385                         );
386                         printf("\n");
387                 }
388                 if (opt_prt & PRNT_RPC) {
389                         print_numbers(
390                         "Server rpc stats:\n"
391                         "calls      badcalls   badauth    badclnt    xdrcall\n",
392                         srvrpcinfo, 5
393                         );
394                         printf("\n");
395                 }
396                 if (opt_prt & PRNT_RC) {
397                         print_numbers(
398                         "Server reply cache:\n"
399                         "hits       misses     nocache\n",
400                         srvrcinfo, 3
401                         );
402                         printf("\n");
403                 }
404
405                 /*
406                  * 2.2 puts all fh-related info after the 'rc' header
407                  * 2.4 puts all fh-related info after the 'fh' header, but relocates
408                  *     'stale' to the start and swaps dir and nondir :-(  
409                  *     We preseve the 2.2 order
410                  */
411                 if (opt_prt & PRNT_FH) {
412                         if (get_stat_info("fh", srvinfo)) {     /* >= 2.4 */
413                                 int t = srvfhinfo[3];
414                                 srvfhinfo[3]=srvfhinfo[4];
415                                 srvfhinfo[4]=t;
416                                 
417                                 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
418                                 
419                                 print_numbers(
420                                         "Server file handle cache:\n"
421                                         "lookup     anon       ncachedir  ncachedir  stale\n",
422                                         srvfhinfo + 1, 5);
423                         } else                                  /* < 2.4 */
424                                 print_numbers(
425                                         "Server file handle cache:\n"
426                                         "lookup     anon       ncachedir  ncachedir  stale\n",
427                                         srvrcinfo + 3, 5);
428                         printf("\n");
429                 }
430                 if (opt_prt & PRNT_CALLS) {
431                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
432                                 print_callstats(
433                                 "Server nfs v2:\n",
434                                     nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
435                                 );
436                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
437                                 print_callstats(
438                                 "Server nfs v3:\n",
439                                 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
440                                 );
441                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
442                                 print_callstats(
443                                 "Server nfs v4:\n",
444                                 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
445                                 );
446                                 print_callstats(
447                                 "Server nfs v4 operations:\n",
448                                 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
449                                 );
450                         }
451                 }
452         }
453
454         if (opt_clt) {
455                 if (opt_prt & PRNT_NET) {
456                         print_numbers(
457                         "Client packet stats:\n"
458                         "packets    udp        tcp        tcpconn\n",
459                         cltnetinfo, 4
460                         );
461                         printf("\n");
462                 }
463                 if (opt_prt & PRNT_RPC) {
464                         print_numbers(
465                         "Client rpc stats:\n"
466                         "calls      retrans    authrefrsh\n",
467                         cltrpcinfo, 3
468                         );
469                         printf("\n");
470                 }
471                 if (opt_prt & PRNT_CALLS) {
472                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
473                                 print_callstats(
474                                 "Client nfs v2:\n",
475                                 nfsv2name, cltproc2info + 1,  sizeof(nfsv2name)/sizeof(char *)
476                                 );
477                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
478                                 print_callstats(
479                                 "Client nfs v3:\n",
480                                 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
481                                 );
482                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
483                                 print_callstats(
484                                 "Client nfs v4:\n",
485                                 nfscltproc4name, cltproc4info + 1,  sizeof(nfscltproc4name)/sizeof(char *)
486                                 );
487                 }
488         }
489
490         return 0;
491 }
492
493 static statinfo *
494 get_stat_info(const char *sp, struct statinfo *statp)
495 {
496         struct statinfo *ip;
497
498         for (ip = statp; ip->tag; ip++) {
499                 if (!strcmp(sp, ip->tag))
500                         return ip;
501         }
502
503         return NULL;
504 }
505
506 static void
507 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
508 {
509         unsigned int    i;
510
511         fputs(hdr, stdout);
512         for (i = 0; i < nr; i++)
513                 printf("%s%-8d", i? "   " : "", info[i]);
514         printf("\n");
515 }
516
517 static void
518 print_callstats(const char *hdr, const char **names,
519                                  unsigned int *info, unsigned int nr)
520 {
521         unsigned long long      total;
522         unsigned long long      pct;
523         int             i, j;
524
525         fputs(hdr, stdout);
526         for (i = 0, total = 0; i < nr; i++)
527                 total += info[i];
528         if (!total)
529                 total = 1;
530         for (i = 0; i < nr; i += 6) {
531                 for (j = 0; j < 6 && i + j < nr; j++)
532                         printf("%-13s", names[i+j]);
533                 printf("\n");
534                 for (j = 0; j < 6 && i + j < nr; j++) {
535                         pct = ((unsigned long long) info[i+j]*100)/total;
536                         printf("%-8d%3llu%% ", info[i+j], pct);
537                 }
538                 printf("\n");
539         }
540         printf("\n");
541 }
542
543
544 static int
545 parse_statfile(const char *name, struct statinfo *statp)
546 {
547         char    buffer[4096], *next;
548         FILE    *fp;
549
550         /* Being unable to read e.g. the nfsd stats file shouldn't
551          * be a fatal error -- it usually means the module isn't loaded.
552          */
553         if ((fp = fopen(name, "r")) == NULL) {
554                 // fprintf(stderr, "Warning: %s: %m\n", name);
555                 return 0;
556         }
557
558         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
559                 struct statinfo *ip;
560                 char            *sp, *line = buffer;
561                 unsigned int    i, cnt;
562                 unsigned int    total = 0;
563
564                 if ((next = strchr(line, '\n')) != NULL)
565                         *next++ = '\0';
566                 if (!(sp = strtok(line, " \t")))
567                         continue;
568
569                 ip = get_stat_info(sp, statp);
570                 if (!ip)
571                         continue;
572
573                 cnt = ip->nrvals;
574
575                 for (i = 0; i < cnt; i++) {
576                         if (!(sp = strtok(NULL, " \t")))
577                                 break;
578                         ip->valptr[i] = atoi(sp);
579                         total += ip->valptr[i];
580                 }
581                 ip->valptr[cnt - 1] = total;
582         }
583
584         fclose(fp);
585         return 1;
586 }
587
588 static int
589 mounts(const char *name)
590 {
591         char    buffer[4096], *next;
592         FILE    *fp;
593
594         /* Being unable to read e.g. the nfsd stats file shouldn't
595          * be a fatal error -- it usually means the module isn't loaded.
596          */
597         if ((fp = fopen(name, "r")) == NULL) {
598                 fprintf(stderr, "Warning: %s: %m\n", name);
599                 return 0;
600         }
601
602         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
603                 char          *line = buffer;
604                 char          *device, *mount, *type, *flags;
605
606                 if ((next = strchr(line, '\n')) != NULL)
607                         *next = '\0';
608
609                 if (!(device = strtok(line, " \t")))
610                         continue;
611
612                 if (!(mount = strtok(NULL, " \t")))
613                         continue;
614
615                 if (!(type = strtok(NULL, " \t")))
616                         continue;
617
618                 if (strcmp(type, "nfs")) {
619                     continue;
620                 }
621
622                 if (!(flags = strtok(NULL, " \t")))
623                         continue;
624
625                 printf("%s from %s\n", mount, device);
626                 printf(" Flags:\t%s\n", flags);
627                 printf("\n");
628
629                 continue;
630         }
631
632         fclose(fp);
633         return 1;
634 }
635
636 static void
637 get_stats(const char *file, statinfo *info, int *opt, int other_opt, const char *label)
638 {
639         if (!parse_statfile(file, info)) {
640                 if (!other_opt) {
641                         fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
642                         exit(2);
643                 }
644                 *opt = 0;
645         }
646 }
647
648 /*
649  * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
650  * denotes the number of subsequent entries.  statinfo value arrays contain an additional
651  * field at the end which contains the sum of all previous elements in the array -- so,
652  * there are stats if the sum's greater than the entry-count.
653  */
654 static int
655 has_stats(const unsigned int *info)
656 {
657         return (info[0] && info[info[0] + 1] > info[0]);
658 }
659
660 /*
661  * take the difference of each individual stat value in 'new' and 'old'
662  * and store the results back into 'new'
663  */
664 static void
665 diff_stats(struct statinfo *new, struct statinfo *old)
666 {
667         int i, j, is_srv, nodiff_first_index, should_diff;
668
669         /*
670          * Different stat types have different formats in the /proc
671          * files: for the proc2/3/4-type stats, the first entry has
672          * the total number of subsequent entries; one does not want
673          * to diff that first entry.  The other stat types aren't like
674          * this.  So, we diff a given entry if it's not of one of the
675          * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
676          * the first entry ("j" > 0).
677          */
678         is_srv = (new == srvinfo);
679         nodiff_first_index = 2 + (2 * is_srv);
680
681         for (i = 0; old[i].tag; i++) {
682                 for (j = 0; j < new[i].nrvals; j++) {
683                         should_diff = (i < nodiff_first_index || j > 0);
684                         if (should_diff)
685                                 new[i].valptr[j] -= old[i].valptr[j];
686                 }
687
688                 /*
689                  * Make sure that the "totals" entry (last value in
690                  * each stat array) for the procX-type stats has the
691                  * "numentries" entry's (first value in procX-type
692                  * stat arrays) constant value added-back after the
693                  * diff -- i.e., it should always be included in the
694                  * total.
695                  */
696                 if (!strncmp("proc", new[i].tag, 4))
697                         new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
698         }
699 }
700
701 static void
702 unpause(int sig)
703 {
704         double time_diff;
705         int minutes, seconds;
706         time_t endtime;
707
708         endtime = time(NULL);
709         time_diff = difftime(endtime, starttime);
710         minutes = time_diff / 60;
711         seconds = (int)time_diff % 60;
712         printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
713 }