]> granicus.if.org Git - sysstat/blob - cifsiostat.c
Merge get_locatime() and get_gmtime() functions
[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 #ifdef TEST
50 void int_handler(int n) { return; }
51 #endif
52
53 unsigned long long uptime_cs[2] = {0, 0};
54 struct io_cifs *cifs_list = NULL;
55
56 int cpu_nr = 0;         /* Nb of processors on the machine */
57 int flags = 0;          /* Flag for common options and system state */
58 int dplaces_nr = -1;    /* Number of decimal places */
59
60 long interval = 0;
61 char timestamp[TIMESTAMP_LEN];
62
63 struct sigaction alrm_act;
64
65 /*
66  ***************************************************************************
67  * Print usage and exit.
68  *
69  * IN:
70  * @progname    Name of sysstat command.
71  ***************************************************************************
72  */
73 void usage(char *progname)
74 {
75         fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
76                 progname);
77
78 #ifdef DEBUG
79         fprintf(stderr, _("Options are:\n"
80                           "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ --pretty ]\n"
81                           "[ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n"));
82 #else
83         fprintf(stderr, _("Options are:\n"
84                           "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ --pretty ]\n"
85                           "[ -h ] [ -k | -m ] [ -t ] [ -V ]\n"));
86 #endif
87         exit(1);
88 }
89
90 /*
91  ***************************************************************************
92  * SIGALRM signal handler.
93  *
94  * IN:
95  * @sig Signal number.
96  ***************************************************************************
97  */
98 void alarm_handler(int sig)
99 {
100         alarm(interval);
101 }
102
103 /*
104  ***************************************************************************
105  * Set every cifs entry to nonexistent status.
106  *
107  * IN:
108  * @clist       Pointer on the start of the linked list.
109  ***************************************************************************
110  */
111 void set_cifs_nonexistent(struct io_cifs *clist)
112 {
113         while (clist != NULL) {
114                 clist->exist = FALSE;
115                 clist = clist->next;
116         }
117 }
118
119 /*
120  ***************************************************************************
121  * Check if a cifs filesystem is present in the list, and add it if requested.
122  *
123  * IN:
124  * @clist       Address of pointer on the start of the linked list.
125  * @name        cifs name.
126  *
127  * RETURNS:
128  * Pointer on the io_cifs structure in the list where the cifs is located
129  * (whether it was already in the list or if it has been added).
130  * NULL if the cifs name is too long or if the cifs doesn't exist and we
131  * don't want to add it.
132  ***************************************************************************
133  */
134 struct io_cifs *add_list_cifs(struct io_cifs **clist, char *name)
135 {
136         struct io_cifs *c, *cs;
137         int i;
138
139         if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN)
140                 /* cifs name is too long */
141                 return NULL;
142
143         while (*clist != NULL) {
144
145                 c = *clist;
146                 if ((i = strcmp(c->name, name)) == 0) {
147                         /* cifs found in list */
148                         c->exist = TRUE;
149                         return c;
150                 }
151                 if (i > 0)
152                         /*
153                          * If no group defined and we don't use /proc/diskstats,
154                          * insert current device in alphabetical order.
155                          * NB: Using /proc/diskstats ("iostat -p ALL") is a bit better than
156                          * using alphabetical order because sda10 comes after sda9...
157                          */
158                         break;
159
160                 clist = &(c->next);
161         }
162
163         /* cifs not found */
164         cs = *clist;
165
166         /* Add cifs to the list */
167         if ((*clist = (struct io_cifs *) malloc(IO_CIFS_SIZE)) == NULL) {
168                 perror("malloc");
169                 exit(4);
170         }
171         memset(*clist, 0, IO_CIFS_SIZE);
172
173         c = *clist;
174         for (i = 0; i < 2; i++) {
175                 if ((c->cifs_stats[i] = (struct cifs_st *) malloc(CIFS_ST_SIZE)) == NULL) {
176                         perror("malloc");
177                         exit(4);
178                 }
179                 memset(c->cifs_stats[i], 0, CIFS_ST_SIZE);
180         }
181         strncpy(c->name, name, sizeof(c->name));
182         c->name[sizeof(c->name) - 1] = '\0';
183         c->exist = TRUE;
184         c->next = cs;
185
186         return c;
187 }
188
189 /*
190  ***************************************************************************
191  * Read CIFS-mount directories stats from /proc/fs/cifs/Stats.
192  *
193  * IN:
194  * @curr        Index in array for current sample statistics.
195  ***************************************************************************
196  */
197 void read_cifs_stat(int curr)
198 {
199         FILE *fp;
200         char line[256];
201         char aux[32];
202         int start = 0;
203         long long unsigned aux_open;
204         long long unsigned all_open = 0;
205         char cifs_name[MAX_NAME_LEN];
206         char name_tmp[MAX_NAME_LEN];
207         struct cifs_st scifs;
208         struct io_cifs *ci;
209
210         if ((fp = fopen(CIFSSTATS, "r")) == NULL)
211                 return;
212
213         sprintf(aux, "%%*d) %%%ds",
214                 MAX_NAME_LEN < 200 ? MAX_NAME_LEN - 1 : 200);
215
216         memset(&scifs, 0, CIFS_ST_SIZE);
217
218         while (fgets(line, sizeof(line), fp) != NULL) {
219
220                 /* Read CIFS directory name */
221                 if (isdigit((unsigned char) line[0]) && sscanf(line, aux , name_tmp) == 1) {
222                         if (start) {
223                                 scifs.fopens = all_open;
224                                 ci = add_list_cifs(&cifs_list, cifs_name);
225                                 if (ci != NULL) {
226                                         *ci->cifs_stats[curr] = scifs;
227                                 }
228                                 all_open = 0;
229                         }
230                         else {
231                                 start = 1;
232                         }
233                         strncpy(cifs_name, name_tmp, sizeof(cifs_name));
234                         cifs_name[sizeof(cifs_name) - 1] = '\0';
235                         memset(&scifs, 0, CIFS_ST_SIZE);
236                 }
237                 else {
238                         if (!strncmp(line, "Reads:", 6)) {
239                                 /*
240                                  * SMB1 format: Reads: %llu Bytes: %llu
241                                  * SMB2 format: Reads: %llu sent %llu failed
242                                  * If this is SMB2 format then only the first variable (rd_ops) will be set.
243                                  */
244                                 sscanf(line, "Reads: %llu Bytes: %llu", &scifs.rd_ops, &scifs.rd_bytes);
245                         }
246                         else if (!strncmp(line, "Bytes read:", 11)) {
247                                 sscanf(line, "Bytes read: %llu  Bytes written: %llu",
248                                        &scifs.rd_bytes, &scifs.wr_bytes);
249                         }
250                         else if (!strncmp(line, "Writes:", 7)) {
251                                 /*
252                                  * SMB1 format: Writes: %llu Bytes: %llu
253                                  * SMB2 format: Writes: %llu sent %llu failed
254                                  * If this is SMB2 format then only the first variable (wr_ops) will be set.
255                                  */
256                                 sscanf(line, "Writes: %llu Bytes: %llu", &scifs.wr_ops, &scifs.wr_bytes);
257                         }
258                         else if (!strncmp(line, "Opens:", 6)) {
259                                 sscanf(line, "Opens: %llu Closes:%llu Deletes: %llu",
260                                        &aux_open, &scifs.fcloses, &scifs.fdeletes);
261                                 all_open += aux_open;
262                         }
263                         else if (!strncmp(line, "Posix Opens:", 12)) {
264                                 sscanf(line, "Posix Opens: %llu", &aux_open);
265                                 all_open += aux_open;
266                         }
267                         else if (!strncmp(line, "Open files:", 11)) {
268                                 sscanf(line, "Open files: %llu total (local), %llu",
269                                        &all_open, &aux_open);
270                                 all_open += aux_open;
271                         }
272                         else if (!strncmp(line, "Closes:", 7)) {
273                                 sscanf(line, "Closes: %llu", &scifs.fcloses);
274                         }
275                 }
276         }
277
278         if (start) {
279                 scifs.fopens = all_open;
280                 ci = add_list_cifs(&cifs_list, cifs_name);
281                 if (ci != NULL) {
282                         *ci->cifs_stats[curr] = scifs;
283                 }
284         }
285
286         fclose(fp);
287 }
288
289 /*
290  ***************************************************************************
291  * Display CIFS stats header.
292  *
293  * OUT:
294  * @fctr        Conversion factor.
295  ***************************************************************************
296  */
297 void write_cifs_stat_header(int *fctr)
298 {
299         if (!DISPLAY_PRETTY(flags)) {
300                 printf("Filesystem            ");
301         }
302         if (DISPLAY_KILOBYTES(flags)) {
303                 printf("        rkB/s        wkB/s");
304                 *fctr = 1024;
305         }
306         else if (DISPLAY_MEGABYTES(flags)) {
307                 printf("        rMB/s        wMB/s");
308                 *fctr = 1024 * 1024;
309         }
310         else {
311                 printf("         rB/s         wB/s");
312                 *fctr = 1;
313         }
314         printf("    rops/s    wops/s         fo/s         fc/s         fd/s");
315         if (DISPLAY_PRETTY(flags)) {
316                 printf(" Filesystem");
317         }
318         printf("\n");
319 }
320
321 /*
322  ***************************************************************************
323  * Write CIFS stats read from /proc/fs/cifs/Stats.
324  *
325  * IN:
326  * @curr        Index in array for current sample statistics.
327  * @itv         Interval of time (in 1/100th of a second).
328  * @fctr        Conversion factor.
329  * @clist       Pointer on the linked list where the cifs is saved.
330  * @ioi         Current sample statistics.
331  * @ioj         Previous sample statistics.
332  ***************************************************************************
333  */
334 void write_cifs_stat(int curr, unsigned long long itv, int fctr,
335                      struct io_cifs *clist, struct cifs_st *ioni,
336                      struct cifs_st *ionj)
337 {
338         double rbytes, wbytes;
339
340         if (!DISPLAY_PRETTY(flags)) {
341                 cprintf_in(IS_STR, "%-22s", clist->name, 0);
342         }
343
344         /*       rB/s   wB/s   fo/s   fc/s   fd/s*/
345         rbytes = S_VALUE(ionj->rd_bytes, ioni->rd_bytes, itv);
346         wbytes = S_VALUE(ionj->wr_bytes, ioni->wr_bytes, itv);
347         if (!DISPLAY_UNIT(flags)) {
348                 rbytes /= fctr;
349                 wbytes /= fctr;
350         }
351         cprintf_f(DISPLAY_UNIT(flags) ? UNIT_BYTE : NO_UNIT, 2, 12, 2,
352                   rbytes, wbytes);
353         cprintf_f(NO_UNIT, 2, 9, 2,
354                   S_VALUE(ionj->rd_ops, ioni->rd_ops, itv),
355                   S_VALUE(ionj->wr_ops, ioni->wr_ops, itv));
356         cprintf_f(NO_UNIT, 3, 12, 2,
357                   S_VALUE(ionj->fopens, ioni->fopens, itv),
358                   S_VALUE(ionj->fcloses, ioni->fcloses, itv),
359                   S_VALUE(ionj->fdeletes, ioni->fdeletes, itv));
360         if (DISPLAY_PRETTY(flags)) {
361                 cprintf_in(IS_STR, " %s", clist->name, 0);
362         }
363         printf("\n");
364 }
365
366 /*
367  ***************************************************************************
368  * Print everything now (stats and uptime).
369  *
370  * IN:
371  * @curr        Index in array for current sample statistics.
372  * @rectime     Current date and time.
373  ***************************************************************************
374  */
375 void write_stats(int curr, struct tm *rectime)
376 {
377         int fctr = 1;
378         unsigned long long itv;
379         struct io_cifs *clist;
380         struct cifs_st *ioni, *ionj;
381
382         /* Test stdout */
383         TEST_STDOUT(STDOUT_FILENO);
384
385         /* Print time stamp */
386         if (DISPLAY_TIMESTAMP(flags)) {
387                 if (DISPLAY_ISO(flags)) {
388                         strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
389                 }
390                 else {
391                         strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
392                 }
393                 printf("%s\n", timestamp);
394 #ifdef DEBUG
395                 if (DISPLAY_DEBUG(flags)) {
396                         fprintf(stderr, "%s\n", timestamp);
397                 }
398 #endif
399         }
400
401         /* Interval of time, reduced to one processor */
402         itv = get_interval(uptime_cs[!curr], uptime_cs[curr]);
403
404         /* Display CIFS stats header */
405         write_cifs_stat_header(&fctr);
406
407         for (clist = cifs_list; clist != NULL; clist = clist->next) {
408
409                 if (!clist->exist)
410                         /* Current cifs non existent */
411                         continue;
412
413                 ioni = clist->cifs_stats[curr];
414                 ionj = clist->cifs_stats[!curr];
415
416 #ifdef DEBUG
417                 if (DISPLAY_DEBUG(flags)) {
418                         /* Debug output */
419                         fprintf(stderr, "name=%s itv=%llu fctr=%d ioni{ rd_bytes=%llu "
420                                         "wr_bytes=%llu rd_ops=%llu wr_ops=%llu fopens=%llu "
421                                         "fcloses=%llu fdeletes=%llu}\n",
422                                 clist->name, itv, fctr,
423                                 ioni->rd_bytes, ioni->wr_bytes,
424                                 ioni->rd_ops,   ioni->wr_ops,
425                                 ioni->fopens,   ioni->fcloses,
426                                 ioni->fdeletes);
427                 }
428 #endif
429                 write_cifs_stat(curr, itv, fctr, clist, ioni, ionj);
430         }
431         printf("\n");
432 }
433
434 /*
435  ***************************************************************************
436  * Main loop: Read stats from the relevant sources and display them.
437  *
438  * IN:
439  * @count       Number of lines of stats to print.
440  * @rectime     Current date and time.
441  ***************************************************************************
442  */
443 void rw_io_stat_loop(long int count, struct tm *rectime)
444 {
445         int curr = 1;
446
447         /* Set a handler for SIGALRM */
448         memset(&alrm_act, 0, sizeof(alrm_act));
449         alrm_act.sa_handler = alarm_handler;
450         sigaction(SIGALRM, &alrm_act, NULL);
451         alarm(interval);
452
453         do {
454                 /* Every device is potentially nonexistent */
455                 set_cifs_nonexistent(cifs_list);
456
457                 /* Read system uptime in 1/100th of a second */
458                 read_uptime(&(uptime_cs[curr]));
459
460                 /* Read CIFS stats */
461                 read_cifs_stat(curr);
462
463                 /* Get time */
464                 get_xtime(rectime, 0, LOCAL_TIME);
465
466                 /* Print results */
467                 write_stats(curr, rectime);
468
469                 if (count > 0) {
470                         count--;
471                 }
472
473                 if (count) {
474                         curr ^= 1;
475                         __pause();
476                 }
477         }
478         while (count);
479 }
480
481 /*
482  ***************************************************************************
483  * Main entry to the cifsiostat program.
484  ***************************************************************************
485  */
486 int main(int argc, char **argv)
487 {
488         int it = 0;
489         int opt = 1;
490         int i;
491         long count = 1;
492         struct utsname header;
493         struct tm rectime;
494
495 #ifdef USE_NLS
496         /* Init National Language Support */
497         init_nls();
498 #endif
499
500         /* Init color strings */
501         init_colors();
502
503         /* Process args... */
504         while (opt < argc) {
505
506 #ifdef DEBUG
507                 if (!strcmp(argv[opt], "--debuginfo")) {
508                         flags |= I_D_DEBUG;
509                         opt++;
510                 } else
511 #endif
512
513                 if (!strcmp(argv[opt], "--human")) {
514                         flags |= I_D_UNIT;
515                         opt++;
516                 }
517
518                 else if (!strcmp(argv[opt], "--pretty")) {
519                         /* Display an easy-to-read CIFS report */
520                         flags |= I_D_PRETTY;
521                         opt++;
522                 }
523
524                 else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
525                         /* Get number of decimal places */
526                         dplaces_nr = atoi(argv[opt] + 6);
527                         if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
528                                 usage(argv[0]);
529                         }
530                         opt++;
531                 }
532
533                 else if (!strncmp(argv[opt], "-", 1)) {
534                         for (i = 1; *(argv[opt] + i); i++) {
535
536                                 switch (*(argv[opt] + i)) {
537
538                                 case 'h':
539                                         /* Option -h is equivalent to --pretty --human */
540                                         flags |= I_D_PRETTY + I_D_UNIT;
541                                         break;
542
543                                 case 'k':
544                                         if (DISPLAY_MEGABYTES(flags)) {
545                                                 usage(argv[0]);
546                                         }
547                                         /* Display stats in kB/s */
548                                         flags |= I_D_KILOBYTES;
549                                         break;
550
551                                 case 'm':
552                                         if (DISPLAY_KILOBYTES(flags)) {
553                                                 usage(argv[0]);
554                                         }
555                                         /* Display stats in MB/s */
556                                         flags |= I_D_MEGABYTES;
557                                         break;
558
559                                 case 't':
560                                         /* Display timestamp */
561                                         flags |= I_D_TIMESTAMP;
562                                         break;
563
564                                 case 'V':
565                                         /* Print version number and exit */
566                                         print_version();
567                                         break;
568
569                                 default:
570                                         usage(argv[0]);
571                                 }
572                         }
573                         opt++;
574                 }
575
576                 else if (!it) {
577                         interval = atol(argv[opt++]);
578                         if (interval < 0) {
579                                 usage(argv[0]);
580                         }
581                         count = -1;
582                         it = 1;
583                 }
584
585                 else if (it > 0) {
586                         count = atol(argv[opt++]);
587                         if ((count < 1) || !interval) {
588                                 usage(argv[0]);
589                         }
590                         it = -1;
591                 }
592                 else {
593                         usage(argv[0]);
594                 }
595         }
596
597         if (!interval) {
598                 count = 1;
599         }
600
601         /* How many processors on this machine? */
602         cpu_nr = get_cpu_nr(~0, FALSE);
603
604         get_xtime(&rectime, 0, LOCAL_TIME);
605
606         /*
607          * Don't buffer data if redirected to a pipe.
608          * Note: With musl-c, the behavior of this function is undefined except
609          * when it is the first operation on the stream.
610          */
611         setbuf(stdout, NULL);
612
613         /* Get system name, release number and hostname */
614         __uname(&header);
615         if (print_gal_header(&rectime, header.sysname, header.release,
616                              header.nodename, header.machine, cpu_nr,
617                              PLAIN_OUTPUT)) {
618                 flags |= I_D_ISO;
619         }
620         printf("\n");
621
622         /* Main loop */
623         rw_io_stat_loop(count, &rectime);
624
625         return 0;
626 }