]> granicus.if.org Git - sysstat/blobdiff - sar.c
sar/sadf: Add extra flexibility in case of a change of file format
[sysstat] / sar.c
diff --git a/sar.c b/sar.c
index 10636216b04d7ea924d4265f021d75999064c951..70ce79c1b0a94beb4a59febf5937d5575994552e 100644 (file)
--- a/sar.c
+++ b/sar.c
@@ -1,6 +1,6 @@
 /*
  * sar: report system activity
- * (C) 1999-2015 by Sebastien GODARD (sysstat <at> orange.fr)
+ * (C) 1999-2019 by Sebastien GODARD (sysstat <at> orange.fr)
  *
  ***************************************************************************
  * This program is free software; you can redistribute it and/or modify it *
@@ -15,7 +15,7 @@
  *                                                                         *
  * You should have received a copy of the GNU General Public License along *
  * with this program; if not, write to the Free Software Foundation, Inc., *
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                   *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
  ***************************************************************************
  */
 
@@ -30,9 +30,6 @@
 
 #include "version.h"
 #include "sa.h"
-#include "common.h"
-#include "ioconf.h"
-#include "pr_stats.h"
 
 #ifdef USE_NLS
 #include <locale.h>
 #define _(string) (string)
 #endif
 
+#ifdef USE_SCCSID
 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
 char *sccsid(void) { return (SCCSID); }
+#endif
+
+#ifdef TEST
+extern time_t __unix_time;
+#endif
 
 /* Interval and count parameters */
 long interval = -1, count = 0;
 
 /* TRUE if a header line must be printed */
-int dis = TRUE;
+int dish = TRUE;
+/* TRUE if data read from file don't match current machine's endianness */
+int endian_mismatch = FALSE;
+/* TRUE if file's data come from a 64 bit machine */
+int arch_64 = FALSE;
+/* Number of decimal places */
+int dplaces_nr = -1;
 
 unsigned int flags = 0;
 unsigned int dm_major; /* Device-mapper major number */
 
 char timestamp[2][TIMESTAMP_LEN];
+extern unsigned int rec_types_nr[];
 
 unsigned long avg_count = 0;
 
@@ -78,6 +88,7 @@ struct tstamp tm_start, tm_end;
 char *args[MAX_ARGV_NR];
 
 extern struct activity *act[];
+extern struct report_format sar_fmt;
 
 struct sigaction int_act;
 int sigint_caught = 0;
@@ -108,12 +119,14 @@ void usage(char *progname)
 {
        print_usage_title(stderr, progname);
        fprintf(stderr, _("Options are:\n"
-                         "[ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNTS ] ] [ -H ] [ -h ]\n"
-                         "[ -p ] [ -q ] [ -R ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
-                         "[ -v ] [ -W ] [ -w ] [ -y ] [ --sadc ]\n"
-                         "[ -I { <int> [,...] | SUM | ALL | XALL } ] [ -P { <cpu> [,...] | ALL } ]\n"
+                         "[ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ]\n"
+                         "[ -p ] [ -q ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
+                         "[ -v ] [ -W ] [ -w ] [ -y ] [ -z ]\n"
+                         "[ -I { <int_list> | SUM | ALL } ] [ -P { <cpu_list> | ALL } ]\n"
                          "[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
-                         "[ -j { ID | LABEL | PATH | UUID | ... } ]\n"
+                         "[ --dev=<dev_list> ] [ --fs=<fs_list> ] [ --iface=<iface_list> ]\n"
+                         "[ --dec={ 0 | 1 | 2 } ] [ --help ] [ --human ] [ --sadc ]\n"
+                         "[ -j { SID | ID | LABEL | PATH | UUID | ... } ]\n"
                          "[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]\n"
                          "[ -i <interval> ] [ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"));
        exit(1);
@@ -130,17 +143,17 @@ void usage(char *progname)
 void display_help(char *progname)
 {
        print_usage_title(stdout, progname);
-       printf(_("Main options and reports:\n"));
-       printf(_("\t-B\tPaging statistics\n"));
-       printf(_("\t-b\tI/O and transfer rate statistics\n"));
-       printf(_("\t-d\tBlock devices statistics\n"));
-       printf(_("\t-F [ MOUNTS ]\n"));
-       printf(_("\t\tFilesystems statistics\n"));
-       printf(_("\t-H\tHugepages utilization statistics\n"));
-       printf(_("\t-I { <int> | SUM | ALL | XALL }\n"
-                "\t\tInterrupts statistics\n"));
+       printf(_("Main options and reports (report name between square brackets):\n"));
+       printf(_("\t-B\tPaging statistics [A_PAGE]\n"));
+       printf(_("\t-b\tI/O and transfer rate statistics [A_IO]\n"));
+       printf(_("\t-d\tBlock devices statistics [A_DISK]\n"));
+       printf(_("\t-F [ MOUNT ]\n"));
+       printf(_("\t\tFilesystems statistics [A_FS]\n"));
+       printf(_("\t-H\tHugepages utilization statistics [A_HUGE]\n"));
+       printf(_("\t-I { <int_list> | SUM | ALL }\n"
+                "\t\tInterrupts statistics [A_IRQ]\n"));
        printf(_("\t-m { <keyword> [,...] | ALL }\n"
-                "\t\tPower management statistics\n"
+                "\t\tPower management statistics [A_PWR_...]\n"
                 "\t\tKeywords are:\n"
                 "\t\tCPU\tCPU instantaneous clock frequency\n"
                 "\t\tFAN\tFans speed\n"
@@ -149,7 +162,7 @@ void display_help(char *progname)
                 "\t\tTEMP\tDevices temperature\n"
                 "\t\tUSB\tUSB devices plugged into the system\n"));
        printf(_("\t-n { <keyword> [,...] | ALL }\n"
-                "\t\tNetwork statistics\n"
+                "\t\tNetwork statistics [A_NET_...]\n"
                 "\t\tKeywords are:\n"
                 "\t\tDEV\tNetwork interfaces\n"
                 "\t\tEDEV\tNetwork interfaces (errors)\n"
@@ -169,18 +182,18 @@ void display_help(char *progname)
                 "\t\tICMP6\tICMP traffic\t(v6)\n"
                 "\t\tEICMP6\tICMP traffic\t(v6) (errors)\n"
                 "\t\tUDP6\tUDP traffic\t(v6)\n"
-                "\t\tFC\tFibre channel HBAs\n"));
-       printf(_("\t-q\tQueue length and load average statistics\n"));
-       printf(_("\t-R\tMemory statistics\n"));
+                "\t\tFC\tFibre channel HBAs\n"
+                "\t\tSOFT\tSoftware-based network processing\n"));
+       printf(_("\t-q\tQueue length and load average statistics [A_QUEUE]\n"));
        printf(_("\t-r [ ALL ]\n"
-                "\t\tMemory utilization statistics\n"));
-       printf(_("\t-S\tSwap space utilization statistics\n"));
+                "\t\tMemory utilization statistics [A_MEMORY]\n"));
+       printf(_("\t-S\tSwap space utilization statistics [A_MEMORY]\n"));
        printf(_("\t-u [ ALL ]\n"
-                "\t\tCPU utilization statistics\n"));
-       printf(_("\t-v\tKernel tables statistics\n"));
-       printf(_("\t-W\tSwapping statistics\n"));
-       printf(_("\t-w\tTask creation and system switching statistics\n"));
-       printf(_("\t-y\tTTY devices statistics\n"));
+                "\t\tCPU utilization statistics [A_CPU]\n"));
+       printf(_("\t-v\tKernel tables statistics [A_KTABLES]\n"));
+       printf(_("\t-W\tSwapping statistics [A_SWAP]\n"));
+       printf(_("\t-w\tTask creation and system switching statistics [A_PCSW]\n"));
+       printf(_("\t-y\tTTY devices statistics [A_SERIAL]\n"));
        exit(0);
 }
 
