]> granicus.if.org Git - sysstat/blob - sar.c
sadc/sar: New format (part 7): Allocate structures dynamically
[sysstat] / sar.c
1 /*
2  * sar: report system activity
3  * (C) 1999-2017 by Sebastien GODARD (sysstat <at> orange.fr)
4  *
5  ***************************************************************************
6  * This program is free software; you can redistribute it and/or modify it *
7  * under the terms of the GNU General Public License as published  by  the *
8  * Free Software Foundation; either version 2 of the License, or (at  your *
9  * option) any later version.                                              *
10  *                                                                         *
11  * This program is distributed in the hope that it  will  be  useful,  but *
12  * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
14  * for more details.                                                       *
15  *                                                                         *
16  * You should have received a copy of the GNU General Public License along *
17  * with this program; if not, write to the Free Software Foundation, Inc., *
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
19  ***************************************************************************
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <time.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <sys/stat.h>
30
31 #include "version.h"
32 #include "sa.h"
33 #include "common.h"
34 #include "ioconf.h"
35 #include "pr_stats.h"
36
37 #ifdef USE_NLS
38 #include <locale.h>
39 #include <libintl.h>
40 #define _(string) gettext(string)
41 #else
42 #define _(string) (string)
43 #endif
44
45 #ifdef USE_SCCSID
46 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
47 char *sccsid(void) { return (SCCSID); }
48 #endif
49
50 /* Interval and count parameters */
51 long interval = -1, count = 0;
52
53 /* TRUE if a header line must be printed */
54 int dis = TRUE;
55 /* TRUE if data read from file don't match current machine's endianness */
56 int endian_mismatch = FALSE;
57 /* TRUE if file's data come from a 64 bit machine */
58 int arch_64 = FALSE;
59
60 unsigned int flags = 0;
61 unsigned int dm_major;  /* Device-mapper major number */
62
63 char timestamp[2][TIMESTAMP_LEN];
64 extern unsigned int rec_types_nr[];
65
66 unsigned long avg_count = 0;
67
68 /* File header */
69 struct file_header file_hdr;
70
71 /* Current record header */
72 struct record_header record_hdr[3];
73
74 /*
75  * Activity sequence.
76  * This array must always be entirely filled (even with trailing zeros).
77  */
78 unsigned int id_seq[NR_ACT];
79
80 struct tm rectime;
81
82 /* Contain the date specified by -s and -e options */
83 struct tstamp tm_start, tm_end;
84
85 char *args[MAX_ARGV_NR];
86
87 extern struct activity *act[];
88 extern struct report_format sar_fmt;
89
90 struct sigaction int_act;
91 int sigint_caught = 0;
92
93 /*
94  ***************************************************************************
95  * Print usage title message.
96  *
97  * IN:
98  * @progname    Name of sysstat command
99  ***************************************************************************
100  */
101 void print_usage_title(FILE *fp, char *progname)
102 {
103         fprintf(fp, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
104                 progname);
105 }
106
107 /*
108  ***************************************************************************
109  * Print usage and exit.
110  *
111  * IN:
112  * @progname    Name of sysstat command
113  ***************************************************************************
114  */
115 void usage(char *progname)
116 {
117         print_usage_title(stderr, progname);
118         fprintf(stderr, _("Options are:\n"
119                           "[ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ]\n"
120                           "[ -p ] [ -q ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
121                           "[ -v ] [ -W ] [ -w ] [ -y ] [ --human ] [ --sadc ]\n"
122                           "[ -I { <int_list> | SUM | ALL } ] [ -P { <cpu_list> | ALL } ]\n"
123                           "[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
124                           "[ -j { ID | LABEL | PATH | UUID | ... } ]\n"
125                           "[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]\n"
126                           "[ -i <interval> ] [ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"));
127         exit(1);
128 }
129
130 /*
131  ***************************************************************************
132  * Display a short help message and exit.
133  *
134  * IN:
135  * @progname    Name of sysstat command
136  ***************************************************************************
137  */
138 void display_help(char *progname)
139 {
140         print_usage_title(stdout, progname);
141         printf(_("Main options and reports:\n"));
142         printf(_("\t-B\tPaging statistics\n"));
143         printf(_("\t-b\tI/O and transfer rate statistics\n"));
144         printf(_("\t-d\tBlock devices statistics\n"));
145         printf(_("\t-F [ MOUNT ]\n"));
146         printf(_("\t\tFilesystems statistics\n"));
147         printf(_("\t-H\tHugepages utilization statistics\n"));
148         printf(_("\t-I { <int_list> | SUM | ALL }\n"
149                  "\t\tInterrupts statistics\n"));
150         printf(_("\t-m { <keyword> [,...] | ALL }\n"
151                  "\t\tPower management statistics\n"
152                  "\t\tKeywords are:\n"
153                  "\t\tCPU\tCPU instantaneous clock frequency\n"
154                  "\t\tFAN\tFans speed\n"
155                  "\t\tFREQ\tCPU average clock frequency\n"
156                  "\t\tIN\tVoltage inputs\n"
157                  "\t\tTEMP\tDevices temperature\n"
158                  "\t\tUSB\tUSB devices plugged into the system\n"));
159         printf(_("\t-n { <keyword> [,...] | ALL }\n"
160                  "\t\tNetwork statistics\n"
161                  "\t\tKeywords are:\n"
162                  "\t\tDEV\tNetwork interfaces\n"
163                  "\t\tEDEV\tNetwork interfaces (errors)\n"
164                  "\t\tNFS\tNFS client\n"
165                  "\t\tNFSD\tNFS server\n"
166                  "\t\tSOCK\tSockets\t(v4)\n"
167                  "\t\tIP\tIP traffic\t(v4)\n"
168                  "\t\tEIP\tIP traffic\t(v4) (errors)\n"
169                  "\t\tICMP\tICMP traffic\t(v4)\n"
170                  "\t\tEICMP\tICMP traffic\t(v4) (errors)\n"
171                  "\t\tTCP\tTCP traffic\t(v4)\n"
172                  "\t\tETCP\tTCP traffic\t(v4) (errors)\n"
173                  "\t\tUDP\tUDP traffic\t(v4)\n"
174                  "\t\tSOCK6\tSockets\t(v6)\n"
175                  "\t\tIP6\tIP traffic\t(v6)\n"
176                  "\t\tEIP6\tIP traffic\t(v6) (errors)\n"
177                  "\t\tICMP6\tICMP traffic\t(v6)\n"
178                  "\t\tEICMP6\tICMP traffic\t(v6) (errors)\n"
179                  "\t\tUDP6\tUDP traffic\t(v6)\n"
180                  "\t\tFC\tFibre channel HBAs\n"
181                  "\t\tSOFT\tSoftware-based network processing\n"));
182         printf(_("\t-q\tQueue length and load average statistics\n"));
183         printf(_("\t-r [ ALL ]\n"
184                  "\t\tMemory utilization statistics\n"));
185         printf(_("\t-S\tSwap space utilization statistics\n"));
186         printf(_("\t-u [ ALL ]\n"
187                  "\t\tCPU utilization statistics\n"));
188         printf(_("\t-v\tKernel tables statistics\n"));
189         printf(_("\t-W\tSwapping statistics\n"));
190         printf(_("\t-w\tTask creation and system switching statistics\n"));
191         printf(_("\t-y\tTTY devices statistics\n"));
192         exit(0);
193 }
194
195 /*
196  ***************************************************************************
197  * Give a hint to the user about where is located the data collector.
198  ***************************************************************************
199  */
200 void which_sadc(void)
201 {
202         struct stat buf;
203
204         if (stat(SADC_PATH, &buf) < 0) {
205                 printf(_("Data collector will be sought in PATH\n"));
206         }
207         else {
208                 printf(_("Data collector found: %s\n"), SADC_PATH);
209         }
210         exit(0);
211 }
212
213 /*
214  ***************************************************************************
215  * SIGINT signal handler.
216  *
217  * IN:
218  * @sig Signal number.
219  ***************************************************************************
220  */
221 void int_handler(int sig)
222 {
223         sigint_caught = 1;
224         printf("\n");   /* Skip "^C" displayed on screen */
225
226 }
227
228 /*
229  ***************************************************************************
230  * Init some structures.
231  ***************************************************************************
232  */
233 void init_structures(void)
234 {
235         int i;
236
237         for (i = 0; i < 3; i++)
238                 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
239 }
240
241 /*
242  ***************************************************************************
243  * Allocate memory for sadc args.
244  *
245  * IN:
246  * @i           Argument number.
247  * @ltemp       Argument value.
248  ***************************************************************************
249  */
250 void salloc(int i, char *ltemp)
251 {
252         if ((args[i] = (char *) malloc(strlen(ltemp) + 1)) == NULL) {
253                 perror("malloc");
254                 exit(4);
255         }
256         strcpy(args[i], ltemp);
257 }
258
259 /*
260  ***************************************************************************
261  * Display an error message.
262  *
263  * IN:
264  * @error_code  Code of error message to display.
265  ***************************************************************************
266  */
267 void print_read_error(int error_code)
268 {
269         switch (error_code) {
270
271                 case END_OF_DATA_UNEXPECTED:
272                         /* Happens when the data collector doesn't send enough data */
273                         fprintf(stderr, _("End of data collecting unexpected\n"));
274                         break;
275
276                 default:
277                         /* Strange data sent by sadc...! */
278                         fprintf(stderr, _("Inconsistent input data\n"));
279                         break;
280         }
281         exit(3);
282 }
283
284 /*
285  ***************************************************************************
286  * Check that every selected activity actually belongs to the sequence list.
287  * If not, then the activity should be unselected since it will not be sent
288  * by sadc. An activity can be not sent if its number of items is zero.
289  *
290  * IN:
291  * @act_nr      Size of sequence list.
292  ***************************************************************************
293  */
294 void reverse_check_act(unsigned int act_nr)
295 {
296         int i, j;
297
298         for (i = 0; i < NR_ACT; i++) {
299
300                 if (IS_SELECTED(act[i]->options)) {
301
302                         for (j = 0; j < act_nr; j++) {
303                                 if (id_seq[j] == act[i]->id)
304                                         break;
305                         }
306                         if (j == act_nr)
307                                 act[i]->options &= ~AO_SELECTED;
308                 }
309         }
310 }
311
312 /*
313  ***************************************************************************
314  * Determine if a stat header line has to be displayed.
315  *
316  * RETURNS:
317  * TRUE if a header line has to be displayed.
318  ***************************************************************************
319 */
320 int check_line_hdr(void)
321 {
322         int i, rc = FALSE;
323
324         /* Get number of options entered on the command line */
325         if (get_activity_nr(act, AO_SELECTED, COUNT_OUTPUTS) > 1)
326                 return TRUE;
327
328         for (i = 0; i < NR_ACT; i++) {
329                 if (IS_SELECTED(act[i]->options)) {
330                         /* Special processing for activities using a bitmap */
331                         if (act[i]->bitmap) {
332                                 if (count_bits(act[i]->bitmap->b_array,
333                                                BITMAP_SIZE(act[i]->bitmap->b_size)) > 1) {
334                                         rc = TRUE;
335                                 }
336                         }
337                         else if (act[i]->nr_ini > 1) {
338                                 rc = TRUE;
339                         }
340                         /* Stop now since we have only one selected activity */
341                         break;
342                 }
343         }
344
345         return rc;
346 }
347
348 /*
349  ***************************************************************************
350  * Print statistics average.
351  *
352  * IN:
353  * @curr                Index in array for current sample statistics.
354  * @read_from_file      Set to TRUE if stats are read from a system activity
355  *                      data file.
356  * @act_id              Activity that can be displayed, or ~0 for all.
357  *                      Remember that when reading stats from a file, only
358  *                      one activity can be displayed at a time.
359  ***************************************************************************
360  */
361 void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
362 {
363         int i;
364         unsigned long long itv;
365
366         /* Interval value in 1/100th of a second */
367         itv = get_interval(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs);
368
369         strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN);
370         timestamp[curr][TIMESTAMP_LEN - 1] = '\0';
371         strcpy(timestamp[!curr], timestamp[curr]);
372
373         /* Test stdout */
374         TEST_STDOUT(STDOUT_FILENO);
375
376         for (i = 0; i < NR_ACT; i++) {
377
378                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
379                         continue;
380
381                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
382                         /* Display current average activity statistics */
383                         (*act[i]->f_print_avg)(act[i], 2, curr, itv);
384                 }
385         }
386
387         if (read_from_file) {
388                 /*
389                  * Reset number of lines printed only if we read stats
390                  * from a system activity file.
391                  */
392                 avg_count = 0;
393         }
394 }
395
396 /*
397  ***************************************************************************
398  * Print system statistics.
399  * This is called when we read stats either from a file or from sadc.
400  *
401  * IN:
402  * @curr                Index in array for current sample statistics.
403  * @read_from_file      Set to TRUE if stats are read from a system activity
404  *                      data file.
405  * @use_tm_start        Set to TRUE if option -s has been used.
406  * @use_tm_end          Set to TRUE if option -e has been used.
407  * @reset               Set to TRUE if last_uptime variable should be
408  *                      reinitialized (used in next_slice() function).
409  * @act_id              Activity that can be displayed or ~0 for all.
410  *                      Remember that when reading stats from a file, only
411  *                      one activity can be displayed at a time.
412  * @reset_cd            TRUE if static cross_day variable should be reset
413  *                      (see below).
414  *
415  * OUT:
416  * @cnt                 Number of remaining lines to display.
417  *
418  * RETURNS:
419  * 1 if stats have been successfully displayed, and 0 otherwise.
420  ***************************************************************************
421  */
422 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
423                 int use_tm_end, int reset, unsigned int act_id, int reset_cd)
424 {
425         int i;
426         unsigned long long itv;
427         static int cross_day = 0;
428
429         if (reset_cd) {
430                 /*
431                  * cross_day is a static variable that is set to 1 when the first
432                  * record of stats from a new day is read from a unique data file
433                  * (in the case where the file contains data from two consecutive
434                  * days). When set to 1, every following records timestamp will
435                  * have its hour value increased by 24.
436                  * Yet when a new activity (being read from the file) is going to
437                  * be displayed, we start reading the file from the beginning
438                  * again, and so cross_day should be reset in this case.
439                  */
440                 cross_day = 0;
441         }
442
443         /* Check time (1) */
444         if (read_from_file) {
445                 if (!next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs,
446                                 reset, interval))
447                         /* Not close enough to desired interval */
448                         return 0;
449         }
450
451         if (!is_iso_time_fmt())
452                 flags |= S_F_PREFD_TIME_OUTPUT;
453
454         /* Get then set previous timestamp */
455         if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[!curr],
456                                            &rectime, NULL))
457                 return 0;
458         set_record_timestamp_string(flags, &record_hdr[!curr],
459                                     NULL, timestamp[!curr], TIMESTAMP_LEN, &rectime);
460
461         /* Get then set current timestamp */
462         if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[curr],
463                                            &rectime, NULL))
464                 return 0;
465         set_record_timestamp_string(flags, &record_hdr[curr],
466                                     NULL, timestamp[curr], TIMESTAMP_LEN, &rectime);
467
468         /* Check if we are beginning a new day */
469         if (use_tm_start && record_hdr[!curr].ust_time &&
470             (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
471             (record_hdr[curr].hour < record_hdr[!curr].hour)) {
472                 cross_day = 1;
473         }
474
475         if (cross_day) {
476                 /*
477                  * This is necessary if we want to properly handle something like:
478                  * sar -s time_start -e time_end with
479                  * time_start(day D) > time_end(day D+1)
480                  */
481                 rectime.tm_hour +=24;
482         }
483
484         /* Check time (2) */
485         if (use_tm_start && (datecmp(&rectime, &tm_start) < 0))
486                 /* it's too soon... */
487                 return 0;
488
489         /* Get interval value in 1/100th of a second */
490         get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv);
491
492         /* Check time (3) */
493         if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) {
494                 /* It's too late... */
495                 *cnt = 0;
496                 return 0;
497         }
498
499         avg_count++;
500
501         /* Test stdout */
502         TEST_STDOUT(STDOUT_FILENO);
503
504         for (i = 0; i < NR_ACT; i++) {
505
506                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
507                         continue;
508
509                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
510                         /* Display current activity statistics */
511                         (*act[i]->f_print)(act[i], !curr, curr, itv);
512                 }
513         }
514
515         return 1;
516 }
517
518 /*
519  ***************************************************************************
520  * Display stats since system startup.
521  *
522  * IN:
523  * @curr        Index in array for current sample statistics.
524  ***************************************************************************
525  */
526 void write_stats_startup(int curr)
527 {
528         int i;
529
530         /* Set to 0 previous structures corresponding to boot time */
531         memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE);
532         record_hdr[!curr].record_type = R_STATS;
533         record_hdr[!curr].hour        = record_hdr[curr].hour;
534         record_hdr[!curr].minute      = record_hdr[curr].minute;
535         record_hdr[!curr].second      = record_hdr[curr].second;
536         record_hdr[!curr].ust_time    = record_hdr[curr].ust_time;
537
538         for (i = 0; i < NR_ACT; i++) {
539                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
540                         /*
541                          * Using nr[curr] and not nr[!curr] below because we initialize
542                          * reference structures for each structure that has been
543                          * currently read in memory.
544                          * No problem with buffers allocation since they all have the
545                          * same size.
546                          */
547                         memset(act[i]->buf[!curr], 0,
548                                (size_t) act[i]->msize * (size_t) act[i]->nr[curr] * (size_t) act[i]->nr2);
549                 }
550         }
551
552         flags |= S_F_SINCE_BOOT;
553         dis = TRUE;
554
555         write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
556                     ALL_ACTIVITIES, TRUE);
557
558         exit(0);
559 }
560
561 /*
562  ***************************************************************************
563  * Read data sent by the data collector.
564  *
565  * IN:
566  * @size        Number of bytes of data to read.
567  *
568  * OUT:
569  * @buffer      Buffer where data will be saved.
570  *
571  * RETURNS:
572  * 1 if end of file has been reached, 0 otherwise.
573  ***************************************************************************
574  */
575 int sa_read(void *buffer, int size)
576 {
577         int n;
578
579         while (size) {
580
581                 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
582                         perror("read");
583                         exit(2);
584                 }
585
586                 if (!n)
587                         return 1;       /* EOF */
588
589                 size -= n;
590                 buffer = (char *) buffer + n;
591         }
592
593         return 0;
594 }
595
596 /*
597  ***************************************************************************
598  * Display a restart message (contents of a R_RESTART record).
599  *
600  * IN:
601  * @tab         Number of tabulations (unused here).
602  * @action      Action expected from current function (unused here).
603  * @cur_date    Date string of current restart message (unused here).
604  * @cur_time    Time string of current restart message.
605  * @utc         True if @cur_time is expressed in UTC (unused here).
606  * @file_hdr    System activity file standard header (unused here).
607  ***************************************************************************
608  */
609 __printf_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time,
610                                   int utc, struct file_header *file_hdr)
611 {
612         char restart[64];
613
614         printf("\n%-11s", cur_time);
615         sprintf(restart, "  LINUX RESTART\t(%d CPU)\n",
616                 file_hdr->sa_cpu_nr > 1 ? file_hdr->sa_cpu_nr - 1 : 1);
617         cprintf_s(IS_RESTART, "%s", restart);
618
619 }
620
621 /*
622  ***************************************************************************
623  * Display a comment (contents of R_COMMENT record).
624  *
625  * IN:
626  * @tab         Number of tabulations (unused here).
627  * @action      Action expected from current function (unused here).
628  * @cur_date    Date string of current comment (unused here).
629  * @cur_time    Time string of current comment.
630  * @utc         True if @cur_time is expressed in UTC (unused here).
631  * @comment     Comment to display.
632  * @file_hdr    System activity file standard header (unused here).
633  ***************************************************************************
634  */
635 __print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
636                                   char *comment, struct file_header *file_hdr)
637 {
638         printf("%-11s", cur_time);
639         cprintf_s(IS_COMMENT, "  COM %s\n", comment);
640 }
641
642 /*
643  ***************************************************************************
644  * Read the various statistics sent by the data collector (sadc).
645  *
646  * IN:
647  * @curr        Index in array for current sample statistics.
648  ***************************************************************************
649  */
650 void read_sadc_stat_bunch(int curr)
651 {
652         int i, p;
653
654         /* Read record header (type is always R_STATS since it is read from sadc) */
655         if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
656                 /*
657                  * SIGINT (sent by sadc) is likely to be received
658                  * while we are stuck in sa_read().
659                  * If this happens then no data have to be read.
660                  */
661                 if (sigint_caught)
662                         return;
663
664                 print_read_error(END_OF_DATA_UNEXPECTED);
665         }
666
667         for (i = 0; i < NR_ACT; i++) {
668
669                 if (!id_seq[i])
670                         continue;
671                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
672
673                 if (HAS_COUNT_FUNCTION(act[p]->options)) {
674                         if (sa_read(&(act[p]->nr[curr]), sizeof(__nr_t))) {
675                                 print_read_error(END_OF_DATA_UNEXPECTED);
676                         }
677                         if (act[p]->nr[curr] > act[p]->nr_max) {
678                                 print_read_error(INCONSISTENT_INPUT_DATA);
679                         }
680                         if (act[p]->nr[curr] > act[p]->nr_allocated) {
681                                 reallocate_all_buffers(act[p]);
682                         }
683
684
685                         /*
686                          * For persistent activities, we must make sure that no statistics
687                          * from a previous iteration remain, especially if the number
688                          * of structures read is smaller than @nr_ini.
689                          */
690                         if (HAS_PERSISTENT_VALUES(act[p]->options)) {
691                             memset(act[p]->buf[curr], 0,
692                                    (size_t) act[p]->fsize * (size_t) act[p]->nr_ini * (size_t) act[p]->nr2);
693                         }
694                 }
695                 if (sa_read(act[p]->buf[curr], act[p]->fsize * act[p]->nr[curr] * act[p]->nr2)) {
696                         print_read_error(END_OF_DATA_UNEXPECTED);
697                 }
698         }
699 }
700
701 /*
702  ***************************************************************************
703  * Read stats for current activity from file and display them.
704  *
705  * IN:
706  * @ifd         Input file descriptor.
707  * @fpos        Position in file where reading must start.
708  * @curr        Index in array for current sample statistics.
709  * @rows        Number of rows of screen.
710  * @act_id      Activity to display.
711  * @file_actlst List of activities in file.
712  * @file        Name of file being read.
713  * @file_magic  file_magic structure filled with file magic header data.
714  * @rec_hdr_tmp Temporary buffer where current record header will be saved.
715  * @endian_mismatch
716  *              TRUE if file's data don't match current machine's endianness.
717  * @arch_64     TRUE if file's data come from a 64 bit machine.
718  *
719  * OUT:
720  * @curr        Index in array for next sample statistics.
721  * @cnt         Number of remaining lines of stats to write.
722  * @eosaf       Set to TRUE if EOF (end of file) has been reached.
723  * @reset       Set to TRUE if last_uptime variable should be reinitialized
724  *              (used in next_slice() function).
725  ***************************************************************************
726  */
727 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
728                            int rows, unsigned int act_id, int *reset,
729                            struct file_activity *file_actlst, char *file,
730                            struct file_magic *file_magic, void *rec_hdr_tmp,
731                            int endian_mismatch, int arch_64)
732 {
733         int p, reset_cd;
734         unsigned long lines = 0;
735         unsigned char rtype;
736         int davg = 0, next, inc;
737
738         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
739                 perror("lseek");
740                 exit(2);
741         }
742
743         /*
744          * Restore the first stats collected.
745          * Used to compute the rate displayed on the first line.
746          */
747         copy_structures(act, id_seq, record_hdr, !*curr, 2);
748
749         *cnt  = count;
750
751         /* Assess number of lines printed */
752         p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
753         if (act[p]->bitmap) {
754                 inc = count_bits(act[p]->bitmap->b_array,
755                                  BITMAP_SIZE(act[p]->bitmap->b_size));
756         }
757         else {
758                 inc = act[p]->nr[*curr];
759         }
760
761         reset_cd = 1;
762
763         do {
764                 /*
765                  * Display <count> lines of stats.
766                  * Start with reading current sample's record header.
767                  */
768                 *eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[*curr],
769                                          &file_hdr, arch_64, endian_mismatch);
770                 rtype = record_hdr[*curr].record_type;
771
772                 if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
773                         /* Read the extra fields since it's not a special record */
774                         read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst,
775                                              endian_mismatch, arch_64, file, file_magic);
776                 }
777
778                 if ((lines >= rows) || !lines) {
779                         lines = 0;
780                         dis = 1;
781                 }
782                 else
783                         dis = 0;
784
785                 if (!*eosaf && (rtype != R_RESTART)) {
786
787                         if (rtype == R_COMMENT) {
788                                 /* Display comment */
789                                 next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
790                                                             &tm_start, &tm_end, R_COMMENT, ifd,
791                                                             &rectime, NULL, file, 0,
792                                                             file_magic, &file_hdr, act, &sar_fmt,
793                                                             endian_mismatch, arch_64);
794                                 if (next) {
795                                         /* A line of comment was actually displayed */
796                                         lines++;
797                                 }
798                                 continue;
799                         }
800
801                         /* next is set to 1 when we were close enough to desired interval */
802                         next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
803                                            *reset, act_id, reset_cd);
804                         reset_cd = 0;
805                         if (next && (*cnt > 0)) {
806                                 (*cnt)--;
807                         }
808                         if (next) {
809                                 davg++;
810                                 *curr ^=1;
811                                 lines += inc;
812                         }
813                         *reset = FALSE;
814                 }
815         }
816         while (*cnt && !*eosaf && (rtype != R_RESTART));
817
818         if (davg) {
819                 write_stats_avg(!*curr, USE_SA_FILE, act_id);
820         }
821
822         *reset = TRUE;
823 }
824
825 /*
826  ***************************************************************************
827  * Read header data sent by sadc.
828  ***************************************************************************
829  */
830 void read_header_data(void)
831 {
832         struct file_magic file_magic;
833         struct file_activity file_act;
834         int rc, i, p;
835         char version[16];
836
837         /* Read magic header */
838         rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
839
840         sprintf(version, "%d.%d.%d.%d",
841                 file_magic.sysstat_version,
842                 file_magic.sysstat_patchlevel,
843                 file_magic.sysstat_sublevel,
844                 file_magic.sysstat_extraversion);
845         if (!file_magic.sysstat_extraversion) {
846                 version[strlen(version) - 2] = '\0';
847         }
848
849         if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
850             (file_magic.format_magic != FORMAT_MAGIC) ||
851             strcmp(version, VERSION)) {
852
853                 /* sar and sadc commands are not consistent */
854                 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
855                         fprintf(stderr,
856                                 _("Using a wrong data collector from a different sysstat version\n"));
857                 }
858
859                 print_read_error(INCONSISTENT_INPUT_DATA);
860         }
861
862         /*
863          * Read header data.
864          * No need to take into account file_magic.header_size. We are sure that
865          * sadc and sar are from the same version (we have checked FORMAT_MAGIC
866          * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
867          */
868         if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
869                 print_read_error(END_OF_DATA_UNEXPECTED);
870         }
871
872         /* All activities are not necessarily selected, but NR_ACT is a max */
873         if (file_hdr.sa_act_nr > NR_ACT) {
874                 print_read_error(INCONSISTENT_INPUT_DATA);
875         }
876
877         if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
878             (file_hdr.rec_size != RECORD_HEADER_SIZE)) {
879                 print_read_error(INCONSISTENT_INPUT_DATA);
880         }
881
882         /* Read activity list */
883         for (i = 0; i < file_hdr.sa_act_nr; i++) {
884
885                 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
886                         print_read_error(END_OF_DATA_UNEXPECTED);
887                 }
888
889                 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
890
891                 if ((p < 0) || (act[p]->fsize != file_act.size)
892                             || (act[p]->gtypes_nr[0] != file_act.types_nr[0])
893                             || (act[p]->gtypes_nr[1] != file_act.types_nr[1])
894                             || (act[p]->gtypes_nr[2] != file_act.types_nr[2])
895                             || (file_act.nr <= 0)
896                             || (file_act.nr2 <= 0)
897                             || (act[p]->magic != file_act.magic)) {
898                         /* Remember that we are reading data from sadc and not from a file... */
899                         print_read_error(INCONSISTENT_INPUT_DATA);
900                 }
901
902                 id_seq[i]      = file_act.id;   /* We necessarily have "i < NR_ACT" */
903                 act[p]->nr_ini = file_act.nr;
904                 act[p]->nr2    = file_act.nr2;
905         }
906
907         while (i < NR_ACT) {
908                 id_seq[i++] = 0;
909         }
910
911         /* Check that all selected activties are actually sent by sadc */
912         reverse_check_act(file_hdr.sa_act_nr);
913
914         return;
915 }
916
917 /*
918  ***************************************************************************
919  * Read statistics from a system activity data file.
920  *
921  * IN:
922  * @from_file   Input file name.
923  ***************************************************************************
924  */
925 void read_stats_from_file(char from_file[])
926 {
927         struct file_magic file_magic;
928         struct file_activity *file_actlst = NULL;
929         char rec_hdr_tmp[MAX_RECORD_HEADER_SIZE];
930         int curr = 1, i, p;
931         int ifd, rtype;
932         int rows, eosaf = TRUE, reset = FALSE;
933         long cnt = 1;
934         off_t fpos;
935
936         /* Get window size */
937         rows = get_win_height();
938
939         /* Read file headers and activity list */
940         check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr,
941                           &file_actlst, id_seq, FALSE, &endian_mismatch, &arch_64);
942
943         /* Perform required allocations */
944         allocate_structures(act);
945
946         /* Print report header */
947         print_report_hdr(flags, &rectime, &file_hdr);
948
949         /* Read system statistics from file */
950         do {
951                 /*
952                  * If this record is a special (RESTART or COMMENT) one, print it and
953                  * (try to) get another one.
954                  */
955                 do {
956                         if (read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[0], &file_hdr,
957                                             arch_64, endian_mismatch)) {
958                                 /* End of sa data file */
959                                 return;
960                         }
961
962                         rtype = record_hdr[0].record_type;
963                         if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
964                                 print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
965                                                      &tm_start, &tm_end, rtype, ifd,
966                                                      &rectime, NULL, from_file, 0, &file_magic,
967                                                      &file_hdr, act, &sar_fmt, endian_mismatch, arch_64);
968                         }
969                         else {
970                                 /*
971                                  * OK: Previous record was not a special one.
972                                  * So read now the extra fields.
973                                  */
974                                 read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
975                                                      file_actlst, endian_mismatch, arch_64,
976                                                      from_file, &file_magic);
977                                 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
978                                                                    &record_hdr[0],
979                                                                    &rectime, NULL))
980                                         /*
981                                          * An error was detected.
982                                          * The timestamp hasn't been updated.
983                                          */
984                                         continue;
985                         }
986                 }
987                 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
988                        (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) ||
989                        (tm_end.use && (datecmp(&rectime, &tm_end) >=0)));
990
991                 /* Save the first stats collected. Will be used to compute the average */
992                 copy_structures(act, id_seq, record_hdr, 2, 0);
993
994                 reset = TRUE;   /* Set flag to reset last_uptime variable */
995
996                 /* Save current file position */
997                 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
998                         perror("lseek");
999                         exit(2);
1000                 }
1001
1002                 /*
1003                  * Read and write stats located between two possible Linux restarts.
1004                  * Activities that should be displayed are saved in id_seq[] array.
1005                  * Since we are reading from a file, we print all the stats for an
1006                  * activity before displaying the next activity.
1007                  * id_seq[] has been created in check_file_actlst(), retaining only
1008                  * activities known by current sysstat version.
1009                  */
1010                 for (i = 0; i < NR_ACT; i++) {
1011
1012                         if (!id_seq[i])
1013                                 continue;
1014
1015                         p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1016                         if (!IS_SELECTED(act[p]->options))
1017                                 continue;
1018
1019                         if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1020                                 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
1021                                                       act[p]->id, &reset, file_actlst,
1022                                                       from_file, &file_magic, rec_hdr_tmp,
1023                                                       endian_mismatch, arch_64);
1024                         }
1025                         else {
1026                                 unsigned int optf, msk;
1027
1028                                 optf = act[p]->opt_flags;
1029
1030                                 for (msk = 1; msk < 0x100; msk <<= 1) {
1031                                         if ((act[p]->opt_flags & 0xff) & msk) {
1032                                                 act[p]->opt_flags &= (0xffffff00 + msk);
1033
1034                                                 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
1035                                                                       rows, act[p]->id, &reset, file_actlst,
1036                                                                       from_file, &file_magic, rec_hdr_tmp,
1037                                                                       endian_mismatch, arch_64);
1038                                                 act[p]->opt_flags = optf;
1039                                         }
1040                                 }
1041                         }
1042                 }
1043
1044                 if (!cnt) {
1045                         /* Go to next Linux restart, if possible */
1046                         do {
1047                                 /* Read next record header */
1048                                 eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr],
1049                                                         &file_hdr, arch_64, endian_mismatch);
1050                                 rtype = record_hdr[curr].record_type;
1051
1052                                 if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
1053                                         read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
1054                                                              file_actlst, endian_mismatch, arch_64,
1055                                                              from_file, &file_magic);
1056                                 }
1057                                 else if (!eosaf && (rtype == R_COMMENT)) {
1058                                         /* This was a COMMENT record: print it */
1059                                         print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1060                                                              &tm_start, &tm_end, R_COMMENT, ifd,
1061                                                              &rectime, NULL, from_file, 0,
1062                                                              &file_magic, &file_hdr, act, &sar_fmt,
1063                                                              endian_mismatch, arch_64);
1064                                 }
1065                         }
1066                         while (!eosaf && (rtype != R_RESTART));
1067                 }
1068
1069                 /* The last record we read was a RESTART one: Print it */
1070                 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1071                         print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1072                                              &tm_start, &tm_end, R_RESTART, ifd,
1073                                              &rectime, NULL, from_file, 0,
1074                                              &file_magic, &file_hdr, act, &sar_fmt,
1075                                              endian_mismatch, arch_64);
1076                 }
1077         }
1078         while (!eosaf);
1079
1080         close(ifd);
1081
1082         free(file_actlst);
1083 }
1084
1085 /*
1086  ***************************************************************************
1087  * Read statistics sent by sadc, the data collector.
1088  ***************************************************************************
1089  */
1090 void read_stats(void)
1091 {
1092         int curr = 1;
1093         unsigned long lines;
1094         unsigned int rows;
1095         int dis_hdr = 0;
1096
1097         /* Don't buffer data if redirected to a pipe... */
1098         setbuf(stdout, NULL);
1099
1100         /* Read stats header */
1101         read_header_data();
1102
1103         if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1104                 fprintf(stderr, _("Requested activities not available\n"));
1105                 exit(1);
1106         }
1107
1108         /* Determine if a stat line header has to be displayed */
1109         dis_hdr = check_line_hdr();
1110
1111         lines = rows = get_win_height();
1112
1113         /* Perform required allocations */
1114         allocate_structures(act);
1115
1116         /* Print report header */
1117         print_report_hdr(flags, &rectime, &file_hdr);
1118
1119         /* Read system statistics sent by the data collector */
1120         read_sadc_stat_bunch(0);
1121
1122         if (!interval) {
1123                 /* Display stats since boot time and exit */
1124                 write_stats_startup(0);
1125         }
1126
1127         /* Save the first stats collected. Will be used to compute the average */
1128         copy_structures(act, id_seq, record_hdr, 2, 0);
1129
1130         /* Set a handler for SIGINT */
1131         memset(&int_act, 0, sizeof(int_act));
1132         int_act.sa_handler = int_handler;
1133         int_act.sa_flags = SA_RESTART;
1134         sigaction(SIGINT, &int_act, NULL);
1135
1136         /* Main loop */
1137         do {
1138
1139                 /* Get stats */
1140                 read_sadc_stat_bunch(curr);
1141                 if (sigint_caught) {
1142                         /*
1143                          * SIGINT signal caught (it is sent by sadc).
1144                          * => Display average stats.
1145                          */
1146                         curr ^= 1; /* No data retrieved from last read */
1147                         break;
1148                 }
1149
1150                 /* Print results */
1151                 if (!dis_hdr) {
1152                         dis = lines / rows;
1153                         if (dis) {
1154                                 lines %= rows;
1155                         }
1156                         lines++;
1157                 }
1158                 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1159                             NO_RESET, ALL_ACTIVITIES, TRUE);
1160
1161                 if (record_hdr[curr].record_type == R_LAST_STATS) {
1162                         /* File rotation is happening: Re-read header data sent by sadc */
1163                         read_header_data();
1164                         allocate_structures(act);
1165                 }
1166
1167                 if (count > 0) {
1168                         count--;
1169                 }
1170                 if (count) {
1171                         curr ^= 1;
1172                 }
1173         }
1174         while (count);
1175
1176         /*
1177          * Print statistics average.
1178          * At least one line of stats must have been displayed for this.
1179          * (There may be no lines at all if we press Ctrl/C immediately).
1180          */
1181         dis = dis_hdr;
1182         if (avg_count) {
1183                 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1184         }
1185 }
1186
1187 /*
1188  ***************************************************************************
1189  * Main entry to the sar program.
1190  ***************************************************************************
1191  */
1192 int main(int argc, char **argv)
1193 {
1194         int i, rc, opt = 1, args_idx = 2;
1195         int fd[2];
1196         int day_offset = 0;
1197         char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1198         char ltemp[20];
1199
1200         /* Compute page shift in kB */
1201         get_kb_shift();
1202
1203         from_file[0] = to_file[0] = '\0';
1204
1205 #ifdef USE_NLS
1206         /* Init National Language Support */
1207         init_nls();
1208 #endif
1209
1210         /* Init color strings */
1211         init_colors();
1212
1213         tm_start.use = tm_end.use = FALSE;
1214
1215         /* Allocate and init activity bitmaps */
1216         allocate_bitmaps(act);
1217
1218         init_structures();
1219
1220         /* Process options */
1221         while (opt < argc) {
1222
1223                 if (!strcmp(argv[opt], "--sadc")) {
1224                         /* Locate sadc */
1225                         which_sadc();
1226                 }
1227
1228                 else if (!strcmp(argv[opt], "--human")) {
1229                         /* Display sizes in a human readable format */
1230                         flags |= S_F_UNIT;
1231                         opt++;
1232                 }
1233
1234                 else if (!strcmp(argv[opt], "-I")) {
1235                         /* Parse -I option */
1236                         if (parse_sar_I_opt(argv, &opt, act)) {
1237                                 usage(argv[0]);
1238                         }
1239                 }
1240
1241                 else if (!strcmp(argv[opt], "-D")) {
1242                         /* Option to tell sar to write to saYYYYMMDD data files */
1243                         flags |= S_F_SA_YYYYMMDD;
1244                         opt++;
1245                 }
1246
1247                 else if (!strcmp(argv[opt], "-P")) {
1248                         /* Parse -P option */
1249                         if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1250                                 usage(argv[0]);
1251                         }
1252                 }
1253
1254                 else if (!strcmp(argv[opt], "-o")) {
1255                         if (to_file[0]) {
1256                                 /* Output file already specified */
1257                                 usage(argv[0]);
1258                         }
1259                         /* Save stats to a file */
1260                         if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1261                             (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1262                                 strncpy(to_file, argv[opt++], MAX_FILE_LEN);
1263                                 to_file[MAX_FILE_LEN - 1] = '\0';
1264                         }
1265                         else {
1266                                 strcpy(to_file, "-");
1267                         }
1268                 }
1269
1270                 else if (!strcmp(argv[opt], "-f")) {
1271                         if (from_file[0] || day_offset) {
1272                                 /* Input file already specified */
1273                                 usage(argv[0]);
1274                         }
1275                         /* Read stats from a file */
1276                         if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1277                             (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1278                                 strncpy(from_file, argv[opt++], MAX_FILE_LEN);
1279                                 from_file[MAX_FILE_LEN - 1] = '\0';
1280                                 /* Check if this is an alternate directory for sa files */
1281                                 check_alt_sa_dir(from_file, day_offset, -1);
1282                         }
1283                         else {
1284                                 set_default_file(from_file, day_offset, -1);
1285                         }
1286                 }
1287
1288                 else if (!strcmp(argv[opt], "-s")) {
1289                         /* Get time start */
1290                         if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1291                                 usage(argv[0]);
1292                         }
1293                 }
1294
1295                 else if (!strcmp(argv[opt], "-e")) {
1296                         /* Get time end */
1297                         if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1298                                 usage(argv[0]);
1299                         }
1300                 }
1301
1302                 else if (!strcmp(argv[opt], "-h")) {
1303                         /* Display help message */
1304                         display_help(argv[0]);
1305                 }
1306
1307                 else if (!strcmp(argv[opt], "-i")) {
1308                         if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1309                                 usage(argv[0]);
1310                         }
1311                         interval = atol(argv[opt++]);
1312                         if (interval < 1) {
1313                                 usage(argv[0]);
1314                         }
1315                         flags |= S_F_INTERVAL_SET;
1316                 }
1317
1318                 else if (!strcmp(argv[opt], "-m")) {
1319                         if (!argv[++opt]) {
1320                                 usage(argv[0]);
1321                         }
1322                         /* Parse option -m */
1323                         if (parse_sar_m_opt(argv, &opt, act)) {
1324                                 usage(argv[0]);
1325                         }
1326                 }
1327
1328                 else if (!strcmp(argv[opt], "-n")) {
1329                         if (!argv[++opt]) {
1330                                 usage(argv[0]);
1331                         }
1332                         /* Parse option -n */
1333                         if (parse_sar_n_opt(argv, &opt, act)) {
1334                                 usage(argv[0]);
1335                         }
1336                 }
1337
1338                 else if ((strlen(argv[opt]) > 1) &&
1339                          (strlen(argv[opt]) < 4) &&
1340                          !strncmp(argv[opt], "-", 1) &&
1341                          (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1342                         if (from_file[0] || day_offset) {
1343                                 /* Input file already specified */
1344                                 usage(argv[0]);
1345                         }
1346                         day_offset = atoi(argv[opt++] + 1);
1347                 }
1348
1349                 else if (!strncmp(argv[opt], "-", 1)) {
1350                         /* Other options not previously tested */
1351                         if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) {
1352                                 if (rc == 1) {
1353                                         usage(argv[0]);
1354                                 }
1355                                 exit(1);
1356                         }
1357                         opt++;
1358                 }
1359
1360                 else if (interval < 0) {
1361                         /* Get interval */
1362                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1363                                 usage(argv[0]);
1364                         }
1365                         interval = atol(argv[opt++]);
1366                         if (interval < 0) {
1367                                 usage(argv[0]);
1368                         }
1369                 }
1370
1371                 else {
1372                         /* Get count value */
1373                         if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1374                             !interval) {
1375                                 usage(argv[0]);
1376                         }
1377                         if (count) {
1378                                 /* Count parameter already set */
1379                                 usage(argv[0]);
1380                         }
1381                         count = atol(argv[opt++]);
1382                         if (count < 1) {
1383                                 usage(argv[0]);
1384                         }
1385                 }
1386         }
1387
1388         /* 'sar' is equivalent to 'sar -f' */
1389         if ((argc == 1) ||
1390             ((interval < 0) && !from_file[0] && !to_file[0])) {
1391                 set_default_file(from_file, day_offset, -1);
1392         }
1393
1394         if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1395                 tm_end.tm_hour += 24;
1396         }
1397
1398         /*
1399          * Check option dependencies.
1400          */
1401         /* You read from a file OR you write to it... */
1402         if (from_file[0] && to_file[0]) {
1403                 fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
1404                 exit(1);
1405         }
1406         /* Use time start or option -i only when reading stats from a file */
1407         if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
1408                 fprintf(stderr,
1409                         _("Not reading from a system activity file (use -f option)\n"));
1410                 exit(1);
1411         }
1412         /* Don't print stats since boot time if -o or -f options are used */
1413         if (!interval && (from_file[0] || to_file[0])) {
1414                 usage(argv[0]);
1415         }
1416
1417         /* Cannot enter a day shift with -o option */
1418         if (to_file[0] && day_offset) {
1419                 usage(argv[0]);
1420         }
1421
1422         if (USE_PRETTY_OPTION(flags)) {
1423                 dm_major = get_devmap_major();
1424         }
1425
1426         if (!count) {
1427                 /*
1428                  * count parameter not set: Display all the contents of the file
1429                  * or generate a report continuously.
1430                  */
1431                 count = -1;
1432         }
1433
1434         /* Default is CPU activity... */
1435         select_default_activity(act);
1436
1437         /* Reading stats from file: */
1438         if (from_file[0]) {
1439                 if (interval < 0) {
1440                         interval = 1;
1441                 }
1442
1443                 /* Read stats from file */
1444                 read_stats_from_file(from_file);
1445
1446                 /* Free stuctures and activity bitmaps */
1447                 free_bitmaps(act);
1448                 free_structures(act);
1449
1450                 return 0;
1451         }
1452
1453         /* Reading stats from sadc: */
1454
1455         /* Create anonymous pipe */
1456         if (pipe(fd) == -1) {
1457                 perror("pipe");
1458                 exit(4);
1459         }
1460
1461         switch (fork()) {
1462
1463         case -1:
1464                 perror("fork");
1465                 exit(4);
1466                 break;
1467
1468         case 0: /* Child */
1469                 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1470                         perror("dup2");
1471                         exit(4);
1472                 }
1473                 CLOSE_ALL(fd);
1474
1475                 /*
1476                  * Prepare options for sadc.
1477                  */
1478                 /* Program name */
1479                 salloc(0, SADC);
1480
1481                 /* Interval value */
1482                 if (interval < 0) {
1483                         usage(argv[0]);
1484                 }
1485                 else if (!interval) {
1486                         strcpy(ltemp, "1");
1487                 }
1488                 else {
1489                         sprintf(ltemp, "%ld", interval);
1490                 }
1491                 salloc(1, ltemp);
1492
1493                 /* Count number */
1494                 if (count >= 0) {
1495                         sprintf(ltemp, "%ld", count + 1);
1496                         salloc(args_idx++, ltemp);
1497                 }
1498
1499                 /* Flags to be passed to sadc */
1500                 salloc(args_idx++, "-z");
1501
1502                 /* Writing data to a file (option -o) */
1503                 if (to_file[0]) {
1504                         /* Set option -D if entered */
1505                         if (USE_SA_YYYYMMDD(flags)) {
1506                                 salloc(args_idx++, "-D");
1507                         }
1508                         /* Collect all possible activities (option -S XALL for sadc) */
1509                         salloc(args_idx++, "-S");
1510                         salloc(args_idx++, K_XALL);
1511                         /* Outfile arg */
1512                         salloc(args_idx++, to_file);
1513                 }
1514                 else {
1515                         /*
1516                          * If option -o hasn't been used, then tell sadc
1517                          * to collect only activities that will be displayed.
1518                          */
1519                         int act_id = 0;
1520
1521                         for (i = 0; i < NR_ACT; i++) {
1522                                 if (IS_SELECTED(act[i]->options)) {
1523                                         act_id |= act[i]->group;
1524                                 }
1525                         }
1526                         if (act_id) {
1527                                 act_id <<= 8;
1528                                 snprintf(ltemp, 19, "%d", act_id);
1529                                 ltemp[19] = '\0';
1530                                 salloc(args_idx++, "-S");
1531                                 salloc(args_idx++, ltemp);
1532                         }
1533                 }
1534
1535                 /* Last arg is NULL */
1536                 args[args_idx] = NULL;
1537
1538                 /* Call now the data collector */
1539                 execv(SADC_PATH, args);
1540                 execvp(SADC, args);
1541                 /*
1542                  * Note: Don't use execl/execlp since we don't have a fixed number of
1543                  * args to give to sadc.
1544                  */
1545                 fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC);
1546                 perror("exec");
1547                 exit(4);
1548                 break;
1549
1550         default: /* Parent */
1551                 if (dup2(fd[0], STDIN_FILENO) < 0) {
1552                         perror("dup2");
1553                         exit(4);
1554                 }
1555                 CLOSE_ALL(fd);
1556
1557                 /* Get now the statistics */
1558                 read_stats();
1559
1560                 break;
1561         }
1562
1563         /* Free structures and activity bitmaps */
1564         free_bitmaps(act);
1565         free_structures(act);
1566
1567         return 0;
1568 }