]> granicus.if.org Git - sysstat/blob - tapestat.c
Merge branch 'scop-grep-E'
[sysstat] / tapestat.c
1 /*
2  * tapestat: report tape statistics
3  * (C) 2015 Hewlett-Packard Development Company, L.P.
4  *
5  * Initial revision by Shane M. SEYMOUR (shane.seymour <at> hpe.com)
6  * Modified for sysstat by Sebastien GODARD (sysstat <at> orange.fr)
7  *
8  ***************************************************************************
9  * This program is free software; you can redistribute it and/or modify it *
10  * under the terms of the GNU General Public License as published  by  the *
11  * Free Software Foundation; either version 2 of the License, or (at  your *
12  * option) any later version.                                              *
13  *                                                                         *
14  * This program is distributed in the hope that it  will  be  useful,  but *
15  * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17  * for more details.                                                       *
18  *                                                                         *
19  * You should have received a copy of the GNU General Public License along *
20  * with this program; if not, write to the Free Software Foundation, Inc., *
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
22  ***************************************************************************
23  */
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <dirent.h>
31 #define __DO_NOT_DEFINE_COMPILE
32 #include <regex.h>
33 #include <inttypes.h>
34 #include <stdint.h>
35 #include <signal.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/utsname.h>
39
40 #ifdef HAVE_SYS_PARAM_H
41 #include <sys/param.h>
42 #undef HZ /* sys/param.h defines HZ but needed for MAXPATHLEN */
43 #endif
44 #ifndef MAXPATHLEN
45 #define MAXPATHLEN 256
46 #endif
47
48 #include "version.h"
49 #include "tapestat.h"
50 #include "rd_stats.h"
51 #include "count.h"
52
53 #ifdef USE_NLS
54 #include <locale.h>
55 #include <libintl.h>
56 #define _(string) gettext(string)
57 #else
58 #define _(string) (string)
59 #endif
60
61 #ifdef USE_SCCSID
62 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
63 char *sccsid(void) { return (SCCSID); }
64 #endif
65
66 #ifdef TEST
67 void int_handler(int n) { return; }
68 #endif
69
70 int cpu_nr = 0;         /* Nb of processors on the machine */
71 int flags = 0;          /* Flag for common options and system state */
72
73 long interval = 0;
74 char timestamp[TIMESTAMP_LEN];
75
76 struct sigaction alrm_act;
77
78 /* Number of decimal places */
79 int dplaces_nr = -1;
80
81 /*
82  * For tape stats - it would be extremely rare for there to be a very large
83  * number of tape drives attached to a system. I wouldn't expect to see more
84  * than 20-30 in a very large configuration and discontinguous ones should
85  * be even more rare. Because of this we keep the old and new data in a
86  * simple data structure with the tape index being the number after the tape
87  * drive, st0 at index 0, etc.
88  */
89 int max_tape_drives = 0;
90 struct tape_stats *tape_new_stats = { NULL };
91 struct tape_stats *tape_old_stats = { NULL };
92 regex_t tape_reg;
93
94 /*
95  ***************************************************************************
96  * Print usage and exit.
97  *
98  * IN:
99  * @progname    Name of sysstat command.
100  ***************************************************************************
101  */
102 void usage(char *progname)
103 {
104         fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
105                 progname);
106         fprintf(stderr, _("Options are:\n"
107                           "[ --human ] [ -k | -m ] [ -t ] [ -V ] [ -y ] [ -z ]\n"));
108         exit(1);
109 }
110
111 /*
112  ***************************************************************************
113  * SIGALRM signal handler. No need to reset the handler here.
114  *
115  * IN:
116  * @sig Signal number.
117  ***************************************************************************
118  */
119 void alarm_handler(int sig)
120 {
121         alarm(interval);
122 }
123
124 /*
125  ***************************************************************************
126  * Initialization.
127  ***************************************************************************
128  */
129 void tape_initialise(void)
130 {
131         /* How many processors on this machine? */
132         cpu_nr = get_cpu_nr(~0, FALSE);
133
134         /* Compile regular expression for tape names */
135         if (regcomp(&tape_reg, "^st[0-9]+$", REG_EXTENDED) != 0) {
136                 exit(1);
137         }
138 }
139
140 /*
141  ***************************************************************************
142  * Free structures.
143  ***************************************************************************
144  */
145 void tape_uninitialise(void)
146 {
147         regfree(&tape_reg);
148         if (tape_old_stats != NULL) {
149                 free(tape_old_stats);
150         }
151         if (tape_new_stats != NULL) {
152                 free(tape_new_stats);
153         }
154 }
155
156 /*
157  ***************************************************************************
158  * Get maximum number of tapes in the system.
159  *
160  * RETURNS:
161  * Number of tapes found.
162  ***************************************************************************
163  */
164 int get_max_tape_drives(void)
165 {
166         DIR *dir;
167         struct dirent *entry;
168         int new_max_tape_drives, tmp, num_stats_dir = 0;
169         regmatch_t match;
170         char stats_dir[MAXPATHLEN + 1];
171         struct stat stat_buf;
172
173         new_max_tape_drives = max_tape_drives;
174
175         /* Open sysfs tree */
176         dir = opendir(SYSFS_CLASS_TAPE_DIR);
177         if (dir == NULL)
178                 return 0;
179
180         while ((entry = readdir(dir)) != NULL) {
181                 if (regexec(&tape_reg, &entry->d_name[0], 1, &match, 0) == 0) {
182                         /* d_name[2] to skip the st at the front */
183                         tmp = atoi(&entry->d_name[2]) + 1;
184                         if (tmp > new_max_tape_drives) {
185                                 new_max_tape_drives = tmp;
186                         }
187                 }
188                 snprintf(stats_dir, MAXPATHLEN, "%s/%s/%s",
189                         SYSFS_CLASS_TAPE_DIR, &entry->d_name[0], "stats");
190                 if (stat(stats_dir, &stat_buf) == 0) {
191                         if (S_ISDIR(stat_buf.st_mode)) {
192                                 num_stats_dir++;
193                         }
194                 }
195         }
196         closedir(dir);
197
198         /* If there are no stats directories make the new number of tape drives 0 */
199         if (num_stats_dir == 0) {
200                 new_max_tape_drives = 0;
201         }
202
203         return new_max_tape_drives;
204 }
205
206 /*
207  ***************************************************************************
208  * Check if new tapes have been added and reallocate structures accordingly.
209  ***************************************************************************
210  */
211 void tape_check_tapes_and_realloc(void)
212 {
213         int new_max_tape_drives, i;
214
215         /* Count again number of tapes */
216         new_max_tape_drives = get_max_tape_drives();
217
218         if (new_max_tape_drives > max_tape_drives && new_max_tape_drives > 0) {
219                 /* New tapes found: Realloc structures */
220                 struct tape_stats *tape_old_stats_t = (struct tape_stats *)
221                         realloc(tape_old_stats, sizeof(struct tape_stats) * new_max_tape_drives);
222                 struct tape_stats *tape_new_stats_t = (struct tape_stats *)
223                         realloc(tape_new_stats, sizeof(struct tape_stats) * new_max_tape_drives);
224                 if ((tape_old_stats_t == NULL) || (tape_new_stats_t == NULL)) {
225                         if (tape_old_stats_t != NULL) {
226                                 free(tape_old_stats_t);
227                                 tape_old_stats_t = NULL;
228                         } else {
229                                 free(tape_old_stats);
230                                 tape_old_stats = NULL;
231                         }
232                         if (tape_new_stats_t != NULL) {
233                                 free(tape_new_stats_t);
234                                 tape_new_stats_t = NULL;
235                         } else {
236                                 free(tape_new_stats);
237                                 tape_new_stats = NULL;
238                         }
239
240                         perror("realloc");
241                         exit(4);
242                 }
243
244                 tape_old_stats = tape_old_stats_t;
245                 tape_new_stats = tape_new_stats_t;
246
247                 for (i = max_tape_drives; i < new_max_tape_drives; i++) {
248                         tape_old_stats[i].valid = TAPE_STATS_INVALID;
249                         tape_new_stats[i].valid = TAPE_STATS_INVALID;
250                 }
251                 max_tape_drives = new_max_tape_drives;
252         }
253 }
254
255 /*
256  ***************************************************************************
257  * Collect initial statistics for all existing tapes in the system.
258  * This function should be called only once.
259  ***************************************************************************
260  */
261 void tape_gather_initial_stats(void)
262 {
263         int new_max_tape_drives, i;
264         FILE *fp;
265         char filename[MAXPATHLEN + 1];
266
267         /* Get number of tapes in the system */
268         new_max_tape_drives = get_max_tape_drives();
269
270         if (new_max_tape_drives == 0) {
271                 /* No tapes found */
272                 fprintf(stderr, _("No tape drives with statistics found\n"));
273                 exit(1);
274         }
275         else {
276                 /* Allocate structures */
277                 if (tape_old_stats == NULL) {
278                         tape_old_stats = (struct tape_stats *)
279                                 malloc(sizeof(struct tape_stats) * new_max_tape_drives);
280                         tape_new_stats = (struct tape_stats *)
281                                 malloc(sizeof(struct tape_stats) * new_max_tape_drives);
282                         for (i = 0; i < new_max_tape_drives; i++) {
283                                 tape_old_stats[i].valid = TAPE_STATS_INVALID;
284                                 tape_new_stats[i].valid = TAPE_STATS_INVALID;
285                         }
286                         max_tape_drives = new_max_tape_drives;
287                 } else
288                         /* This should only be called once */
289                         return;
290         }
291
292         /* Read stats for each tape */
293         for (i = 0; i < max_tape_drives; i++) {
294                 /*
295                  * Everything starts out valid but failing to open
296                  * a file gets the tape drive marked invalid.
297                  */
298                 tape_new_stats[i].valid = TAPE_STATS_VALID;
299                 tape_old_stats[i].valid = TAPE_STATS_VALID;
300
301                 __gettimeofday(&tape_old_stats[i].tv, NULL);
302
303                 tape_new_stats[i].tv.tv_sec = tape_old_stats[i].tv.tv_sec;
304                 tape_new_stats[i].tv.tv_usec = tape_old_stats[i].tv.tv_usec;
305
306                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_ns", read_time)
307                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_ns", write_time)
308                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "io_ns", other_time)
309                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_byte_cnt", read_bytes)
310                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_byte_cnt", write_bytes)
311                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_cnt", read_count)
312                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_cnt", write_count)
313                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "other_cnt", other_count)
314                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "resid_cnt", resid_count)
315
316                 tape_old_stats[i].read_time = 0;
317                 tape_old_stats[i].write_time = 0;
318                 tape_old_stats[i].other_time = 0;
319                 tape_old_stats[i].read_bytes = 0;
320                 tape_old_stats[i].write_bytes = 0;
321                 tape_old_stats[i].read_count = 0;
322                 tape_old_stats[i].write_count = 0;
323                 tape_old_stats[i].other_count = 0;
324                 tape_old_stats[i].resid_count = 0;
325         }
326 }
327
328 /*
329  ***************************************************************************
330  * Collect a new sample of statistics for all existing tapes in the system.
331  ***************************************************************************
332  */
333 void tape_get_updated_stats(void)
334 {
335         int i;
336         FILE *fp;
337         char filename[MAXPATHLEN + 1] = { 0 };
338
339         /* Check tapes and realloc structures if  needed */
340         tape_check_tapes_and_realloc();
341
342         for (i = 0; i < max_tape_drives; i++) {
343                 /*
344                  * Everything starts out valid but failing
345                  * to open a file gets the tape drive marked invalid.
346                  */
347                 tape_new_stats[i].valid = TAPE_STATS_VALID;
348                 __gettimeofday(&tape_new_stats[i].tv, NULL);
349
350                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_ns", read_time)
351                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_ns", write_time)
352                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "io_ns", other_time)
353                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_byte_cnt", read_bytes)
354                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_byte_cnt", write_bytes)
355                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_cnt", read_count)
356                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_cnt", write_count)
357                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "other_cnt", other_count)
358                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "resid_cnt", resid_count)
359
360                 if ((tape_new_stats[i].read_time < tape_old_stats[i].read_time) ||
361                     (tape_new_stats[i].write_time < tape_old_stats[i].write_time) ||
362                     (tape_new_stats[i].other_time < tape_old_stats[i].other_time)) {
363                         tape_new_stats[i].valid = TAPE_STATS_INVALID;
364                 }
365         }
366 }
367
368 /*
369  ***************************************************************************
370  * Display tapes statistics headings.
371  ***************************************************************************
372  */
373 void tape_write_headings(void)
374 {
375         printf("Tape:     r/s     w/s   ");
376         if (DISPLAY_MEGABYTES(flags)) {
377                 printf("MB_read/s   MB_wrtn/s");
378         } else {
379                 printf("kB_read/s   kB_wrtn/s");
380         }
381         printf("  %%Rd  %%Wr  %%Oa    Rs/s    Ot/s\n");
382 }
383
384 /*
385  ***************************************************************************
386  * Calculate statistics for current tape.
387  *
388  * IN:
389  * @i           Index in array for current tape.
390  *
391  * OUT:
392  * @stats       Statistics for current tape.
393  ***************************************************************************
394  */
395 void tape_calc_one_stats(struct calc_stats *stats, int i)
396 {
397         uint64_t duration;
398         double temp;
399         FILE *fp;
400
401         /* Duration in ms done in ms to prevent rounding issues with using seconds */
402         duration = (tape_new_stats[i].tv.tv_sec -
403                 tape_old_stats[i].tv.tv_sec) * 1000;
404         duration -= tape_old_stats[i].tv.tv_usec / 1000;
405         duration += tape_new_stats[i].tv.tv_usec / 1000;
406
407         /* If duration is zero we need to calculate the ms since boot time */
408         if (duration == 0) {
409                 fp = fopen(UPTIME, "r");
410
411                 /*
412                  * Get uptime from /proc/uptime and if we can't then just set duration to
413                  * be 0 - it will mean that we don't calculate stats.
414                  */
415                 if (fp == NULL) {
416                         duration = 0;
417                 } else {
418                         if (fscanf(fp, "%lf", &temp) != 1) {
419                                 temp = 0;
420                         }
421                         duration = (uint64_t) (temp * 1000);
422                         fclose(fp);
423                 }
424         }
425
426         /* The second value passed into the macro is the thing being calculated */
427         CALC_STAT_CNT(read_count, reads_per_second)
428         CALC_STAT_CNT(write_count, writes_per_second)
429         CALC_STAT_CNT(other_count, other_per_second)
430         CALC_STAT_KB(read_bytes, kbytes_read_per_second)
431         CALC_STAT_KB(write_bytes, kbytes_written_per_second)
432         CALC_STAT_PCT(read_time, read_pct_wait)
433         CALC_STAT_PCT(write_time, write_pct_wait)
434         CALC_STAT_PCT(other_time, all_pct_wait)
435         CALC_STAT_CNT(resid_count, resids_per_second)
436 }
437
438 /*
439  ***************************************************************************
440  * Display statistics for current tape.
441  *
442  * IN:
443  * @tape        Statistics for current tape.
444  * @i           Index in array for current tape.
445  ***************************************************************************
446  */
447 void tape_write_stats(struct calc_stats *tape, int i)
448 {
449         char buffer[32];
450         uint64_t divisor = 1;
451
452         if (DISPLAY_MEGABYTES(flags))
453                 divisor = 1024;
454
455         sprintf(buffer, "st%i        ", i);
456         buffer[5] = 0;
457         cprintf_in(IS_STR, "%s", buffer, 0);
458         cprintf_u64(NO_UNIT, 2, 7,
459                     tape->reads_per_second,
460                     tape->writes_per_second);
461         cprintf_u64(DISPLAY_UNIT(flags) ? UNIT_KILOBYTE : NO_UNIT, 2, 11,
462                     DISPLAY_UNIT(flags) ? tape->kbytes_read_per_second
463                                         : tape->kbytes_read_per_second / divisor,
464                     DISPLAY_UNIT(flags) ? tape->kbytes_written_per_second
465                                         : tape->kbytes_written_per_second / divisor);
466         cprintf_pc(DISPLAY_UNIT(flags), 3, 4, 0,
467                    (double) tape->read_pct_wait,
468                    (double) tape->write_pct_wait,
469                    (double) tape->all_pct_wait);
470         cprintf_u64(NO_UNIT, 2, 7,
471                     tape->resids_per_second,
472                     tape->other_per_second);
473         printf("\n");
474 }
475
476 /*
477  ***************************************************************************
478  * Print everything now (stats and uptime).
479  *
480  * IN:
481  * @rectime     Current date and time.
482  ***************************************************************************
483  */
484 void write_stats(struct tm *rectime)
485 {
486         int i;
487         struct calc_stats tape;
488         struct tape_stats *tmp;
489
490         /* Test stdout */
491         TEST_STDOUT(STDOUT_FILENO);
492
493         /* Print time stamp */
494         if (DISPLAY_TIMESTAMP(flags)) {
495                 if (DISPLAY_ISO(flags)) {
496                         strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
497                 }
498                 else {
499                         strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
500                 }
501                 printf("%s\n", timestamp);
502         }
503
504         /* Print the headings */
505         tape_write_headings();
506
507         /*
508          * If either new or old is invalid or the I/Os per second is 0 and
509          * zero omit is true then we print nothing.
510          */
511         if (max_tape_drives > 0) {
512
513                 for (i = 0; i < max_tape_drives; i++) {
514                         if ((tape_new_stats[i].valid == TAPE_STATS_VALID) &&
515                                 (tape_old_stats[i].valid == TAPE_STATS_VALID)) {
516                                 tape_calc_one_stats(&tape, i);
517                                 if (!(DISPLAY_ZERO_OMIT(flags)
518                                         && (tape.other_per_second == 0)
519                                         && (tape.reads_per_second == 0)
520                                         && (tape.writes_per_second == 0)
521                                         && (tape.kbytes_read_per_second == 0)
522                                         && (tape.kbytes_written_per_second == 0)
523                                         && (tape.read_pct_wait == 0)
524                                         && (tape.write_pct_wait == 0)
525                                         && (tape.all_pct_wait == 0)
526                                         && (tape.resids_per_second == 0))) {
527                                         tape_write_stats(&tape, i);
528                                 }
529                         }
530                 }
531                 /*
532                  * Swap new and old so next time we compare against the new old stats.
533                  * If a new tape drive appears it won't appear in the output until after
534                  * the second time we gather information about it.
535                  */
536                 tmp = tape_old_stats;
537                 tape_old_stats = tape_new_stats;
538                 tape_new_stats = tmp;
539         }
540         printf("\n");
541 }
542
543 /*
544  ***************************************************************************
545  * Main loop: Read tape stats from the relevant sources and display them.
546  *
547  * IN:
548  * @count       Number of lines of stats to print.
549  * @rectime     Current date and time.
550  ***************************************************************************
551  */
552 void rw_tape_stat_loop(long int count, struct tm *rectime)
553 {
554         struct tape_stats *tmp;
555         int skip = 0;
556
557         /* Should we skip first report? */
558         if (DISPLAY_OMIT_SINCE_BOOT(flags) && interval > 0) {
559                 skip = 1;
560         }
561
562         do {
563
564                 if (tape_new_stats == NULL) {
565                         tape_gather_initial_stats();
566                 } else {
567                         tape_get_updated_stats();
568                 }
569
570                 /* Get time */
571                 get_localtime(rectime, 0);
572
573                 /* Check whether we should skip first report */
574                 if (!skip) {
575                         /* Print results */
576                         write_stats(rectime);
577
578                         if (count > 0) {
579                                 count--;
580                         }
581                 }
582                 else {
583                         skip = 0;
584                         tmp = tape_old_stats;
585                         tape_old_stats = tape_new_stats;
586                         tape_new_stats = tmp;
587                 }
588
589                 if (count) {
590                         __pause();
591                 }
592         }
593         while (count);
594 }
595
596 /*
597  ***************************************************************************
598  * Main entry to the tapestat program.
599  ***************************************************************************
600  */
601 int main(int argc, char **argv)
602 {
603         int it = 0;
604         int opt = 1;
605         int i;
606         long count = 1;
607         struct utsname header;
608         struct tm rectime;
609
610 #ifdef USE_NLS
611         /* Init National Language Support */
612         init_nls();
613 #endif
614
615         /* Init color strings */
616         init_colors();
617
618         /* Process args... */
619         while (opt < argc) {
620
621                 if (!strcmp(argv[opt], "--human")) {
622                         flags |= T_D_UNIT;
623                         opt++;
624                 }
625
626                 else if (!strncmp(argv[opt], "-", 1)) {
627                         for (i = 1; *(argv[opt] + i); i++) {
628
629                                 switch (*(argv[opt] + i)) {
630
631                                 case 'k':
632                                         if (DISPLAY_MEGABYTES(flags)) {
633                                                 usage(argv[0]);
634                                         }
635                                         /* Display stats in kB/s */
636                                         flags |= T_D_KILOBYTES;
637                                         break;
638
639                                 case 'm':
640                                         if (DISPLAY_KILOBYTES(flags)) {
641                                                 usage(argv[0]);
642                                         }
643                                         /* Display stats in MB/s */
644                                         flags |= T_D_MEGABYTES;
645                                         break;
646
647                                 case 't':
648                                         /* Display timestamp */
649                                         flags |= T_D_TIMESTAMP;
650                                         break;
651
652                                 case 'y':
653                                         /* Don't display stats since system restart */
654                                         flags |= T_D_OMIT_SINCE_BOOT;
655                                         break;
656
657                                 case 'z':
658                                         /* Omit output for devices with no activity */
659                                         flags |= T_D_ZERO_OMIT;
660                                         break;
661
662                                 case 'V':
663                                         /* Print version number and exit */
664                                         print_version();
665                                         break;
666
667                                 default:
668                                         usage(argv[0]);
669                                 }
670                         }
671                         opt++;
672                 }
673
674                 else if (!it) {
675                         interval = atol(argv[opt++]);
676                         if (interval < 0) {
677                                 usage(argv[0]);
678                         }
679                         count = -1;
680                         it = 1;
681                 }
682
683                 else if (it > 0) {
684                         count = atol(argv[opt++]);
685                         if ((count < 1) || !interval) {
686                                 usage(argv[0]);
687                         }
688                         it = -1;
689                 }
690                 else {
691                         usage(argv[0]);
692                 }
693         }
694
695         if (!interval) {
696                 count = 1;
697         }
698
699         tape_initialise();
700
701         get_localtime(&rectime, 0);
702
703         /*
704          * Don't buffer data if redirected to a pipe.
705          * Note: With musl-c, the behavior of this function is undefined except
706          * when it is the first operation on the stream.
707          */
708         setbuf(stdout, NULL);
709
710         /* Get system name, release number and hostname */
711         __uname(&header);
712         if (print_gal_header(&rectime, header.sysname, header.release,
713                              header.nodename, header.machine, cpu_nr,
714                              PLAIN_OUTPUT)) {
715                 flags |= T_D_ISO;
716         }
717         printf("\n");
718
719         /* Set a handler for SIGALRM */
720         memset(&alrm_act, 0, sizeof(alrm_act));
721         alrm_act.sa_handler = alarm_handler;
722         sigaction(SIGALRM, &alrm_act, NULL);
723         alarm(interval);
724
725         /* Main loop */
726         rw_tape_stat_loop(count, &rectime);
727
728         /* Free structures */
729         tape_uninitialise();
730
731         return 0;
732 }