@@ -202,7 +215,6 @@ void which_sadc(void)
        exit(0);
 }
 
-
 /*
  ***************************************************************************
  * SIGINT signal handler.
@@ -251,13 +263,26 @@ void salloc(int i, char *ltemp)
 
 /*
  ***************************************************************************
- * Display an error message. Happens when the data collector doesn't send
- * enough data.
+ * Display an error message.
+ *
+ * IN:
+ * @error_code Code of error message to display.
  ***************************************************************************
  */
-void print_read_error(void)
+void print_read_error(int error_code)
 {
-       fprintf(stderr, _("End of data collecting unexpected\n"));
+       switch (error_code) {
+
+               case END_OF_DATA_UNEXPECTED:
+                       /* Happens when the data collector doesn't send enough data */
+                       fprintf(stderr, _("End of data collecting unexpected\n"));
+                       break;
+
+               default:
+                       /* Strange data sent by sadc...! */
+                       fprintf(stderr, _("Inconsistent input data\n"));
+                       break;
+       }
        exit(3);
 }
 
@@ -265,7 +290,7 @@ void print_read_error(void)
  ***************************************************************************
  * Check that every selected activity actually belongs to the sequence list.
  * If not, then the activity should be unselected since it will not be sent
- * by sadc. An activity can be not sent if its number of items is null.
+ * by sadc. An activity can be not sent if its number of items is zero.
  *
  * IN:
  * @act_nr     Size of sequence list.
@@ -289,46 +314,6 @@ void reverse_check_act(unsigned int act_nr)
        }
 }
 
-/*
- ***************************************************************************
- * Fill the (struct tm) rectime structure with current record's time,
- * based on current record's time data saved in file.
- * The resulting timestamp is expressed in the locale of the file creator
- * or in the user's own locale depending on whether option -t has been used
- * or not.
- *
- * IN:
- * @curr       Index in array for current sample statistics.
- *
- * RETURNS:
- * 1 if an error was detected, or 0 otherwise.
- ***************************************************************************
-*/
-int sar_get_record_timestamp_struct(int curr)
-{
-       struct tm *ltm;
-
-       /* Check if option -t was specified on the command line */
-       if (PRINT_TRUE_TIME(flags)) {
-               /* -t */
-               rectime.tm_hour = record_hdr[curr].hour;
-               rectime.tm_min  = record_hdr[curr].minute;
-               rectime.tm_sec  = record_hdr[curr].second;
-       }
-       else {
-               if ((ltm = localtime((const time_t *) &record_hdr[curr].ust_time)) == NULL)
-                       /*
-                        * An error was detected.
-                        * The rectime structure has NOT been updated.
-                        */
-                       return 1;
-
-               rectime = *ltm;
-       }
-
-       return 0;
-}
-
 /*
  ***************************************************************************
  * Determine if a stat header line has to be displayed.
@@ -354,7 +339,7 @@ int check_line_hdr(void)
                                        rc = TRUE;
                                }
                        }
-                       else if (act[i]->nr > 1) {
+                       else if (act[i]->nr_ini > 1) {
                                rc = TRUE;
                        }
                        /* Stop now since we have only one selected activity */
@@ -365,34 +350,6 @@ int check_line_hdr(void)
        return rc;
 }
 
-/*
- ***************************************************************************
- * Set current record's timestamp string.
- *
- * IN:
- * @curr       Index in array for current sample statistics.
- * @len                Maximum length of timestamp string.
- *
- * OUT:
- * @cur_time   Timestamp string.
- *
- * RETURNS:
- * 1 if an error was detected, or 0 otherwise.
- ***************************************************************************
-*/
-int set_record_timestamp_string(int curr, char *cur_time, int len)
-{
-       /* Fill timestamp structure */
-       if (sar_get_record_timestamp_struct(curr))
-               /* Error detected */
-               return 1;
-
-       /* Set cur_time date value */
-       strftime(cur_time, len, "%X", &rectime);
-
-       return 0;
-}
-
 /*
  ***************************************************************************
  * Print statistics average.
@@ -409,23 +366,14 @@ int set_record_timestamp_string(int curr, char *cur_time, int len)
 void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
 {
        int i;
-       unsigned long long itv, g_itv;
-       static __nr_t cpu_nr = -1;
-
-       if (cpu_nr < 0)
-               cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
-
-       /* Interval value in jiffies */
-       g_itv = get_interval(record_hdr[2].uptime, record_hdr[curr].uptime);
+       unsigned long long itv;
 
-       if (cpu_nr > 1)
-               itv = get_interval(record_hdr[2].uptime0, record_hdr[curr].uptime0);
-       else
-               itv = g_itv;
+       /* Interval value in 1/100th of a second */
+       itv = get_interval(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs);
 
        strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN);
        timestamp[curr][TIMESTAMP_LEN - 1] = '\0';
