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>
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. *
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 *
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 ***************************************************************************
28 #include <sys/utsname.h>
32 #include "cifsiostat.h"
39 #define _(string) gettext(string)
41 #define _(string) (string)
45 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
46 char *sccsid(void) { return (SCCSID); }
50 void int_handler(int n) { return; }
53 unsigned long long uptime_cs[2] = {0, 0};
54 struct io_cifs *cifs_list = NULL;
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 */
61 char timestamp[TIMESTAMP_LEN];
63 struct sigaction alrm_act;
66 ***************************************************************************
67 * Print usage and exit.
70 * @progname Name of sysstat command.
71 ***************************************************************************
73 void usage(char *progname)
75 fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
79 fprintf(stderr, _("Options are:\n"
80 "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ --pretty ]\n"
81 "[ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n"));
83 fprintf(stderr, _("Options are:\n"
84 "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ --pretty ]\n"
85 "[ -h ] [ -k | -m ] [ -t ] [ -V ]\n"));
91 ***************************************************************************
92 * SIGALRM signal handler.
96 ***************************************************************************
98 void alarm_handler(int sig)
104 ***************************************************************************
105 * Set every cifs entry to nonexistent status.
108 * @clist Pointer on the start of the linked list.
109 ***************************************************************************
111 void set_cifs_nonexistent(struct io_cifs *clist)
113 while (clist != NULL) {
114 clist->exist = FALSE;
120 ***************************************************************************
121 * Check if a cifs filesystem is present in the list, and add it if requested.
124 * @clist Address of pointer on the start of the linked list.
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 ***************************************************************************
134 struct io_cifs *add_list_cifs(struct io_cifs **clist, char *name)
136 struct io_cifs *c, *cs;
139 if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN)
140 /* cifs name is too long */
143 while (*clist != NULL) {
146 if ((i = strcmp(c->name, name)) == 0) {
147 /* cifs found in list */
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...
166 /* Add cifs to the list */
167 if ((*clist = (struct io_cifs *) malloc(IO_CIFS_SIZE)) == NULL) {
171 memset(*clist, 0, IO_CIFS_SIZE);
174 for (i = 0; i < 2; i++) {
175 if ((c->cifs_stats[i] = (struct cifs_st *) malloc(CIFS_ST_SIZE)) == NULL) {
179 memset(c->cifs_stats[i], 0, CIFS_ST_SIZE);
181 strncpy(c->name, name, sizeof(c->name));
182 c->name[sizeof(c->name) - 1] = '\0';
190 ***************************************************************************
191 * Read CIFS-mount directories stats from /proc/fs/cifs/Stats.
194 * @curr Index in array for current sample statistics.
195 ***************************************************************************
197 void read_cifs_stat(int curr)
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;
210 if ((fp = fopen(CIFSSTATS, "r")) == NULL)
213 sprintf(aux, "%%*d) %%%ds",
214 MAX_NAME_LEN < 200 ? MAX_NAME_LEN - 1 : 200);
216 memset(&scifs, 0, CIFS_ST_SIZE);
218 while (fgets(line, sizeof(line), fp) != NULL) {
220 /* Read CIFS directory name */
221 if (isdigit((unsigned char) line[0]) && sscanf(line, aux , name_tmp) == 1) {
223 scifs.fopens = all_open;
224 ci = add_list_cifs(&cifs_list, cifs_name);
226 *ci->cifs_stats[curr] = scifs;
233 strncpy(cifs_name, name_tmp, sizeof(cifs_name));
234 cifs_name[sizeof(cifs_name) - 1] = '\0';
235 memset(&scifs, 0, CIFS_ST_SIZE);
238 if (!strncmp(line, "Reads:", 6)) {
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.
244 sscanf(line, "Reads: %llu Bytes: %llu", &scifs.rd_ops, &scifs.rd_bytes);
246 else if (!strncmp(line, "Bytes read:", 11)) {
247 sscanf(line, "Bytes read: %llu Bytes written: %llu",
248 &scifs.rd_bytes, &scifs.wr_bytes);
250 else if (!strncmp(line, "Writes:", 7)) {
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.
256 sscanf(line, "Writes: %llu Bytes: %llu", &scifs.wr_ops, &scifs.wr_bytes);
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;
263 else if (!strncmp(line, "Posix Opens:", 12)) {
264 sscanf(line, "Posix Opens: %llu", &aux_open);
265 all_open += aux_open;
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;
272 else if (!strncmp(line, "Closes:", 7)) {
273 sscanf(line, "Closes: %llu", &scifs.fcloses);
279 scifs.fopens = all_open;
280 ci = add_list_cifs(&cifs_list, cifs_name);
282 *ci->cifs_stats[curr] = scifs;
290 ***************************************************************************
291 * Display CIFS stats header.
294 * @fctr Conversion factor.
295 ***************************************************************************
297 void write_cifs_stat_header(int *fctr)
299 if (!DISPLAY_PRETTY(flags)) {
300 printf("Filesystem ");
302 if (DISPLAY_KILOBYTES(flags)) {
303 printf(" rkB/s wkB/s");
306 else if (DISPLAY_MEGABYTES(flags)) {
307 printf(" rMB/s wMB/s");
311 printf(" rB/s wB/s");
314 printf(" rops/s wops/s fo/s fc/s fd/s");
315 if (DISPLAY_PRETTY(flags)) {
316 printf(" Filesystem");
322 ***************************************************************************
323 * Write CIFS stats read from /proc/fs/cifs/Stats.
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 ***************************************************************************
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)
338 double rbytes, wbytes;
340 if (!DISPLAY_PRETTY(flags)) {
341 cprintf_in(IS_STR, "%-22s", clist->name, 0);
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)) {
351 cprintf_f(DISPLAY_UNIT(flags) ? UNIT_BYTE : NO_UNIT, 2, 12, 2,
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);
367 ***************************************************************************
368 * Print everything now (stats and uptime).
371 * @curr Index in array for current sample statistics.
372 * @rectime Current date and time.
373 ***************************************************************************
375 void write_stats(int curr, struct tm *rectime)
378 unsigned long long itv;
379 struct io_cifs *clist;
380 struct cifs_st *ioni, *ionj;
383 TEST_STDOUT(STDOUT_FILENO);
385 /* Print time stamp */
386 if (DISPLAY_TIMESTAMP(flags)) {
387 if (DISPLAY_ISO(flags)) {
388 strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
391 strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
393 printf("%s\n", timestamp);
395 if (DISPLAY_DEBUG(flags)) {
396 fprintf(stderr, "%s\n", timestamp);
401 /* Interval of time, reduced to one processor */
402 itv = get_interval(uptime_cs[!curr], uptime_cs[curr]);
404 /* Display CIFS stats header */
405 write_cifs_stat_header(&fctr);
407 for (clist = cifs_list; clist != NULL; clist = clist->next) {
410 /* Current cifs non existent */
413 ioni = clist->cifs_stats[curr];
414 ionj = clist->cifs_stats[!curr];
417 if (DISPLAY_DEBUG(flags)) {
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,
429 write_cifs_stat(curr, itv, fctr, clist, ioni, ionj);
435 ***************************************************************************
436 * Main loop: Read stats from the relevant sources and display them.
439 * @count Number of lines of stats to print.
440 * @rectime Current date and time.
441 ***************************************************************************
443 void rw_io_stat_loop(long int count, struct tm *rectime)
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);
454 /* Every device is potentially nonexistent */
455 set_cifs_nonexistent(cifs_list);
457 /* Read system uptime in 1/100th of a second */
458 read_uptime(&(uptime_cs[curr]));
460 /* Read CIFS stats */
461 read_cifs_stat(curr);
464 get_xtime(rectime, 0, LOCAL_TIME);
467 write_stats(curr, rectime);
482 ***************************************************************************
483 * Main entry to the cifsiostat program.
484 ***************************************************************************
486 int main(int argc, char **argv)
492 struct utsname header;
496 /* Init National Language Support */
500 /* Init color strings */
503 /* Process args... */
507 if (!strcmp(argv[opt], "--debuginfo")) {
513 if (!strcmp(argv[opt], "--human")) {
518 else if (!strcmp(argv[opt], "--pretty")) {
519 /* Display an easy-to-read CIFS report */
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)) {
533 else if (!strncmp(argv[opt], "-", 1)) {
534 for (i = 1; *(argv[opt] + i); i++) {
536 switch (*(argv[opt] + i)) {
539 /* Option -h is equivalent to --pretty --human */
540 flags |= I_D_PRETTY + I_D_UNIT;
544 if (DISPLAY_MEGABYTES(flags)) {
547 /* Display stats in kB/s */
548 flags |= I_D_KILOBYTES;
552 if (DISPLAY_KILOBYTES(flags)) {
555 /* Display stats in MB/s */
556 flags |= I_D_MEGABYTES;
560 /* Display timestamp */
561 flags |= I_D_TIMESTAMP;
565 /* Print version number and exit */
577 interval = atol(argv[opt++]);
586 count = atol(argv[opt++]);
587 if ((count < 1) || !interval) {
601 /* How many processors on this machine? */
602 cpu_nr = get_cpu_nr(~0, FALSE);
604 get_xtime(&rectime, 0, LOCAL_TIME);
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.
611 setbuf(stdout, NULL);
613 /* Get system name, release number and hostname */
615 if (print_gal_header(&rectime, header.sysname, header.release,
616 header.nodename, header.machine, cpu_nr,
623 rw_io_stat_loop(count, &rectime);