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