-       strcpy(timestamp[!curr], timestamp[curr]);
+       memcpy(timestamp[!curr], timestamp[curr], TIMESTAMP_LEN);
 
        /* Test stdout */
        TEST_STDOUT(STDOUT_FILENO);
@@ -435,10 +383,9 @@ void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
                if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
                        continue;
 
-               if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
+               if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
                        /* Display current average activity statistics */
-                       (*act[i]->f_print_avg)(act[i], 2, curr,
-                                              NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
+                       (*act[i]->f_print_avg)(act[i], 2, curr, itv);
                }
        }
 
@@ -454,6 +401,7 @@ void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
 /*
  ***************************************************************************
  * Print system statistics.
+ * This is called when we read stats either from a file or from sadc.
  *
  * IN:
  * @curr               Index in array for current sample statistics.
@@ -479,13 +427,9 @@ void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
                int use_tm_end, int reset, unsigned int act_id, int reset_cd)
 {
-       int i;
-       unsigned long long itv, g_itv;
+       int i, prev_hour;
+       unsigned long long itv;
        static int cross_day = 0;
-       static __nr_t cpu_nr = -1;
-
-       if (cpu_nr < 0)
-               cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
 
        if (reset_cd) {
                /*
@@ -503,46 +447,49 @@ int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
 
        /* Check time (1) */
        if (read_from_file) {
-               if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0,
+               if (!next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs,
                                reset, interval))
                        /* Not close enough to desired interval */
                        return 0;
        }
 
-       /* Set previous timestamp */
-       if (set_record_timestamp_string(!curr, timestamp[!curr], 16))
+       /* Get then set previous timestamp */
+       if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[!curr],
+                                          &rectime))
                return 0;
-       /* Set current timestamp */
-       if (set_record_timestamp_string(curr,  timestamp[curr],  16))
+       prev_hour = rectime.tm_hour;
+       set_record_timestamp_string(flags, &record_hdr[!curr],
+                                   NULL, timestamp[!curr], TIMESTAMP_LEN, &rectime);
+
+       /* Get then set current timestamp */
+       if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[curr],
+                                          &rectime))
                return 0;
+       set_record_timestamp_string(flags, &record_hdr[curr],
+                                   NULL, timestamp[curr], TIMESTAMP_LEN, &rectime);
 
-       /* Check if we are beginning a new day */
+       /*
+        * Check if we are beginning a new day.
+        * Use rectime.tm_hour and prev_hour instead of record_hdr[].hour for comparison
+        * to take into account the current timezone (hours displayed will depend on the
+        * TZ variable value).
+        */
        if (use_tm_start && record_hdr[!curr].ust_time &&
            (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
-           (record_hdr[curr].hour < record_hdr[!curr].hour)) {
+           (rectime.tm_hour < prev_hour)) {
                cross_day = 1;
        }
 
-       if (cross_day) {
-               /*
-                * This is necessary if we want to properly handle something like:
-                * sar -s time_start -e time_end with
-                * time_start(day D) > time_end(day D+1)
-                */
-               rectime.tm_hour +=24;
-       }
-
        /* Check time (2) */
-       if (use_tm_start && (datecmp(&rectime, &tm_start) < 0))
+       if (use_tm_start && (datecmp(&rectime, &tm_start, cross_day) < 0))
                /* it's too soon... */
                return 0;
 
-       /* Get interval values */
-       get_itv_value(&record_hdr[curr], &record_hdr[!curr],
-                     cpu_nr, &itv, &g_itv);
+       /* Get interval value in 1/100th of a second */
+       get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv);
 
        /* Check time (3) */
-       if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) {
+       if (use_tm_end && (datecmp(&rectime, &tm_end, cross_day) > 0)) {
                /* It's too late... */
                *cnt = 0;
                return 0;
@@ -558,10 +505,9 @@ int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
                if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
                        continue;
 
-               if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
+               if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
                        /* Display current activity statistics */
-                       (*act[i]->f_print)(act[i], !curr, curr,
-                                          NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
+                       (*act[i]->f_print)(act[i], !curr, curr, itv);
                }
        }
 
@@ -589,14 +535,21 @@ void write_stats_startup(int curr)
        record_hdr[!curr].ust_time    = record_hdr[curr].ust_time;
 
        for (i = 0; i < NR_ACT; i++) {
-               if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
+               if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
+                       /*
+                        * Using nr[curr] and not nr[!curr] below because we initialize
+                        * reference structures for each structure that has been
+                        * currently read in memory.
+                        * No problem with buffers allocation since they all have the
+                        * same size.
+                        */
                        memset(act[i]->buf[!curr], 0,
-                              (size_t) act[i]->msize * (size_t) act[i]->nr * (size_t) act[i]->nr2);
+                              (size_t) act[i]->msize * (size_t) act[i]->nr[curr] * (size_t) act[i]->nr2);
                }
        }
 
        flags |= S_F_SINCE_BOOT;
-       dis = TRUE;
+       dish = TRUE;
 
        write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
                    ALL_ACTIVITIES, TRUE);
@@ -618,9 +571,9 @@ void write_stats_startup(int curr)
  * 1 if end of file has been reached, 0 otherwise.
  ***************************************************************************
  */
