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