]> granicus.if.org Git - sysstat/blob - tapestat.c
Update non regression tests
[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
182                 if (regexec(&tape_reg, &entry->d_name[0], 1, &match, 0) == 0) {
183                         /* d_name[2] to skip the st at the front */
184                         tmp = atoi(&entry->d_name[2]) + 1;
185                         if (tmp > new_max_tape_drives) {
186                                 new_max_tape_drives = tmp;
187                         }
188                 }
189                 snprintf(stats_dir, MAXPATHLEN, "%s/%s/%s",
190                         SYSFS_CLASS_TAPE_DIR, &entry->d_name[0], "stats");
191                 if (stat(stats_dir, &stat_buf) == 0) {
192                         if (S_ISDIR(stat_buf.st_mode)) {
193                                 num_stats_dir++;
194                         }
195                 }
196         }
197         closedir(dir);
198
199         /* If there are no stats directories make the new number of tape drives 0 */
200         if (num_stats_dir == 0) {
201                 new_max_tape_drives = 0;
202         }
203
204         return new_max_tape_drives;
205 }
206
207 /*
208  ***************************************************************************
209  * Check if new tapes have been added and reallocate structures accordingly.
210  ***************************************************************************
211  */
212 void tape_check_tapes_and_realloc(void)
213 {
214         int new_max_tape_drives;
215
216         /* Count again number of tapes */
217         new_max_tape_drives = get_max_tape_drives();
218
219         if (new_max_tape_drives > max_tape_drives && new_max_tape_drives > 0) {
220                 int i;
221
222                 /* New tapes found: Realloc structures */
223                 struct tape_stats *tape_old_stats_t = (struct tape_stats *)
224                         realloc(tape_old_stats, sizeof(struct tape_stats) * new_max_tape_drives);
225                 struct tape_stats *tape_new_stats_t = (struct tape_stats *)
226                         realloc(tape_new_stats, sizeof(struct tape_stats) * new_max_tape_drives);
227                 if ((tape_old_stats_t == NULL) || (tape_new_stats_t == NULL)) {
228                         if (tape_old_stats_t != NULL) {
229                                 free(tape_old_stats_t);
230                                 tape_old_stats_t = NULL;
231                         } else {
232                                 free(tape_old_stats);
233                                 tape_old_stats = NULL;
234                         }
235                         if (tape_new_stats_t != NULL) {
236                                 free(tape_new_stats_t);
237                                 tape_new_stats_t = NULL;
238                         } else {
239                                 free(tape_new_stats);
240                                 tape_new_stats = NULL;
241                         }
242
243                         perror("realloc");
244                         exit(4);
245                 }
246
247                 tape_old_stats = tape_old_stats_t;
248                 tape_new_stats = tape_new_stats_t;
249
250                 for (i = max_tape_drives; i < new_max_tape_drives; i++) {
251                         tape_old_stats[i].valid = TAPE_STATS_INVALID;
252                         tape_new_stats[i].valid = TAPE_STATS_INVALID;
253                 }
254                 max_tape_drives = new_max_tape_drives;
255         }
256 }
257
258 /*
259  ***************************************************************************
260  * Collect initial statistics for all existing tapes in the system.
261  * This function should be called only once.
262  ***************************************************************************
263  */
264 void tape_gather_initial_stats(void)
265 {
266         int new_max_tape_drives, i;
267         FILE *fp;
268         char filename[MAXPATHLEN + 1];
269
270         /* Get number of tapes in the system */
271         new_max_tape_drives = get_max_tape_drives();
272
273         if (new_max_tape_drives == 0) {
274                 /* No tapes found */
275                 fprintf(stderr, _("No tape drives with statistics found\n"));
276                 exit(1);
277         }
278         else {
279                 /* Allocate structures */
280                 if (tape_old_stats == NULL) {
281                         tape_old_stats = (struct tape_stats *)
282                                 malloc(sizeof(struct tape_stats) * new_max_tape_drives);
283                         tape_new_stats = (struct tape_stats *)
284                                 malloc(sizeof(struct tape_stats) * new_max_tape_drives);
285                         for (i = 0; i < new_max_tape_drives; i++) {
286                                 tape_old_stats[i].valid = TAPE_STATS_INVALID;
287                                 tape_new_stats[i].valid = TAPE_STATS_INVALID;
288                         }
289                         max_tape_drives = new_max_tape_drives;
290                 } else
291                         /* This should only be called once */
292                         return;
293         }
294
295         /* Read stats for each tape */
296         for (i = 0; i < max_tape_drives; i++) {
297                 /*
298                  * Everything starts out valid but failing to open
299                  * a file gets the tape drive marked invalid.
300                  */
301                 tape_new_stats[i].valid = TAPE_STATS_VALID;
302                 tape_old_stats[i].valid = TAPE_STATS_VALID;
303
304                 __gettimeofday(&tape_old_stats[i].tv, NULL);
305
306                 tape_new_stats[i].tv.tv_sec = tape_old_stats[i].tv.tv_sec;
307                 tape_new_stats[i].tv.tv_usec = tape_old_stats[i].tv.tv_usec;
308
309                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_ns", read_time)
310                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_ns", write_time)
311                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "io_ns", other_time)
312                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_byte_cnt", read_bytes)
313                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_byte_cnt", write_bytes)
314                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_cnt", read_count)
315                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_cnt", write_count)
316                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "other_cnt", other_count)
317                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "resid_cnt", resid_count)
318
319                 tape_old_stats[i].read_time = 0;
320                 tape_old_stats[i].write_time = 0;
321                 tape_old_stats[i].other_time = 0;
322                 tape_old_stats[i].read_bytes = 0;
323                 tape_old_stats[i].write_bytes = 0;
324                 tape_old_stats[i].read_count = 0;
325                 tape_old_stats[i].write_count = 0;
326                 tape_old_stats[i].other_count = 0;
327                 tape_old_stats[i].resid_count = 0;
328         }
329 }
330
331 /*
332  ***************************************************************************
333  * Collect a new sample of statistics for all existing tapes in the system.
334  ***************************************************************************
335  */
336 void tape_get_updated_stats(void)
337 {
338         int i;
339         FILE *fp;
340         char filename[MAXPATHLEN + 1] = { 0 };
341
342         /* Check tapes and realloc structures if  needed */
343         tape_check_tapes_and_realloc();
344
345         for (i = 0; i < max_tape_drives; i++) {
346                 /*
347                  * Everything starts out valid but failing
348                  * to open a file gets the tape drive marked invalid.
349                  */
350                 tape_new_stats[i].valid = TAPE_STATS_VALID;
351                 __gettimeofday(&tape_new_stats[i].tv, NULL);
352
353                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_ns", read_time)
354                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_ns", write_time)
355                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "io_ns", other_time)
356                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_byte_cnt", read_bytes)
357                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_byte_cnt", write_bytes)
358                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_cnt", read_count)
359                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_cnt", write_count)
360                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "other_cnt", other_count)
361                 TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "resid_cnt", resid_count)
362
363                 if ((tape_new_stats[i].read_time < tape_old_stats[i].read_time) ||
364                     (tape_new_stats[i].write_time < tape_old_stats[i].write_time) ||
365                     (tape_new_stats[i].other_time < tape_old_stats[i].other_time)) {
366                         tape_new_stats[i].valid = TAPE_STATS_INVALID;
367                 }
368         }
369 }
370
371 /*
372  ***************************************************************************
373  * Display tapes statistics headings.
374  ***************************************************************************
375  */
376 void tape_write_headings(void)
377 {
378         printf("Tape:     r/s     w/s   ");
379         if (DISPLAY_MEGABYTES(flags)) {
380                 printf("MB_read/s   MB_wrtn/s");
381         } else {
382                 printf("kB_read/s   kB_wrtn/s");
383         }
384         printf("  %%Rd  %%Wr  %%Oa    Rs/s    Ot/s\n");
385 }
386
387 /*
388  ***************************************************************************
389  * Calculate statistics for current tape.
390  *
391  * IN:
392  * @i           Index in array for current tape.
393  *
394  * OUT:
395  * @stats       Statistics for current tape.
396  ***************************************************************************
397  */
398 void tape_calc_one_stats(struct calc_stats *stats, int i)
399 {
400         uint64_t duration;
401         double temp;
402         FILE *fp;
403
404         /* Duration in ms done in ms to prevent rounding issues with using seconds */
405         duration = (tape_new_stats[i].tv.tv_sec -
406                 tape_old_stats[i].tv.tv_sec) * 1000;
407         duration -= tape_old_stats[i].tv.tv_usec / 1000;
408         duration += tape_new_stats[i].tv.tv_usec / 1000;
409
410         /* If duration is zero we need to calculate the ms since boot time */
411         if (duration == 0) {
412                 fp = fopen(UPTIME, "r");
413
414                 /*
415                  * Get uptime from /proc/uptime and if we can't then just set duration to
416                  * be 0 - it will mean that we don't calculate stats.
417                  */
418                 if (fp == NULL) {
419                         duration = 0;
420                 } else {
421                         if (fscanf(fp, "%lf", &temp) != 1) {
422                                 temp = 0;
423                         }
424                         duration = (uint64_t) (temp * 1000);
425                         fclose(fp);
426                 }
427         }
428
429         /* The second value passed into the macro is the thing being calculated */
430         CALC_STAT_CNT(read_count, reads_per_second)
431         CALC_STAT_CNT(write_count, writes_per_second)
432         CALC_STAT_CNT(other_count, other_per_second)
433         CALC_STAT_KB(read_bytes, kbytes_read_per_second)
434         CALC_STAT_KB(write_bytes, kbytes_written_per_second)
435         CALC_STAT_PCT(read_time, read_pct_wait)
436         CALC_STAT_PCT(write_time, write_pct_wait)
437         CALC_STAT_PCT(other_time, all_pct_wait)
438         CALC_STAT_CNT(resid_count, resids_per_second)
439 }
440
441 /*
442  ***************************************************************************
443  * Display statistics for current tape.
444  *
445  * IN:
446  * @tape        Statistics for current tape.
447  * @i           Index in array for current tape.
448  ***************************************************************************
449  */
450 void tape_write_stats(struct calc_stats *tape, int i)
451 {
452         char buffer[32];
453         uint64_t divisor = 1;
454
455         if (DISPLAY_MEGABYTES(flags))
456                 divisor = 1024;
457
458         sprintf(buffer, "st%i        ", i);
459         buffer[5] = 0;
460         cprintf_in(IS_STR, "%s", buffer, 0);
461         cprintf_u64(NO_UNIT, 2, 7,
462                     tape->reads_per_second,
463                     tape->writes_per_second);
464         cprintf_u64(DISPLAY_UNIT(flags) ? UNIT_KILOBYTE : NO_UNIT, 2, 11,
465                     DISPLAY_UNIT(flags) ? tape->kbytes_read_per_second
466                                         : tape->kbytes_read_per_second / divisor,
467                     DISPLAY_UNIT(flags) ? tape->kbytes_written_per_second
468                                         : tape->kbytes_written_per_second / divisor);
469         cprintf_xpc(DISPLAY_UNIT(flags), XHIGH, 3, 4, 0,
470                    (double) tape->read_pct_wait,
471                    (double) tape->write_pct_wait,
472                    (double) tape->all_pct_wait);
473         cprintf_u64(NO_UNIT, 2, 7,
474                     tape->resids_per_second,
475                     tape->other_per_second);
476         printf("\n");
477 }
478
479 /*
480  ***************************************************************************
481  * Print everything now (stats and uptime).
482  *
483  * IN:
484  * @rectime     Current date and time.
485  ***************************************************************************
486  */
487 void write_stats(struct tm *rectime)
488 {
489         struct calc_stats tape;
490         struct tape_stats *tmp;
491
492         /* Test stdout */
493         TEST_STDOUT(STDOUT_FILENO);
494
495         /* Print time stamp */
496         if (DISPLAY_TIMESTAMP(flags)) {
497                 if (DISPLAY_ISO(flags)) {
498                         strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
499                 }
500                 else {
501                         strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
502                 }
503                 printf("%s\n", timestamp);
504         }
505
506         /* Print the headings */
507         tape_write_headings();
508
509         /*
510          * If either new or old is invalid or the I/Os per second is 0 and
511          * zero omit is true then we print nothing.
512          */
513         if (max_tape_drives > 0) {
514                 int i;
515
516                 for (i = 0; i < max_tape_drives; i++) {
517                         if ((tape_new_stats[i].valid == TAPE_STATS_VALID) &&
518                                 (tape_old_stats[i].valid == TAPE_STATS_VALID)) {
519                                 tape_calc_one_stats(&tape, i);
520                                 if (!(DISPLAY_ZERO_OMIT(flags)
521                                         && (tape.other_per_second == 0)
522                                         && (tape.reads_per_second == 0)
523                                         && (tape.writes_per_second == 0)
524                                         && (tape.kbytes_read_per_second == 0)
525                                         && (tape.kbytes_written_per_second == 0)
526                                         && (tape.read_pct_wait == 0)
527                                         && (tape.write_pct_wait == 0)
528                                         && (tape.all_pct_wait == 0)
529                                         && (tape.resids_per_second == 0))) {
530                                         tape_write_stats(&tape, i);
531                                 }
532                         }
533                 }
534                 /*
535                  * Swap new and old so next time we compare against the new old stats.
536                  * If a new tape drive appears it won't appear in the output until after
537                  * the second time we gather information about it.
538                  */
539                 tmp = tape_old_stats;
540                 tape_old_stats = tape_new_stats;
541                 tape_new_stats = tmp;
542         }
543         printf("\n");
544 }
545
546 /*
547  ***************************************************************************
548  * Main loop: Read tape stats from the relevant sources and display them.
549  *
550  * IN:
551  * @count       Number of lines of stats to print.
552  * @rectime     Current date and time.
553  ***************************************************************************
554  */
555 void rw_tape_stat_loop(long int count, struct tm *rectime)
556 {
557         struct tape_stats *tmp;
558         int skip = 0;
559
560         /* Should we skip first report? */
561         if (DISPLAY_OMIT_SINCE_BOOT(flags) && interval > 0) {
562                 skip = 1;
563         }
564
565         do {
566
567                 if (tape_new_stats == NULL) {
568                         tape_gather_initial_stats();
569                 } else {
570                         tape_get_updated_stats();
571                 }
572
573                 /* Get time */
574                 get_xtime(rectime, 0, LOCAL_TIME);
575
576                 /* Check whether we should skip first report */
577                 if (!skip) {
578                         /* Print results */
579                         write_stats(rectime);
580
581                         if (count > 0) {
582                                 count--;
583                         }
584                 }
585                 else {
586                         skip = 0;
587                         tmp = tape_old_stats;
588                         tape_old_stats = tape_new_stats;
589                         tape_new_stats = tmp;
590                 }
591
592                 if (count) {
593                         __pause();
594                 }
595         }
596         while (count);
597 }
598
599 /*
600  ***************************************************************************
601  * Main entry to the tapestat program.
602  ***************************************************************************
603  */
604 int main(int argc, char **argv)
605 {
606         int it = 0;
607         int opt = 1;
608         int i;
609         long count = 1;
610         struct utsname header;
611         struct tm rectime;
612
613 #ifdef USE_NLS
614         /* Init National Language Support */
615         init_nls();
616 #endif
617
618         /* Init color strings */
619         init_colors();
620
621         /* Process args... */
622         while (opt < argc) {
623
624                 if (!strcmp(argv[opt], "--human")) {
625                         flags |= T_D_UNIT;
626                         opt++;
627                 }
628
629                 else if (!strncmp(argv[opt], "-", 1)) {
630                         for (i = 1; *(argv[opt] + i); i++) {
631
632                                 switch (*(argv[opt] + i)) {
633
634                                 case 'k':
635                                         if (DISPLAY_MEGABYTES(flags)) {
636                                                 usage(argv[0]);
637                                         }
638                                         /* Display stats in kB/s */
639                                         flags |= T_D_KILOBYTES;
640                                         break;
641
642                                 case 'm':
643                                         if (DISPLAY_KILOBYTES(flags)) {
644                                                 usage(argv[0]);
645                                         }
646                                         /* Display stats in MB/s */
647                                         flags |= T_D_MEGABYTES;
648                                         break;
649
650                                 case 't':
651                                         /* Display timestamp */
652                                         flags |= T_D_TIMESTAMP;
653                                         break;
654
655                                 case 'y':
656                                         /* Don't display stats since system restart */
657                                         flags |= T_D_OMIT_SINCE_BOOT;
658                                         break;
659
660                                 case 'z':
661                                         /* Omit output for devices with no activity */
662                                         flags |= T_D_ZERO_OMIT;
663                                         break;
664
665                                 case 'V':
666                                         /* Print version number and exit */
667                                         print_version();
668                                         break;
669
670                                 default:
671                                         usage(argv[0]);
672                                 }
673                         }
674                         opt++;
675                 }
676
677                 else if (!it) {
678                         interval = atol(argv[opt++]);
679                         if (interval < 0) {
680                                 usage(argv[0]);
681                         }
682                         count = -1;
683                         it = 1;
684                 }
685
686                 else if (it > 0) {
687                         count = atol(argv[opt++]);
688                         if ((count < 1) || !interval) {
689                                 usage(argv[0]);
690                         }
691                         it = -1;
692                 }
693                 else {
694                         usage(argv[0]);
695                 }
696         }
697
698         if (!interval) {
699                 count = 1;
700         }
701
702         tape_initialise();
703
704         get_xtime(&rectime, 0, LOCAL_TIME);
705
706         /*
707          * Don't buffer data if redirected to a pipe.
708          * Note: With musl-c, the behavior of this function is undefined except
709          * when it is the first operation on the stream.
710          */
711         setbuf(stdout, NULL);
712
713         /* Get system name, release number and hostname */
714         __uname(&header);
715         if (print_gal_header(&rectime, header.sysname, header.release,
716                              header.nodename, header.machine, cpu_nr,
717                              PLAIN_OUTPUT)) {
718                 flags |= T_D_ISO;
719         }
720         printf("\n");
721
722         /* Set a handler for SIGALRM */
723         memset(&alrm_act, 0, sizeof(alrm_act));
724         alrm_act.sa_handler = alarm_handler;
725         sigaction(SIGALRM, &alrm_act, NULL);
726         alarm(interval);
727
728         /* Main loop */
729         rw_tape_stat_loop(count, &rectime);
730
731         /* Free structures */
732         tape_uninitialise();
733
734         return 0;
735 }