-int sa_read(void *buffer, int size)
+int sa_read(void *buffer, size_t size)
 {
-       int n;
+       ssize_t n;
 
        while (size) {
 
@@ -641,63 +594,52 @@ int sa_read(void *buffer, int size)
 
 /*
  ***************************************************************************
- * Print a Linux restart message (contents of a RESTART record) or a
- * comment (contents of a COMMENT record).
+ * Display a restart message (contents of a R_RESTART record).
  *
  * IN:
- * @curr               Index in array for current sample statistics.
- * @use_tm_start       Set to TRUE if option -s has been used.
- * @use_tm_end         Set to TRUE if option -e has been used.
- * @rtype              Record type to display.
- * @ifd                        Input file descriptor.
- * @file               Name of file being read.
- * @file_magic         file_magic structure filled with file magic header
- *                     data.
- *
- * RETURNS:
- * 1 if the record has been successfully displayed, and 0 otherwise.
+ * @tab                Number of tabulations (unused here).
+ * @action     Action expected from current function (unused here).
+ * @cur_date   Date string of current restart message (unused here).
+ * @cur_time   Time string of current restart message.
+ * @utc                True if @cur_time is expressed in UTC (unused here).
+ * @file_hdr   System activity file standard header.
+ * @record_hdr Current record header (unused here).
  ***************************************************************************
  */
-int sar_print_special(int curr, int use_tm_start, int use_tm_end, int rtype,
-                     int ifd, char *file, struct file_magic *file_magic)
+__printf_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time,
+                                 int utc, struct file_header *file_hdr,
+                                 struct record_header *record_hdr)
 {
-       char cur_time[26];
-       int dp = 1;
-       unsigned int new_cpu_nr;
-
-       if (set_record_timestamp_string(curr, cur_time, 26))
-               return 0;
-
-       /* The record must be in the interval specified by -s/-e options */
-       if ((use_tm_start && (datecmp(&rectime, &tm_start) < 0)) ||
-           (use_tm_end && (datecmp(&rectime, &tm_end) > 0))) {
-               dp = 0;
-       }
-
-       if (rtype == R_RESTART) {
-               /* Don't forget to read the volatile activities structures */
-               new_cpu_nr = read_vol_act_structures(ifd, act, file, file_magic,
-                                                    file_hdr.sa_vol_act_nr);
-
-               if (dp) {
-                       printf("\n%-11s       LINUX RESTART\t(%d CPU)\n",
-                              cur_time, new_cpu_nr > 1 ? new_cpu_nr - 1 : 1);
-                       return 1;
-               }
-       }
-       else if (rtype == R_COMMENT) {
-               char file_comment[MAX_COMMENT_LEN];
+       char restart[64];
 
-               /* Don't forget to read comment record even if it won't be displayed... */
-               replace_nonprintable_char(ifd, file_comment);
+       printf("\n%-11s", cur_time);
+       sprintf(restart, "  LINUX RESTART\t(%d CPU)\n",
+               file_hdr->sa_cpu_nr > 1 ? file_hdr->sa_cpu_nr - 1 : 1);
+       cprintf_s(IS_RESTART, "%s", restart);
 
-               if (dp && DISPLAY_COMMENT(flags)) {
-                       printf("%-11s  COM %s\n", cur_time, file_comment);
-                       return 1;
-               }
-       }
+}
 
-       return 0;
+/*
+ ***************************************************************************
+ * Display a comment (contents of R_COMMENT record).
+ *
+ * IN:
+ * @tab                Number of tabulations (unused here).
+ * @action     Action expected from current function (unused here).
+ * @cur_date   Date string of current comment (unused here).
+ * @cur_time   Time string of current comment.
+ * @utc                True if @cur_time is expressed in UTC (unused here).
+ * @comment    Comment to display.
+ * @file_hdr   System activity file standard header (unused here).
+ * @record_hdr Current record header (unused here).
+ ***************************************************************************
+ */
+__print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
+                                 char *comment, struct file_header *file_hdr,
+                                 struct record_header *record_hdr)
+{
+       printf("%-11s", cur_time);
+       cprintf_s(IS_COMMENT, "  COM %s\n", comment);
 }
 
 /*
@@ -714,7 +656,18 @@ void read_sadc_stat_bunch(int curr)
 
        /* Read record header (type is always R_STATS since it is read from sadc) */
        if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
-               print_read_error();
+               /*
+                * SIGINT (sent by sadc) is likely to be received
+                * while we are stuck in sa_read().
+                * If this happens then no data have to be read.
+                */
+               if (sigint_caught)
+                       return;
+
+#ifdef DEBUG
+               fprintf(stderr, "%s: Record header\n", __FUNCTION__);
+#endif
+               print_read_error(END_OF_DATA_UNEXPECTED);
        }
 
        for (i = 0; i < NR_ACT; i++) {
@@ -723,8 +676,41 @@ void read_sadc_stat_bunch(int curr)
                        continue;
                p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
 
-               if (sa_read(act[p]->buf[curr], act[p]->fsize * act[p]->nr * act[p]->nr2)) {
-                       print_read_error();
+               if (HAS_COUNT_FUNCTION(act[p]->options)) {
+                       if (sa_read(&(act[p]->nr[curr]), sizeof(__nr_t))) {
+#ifdef DEBUG
+                               fprintf(stderr, "%s: Nb of items\n", __FUNCTION__);
+#endif
+                               print_read_error(END_OF_DATA_UNEXPECTED);
+                       }
+                       if ((act[p]->nr[curr] > act[p]->nr_max) || (act[p]->nr[curr] < 0)) {
+#ifdef DEBUG
+                               fprintf(stderr, "%s: %s: nr=%d nr_max=%d\n",
+                                       __FUNCTION__, act[p]->name, act[p]->nr[curr], act[p]->nr_max);
+#endif
+                               print_read_error(INCONSISTENT_INPUT_DATA);
+                       }
+                       if (act[p]->nr[curr] > act[p]->nr_allocated) {
+                               reallocate_all_buffers(act[p], act[p]->nr[curr]);
+                       }
+
+
+                       /*
+                        * For persistent activities, we must make sure that no statistics
+                         * from a previous iteration remain, especially if the number
+                         * of structures read is smaller than @nr_ini.
+                         */
+                        if (HAS_PERSISTENT_VALUES(act[p]->options)) {
+                            memset(act[p]->buf[curr], 0,
+                                   (size_t) act[p]->fsize * (size_t) act[p]->nr_ini * (size_t) act[p]->nr2);
+                        }
+                }
+               if (sa_read(act[p]->buf[curr],
+                           (size_t) act[p]->fsize * (size_t) act[p]->nr[curr] * (size_t) act[p]->nr2)) {
+#ifdef DEBUG
+                       fprintf(stderr, "%s: Statistics\n", __FUNCTION__);
+#endif
+                       print_read_error(END_OF_DATA_UNEXPECTED);
                }
        }
 }
@@ -742,24 +728,30 @@ void read_sadc_stat_bunch(int curr)
  * @file_actlst        List of activities in file.
  * @file       Name of file being read.
  * @file_magic file_magic structure filled with file magic header data.
+ * @rec_hdr_tmp        Temporary buffer where current record header will be saved.
+ * @endian_mismatch
+ *             TRUE if file's data don't match current machine's endianness.
+ * @arch_64    TRUE if file's data come from a 64 bit machine.
+ * @b_size     Size of @rec_hdr_tmp buffer.
  *
  * OUT:
  * @curr       Index in array for next sample statistics.
  * @cnt                Number of remaining lines of stats to write.
  * @eosaf      Set to TRUE if EOF (end of file) has been reached.
