]> granicus.if.org Git - sysstat/blob - cifsiostat.c
Various cosmetic changes in manual pages and usage messages displayed by sysstat...
[sysstat] / cifsiostat.c
1 /*
2  * cifsiostat: Report I/O statistics for CIFS filesystems.
3  * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved
4  * Written by Ivana Varekova <varekova@redhat.com>
5  *
6  ***************************************************************************
7  * This program is free software; you can redistribute it and/or modify it *
8  * under the terms of the GNU General Public License as published  by  the *
9  * Free Software Foundation; either version 2 of the License, or (at  your *
10  * option) any later version.                                              *
11  *                                                                         *
12  * This program is distributed in the hope that it  will  be  useful,  but *
13  * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
14  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
15  * for more details.                                                       *
16  *                                                                         *
17  * You should have received a copy of the GNU General Public License along *
18  * with this program; if not, write to the Free Software Foundation, Inc., *
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                   *
20  ***************************************************************************
21  */
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <signal.h>
28 #include <sys/utsname.h>
29 #include <ctype.h>
30
31 #include "version.h"
32 #include "cifsiostat.h"
33 #include "common.h"
34
35 #ifdef USE_NLS
36 #include <locale.h>
37 #include <libintl.h>
38 #define _(string) gettext(string)
39 #else
40 #define _(string) (string)
41 #endif
42
43 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
44 char *sccsid(void) { return (SCCSID); }
45
46 unsigned long long uptime[2]  = {0, 0};
47 unsigned long long uptime0[2] = {0, 0};
48 struct cifs_stats *st_cifs[2];
49 struct io_hdr_stats *st_hdr_cifs;
50
51 int cifs_nr = 0;        /* Nb of CIFS mounted directories found */
52 int cpu_nr = 0;         /* Nb of processors on the machine */
53 int flags = 0;          /* Flag for common options and system state */
54
55 long interval = 0;
56 char timestamp[64];
57
58
59 /*
60  ***************************************************************************
61  * Print usage and exit.
62  *
63  * IN:
64  * @progname    Name of sysstat command.
65  ***************************************************************************
66  */
67 void usage(char *progname)
68 {
69         fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
70                 progname);
71
72 #ifdef DEBUG
73         fprintf(stderr, _("Options are:\n"
74                           "[ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n"));
75 #else
76         fprintf(stderr, _("Options are:\n"
77                           "[ -h ] [ -k | -m ] [ -t ] [ -V ]\n"));
78 #endif
79         exit(1);
80 }
81
82 /*
83  ***************************************************************************
84  * SIGALRM signal handler.
85  *
86  * IN:
87  * @sig Signal number. Set to 0 for the first time, then to SIGALRM.
88  ***************************************************************************
89  */
90 void alarm_handler(int sig)
91 {
92         signal(SIGALRM, alarm_handler);
93         alarm(interval);
94 }
95
96 /*
97  ***************************************************************************
98  * Find number of CIFS-mounted points that are registered in
99  * /proc/fs/cifs/Stats.
100  *
101  * RETURNS:
102  * Number of CIFS-mounted points.
103  ***************************************************************************
104  */
105 int get_cifs_nr(void)
106 {
107         FILE *fp;
108         char line[128];
109         int cifs = 0;
110
111         if ((fp = fopen(CIFSSTATS, "r")) == NULL)
112                 /* File non-existent */
113                 return 0;
114
115         while (fgets(line, 128, fp) != NULL) {
116                 
117                 if (!strncmp(line, "Share (unique mount targets): ", 30)) {
118                         sscanf(line + 30, "%d", &cifs);
119                         break;
120                 }
121         }
122
123         /* Close file */
124         fclose(fp);
125
126         return cifs;
127 }
128
129 /*
130  ***************************************************************************
131  * Set every cifs_io entry to inactive state (unregistered).
132  ***************************************************************************
133  */
134 void set_entries_inactive(void)
135 {
136         int i;
137         struct io_hdr_stats *shi = st_hdr_cifs;
138
139         for (i = 0; i < cifs_nr; i++, shi++) {
140                 shi->active = FALSE;
141         }
142 }
143
144 /*
145  ***************************************************************************
146  * Free inactive entries (mark them as unused).
147  ***************************************************************************
148  */
149 void free_inactive_entries(void)
150 {
151         int i;
152         struct io_hdr_stats *shi = st_hdr_cifs;
153
154         for (i = 0; i < cifs_nr; i++, shi++) {
155                 if (!shi->active) {
156                         shi->used = FALSE;
157                 }
158         }
159 }
160
161 /*
162  ***************************************************************************
163  * Allocate and init structures, according to system state.
164  ***************************************************************************
165  */
166 void io_sys_init(void)
167 {
168         int i;
169         
170         /* How many processors on this machine? */
171         cpu_nr = get_cpu_nr(~0);
172
173         /* Get number of CIFS directories in /proc/fs/cifs/Stats */
174         if ((cifs_nr = get_cifs_nr()) > 0) {
175                 cifs_nr += NR_CIFS_PREALLOC;
176         }
177         if ((st_hdr_cifs = (struct io_hdr_stats *) calloc(cifs_nr, IO_HDR_STATS_SIZE)) == NULL) {
178                 perror("malloc");
179                 exit(4);
180         }
181         
182         /* Allocate structures for number of CIFS directories found */
183         for (i = 0; i < 2; i++) {
184                 if ((st_cifs[i] =
185                     (struct cifs_stats *) calloc(cifs_nr, CIFS_STATS_SIZE)) == NULL) {
186                         perror("malloc");
187                         exit(4);
188                 }
189         }
190 }
191
192 /*
193  ***************************************************************************
194  * Free various structures.
195  ***************************************************************************
196 */
197 void io_sys_free(void)
198 {
199         int i;
200
201         /* Free CIFS directories structures */
202         for (i = 0; i < 2; i++) {
203
204                 if (st_cifs[i]) {
205                         free(st_cifs[i]);
206                 }
207         }
208         
209         if (st_hdr_cifs) {
210                 free(st_hdr_cifs);
211         }
212 }
213
214 /*
215  ***************************************************************************
216  * Save stats for current CIFS filesystem.
217  *
218  * IN:
219  * @name                Name of CIFS filesystem.
220  * @curr                Index in array for current sample statistics.
221  * @st_io               Structure with CIFS statistics to save.
222  ***************************************************************************
223  */
224 void save_stats(char *name, int curr, struct cifs_stats *st_io)
225 {
226         int i, j;
227         struct io_hdr_stats *st_hdr_cifs_i;
228         struct cifs_stats *st_cifs_i;
229
230         /* Look for CIFS directory in data table */
231         for (i = 0; i < cifs_nr; i++) {
232                 st_hdr_cifs_i = st_hdr_cifs + i;
233                 if ((st_hdr_cifs_i->used == TRUE) &&
234                     (!strcmp(st_hdr_cifs_i->name, name))) {
235                         break;
236                 }
237         }
238
239         if (i == cifs_nr) {
240                 /*
241                  * This is a new filesystem: Look for an unused entry to store it.
242                  */
243                 for (i = 0; i < cifs_nr; i++) {
244                         st_hdr_cifs_i = st_hdr_cifs + i;
245                         if (!st_hdr_cifs_i->used) {
246                                 /* Unused entry found... */
247                                 st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
248                                 st_hdr_cifs_i->active = TRUE;
249                                 strcpy(st_hdr_cifs_i->name, name);
250                                 st_cifs_i = st_cifs[curr] + i;
251                                 *st_cifs_i = *((struct cifs_stats *) st_io);
252                                 break;
253                         }
254                 }
255                 if (i == cifs_nr) {
256                         /*
257                          * It is a new CIFS directory
258                          * but there is no free structure to store it.
259                          */
260
261                         /* All entries are used: The number has to be increased */
262                         cifs_nr = cifs_nr + 5;
263
264                         /* Increase the size of st_hdr_ionfs buffer */
265                         if ((st_hdr_cifs = (struct io_hdr_stats *)
266                                 realloc(st_hdr_cifs, cifs_nr * IO_HDR_STATS_SIZE)) == NULL) {
267                                 perror("malloc");
268                                 exit(4);
269                         }
270
271                         /* Set the new entries inactive */
272                         for (j = 0; j < 5; j++) {
273                                 st_hdr_cifs_i = st_hdr_cifs + i + j;
274                                 st_hdr_cifs_i->used = FALSE;
275                                 st_hdr_cifs_i->active = FALSE;
276                         }
277
278                         /* Increase the size of st_hdr_ionfs buffer */
279                         for (j = 0; j < 2; j++) {
280                                 if ((st_cifs[j] = (struct cifs_stats *)
281                                         realloc(st_cifs[j], cifs_nr * CIFS_STATS_SIZE)) == NULL) {
282                                         perror("malloc");
283                                         exit(4);
284                                 }
285                                 memset(st_cifs[j] + i, 0, 5 * CIFS_STATS_SIZE);
286                         }
287                         /* Now i shows the first unused entry of the new block */
288                         st_hdr_cifs_i = st_hdr_cifs + i;
289                         st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
290                         st_hdr_cifs_i->active = TRUE;
291                         strcpy(st_hdr_cifs_i->name, name);
292                         st_cifs_i = st_cifs[curr] + i;
293                         *st_cifs_i = *st_io;
294                 }
295         } else {
296                 st_hdr_cifs_i = st_hdr_cifs + i;
297                 st_hdr_cifs_i->active = TRUE;
298                 st_hdr_cifs_i->used = TRUE;
299                 st_cifs_i = st_cifs[curr] + i;
300                 *st_cifs_i = *st_io;
301         }
302         /*
303          * else it was a new CIFS directory
304          * but there was no free structure to store it.
305          */
306 }
307
308 /*
309  ***************************************************************************
310  * Read CIFS-mount directories stats from /proc/fs/cifs/Stats.
311  *
312  * IN:
313  * @curr        Index in array for current sample statistics.
314  ***************************************************************************
315  */
316 void read_cifs_stat(int curr)
317 {
318         FILE *fp;
319         char line[256];
320         char aux[32];
321         int start = 0;
322         long long unsigned aux_open;
323         long long unsigned all_open = 0;
324         char cifs_name[MAX_NAME_LEN];
325         char name_tmp[MAX_NAME_LEN];
326         struct cifs_stats scifs;
327
328         /* Every CIFS entry is potentially unregistered */
329         set_entries_inactive();
330
331         if ((fp = fopen(CIFSSTATS, "r")) == NULL)
332                 return;
333
334         sprintf(aux, "%%*d) %%%ds",
335                 MAX_NAME_LEN < 200 ? MAX_NAME_LEN - 1 : 200);
336
337         while (fgets(line, 256, fp) != NULL) {
338
339                 /* Read CIFS directory name */
340                 if (isdigit((unsigned char) line[0]) && sscanf(line, aux , name_tmp) == 1) {
341                         if (start) {
342                                 scifs.fopens = all_open;
343                                 save_stats(cifs_name, curr, &scifs);
344                                 all_open = 0;
345                         }
346                         else {
347                                 start = 1;
348                         }
349                         strcpy(cifs_name, name_tmp);
350                 }
351                 else {
352                         if (!strncmp(line, "Reads:", 6)) {
353                                 sscanf(line, "Reads: %llu Bytes: %llu", &scifs.rd_ops, &scifs.rd_bytes);
354                         }
355                         if (!strncmp(line, "Writes:", 7)) {
356                                 sscanf(line, "Writes: %llu Bytes: %llu", &scifs.wr_ops, &scifs.wr_bytes);
357                         }
358                         if (!strncmp(line, "Opens:", 6)) {
359                                 sscanf(line, "Opens: %llu Closes:%llu Deletes: %llu",
360                                        &aux_open, &scifs.fcloses, &scifs.fdeletes);
361                                 all_open += aux_open;
362                         }
363                         if (!strncmp(line, "Posix Opens:", 12)) {
364                                 sscanf(line, "Posix Opens: %llu", &aux_open);
365                                 all_open += aux_open;
366                         }
367                 }
368         }
369         
370         if (start) {
371                 scifs.fopens = all_open;
372                 save_stats(cifs_name, curr, &scifs);
373         }
374
375         fclose(fp);
376
377         /* Free structures corresponding to unregistered filesystems */
378         free_inactive_entries();
379 }
380
381 /*
382  ***************************************************************************
383  * Display CIFS stats header.
384  *
385  * OUT:
386  * @fctr        Conversion factor.
387  ***************************************************************************
388  */
389 void write_cifs_stat_header(int *fctr)
390 {
391         printf("Filesystem:           ");
392         if (DISPLAY_KILOBYTES(flags)) {
393                 printf("        rkB/s        wkB/s");
394                 *fctr = 1024;
395         }
396         else if (DISPLAY_MEGABYTES(flags)) {
397                 printf("        rMB/s        wMB/s");
398                 *fctr = 1024 * 1024;
399         }
400         else {
401                 printf("         rB/s         wB/s");
402                 *fctr = 1;
403         }
404         printf("    rops/s    wops/s         fo/s         fc/s         fd/s\n");
405 }
406
407 /*
408  ***************************************************************************
409  * Write CIFS stats read from /proc/fs/cifs/Stats.
410  *
411  * IN:
412  * @curr        Index in array for current sample statistics.
413  * @itv         Interval of time.
414  * @fctr        Conversion factor.
415  * @shi         Structures describing the CIFS filesystems.
416  * @ioi         Current sample statistics.
417  * @ioj         Previous sample statistics.
418  ***************************************************************************
419  */
420 void write_cifs_stat(int curr, unsigned long long itv, int fctr,
421                      struct io_hdr_stats *shi, struct cifs_stats *ioni,
422                      struct cifs_stats *ionj)
423 {
424         if (DISPLAY_HUMAN_READ(flags)) {
425                 printf("%-22s\n%23s", shi->name, "");
426         }
427         else {
428                 printf("%-22s ", shi->name);
429         }
430
431         /*       rB/s   wB/s   fo/s   fc/s   fd/s*/
432         printf("%12.2f %12.2f %9.2f %9.2f %12.2f %12.2f %12.2f \n",
433                S_VALUE(ionj->rd_bytes, ioni->rd_bytes, itv) / fctr,
434                S_VALUE(ionj->wr_bytes, ioni->wr_bytes, itv) / fctr,
435                S_VALUE(ionj->rd_ops, ioni->rd_ops, itv),
436                S_VALUE(ionj->wr_ops, ioni->wr_ops, itv),
437                S_VALUE(ionj->fopens, ioni->fopens, itv),
438                S_VALUE(ionj->fcloses, ioni->fcloses, itv),
439                S_VALUE(ionj->fdeletes, ioni->fdeletes, itv));
440 }
441
442 /*
443  ***************************************************************************
444  * Print everything now (stats and uptime).
445  *
446  * IN:
447  * @curr        Index in array for current sample statistics.
448  * @rectime     Current date and time.
449  ***************************************************************************
450  */
451 void write_stats(int curr, struct tm *rectime)
452 {
453         int i, fctr = 1;
454         unsigned long long itv;
455         struct io_hdr_stats *shi;
456         struct cifs_stats *ioni, *ionj;
457
458         /* Test stdout */
459         TEST_STDOUT(STDOUT_FILENO);
460
461         /* Print time stamp */
462         if (DISPLAY_TIMESTAMP(flags)) {
463                 if (DISPLAY_ISO(flags)) {
464                         strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
465                 }
466                 else {
467                         strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
468                 }
469                 printf("%s\n", timestamp);
470 #ifdef DEBUG
471                 if (DISPLAY_DEBUG(flags)) {
472                         fprintf(stderr, "%s\n", timestamp);
473                 }
474 #endif
475         }
476
477         /* Interval is multiplied by the number of processors */
478         itv = get_interval(uptime[!curr], uptime[curr]);
479
480         if (cpu_nr > 1) {
481                 /* On SMP machines, reduce itv to one processor (see note above) */
482                 itv = get_interval(uptime0[!curr], uptime0[curr]);
483         }
484
485         shi = st_hdr_cifs;
486
487         /* Display CIFS stats header */
488         write_cifs_stat_header(&fctr);
489
490         for (i = 0; i < cifs_nr; i++, shi++) {
491                 if (shi->used) {
492                         ioni = st_cifs[curr]  + i;
493                         ionj = st_cifs[!curr] + i;
494 #ifdef DEBUG
495                         if (DISPLAY_DEBUG(flags)) {
496                                 /* Debug output */
497                                 fprintf(stderr, "name=%s itv=%llu fctr=%d ioni{ rd_bytes=%llu "
498                                                 "wr_bytes=%llu rd_ops=%llu wr_ops=%llu fopens=%llu "
499                                                 "fcloses=%llu fdeletes=%llu}\n",
500                                         shi->name, itv, fctr,
501                                         ioni->rd_bytes, ioni->wr_bytes,
502                                         ioni->rd_ops,   ioni->wr_ops,
503                                         ioni->fopens,   ioni->fcloses,
504                                         ioni->fdeletes);
505                         }
506 #endif
507                         write_cifs_stat(curr, itv, fctr, shi, ioni, ionj);
508                 }
509         }
510         printf("\n");
511 }
512
513 /*
514  ***************************************************************************
515  * Main loop: Read stats from the relevant sources and display them.
516  *
517  * IN:
518  * @count       Number of lines of stats to print.
519  * @rectime     Current date and time.
520  ***************************************************************************
521  */
522 void rw_io_stat_loop(long int count, struct tm *rectime)
523 {
524         int curr = 1;
525
526         /* Don't buffer data if redirected to a pipe */
527         setbuf(stdout, NULL);
528         
529         do {
530                 if (cpu_nr > 1) {
531                         /*
532                          * Read system uptime (only for SMP machines).
533                          * Init uptime0. So if /proc/uptime cannot fill it,
534                          * this will be done by /proc/stat.
535                          */
536                         uptime0[curr] = 0;
537                         read_uptime(&(uptime0[curr]));
538                 }
539
540                 /* Read CIFS stats */
541                 read_cifs_stat(curr);
542
543                 /* Get time */
544                 get_localtime(rectime, 0);
545
546                 /* Print results */
547                 write_stats(curr, rectime);
548
549                 if (count > 0) {
550                         count--;
551                 }
552
553                 if (count) {
554                         curr ^= 1;
555                         pause();
556                 }
557         }
558         while (count);
559 }
560
561 /*
562  ***************************************************************************
563  * Main entry to the cifsiostat program.
564  ***************************************************************************
565  */
566 int main(int argc, char **argv)
567 {
568         int it = 0;
569         int opt = 1;
570         int i;
571         long count = 1;
572         struct utsname header;
573         struct tm rectime;
574
575 #ifdef USE_NLS
576         /* Init National Language Support */
577         init_nls();
578 #endif
579
580         /* Get HZ */
581         get_HZ();
582
583         /* Process args... */
584         while (opt < argc) {
585
586 #ifdef DEBUG
587                 if (!strcmp(argv[opt], "--debuginfo")) {
588                         flags |= I_D_DEBUG;
589                         opt++;
590                 } else
591 #endif
592                 if (!strncmp(argv[opt], "-", 1)) {
593                         for (i = 1; *(argv[opt] + i); i++) {
594
595                                 switch (*(argv[opt] + i)) {
596
597                                 case 'h':
598                                         /* Display an easy-to-read CIFS report */
599                                         flags |= I_D_HUMAN_READ;
600                                         break;
601         
602                                 case 'k':
603                                         if (DISPLAY_MEGABYTES(flags)) {
604                                                 usage(argv[0]);
605                                         }
606                                         /* Display stats in kB/s */
607                                         flags |= I_D_KILOBYTES;
608                                         break;
609
610                                 case 'm':
611                                         if (DISPLAY_KILOBYTES(flags)) {
612                                                 usage(argv[0]);
613                                         }
614                                         /* Display stats in MB/s */
615                                         flags |= I_D_MEGABYTES;
616                                         break;
617
618                                 case 't':
619                                         /* Display timestamp */
620                                         flags |= I_D_TIMESTAMP;
621                                         break;
622
623                                 case 'V':
624                                         /* Print version number and exit */
625                                         print_version();
626                                         break;
627         
628                                 default:
629                                         usage(argv[0]);
630                                 }
631                         }
632                         opt++;
633                 }
634
635                 else if (!it) {
636                         interval = atol(argv[opt++]);
637                         if (interval < 0) {
638                                 usage(argv[0]);
639                         }
640                         count = -1;
641                         it = 1;
642                 }
643
644                 else if (it > 0) {
645                         count = atol(argv[opt++]);
646                         if ((count < 1) || !interval) {
647                                 usage(argv[0]);
648                         }
649                         it = -1;
650                 }
651                 else {
652                         usage(argv[0]);
653                 }
654         }
655
656         if (!interval) {
657                 count = 1;
658         }
659
660         /* Init structures according to machine architecture */
661         io_sys_init();
662
663         get_localtime(&rectime, 0);
664
665         /* Get system name, release number and hostname */
666         uname(&header);
667         if (print_gal_header(&rectime, header.sysname, header.release,
668                              header.nodename, header.machine, cpu_nr)) {
669                 flags |= I_D_ISO;
670         }
671         printf("\n");
672
673         /* Set a handler for SIGALRM */
674         alarm_handler(0);
675
676         /* Main loop */
677         rw_io_stat_loop(count, &rectime);
678
679         /* Free structures */
680         io_sys_free();
681
682         return 0;
683 }