]> granicus.if.org Git - sysstat/blob - cifsiostat.c
17714be16d3f98f88696f108d1cc16c8c98b6a31
[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 "count.h"
34 #include "common.h"
35
36 #ifdef USE_NLS
37 #include <locale.h>
38 #include <libintl.h>
39 #define _(string) gettext(string)
40 #else
41 #define _(string) (string)
42 #endif
43
44 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
45 char *sccsid(void) { return (SCCSID); }
46
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[TIMESTAMP_LEN];
57
58 struct sigaction alrm_act;
59
60 /*
61  ***************************************************************************
62  * Print usage and exit.
63  *
64  * IN:
65  * @progname    Name of sysstat command.
66  ***************************************************************************
67  */
68 void usage(char *progname)
69 {
70         fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
71                 progname);
72
73 #ifdef DEBUG
74         fprintf(stderr, _("Options are:\n"
75                           "[ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n"));
76 #else
77         fprintf(stderr, _("Options are:\n"
78                           "[ -h ] [ -k | -m ] [ -t ] [ -V ]\n"));
79 #endif
80         exit(1);
81 }
82
83 /*
84  ***************************************************************************
85  * SIGALRM signal handler.
86  *
87  * IN:
88  * @sig Signal number.
89  ***************************************************************************
90  */
91 void alarm_handler(int sig)
92 {
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, sizeof(line), 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, FALSE);
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
178         if (cifs_nr > 0) {
179                 if ((st_hdr_cifs = (struct io_hdr_stats *) calloc(cifs_nr, IO_HDR_STATS_SIZE)) == NULL) {
180                         perror("malloc");
181                         exit(4);
182                 }
183
184                 /* Allocate structures for number of CIFS directories found */
185                 for (i = 0; i < 2; i++) {
186                         if ((st_cifs[i] =
187                         (struct cifs_stats *) calloc(cifs_nr, CIFS_STATS_SIZE)) == NULL) {
188                                 perror("malloc");
189                                 exit(4);
190                         }
191                 }
192         }
193         else {
194                 /*
195                  * cifs_nr value is probably zero, but it can also be negative
196                  * (possible overflow when adding NR_CIFS_PREALLOC above).
197                  */
198                 cifs_nr = 0;
199         }
200 }
201
202 /*
203  ***************************************************************************
204  * Free various structures.
205  ***************************************************************************
206 */
207 void io_sys_free(void)
208 {
209         int i;
210
211         /* Free CIFS directories structures */
212         for (i = 0; i < 2; i++) {
213                 free(st_cifs[i]);
214         }
215
216         free(st_hdr_cifs);
217 }
218
219 /*
220  ***************************************************************************
221  * Save stats for current CIFS filesystem.
222  *
223  * IN:
224  * @name                Name of CIFS filesystem.
225  * @curr                Index in array for current sample statistics.
226  * @st_io               Structure with CIFS statistics to save.
227  ***************************************************************************
228  */
229 void save_stats(char *name, int curr, struct cifs_stats *st_io)
230 {
231         int i, j;
232         struct io_hdr_stats *st_hdr_cifs_i;
233         struct cifs_stats *st_cifs_i;
234
235         /* Look for CIFS directory in data table */
236         for (i = 0; i < cifs_nr; i++) {
237                 st_hdr_cifs_i = st_hdr_cifs + i;
238                 if ((st_hdr_cifs_i->used == TRUE) &&
239                     (!strcmp(st_hdr_cifs_i->name, name))) {
240                         break;
241                 }
242         }
243
244         if (i == cifs_nr) {
245                 /*
246                  * This is a new filesystem: Look for an unused entry to store it.
247                  */
248                 for (i = 0; i < cifs_nr; i++) {
249                         st_hdr_cifs_i = st_hdr_cifs + i;
250                         if (!st_hdr_cifs_i->used) {
251                                 /* Unused entry found... */
252                                 st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
253                                 st_hdr_cifs_i->active = TRUE;
254                                 strncpy(st_hdr_cifs_i->name, name, MAX_NAME_LEN - 1);
255                                 st_hdr_cifs_i->name[MAX_NAME_LEN - 1] = '\0';
256                                 st_cifs_i = st_cifs[curr] + i;
257                                 *st_cifs_i = *((struct cifs_stats *) st_io);
258                                 break;
259                         }
260                 }
261                 if (i == cifs_nr) {
262                         /*
263                          * It is a new CIFS directory
264                          * but there is no free structure to store it.
265                          */
266
267                         /* All entries are used: The number has to be increased */
268                         cifs_nr = cifs_nr + 5;
269
270                         /* Increase the size of st_hdr_ionfs buffer */
271                         if ((st_hdr_cifs = (struct io_hdr_stats *)
272                                 realloc(st_hdr_cifs, cifs_nr * IO_HDR_STATS_SIZE)) == NULL) {
273                                 perror("malloc");
274                                 exit(4);
275                         }
276
277                         /* Set the new entries inactive */
278                         for (j = 0; j < 5; j++) {
279                                 st_hdr_cifs_i = st_hdr_cifs + i + j;
280                                 st_hdr_cifs_i->used = FALSE;
281                                 st_hdr_cifs_i->active = FALSE;
282                         }
283
284                         /* Increase the size of st_hdr_ionfs buffer */
285                         for (j = 0; j < 2; j++) {
286                                 if ((st_cifs[j] = (struct cifs_stats *)
287                                         realloc(st_cifs[j], cifs_nr * CIFS_STATS_SIZE)) == NULL) {
288                                         perror("malloc");
289                                         exit(4);
290                                 }
291                                 memset(st_cifs[j] + i, 0, 5 * CIFS_STATS_SIZE);
292                         }
293                         /* Now i shows the first unused entry of the new block */
294                         st_hdr_cifs_i = st_hdr_cifs + i;
295                         st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
296                         st_hdr_cifs_i->active = TRUE;
297                         strncpy(st_hdr_cifs_i->name, name, MAX_NAME_LEN - 1);
298                         st_hdr_cifs_i->name[MAX_NAME_LEN - 1] = '\0';
299                         st_cifs_i = st_cifs[curr] + i;
300                         *st_cifs_i = *st_io;
301                 }
302         } else {
303                 st_hdr_cifs_i = st_hdr_cifs + i;
304                 st_hdr_cifs_i->active = TRUE;
305                 st_hdr_cifs_i->used = TRUE;
306                 st_cifs_i = st_cifs[curr] + i;
307                 *st_cifs_i = *st_io;
308         }
309         /*
310          * else it was a new CIFS directory
311          * but there was no free structure to store it.
312          */
313 }
314
315 /*
316  ***************************************************************************
317  * Read CIFS-mount directories stats from /proc/fs/cifs/Stats.
318  *
319  * IN:
320  * @curr        Index in array for current sample statistics.
321  ***************************************************************************
322  */
323 void read_cifs_stat(int curr)
324 {
325         FILE *fp;
326         char line[256];
327         char aux[32];
328         int start = 0;
329         long long unsigned aux_open;
330         long long unsigned all_open = 0;
331         char cifs_name[MAX_NAME_LEN];
332         char name_tmp[MAX_NAME_LEN];
333         struct cifs_stats scifs = {0, 0, 0, 0, 0, 0, 0};
334
335         /* Every CIFS entry is potentially unregistered */
336         set_entries_inactive();
337
338         if ((fp = fopen(CIFSSTATS, "r")) == NULL)
339                 return;
340
341         sprintf(aux, "%%*d) %%%ds",
342                 MAX_NAME_LEN < 200 ? MAX_NAME_LEN - 1 : 200);
343
344         while (fgets(line, sizeof(line), fp) != NULL) {
345
346                 /* Read CIFS directory name */
347                 if (isdigit((unsigned char) line[0]) && sscanf(line, aux , name_tmp) == 1) {
348                         if (start) {
349                                 scifs.fopens = all_open;
350                                 save_stats(cifs_name, curr, &scifs);
351                                 all_open = 0;
352                         }
353                         else {
354                                 start = 1;
355                         }
356                         strncpy(cifs_name, name_tmp, MAX_NAME_LEN);
357                         cifs_name[MAX_NAME_LEN - 1] = '\0';
358                 }
359                 else {
360                         if (!strncmp(line, "Reads:", 6)) {
361                                 sscanf(line, "Reads: %llu Bytes: %llu", &scifs.rd_ops, &scifs.rd_bytes);
362                         }
363                         if (!strncmp(line, "Writes:", 7)) {
364                                 sscanf(line, "Writes: %llu Bytes: %llu", &scifs.wr_ops, &scifs.wr_bytes);
365                         }
366                         if (!strncmp(line, "Opens:", 6)) {
367                                 sscanf(line, "Opens: %llu Closes:%llu Deletes: %llu",
368                                        &aux_open, &scifs.fcloses, &scifs.fdeletes);
369                                 all_open += aux_open;
370                         }
371                         if (!strncmp(line, "Posix Opens:", 12)) {
372                                 sscanf(line, "Posix Opens: %llu", &aux_open);
373                                 all_open += aux_open;
374                         }
375                 }
376         }
377
378         if (start) {
379                 scifs.fopens = all_open;
380                 save_stats(cifs_name, curr, &scifs);
381         }
382
383         fclose(fp);
384
385         /* Free structures corresponding to unregistered filesystems */
386         free_inactive_entries();
387 }
388
389 /*
390  ***************************************************************************
391  * Display CIFS stats header.
392  *
393  * OUT:
394  * @fctr        Conversion factor.
395  ***************************************************************************
396  */
397 void write_cifs_stat_header(int *fctr)
398 {
399         printf("Filesystem:           ");
400         if (DISPLAY_KILOBYTES(flags)) {
401                 printf("        rkB/s        wkB/s");
402                 *fctr = 1024;
403         }
404         else if (DISPLAY_MEGABYTES(flags)) {
405                 printf("        rMB/s        wMB/s");
406                 *fctr = 1024 * 1024;
407         }
408         else {
409                 printf("         rB/s         wB/s");
410                 *fctr = 1;
411         }
412         printf("    rops/s    wops/s         fo/s         fc/s         fd/s\n");
413 }
414
415 /*
416  ***************************************************************************
417  * Write CIFS stats read from /proc/fs/cifs/Stats.
418  *
419  * IN:
420  * @curr        Index in array for current sample statistics.
421  * @itv         Interval of time.
422  * @fctr        Conversion factor.
423  * @shi         Structures describing the CIFS filesystems.
424  * @ioi         Current sample statistics.
425  * @ioj         Previous sample statistics.
426  ***************************************************************************
427  */
428 void write_cifs_stat(int curr, unsigned long long itv, int fctr,
429                      struct io_hdr_stats *shi, struct cifs_stats *ioni,
430                      struct cifs_stats *ionj)
431 {
432         if (DISPLAY_HUMAN_READ(flags)) {
433                 cprintf_in(IS_STR, "%-22s\n", shi->name, 0);
434                 printf("%22s", "");
435         }
436         else {
437                 cprintf_in(IS_STR, "%-22s", shi->name, 0);
438         }
439
440         /*       rB/s   wB/s   fo/s   fc/s   fd/s*/
441         cprintf_f(-1, 2, 12, 2,
442                   S_VALUE(ionj->rd_bytes, ioni->rd_bytes, itv) / fctr,
443                   S_VALUE(ionj->wr_bytes, ioni->wr_bytes, itv) / fctr);
444         cprintf_f(-1, 2, 9, 2,
445                   S_VALUE(ionj->rd_ops, ioni->rd_ops, itv),
446                   S_VALUE(ionj->wr_ops, ioni->wr_ops, itv));
447         cprintf_f(-1, 3, 12, 2,
448                   S_VALUE(ionj->fopens, ioni->fopens, itv),
449                   S_VALUE(ionj->fcloses, ioni->fcloses, itv),
450                   S_VALUE(ionj->fdeletes, ioni->fdeletes, itv));
451         printf("\n");
452 }
453
454 /*
455  ***************************************************************************
456  * Print everything now (stats and uptime).
457  *
458  * IN:
459  * @curr        Index in array for current sample statistics.
460  * @rectime     Current date and time.
461  ***************************************************************************
462  */
463 void write_stats(int curr, struct tm *rectime)
464 {
465         int i, fctr = 1;
466         unsigned long long itv;
467         struct io_hdr_stats *shi;
468         struct cifs_stats *ioni, *ionj;
469
470         /* Test stdout */
471         TEST_STDOUT(STDOUT_FILENO);
472
473         /* Print time stamp */
474         if (DISPLAY_TIMESTAMP(flags)) {
475                 if (DISPLAY_ISO(flags)) {
476                         strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
477                 }
478                 else {
479                         strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
480                 }
481                 printf("%s\n", timestamp);
482 #ifdef DEBUG
483                 if (DISPLAY_DEBUG(flags)) {
484                         fprintf(stderr, "%s\n", timestamp);
485                 }
486 #endif
487         }
488
489         /* Interval of time, reduced to one processor */
490         itv = get_interval(uptime0[!curr], uptime0[curr]);
491
492         shi = st_hdr_cifs;
493
494         /* Display CIFS stats header */
495         write_cifs_stat_header(&fctr);
496
497         for (i = 0; i < cifs_nr; i++, shi++) {
498                 if (shi->used) {
499                         ioni = st_cifs[curr]  + i;
500                         ionj = st_cifs[!curr] + i;
501 #ifdef DEBUG
502                         if (DISPLAY_DEBUG(flags)) {
503                                 /* Debug output */
504                                 fprintf(stderr, "name=%s itv=%llu fctr=%d ioni{ rd_bytes=%llu "
505                                                 "wr_bytes=%llu rd_ops=%llu wr_ops=%llu fopens=%llu "
506                                                 "fcloses=%llu fdeletes=%llu}\n",
507                                         shi->name, itv, fctr,
508                                         ioni->rd_bytes, ioni->wr_bytes,
509                                         ioni->rd_ops,   ioni->wr_ops,
510                                         ioni->fopens,   ioni->fcloses,
511                                         ioni->fdeletes);
512                         }
513 #endif
514                         write_cifs_stat(curr, itv, fctr, shi, ioni, ionj);
515                 }
516         }
517         printf("\n");
518 }
519
520 /*
521  ***************************************************************************
522  * Main loop: Read stats from the relevant sources and display them.
523  *
524  * IN:
525  * @count       Number of lines of stats to print.
526  * @rectime     Current date and time.
527  ***************************************************************************
528  */
529 void rw_io_stat_loop(long int count, struct tm *rectime)
530 {
531         int curr = 1;
532
533         /* Don't buffer data if redirected to a pipe */
534         setbuf(stdout, NULL);
535
536         do {
537                 /* Read system uptime (reduced to one processor) */
538                 uptime0[curr] = 0;
539                 read_uptime(&(uptime0[curr]));
540                 if (!uptime0[curr])
541                         /* Cannot read system uptime (/proc/uptime doesn't exist) */
542                         exit(2);
543
544                 /* Read CIFS stats */
545                 read_cifs_stat(curr);
546
547                 /* Get time */
548                 get_localtime(rectime, 0);
549
550                 /* Print results */
551                 write_stats(curr, rectime);
552
553                 if (count > 0) {
554                         count--;
555                 }
556
557                 if (count) {
558                         curr ^= 1;
559                         pause();
560                 }
561         }
562         while (count);
563 }
564
565 /*
566  ***************************************************************************
567  * Main entry to the cifsiostat program.
568  ***************************************************************************
569  */
570 int main(int argc, char **argv)
571 {
572         int it = 0;
573         int opt = 1;
574         int i;
575         long count = 1;
576         struct utsname header;
577         struct tm rectime;
578
579 #ifdef USE_NLS
580         /* Init National Language Support */
581         init_nls();
582 #endif
583
584         /* Init color strings */
585         init_colors();
586
587         /* Get HZ */
588         get_HZ();
589
590         /* Process args... */
591         while (opt < argc) {
592
593 #ifdef DEBUG
594                 if (!strcmp(argv[opt], "--debuginfo")) {
595                         flags |= I_D_DEBUG;
596                         opt++;
597                 } else
598 #endif
599                 if (!strncmp(argv[opt], "-", 1)) {
600                         for (i = 1; *(argv[opt] + i); i++) {
601
602                                 switch (*(argv[opt] + i)) {
603
604                                 case 'h':
605                                         /* Display an easy-to-read CIFS report */
606                                         flags |= I_D_HUMAN_READ;
607                                         break;
608
609                                 case 'k':
610                                         if (DISPLAY_MEGABYTES(flags)) {
611                                                 usage(argv[0]);
612                                         }
613                                         /* Display stats in kB/s */
614                                         flags |= I_D_KILOBYTES;
615                                         break;
616
617                                 case 'm':
618                                         if (DISPLAY_KILOBYTES(flags)) {
619                                                 usage(argv[0]);
620                                         }
621                                         /* Display stats in MB/s */
622                                         flags |= I_D_MEGABYTES;
623                                         break;
624
625                                 case 't':
626                                         /* Display timestamp */
627                                         flags |= I_D_TIMESTAMP;
628                                         break;
629
630                                 case 'V':
631                                         /* Print version number and exit */
632                                         print_version();
633                                         break;
634
635                                 default:
636                                         usage(argv[0]);
637                                 }
638                         }
639                         opt++;
640                 }
641
642                 else if (!it) {
643                         interval = atol(argv[opt++]);
644                         if (interval < 0) {
645                                 usage(argv[0]);
646                         }
647                         count = -1;
648                         it = 1;
649                 }
650
651                 else if (it > 0) {
652                         count = atol(argv[opt++]);
653                         if ((count < 1) || !interval) {
654                                 usage(argv[0]);
655                         }
656                         it = -1;
657                 }
658                 else {
659                         usage(argv[0]);
660                 }
661         }
662
663         if (!interval) {
664                 count = 1;
665         }
666
667         /* Init structures according to machine architecture */
668         io_sys_init();
669
670         get_localtime(&rectime, 0);
671
672         /* Get system name, release number and hostname */
673         uname(&header);
674         if (print_gal_header(&rectime, header.sysname, header.release,
675                              header.nodename, header.machine, cpu_nr,
676                              PLAIN_OUTPUT)) {
677                 flags |= I_D_ISO;
678         }
679         printf("\n");
680
681         /* Set a handler for SIGALRM */
682         memset(&alrm_act, 0, sizeof(alrm_act));
683         alrm_act.sa_handler = alarm_handler;
684         sigaction(SIGALRM, &alrm_act, NULL);
685         alarm(interval);
686
687         /* Main loop */
688         rw_io_stat_loop(count, &rectime);
689
690         /* Free structures */
691         io_sys_free();
692
693         return 0;
694 }