- * @reset      Set to TRUE if last_uptime variable should be
- *             reinitialized (used in next_slice() function).
+ * @reset      Set to TRUE if last_uptime variable should be reinitialized
+ *             (used in next_slice() function).
  ***************************************************************************
  */
 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
                           int rows, unsigned int act_id, int *reset,
                           struct file_activity *file_actlst, char *file,
-                          struct file_magic *file_magic)
+                          struct file_magic *file_magic, void *rec_hdr_tmp,
+                          int endian_mismatch, int arch_64, size_t b_size)
 {
        int p, reset_cd;
        unsigned long lines = 0;
        unsigned char rtype;
-       int davg = 0, next, inc;
+       int davg = 0, next, inc = 0;
 
        if (lseek(ifd, fpos, SEEK_SET) < fpos) {
                perror("lseek");
@@ -774,44 +766,52 @@ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf
 
        *cnt  = count;
 
-       /* Assess number of lines printed */
+       /* Assess number of lines printed when a bitmap is used */
        p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
        if (act[p]->bitmap) {
                inc = count_bits(act[p]->bitmap->b_array,
                                 BITMAP_SIZE(act[p]->bitmap->b_size));
        }
-       else {
-               inc = act[p]->nr;
-       }
-
        reset_cd = 1;
 
        do {
-               /* Display count lines of stats */
-               *eosaf = sa_fread(ifd, &record_hdr[*curr],
-                                 RECORD_HEADER_SIZE, SOFT_SIZE);
+               /*
+                * Display <count> lines of stats.
+                * Start with reading current sample's record header.
+                */
+               *eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[*curr],
+                                        &file_hdr, arch_64, endian_mismatch, UEOF_STOP, b_size);
                rtype = record_hdr[*curr].record_type;
 
                if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
                        /* Read the extra fields since it's not a special record */
-                       read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst);
+                       read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst,
+                                            endian_mismatch, arch_64, file, file_magic, UEOF_STOP);
                }
 
                if ((lines >= rows) || !lines) {
                        lines = 0;
-                       dis = 1;
+                       dish = 1;
                }
                else
-                       dis = 0;
+                       dish = 0;
 
                if (!*eosaf && (rtype != R_RESTART)) {
 
                        if (rtype == R_COMMENT) {
                                /* Display comment */
-                               next = sar_print_special(*curr, tm_start.use, tm_end.use,
-                                                    R_COMMENT, ifd, file, file_magic);
-                               if (next) {
-                                       /* A line of comment was actually displayed */
+                               next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
+                                                           &tm_start, &tm_end, R_COMMENT, ifd,
+                                                           &rectime, file, 0,
+                                                           file_magic, &file_hdr, act, &sar_fmt,
+                                                           endian_mismatch, arch_64);
+                               if (next && lines) {
+                                       /*
+                                        * A line of comment was actually displayed: Count it in the
+                                        * total number of displayed lines.
+                                        * If no lines of stats had been previously displayed, ignore it
+                                        * to make sure the header line will be displayed.
+                                        */
                                        lines++;
                                }
                                continue;
@@ -824,16 +824,29 @@ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf
                        if (next && (*cnt > 0)) {
                                (*cnt)--;
                        }
+
                        if (next) {
                                davg++;
-                               *curr ^=1;
-                               lines += inc;
+                               *curr ^= 1;
+
+                               if (inc) {
+                                       lines += inc;
+                               }
+                               else {
+                                       lines += act[p]->nr[*curr];
+                               }
                        }
                        *reset = FALSE;
                }
        }
        while (*cnt && !*eosaf && (rtype != R_RESTART));
 
+       /*
+        * At this moment, if we had a R_RESTART record, we still haven't read
+        * the number of CPU following it (nor the possible extra structures).
+        * But in this case, we always have @cnt != 0.
+        */
+
        if (davg) {
                write_stats_avg(!*curr, USE_SA_FILE, act_id);
        }
@@ -875,7 +888,11 @@ void read_header_data(void)
                                _("Using a wrong data collector from a different sysstat version\n"));
                }
 
-               goto input_error;
+#ifdef DEBUG
+               fprintf(stderr, "%s: sysstat_magic=%x format_magic=%x version=%s\n",
+                       __FUNCTION__, file_magic.sysstat_magic, file_magic.format_magic, version);
+#endif
+               print_read_error(INCONSISTENT_INPUT_DATA);
        }
 
        /*
@@ -885,31 +902,67 @@ void read_header_data(void)
         * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
         */
        if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
-               print_read_error();
+#ifdef DEBUG
+               fprintf(stderr, "%s: File header\n", __FUNCTION__);
+#endif
+               print_read_error(END_OF_DATA_UNEXPECTED);
+       }
+
+       /* All activities are not necessarily selected, but NR_ACT is a max */
+       if (file_hdr.sa_act_nr > NR_ACT) {
+#ifdef DEBUG
+               fprintf(stderr, "%s: sa_act_nr=%d\n", __FUNCTION__, file_hdr.sa_act_nr);
+#endif
+               print_read_error(INCONSISTENT_INPUT_DATA);
        }
 
-       if (file_hdr.sa_act_nr > NR_ACT)
-               goto input_error;
+       if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
+           (file_hdr.rec_size != RECORD_HEADER_SIZE)) {
+#ifdef DEBUG
+               fprintf(stderr, "%s: act_size=%u/%lu rec_size=%u/%lu\n", __FUNCTION__,
+                       file_hdr.act_size, FILE_ACTIVITY_SIZE, file_hdr.rec_size, RECORD_HEADER_SIZE);
+#endif
+               print_read_error(INCONSISTENT_INPUT_DATA);
+       }
 
        /* Read activity list */
        for (i = 0; i < file_hdr.sa_act_nr; i++) {
 
                if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
-                       print_read_error();
+#ifdef DEBUG
+                       fprintf(stderr, "%s: File activity (%d)\n", __FUNCTION__, i);
+#endif
+                       print_read_error(END_OF_DATA_UNEXPECTED);
                }
 
                p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
 
                if ((p < 0) || (act[p]->fsize != file_act.size)
-                           || !file_act.nr
-                           || !file_act.nr2
-                           || (act[p]->magic != file_act.magic))
+                           || (act[p]->gtypes_nr[0] != file_act.types_nr[0])
+                           || (act[p]->gtypes_nr[1] != file_act.types_nr[1])
+                           || (act[p]->gtypes_nr[2] != file_act.types_nr[2])
+                           || (file_act.nr <= 0)
+                           || (file_act.nr2 <= 0)
+                           || (act[p]->magic != file_act.magic)) {
+#ifdef DEBUG
+                       if (p < 0) {
+                               fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
+                       }
+                       else {
+                               fprintf(stderr, "%s: %s: size=%d/%d magic=%x/%x nr=%d nr2=%d types=%d,%d,%d/%d,%d,%d\n",
+                                       __FUNCTION__, act[p]->name, act[p]->fsize, file_act.size,
+                                       act[p]->magic, file_act.magic, file_act.nr, file_act.nr2,
+                                       act[p]->gtypes_nr[0], act[p]->gtypes_nr[1], act[p]->gtypes_nr[2],
+                                       file_act.types_nr[0], file_act.types_nr[1], file_act.types_nr[2]);
+                       }
+#endif
                        /* Remember that we are reading data from sadc and not from a file... */
-                       goto input_error;
+                       print_read_error(INCONSISTENT_INPUT_DATA);
+               }
 
