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