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