-               id_seq[i]   = file_act.id;      /* We necessarily have "i < NR_ACT" */
-               act[p]->nr  = file_act.nr;
-               act[p]->nr2 = file_act.nr2;
+               id_seq[i]      = file_act.id;   /* We necessarily have "i < NR_ACT" */
+               act[p]->nr_ini = file_act.nr;
+               act[p]->nr2    = file_act.nr2;
        }
 
        while (i < NR_ACT) {
@@ -920,13 +973,6 @@ void read_header_data(void)
        reverse_check_act(file_hdr.sa_act_nr);
 
        return;
-
-input_error:
-
-       /* Strange data sent by sadc...! */
-       fprintf(stderr, _("Inconsistent input data\n"));
-
-       exit(3);
 }
 
 /*
@@ -941,6 +987,7 @@ void read_stats_from_file(char from_file[])
 {
        struct file_magic file_magic;
        struct file_activity *file_actlst = NULL;
+       char rec_hdr_tmp[MAX_RECORD_HEADER_SIZE];
        int curr = 1, i, p;
        int ifd, rtype;
        int rows, eosaf = TRUE, reset = FALSE;
@@ -951,15 +998,14 @@ void read_stats_from_file(char from_file[])
        rows = get_win_height();
 
        /* Read file headers and activity list */
-       check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr,
-                         &file_actlst, id_seq, FALSE);
+       check_file_actlst(&ifd, from_file, act, flags, &file_magic, &file_hdr,
+                         &file_actlst, id_seq, FALSE, &endian_mismatch, &arch_64);
 
        /* Perform required allocations */
        allocate_structures(act);
 
        /* Print report header */
-       print_report_hdr(flags, &rectime, &file_hdr,
-                        act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
+       print_report_hdr(flags, &rectime, &file_hdr);
 
        /* Read system statistics from file */
        do {
@@ -968,14 +1014,18 @@ void read_stats_from_file(char from_file[])
                 * (try to) get another one.
                 */
                do {
-                       if (sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE))
+                       if (read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[0], &file_hdr,
+                                           arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp))) {
                                /* End of sa data file */
                                return;
+                       }
 
                        rtype = record_hdr[0].record_type;
                        if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
-                               sar_print_special(0, tm_start.use, tm_end.use, rtype,
-                                                 ifd, from_file, &file_magic);
+                               print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
+                                                    &tm_start, &tm_end, rtype, ifd,
+                                                    &rectime, from_file, 0, &file_magic,
+                                                    &file_hdr, act, &sar_fmt, endian_mismatch, arch_64);
                        }
                        else {
                                /*
@@ -983,8 +1033,10 @@ void read_stats_from_file(char from_file[])
                                 * So read now the extra fields.
                                 */
                                read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
-                                                    file_actlst);
-                               if (sar_get_record_timestamp_struct(0))
+                                                    file_actlst, endian_mismatch, arch_64,
+                                                    from_file, &file_magic, UEOF_STOP);
+                               if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
+                                                                  &record_hdr[0], &rectime))
                                        /*
                                         * An error was detected.
                                         * The timestamp hasn't been updated.
@@ -993,8 +1045,8 @@ void read_stats_from_file(char from_file[])
                        }
                }
                while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
-                      (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) ||
-                      (tm_end.use && (datecmp(&rectime, &tm_end) >=0)));
+                      (tm_start.use && (datecmp(&rectime, &tm_start, FALSE) < 0)) ||
+                      (tm_end.use && (datecmp(&rectime, &tm_end, FALSE) >= 0)));
 
                /* Save the first stats collected. Will be used to compute the average */
                copy_structures(act, id_seq, record_hdr, 2, 0);
@@ -1010,6 +1062,10 @@ void read_stats_from_file(char from_file[])
                /*
                 * Read and write stats located between two possible Linux restarts.
                 * Activities that should be displayed are saved in id_seq[] array.
+                * Since we are reading from a file, we print all the stats for an
+                * activity before displaying the next activity.
+                * id_seq[] has been created in check_file_actlst(), retaining only
+                * activities known by current sysstat version.
                 */
                for (i = 0; i < NR_ACT; i++) {
 
@@ -1023,7 +1079,8 @@ void read_stats_from_file(char from_file[])
                        if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
                                handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
                                                      act[p]->id, &reset, file_actlst,
-                                                     from_file, &file_magic);
+                                                     from_file, &file_magic, rec_hdr_tmp,
+                                                     endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
                        }
                        else {
                                unsigned int optf, msk;
@@ -1034,30 +1091,43 @@ void read_stats_from_file(char from_file[])
                                        if ((act[p]->opt_flags & 0xff) & msk) {
                                                act[p]->opt_flags &= (0xffffff00 + msk);
 
-                                               handle_curr_act_stats(ifd, fpos, &curr, &cnt,
-                                                                     &eosaf, rows, act[p]->id,
-                                                                     &reset, file_actlst,
-                                                                     from_file, &file_magic);
+                                               handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
+                                                                     rows, act[p]->id, &reset, file_actlst,
+                                                                     from_file, &file_magic, rec_hdr_tmp,
+                                                                     endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
                                                act[p]->opt_flags = optf;
                                        }
                                }
                        }
                }
-
                if (!cnt) {
-                       /* Go to next Linux restart, if possible */
+                       /*
+                        * Go to next Linux restart, if possible.
+                        * Note: If we have @cnt == 0 then the last record we read was not a R_RESTART one
+                        * (else we would have had @cnt != 0, i.e. we would have stopped reading previous activity
+                        * because such a R_RESTART record would have been read, not because all the <count> lines
+                        * had been printed).
+                        * Remember @cnt is decremented only when a real line of stats have been displayed
+                        * (not when a special record has been read).
+                        */
                        do {
-                               eosaf = sa_fread(ifd, &record_hdr[curr], RECORD_HEADER_SIZE,
-                                                SOFT_SIZE);
+                               /* Read next record header */
+                               eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr],
+                                                       &file_hdr, arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp));
                                rtype = record_hdr[curr].record_type;
+
                                if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
                                        read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
-                                                            file_actlst);
+                                                            file_actlst, endian_mismatch, arch_64,
+                                                            from_file, &file_magic, UEOF_STOP);
                                }
                                else if (!eosaf && (rtype == R_COMMENT)) {
                                        /* This was a COMMENT record: print it */
-                                       sar_print_special(curr, tm_start.use, tm_end.use, R_COMMENT,
-                                                         ifd, from_file, &file_magic);
+                                       print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
+                                                            &tm_start, &tm_end, R_COMMENT, ifd,
+                                                            &rectime, from_file, 0,
+                                                            &file_magic, &file_hdr, act, &sar_fmt,
+                                                            endian_mismatch, arch_64);
                                }
                        }
                        while (!eosaf && (rtype != R_RESTART));
@@ -1065,8 +1135,11 @@ void read_stats_from_file(char from_file[])
 
                /* The last record we read was a RESTART one: Print it */
                if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
-                       sar_print_special(curr, tm_start.use, tm_end.use, R_RESTART,
-                                         ifd, from_file, &file_magic);
+                       print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
+                                            &tm_start, &tm_end, R_RESTART, ifd,
+                                            &rectime, from_file, 0,
+                                            &file_magic, &file_hdr, act, &sar_fmt,
+                                            endian_mismatch, arch_64);
                }
        }
        while (!eosaf);
@@ -1095,8 +1168,8 @@ void read_stats(void)
        read_header_data();
 
        if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
-               fprintf(stderr, _("Requested activities not available\n"));
-               exit(1);
+               /* Requested activities not available: Exit */
+               print_collect_error();
        }
 
        /* Determine if a stat line header has to be displayed */
@@ -1108,8 +1181,7 @@ void read_stats(void)
        allocate_structures(act);
 
        /* Print report header */
-       print_report_hdr(flags, &rectime, &file_hdr,
-                        act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
+       print_report_hdr(flags, &rectime, &file_hdr);
 
        /* Read system statistics sent by the data collector */
        read_sadc_stat_bunch(0);
@@ -1133,11 +1205,19 @@ void read_stats(void)
 
                /* Get stats */
                read_sadc_stat_bunch(curr);
+               if (sigint_caught) {
+                       /*
+                        * SIGINT signal caught (it is sent by sadc).
+                        * => Display average stats.
+                        */
+                       curr ^= 1; /* No data retrieved from last read */
+                       break;
+               }
 
                /* Print results */
                if (!dis_hdr) {
-                       dis = lines / rows;
-                       if (dis) {
+                       dish = lines / rows;
+                       if (dish) {
                                lines %= rows;
                        }
                        lines++;
@@ -1155,20 +1235,20 @@ void read_stats(void)
                        count--;
                }
                if (count) {
-                       if (sigint_caught) {
-                               /* SIGINT signal caught => Display average stats */
-                               count = 0;
-                       }
-                       else {
-                               curr ^= 1;
-                       }
+                       curr ^= 1;
                }
        }
        while (count);
 
-       /* Print statistics average */
-       dis = dis_hdr;
-       write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
+       /*
+        * Print statistics average.
+        * At least one line of stats must have been displayed for this.
+        * (There may be no lines at all if we press Ctrl/C immediately).
+        */
+       dish = dis_hdr;
+       if (avg_count) {
+               write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
+       }
 }
 
 /*
@@ -1178,14 +1258,11 @@ void read_stats(void)
  */
 int main(int argc, char **argv)
 {
-       int i, rc, opt = 1, args_idx = 2;
+       int i, rc, opt = 1, args_idx = 1, p, q;
        int fd[2];
        int day_offset = 0;
        char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
-       char ltemp[20];
-
-       /* Get HZ */
-       get_HZ();
+       char ltemp[1024];
 
        /* Compute page shift in kB */
        get_kb_shift();
@@ -1197,6 +1274,9 @@ int main(int argc, char **argv)
        init_nls();
 #endif
 
+       /* Init color strings */
+       init_colors();
+
        tm_start.use = tm_end.use = FALSE;
 
        /* Allocate and init activity bitmaps */
@@ -1212,14 +1292,51 @@ int main(int argc, char **argv)
                        which_sadc();
                }
 
-               else if (!strcmp(argv[opt], "-I")) {
-                       if (argv[++opt]) {
-                               /* Parse -I option */
-                               if (parse_sar_I_opt(argv, &opt, act)) {
-                                       usage(argv[0]);
-                               }
+               else if (!strncmp(argv[opt], "--dev=", 6)) {
+                       /* Parse devices entered on the command line */
+                       p = get_activity_position(act, A_DISK, EXIT_IF_NOT_FOUND);
+                       parse_sa_devices(argv[opt], act[p], MAX_DEV_LEN, &opt, 6);
+               }
+
+               else if (!strncmp(argv[opt], "--fs=", 5)) {
+                       /* Parse devices entered on the command line */
+                       p = get_activity_position(act, A_FS, EXIT_IF_NOT_FOUND);
+                       parse_sa_devices(argv[opt], act[p], MAX_FS_LEN, &opt, 5);
+               }
+
+               else if (!strncmp(argv[opt], "--iface=", 8)) {
+                       /* Parse devices entered on the command line */
+                       p = get_activity_position(act, A_NET_DEV, EXIT_IF_NOT_FOUND);
+                       parse_sa_devices(argv[opt], act[p], MAX_IFACE_LEN, &opt, 8);
+                       q = get_activity_position(act, A_NET_EDEV, EXIT_IF_NOT_FOUND);
+                       act[q]->item_list = act[p]->item_list;
+                       act[q]->item_list_sz = act[p]->item_list_sz;
+                       act[q]->options |= AO_LIST_ON_CMDLINE;
+               }
+
+               else if (!strcmp(argv[opt], "--help")) {
+                       /* Display help message */
+                       display_help(argv[0]);
+               }
+
+               else if (!strcmp(argv[opt], "--human")) {
+                       /* Display sizes in a human readable format */
+                       flags |= S_F_UNIT;
+                       opt++;
+               }
+
+               else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
+                       /* Get number of decimal places */
+                       dplaces_nr = atoi(argv[opt] + 6);
+                       if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
+                               usage(argv[0]);
                        }
-                       else {
+                       opt++;
+               }
+
+               else if (!strcmp(argv[opt], "-I")) {
+                       /* Parse -I option */
+                       if (parse_sar_I_opt(argv, &opt, &flags, act)) {
                                usage(argv[0]);
                        }
                }
@@ -1285,11 +1402,6 @@ int main(int argc, char **argv)
                        }
                }
 
-               else if (!strcmp(argv[opt], "-h")) {
-                       /* Display help message */
-                       display_help(argv[0]);
-               }
-
                else if (!strcmp(argv[opt], "-i")) {
                        if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
                                usage(argv[0]);
@@ -1302,29 +1414,33 @@ int main(int argc, char **argv)
                }
 
                else if (!strcmp(argv[opt], "-m")) {
-                       if (argv[++opt]) {
-                               /* Parse option -m */
-                               if (parse_sar_m_opt(argv, &opt, act)) {
-                                       usage(argv[0]);
-                               }
+                       if (!argv[++opt]) {
+                               usage(argv[0]);
                        }
-                       else {
+                       /* Parse option -m */
+                       if (parse_sar_m_opt(argv, &opt, act)) {
                                usage(argv[0]);
                        }
                }
 
                else if (!strcmp(argv[opt], "-n")) {
-                       if (argv[++opt]) {
-                               /* Parse option -n */
-                               if (parse_sar_n_opt(argv, &opt, act)) {
-                                       usage(argv[0]);
-                               }
+                       if (!argv[++opt]) {
+                               usage(argv[0]);
                        }
-                       else {
+                       /* Parse option -n */
+                       if (parse_sar_n_opt(argv, &opt, act)) {
                                usage(argv[0]);
                        }
                }
 
+#ifdef TEST
+               else if (!strncmp(argv[opt], "--unix_time=", 12)) {
+                       if (strspn(argv[opt] + 12, DIGITS) != strlen(argv[opt] + 12)) {
+                               usage(argv[0]);
+                       }
+                       __unix_time = atoll(argv[opt++] + 12);
+               }
+#endif
                else if ((strlen(argv[opt]) > 1) &&
                         (strlen(argv[opt]) < 4) &&
                         !strncmp(argv[opt], "-", 1) &&
@@ -1377,7 +1493,7 @@ int main(int argc, char **argv)
 
        /* 'sar' is equivalent to 'sar -f' */
        if ((argc == 1) ||
-           ((interval < 0) && !from_file[0] && !to_file[0])) {
+           (((interval < 0) || INTERVAL_SET(flags)) && !from_file[0] && !to_file[0])) {
                set_default_file(from_file, day_offset, -1);
        }
 
@@ -1393,6 +1509,10 @@ int main(int argc, char **argv)
                fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
                exit(1);
        }
+       if (USE_OPTION_A(flags)) {
+               /* Set -P ALL -I ALL if needed */
+               set_bitmaps(act, &flags);
+       }
        /* Use time start or option -i only when reading stats from a file */
        if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
                fprintf(stderr,
@@ -1424,6 +1544,10 @@ int main(int argc, char **argv)
        /* Default is CPU activity... */
        select_default_activity(act);
 
+       /* Check S_TIME_FORMAT variable contents */
+       if (!is_iso_time_fmt())
+               flags |= S_F_PREFD_TIME_OUTPUT;
+
        /* Reading stats from file: */
        if (from_file[0]) {
                if (interval < 0) {
@@ -1474,11 +1598,16 @@ int main(int argc, char **argv)
                }
                else if (!interval) {
                        strcpy(ltemp, "1");
+                       /*
+                        * Display stats since system startup: Set <interval> to 1.
+                        * <count> arg will also be set to 1 below.
+                        */
+                       salloc(args_idx++, ltemp);
                }
                else {
                        sprintf(ltemp, "%ld", interval);
                }
-               salloc(1, ltemp);
+               salloc(args_idx++, ltemp);
 
                /* Count number */
                if (count >= 0) {
@@ -1486,8 +1615,14 @@ int main(int argc, char **argv)
                        salloc(args_idx++, ltemp);
                }
 
+#ifdef TEST
+               if (__unix_time) {
+                       sprintf(ltemp, "--unix_time=%ld", __unix_time);
+                       salloc(args_idx++, ltemp);
+               }
+#endif
                /* Flags to be passed to sadc */
-               salloc(args_idx++, "-z");
+               salloc(args_idx++, "-Z");
 
                /* Writing data to a file (option -o) */
                if (to_file[0]) {
@@ -1506,27 +1641,29 @@ int main(int argc, char **argv)
                         * If option -o hasn't been used, then tell sadc
                         * to collect only activities that will be displayed.
                         */
-                       int act_id = 0;
-
+                       salloc(args_idx++, "-S");
+                       strcpy(ltemp, K_A_NULL);
                        for (i = 0; i < NR_ACT; i++) {
                                if (IS_SELECTED(act[i]->options)) {
-                                       act_id |= act[i]->group;
+                                       strcat(ltemp, ",");
+                                       strcat(ltemp, act[i]->name);
                                }
                        }
-                       if (act_id) {
-                               act_id <<= 8;
-                               snprintf(ltemp, 19, "%d", act_id);
-                               ltemp[19] = '\0';
-                               salloc(args_idx++, "-S");
-                               salloc(args_idx++, ltemp);
-                       }
+                       salloc(args_idx++, ltemp);
                }
 
                /* Last arg is NULL */
                args[args_idx] = NULL;
 
                /* Call now the data collector */
+#ifdef DEBUG
+               fprintf(stderr, "%s: 1.sadc: %s\n", __FUNCTION__, SADC_PATH);
+#endif
+
                execv(SADC_PATH, args);
+#ifdef DEBUG
+               fprintf(stderr, "%s: 2.sadc: %s\n", __FUNCTION__, SADC);
+#endif
                execvp(SADC, args);
                /*
                 * Note: Don't use execl/execlp since we don't have a fixed number of