]> granicus.if.org Git - sysstat/blob - sar.c
SVG: Print time zone instead of UTC in true time mode (#331)
[sysstat] / sar.c
1 /*
2  * sar: report system activity
3  * (C) 1999-2022 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> ] "
130                           "[ --int=<int_list> ]\n"
131                           "[ --dec={ 0 | 1 | 2 } ] [ --help ] [ --human ] [ --pretty ] [ --sadc ]\n"
132                           "[ -j { SID | ID | LABEL | PATH | UUID | ... } ]\n"
133                           "[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]\n"
134                           "[ -i <interval> ] [ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"));
135         exit(1);
136 }
137
138 /*
139  ***************************************************************************
140  * Display a short help message and exit.
141  *
142  * IN:
143  * @progname    Name of sysstat command
144  ***************************************************************************
145  */
146 void display_help(char *progname)
147 {
148         print_usage_title(stdout, progname);
149         printf(_("Main options and reports (report name between square brackets):\n"));
150         printf(_("\t-B\tPaging statistics [A_PAGE]\n"));
151         printf(_("\t-b\tI/O and transfer rate statistics [A_IO]\n"));
152         printf(_("\t-d\tBlock devices statistics [A_DISK]\n"));
153         printf(_("\t-F [ MOUNT ]\n"));
154         printf(_("\t\tFilesystems statistics [A_FS]\n"));
155         printf(_("\t-H\tHugepages utilization statistics [A_HUGE]\n"));
156         printf(_("\t-I { <int_list> | SUM | ALL }\n"
157                  "\t\tInterrupts statistics [A_IRQ]\n"));
158         printf(_("\t-m { <keyword> [,...] | ALL }\n"
159                  "\t\tPower management statistics [A_PWR_...]\n"
160                  "\t\tKeywords are:\n"
161                  "\t\tCPU\tCPU instantaneous clock frequency\n"
162                  "\t\tFAN\tFans speed\n"
163                  "\t\tFREQ\tCPU average clock frequency\n"
164                  "\t\tIN\tVoltage inputs\n"
165                  "\t\tTEMP\tDevices temperature\n"
166                  "\t\tUSB\tUSB devices plugged into the system\n"));
167         printf(_("\t-n { <keyword> [,...] | ALL }\n"
168                  "\t\tNetwork statistics [A_NET_...]\n"
169                  "\t\tKeywords are:\n"
170                  "\t\tDEV\tNetwork interfaces\n"
171                  "\t\tEDEV\tNetwork interfaces (errors)\n"
172                  "\t\tNFS\tNFS client\n"
173                  "\t\tNFSD\tNFS server\n"
174                  "\t\tSOCK\tSockets\t(v4)\n"
175                  "\t\tIP\tIP traffic\t(v4)\n"
176                  "\t\tEIP\tIP traffic\t(v4) (errors)\n"
177                  "\t\tICMP\tICMP traffic\t(v4)\n"
178                  "\t\tEICMP\tICMP traffic\t(v4) (errors)\n"
179                  "\t\tTCP\tTCP traffic\t(v4)\n"
180                  "\t\tETCP\tTCP traffic\t(v4) (errors)\n"
181                  "\t\tUDP\tUDP traffic\t(v4)\n"
182                  "\t\tSOCK6\tSockets\t(v6)\n"
183                  "\t\tIP6\tIP traffic\t(v6)\n"
184                  "\t\tEIP6\tIP traffic\t(v6) (errors)\n"
185                  "\t\tICMP6\tICMP traffic\t(v6)\n"
186                  "\t\tEICMP6\tICMP traffic\t(v6) (errors)\n"
187                  "\t\tUDP6\tUDP traffic\t(v6)\n"
188                  "\t\tFC\tFibre channel HBAs\n"
189                  "\t\tSOFT\tSoftware-based network processing\n"));
190         printf(_("\t-q [ <keyword> [,...] | PSI | ALL ]\n"
191                  "\t\tSystem load and pressure-stall statistics\n"
192                  "\t\tKeywords are:\n"
193                  "\t\tLOAD\tQueue length and load average statistics [A_QUEUE]\n"
194                  "\t\tCPU\tPressure-stall CPU statistics [A_PSI_CPU]\n"
195                  "\t\tIO\tPressure-stall I/O statistics [A_PSI_IO]\n"
196                  "\t\tMEM\tPressure-stall memory statistics [A_PSI_MEM]\n"));
197         printf(_("\t-r [ ALL ]\n"
198                  "\t\tMemory utilization statistics [A_MEMORY]\n"));
199         printf(_("\t-S\tSwap space utilization statistics [A_MEMORY]\n"));
200         printf(_("\t-u [ ALL ]\n"
201                  "\t\tCPU utilization statistics [A_CPU]\n"));
202         printf(_("\t-v\tKernel tables statistics [A_KTABLES]\n"));
203         printf(_("\t-W\tSwapping statistics [A_SWAP]\n"));
204         printf(_("\t-w\tTask creation and system switching statistics [A_PCSW]\n"));
205         printf(_("\t-y\tTTY devices statistics [A_SERIAL]\n"));
206         exit(0);
207 }
208
209 /*
210  ***************************************************************************
211  * Give a hint to the user about where is located the data collector.
212  ***************************************************************************
213  */
214 void which_sadc(void)
215 {
216         struct stat buf;
217
218         if (stat(SADC_PATH, &buf) < 0) {
219                 printf(_("Data collector will be sought in PATH\n"));
220         }
221         else {
222                 printf(_("Data collector found: %s\n"), SADC_PATH);
223         }
224         exit(0);
225 }
226
227 /*
228  ***************************************************************************
229  * SIGINT signal handler.
230  *
231  * IN:
232  * @sig Signal number.
233  ***************************************************************************
234  */
235 void int_handler(int sig)
236 {
237         sigint_caught = 1;
238         printf("\n");   /* Skip "^C" displayed on screen */
239
240 }
241
242 /*
243  ***************************************************************************
244  * Init some structures.
245  ***************************************************************************
246  */
247 void init_structures(void)
248 {
249         int i;
250
251         for (i = 0; i < 3; i++)
252                 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
253 }
254
255 /*
256  ***************************************************************************
257  * Allocate memory for sadc args.
258  *
259  * IN:
260  * @i           Argument number.
261  * @ltemp       Argument value.
262  ***************************************************************************
263  */
264 void salloc(int i, char *ltemp)
265 {
266         if ((args[i] = (char *) malloc(strlen(ltemp) + 1)) == NULL) {
267                 perror("malloc");
268                 exit(4);
269         }
270         strcpy(args[i], ltemp);
271 }
272
273 /*
274  ***************************************************************************
275  * Display an error message.
276  *
277  * IN:
278  * @error_code  Code of error message to display.
279  ***************************************************************************
280  */
281 void print_read_error(int error_code)
282 {
283         switch (error_code) {
284
285                 case END_OF_DATA_UNEXPECTED:
286                         /* Happens when the data collector doesn't send enough data */
287                         fprintf(stderr, _("End of data collecting unexpected\n"));
288                         break;
289
290                 default:
291                         /* Strange data sent by sadc...! */
292                         fprintf(stderr, _("Inconsistent input data\n"));
293                         break;
294         }
295         exit(3);
296 }
297
298 /*
299  ***************************************************************************
300  * Check that every selected activity actually belongs to the sequence list.
301  * If not, then the activity should be unselected since it will not be sent
302  * by sadc. An activity can be not sent if its number of items is zero.
303  *
304  * IN:
305  * @act_nr      Size of sequence list.
306  ***************************************************************************
307  */
308 void reverse_check_act(unsigned int act_nr)
309 {
310         int i, j;
311
312         for (i = 0; i < NR_ACT; i++) {
313
314                 if (IS_SELECTED(act[i]->options)) {
315
316                         for (j = 0; j < act_nr; j++) {
317                                 if (id_seq[j] == act[i]->id)
318                                         break;
319                         }
320                         if (j == act_nr)
321                                 act[i]->options &= ~AO_SELECTED;
322                 }
323         }
324 }
325
326 /*
327  ***************************************************************************
328  * Determine if a stat header line has to be displayed.
329  *
330  * RETURNS:
331  * TRUE if a header line has to be displayed.
332  ***************************************************************************
333 */
334 int check_line_hdr(void)
335 {
336         int i, rc = FALSE;
337
338         /* Get number of options entered on the command line */
339         if (get_activity_nr(act, AO_SELECTED, COUNT_OUTPUTS) > 1)
340                 return TRUE;
341
342         for (i = 0; i < NR_ACT; i++) {
343                 if (IS_SELECTED(act[i]->options)) {
344                         /* Special processing for activities using a bitmap */
345                         if (act[i]->bitmap) {
346                                 if (count_bits(act[i]->bitmap->b_array,
347                                                BITMAP_SIZE(act[i]->bitmap->b_size)) > 1) {
348                                         rc = TRUE;
349                                 }
350                         }
351                         else if (act[i]->nr_ini > 1) {
352                                 rc = TRUE;
353                         }
354                         /* Stop now since we have only one selected activity */
355                         break;
356                 }
357         }
358
359         return rc;
360 }
361
362 /*
363  ***************************************************************************
364  * Print statistics average.
365  *
366  * IN:
367  * @curr                Index in array for current sample statistics.
368  * @read_from_file      Set to TRUE if stats are read from a system activity
369  *                      data file.
370  * @act_id              Activity that can be displayed, or ~0 for all.
371  *                      Remember that when reading stats from a file, only
372  *                      one activity can be displayed at a time.
373  ***************************************************************************
374  */
375 void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
376 {
377         int i;
378         unsigned long long itv;
379
380         /* Interval value in 1/100th of a second */
381         itv = get_interval(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs);
382
383         strncpy(timestamp[curr], _("Average:"), sizeof(timestamp[curr]));
384         timestamp[curr][sizeof(timestamp[curr]) - 1] = '\0';
385         memcpy(timestamp[!curr], timestamp[curr], sizeof(timestamp[!curr]));
386
387         /* Test stdout */
388         TEST_STDOUT(STDOUT_FILENO);
389
390         for (i = 0; i < NR_ACT; i++) {
391
392                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
393                         continue;
394
395                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
396                         /* Display current average activity statistics */
397                         (*act[i]->f_print_avg)(act[i], 2, curr, itv);
398                 }
399         }
400
401         if (read_from_file) {
402                 /*
403                  * Reset number of lines printed only if we read stats
404                  * from a system activity file.
405                  */
406                 avg_count = 0;
407         }
408 }
409
410 /*
411  ***************************************************************************
412  * Print system statistics.
413  * This is called when we read stats either from a file or from sadc.
414  *
415  * IN:
416  * @curr                Index in array for current sample statistics.
417  * @read_from_file      Set to TRUE if stats are read from a system activity
418  *                      data file.
419  * @use_tm_start        Set to TRUE if option -s has been used.
420  * @use_tm_end          Set to TRUE if option -e has been used.
421  * @reset               Set to TRUE if last_uptime variable should be
422  *                      reinitialized (used in next_slice() function).
423  * @act_id              Activity that can be displayed or ~0 for all.
424  *                      Remember that when reading stats from a file, only
425  *                      one activity can be displayed at a time.
426  * @reset_cd            TRUE if static cross_day variable should be reset
427  *                      (see below).
428  *
429  * OUT:
430  * @cnt                 Number of remaining lines to display.
431  *
432  * RETURNS:
433  * 1 if stats have been successfully displayed, and 0 otherwise.
434  ***************************************************************************
435  */
436 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
437                 int use_tm_end, int reset, unsigned int act_id, int reset_cd)
438 {
439         int i, prev_hour, rc = 0;
440         unsigned long long itv;
441         static int cross_day = FALSE;
442
443         if (reset_cd) {
444                 /*
445                  * cross_day is a static variable that is set to 1 when the first
446                  * record of stats from a new day is read from a unique data file
447                  * (in the case where the file contains data from two consecutive
448                  * days). When set to TRUE, every following records timestamp will
449                  * have its hour value increased by 24.
450                  * Yet when a new activity (being read from the file) is going to
451                  * be displayed, we start reading the file from the beginning
452                  * again, and so cross_day should be reset in this case.
453                  */
454                 cross_day = FALSE;
455         }
456
457         /* Check time (1) */
458         if (read_from_file && !next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs,
459                                           reset, interval))
460                 /* Not close enough to desired interval */
461                 return 0;
462
463         /* Get then set previous timestamp */
464         if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[!curr],
465                                            &rectime))
466                 return 0;
467         prev_hour = rectime.tm_hour;
468         set_record_timestamp_string(flags, &record_hdr[!curr],
469                                     NULL, timestamp[!curr], TIMESTAMP_LEN, &rectime);
470
471         /* Get then set current timestamp */
472         if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[curr],
473                                            &rectime))
474                 return 0;
475         set_record_timestamp_string(flags, &record_hdr[curr],
476                                     NULL, timestamp[curr], TIMESTAMP_LEN, &rectime);
477
478         /*
479          * Check if we are beginning a new day.
480          * Use rectime.tm_hour and prev_hour instead of record_hdr[].hour for comparison
481          * to take into account the current timezone (hours displayed will depend on the
482          * TZ variable value).
483          */
484         if (use_tm_start && record_hdr[!curr].ust_time &&
485             (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
486             (rectime.tm_hour < prev_hour)) {
487                 cross_day = TRUE;
488         }
489
490         /* Check time (2) */
491         if (use_tm_end && (datecmp(&rectime, &tm_end, cross_day) > 0)) {
492                 /* End time exceeded */
493                 *cnt = 0;
494                 return 0;
495         }
496
497         /* Get interval value in 1/100th of a second */
498         get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv);
499
500         avg_count++;
501
502         /* Test stdout */
503         TEST_STDOUT(STDOUT_FILENO);
504
505         for (i = 0; i < NR_ACT; i++) {
506
507                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
508                         continue;
509
510                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
511                         /* Display current activity statistics */
512                         (*act[i]->f_print)(act[i], !curr, curr, itv);
513                         rc = 1;
514                 }
515         }
516
517         return rc;
518 }
519
520 /*
521  ***************************************************************************
522  * Display stats since system startup.
523  *
524  * IN:
525  * @curr        Index in array for current sample statistics.
526  ***************************************************************************
527  */
528 void write_stats_startup(int curr)
529 {
530         int i;
531
532         /* Set to 0 previous structures corresponding to boot time */
533         memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE);
534         record_hdr[!curr].record_type = R_STATS;
535         record_hdr[!curr].hour        = record_hdr[curr].hour;
536         record_hdr[!curr].minute      = record_hdr[curr].minute;
537         record_hdr[!curr].second      = record_hdr[curr].second;
538         record_hdr[!curr].ust_time    = record_hdr[curr].ust_time;
539
540         for (i = 0; i < NR_ACT; i++) {
541                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
542                         /*
543                          * Using nr[curr] and not nr[!curr] below because we initialize
544                          * reference structures for each structure that has been
545                          * currently read in memory.
546                          * No problem with buffers allocation since they all have the
547                          * same size.
548                          */
549                         memset(act[i]->buf[!curr], 0,
550                                (size_t) act[i]->msize * (size_t) act[i]->nr[curr] * (size_t) act[i]->nr2);
551                 }
552         }
553
554         flags |= S_F_SINCE_BOOT;
555         dish = TRUE;
556
557         write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
558                     ALL_ACTIVITIES, TRUE);
559
560         exit(0);
561 }
562
563 /*
564  ***************************************************************************
565  * Read data sent by the data collector.
566  *
567  * IN:
568  * @size        Number of bytes of data to read.
569  *
570  * OUT:
571  * @buffer      Buffer where data will be saved.
572  *
573  * RETURNS:
574  * 0 if all the data have been successfully read.
575  * Otherwise, return the number of bytes left to be read.
576  ***************************************************************************
577  */
578 size_t sa_read(void *buffer, size_t size)
579 {
580         ssize_t n;
581
582         while (size) {
583
584                 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
585                         perror("read");
586                         exit(2);
587                 }
588
589                 if (!n)
590                         return size;    /* EOF */
591
592                 size -= n;
593                 buffer = (char *) buffer + n;
594         }
595
596         return 0;
597 }
598
599 /*
600  ***************************************************************************
601  * Display a restart message (contents of a R_RESTART record).
602  *
603  * IN:
604  * @tab         Number of tabulations (unused here).
605  * @action      Action expected from current function (unused here).
606  * @cur_date    Date string of current restart message (unused here).
607  * @cur_time    Time string of current restart message.
608  * @utc         True if @cur_time is expressed in UTC (unused here).
609  * @file_hdr    System activity file standard header.
610  * @record_hdr  Current record header (unused here).
611  ***************************************************************************
612  */
613 __printf_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time,
614                                   int utc, struct file_header *file_hdr,
615                                   struct record_header *record_hdr)
616 {
617         char restart[64];
618
619         printf("\n%-11s", cur_time);
620         sprintf(restart, "  LINUX RESTART\t(%d CPU)\n",
621                 file_hdr->sa_cpu_nr > 1 ? file_hdr->sa_cpu_nr - 1 : 1);
622         cprintf_s(IS_RESTART, "%s", restart);
623
624 }
625
626 /*
627  ***************************************************************************
628  * Display a comment (contents of R_COMMENT record).
629  *
630  * IN:
631  * @tab         Number of tabulations (unused here).
632  * @action      Action expected from current function (unused here).
633  * @cur_date    Date string of current comment (unused here).
634  * @cur_time    Time string of current comment.
635  * @utc         True if @cur_time is expressed in UTC (unused here).
636  * @comment     Comment to display.
637  * @file_hdr    System activity file standard header (unused here).
638  * @record_hdr  Current record header (unused here).
639  ***************************************************************************
640  */
641 __print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
642                                   char *comment, struct file_header *file_hdr,
643                                   struct record_header *record_hdr)
644 {
645         printf("%-11s", cur_time);
646         cprintf_s(IS_COMMENT, "  COM %s\n", comment);
647 }
648
649 /*
650  ***************************************************************************
651  * Read the various statistics sent by the data collector (sadc).
652  *
653  * IN:
654  * @curr        Index in array for current sample statistics.
655  ***************************************************************************
656  */
657 void read_sadc_stat_bunch(int curr)
658 {
659         int i, p;
660
661         /* Read record header (type is always R_STATS since it is read from sadc) */
662         if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
663                 /*
664                  * SIGINT (sent by sadc) is likely to be received
665                  * while we are stuck in sa_read().
666                  * If this happens then no data have to be read.
667                  */
668                 if (sigint_caught)
669                         return;
670
671 #ifdef DEBUG
672                 fprintf(stderr, "%s: Record header\n", __FUNCTION__);
673 #endif
674                 print_read_error(END_OF_DATA_UNEXPECTED);
675         }
676
677         for (i = 0; i < NR_ACT; i++) {
678
679                 if (!id_seq[i])
680                         continue;
681                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
682
683                 if (HAS_COUNT_FUNCTION(act[p]->options)) {
684                         if (sa_read(&(act[p]->nr[curr]), sizeof(__nr_t))) {
685 #ifdef DEBUG
686                                 fprintf(stderr, "%s: Nb of items\n", __FUNCTION__);
687 #endif
688                                 print_read_error(END_OF_DATA_UNEXPECTED);
689                         }
690                         if ((act[p]->nr[curr] > act[p]->nr_max) || (act[p]->nr[curr] < 0)) {
691 #ifdef DEBUG
692                                 fprintf(stderr, "%s: %s: nr=%d nr_max=%d\n",
693                                         __FUNCTION__, act[p]->name, act[p]->nr[curr], act[p]->nr_max);
694 #endif
695                                 print_read_error(INCONSISTENT_INPUT_DATA);
696                         }
697                         if (act[p]->nr[curr] > act[p]->nr_allocated) {
698                                 reallocate_all_buffers(act[p], act[p]->nr[curr]);
699                         }
700
701                         /*
702                          * For persistent activities, we must make sure that no statistics
703                          * from a previous iteration remain, especially if the number
704                          * of structures read is smaller than @nr_ini.
705                          */
706                         if (HAS_PERSISTENT_VALUES(act[p]->options)) {
707                             memset(act[p]->buf[curr], 0,
708                                    (size_t) act[p]->fsize * (size_t) act[p]->nr_ini * (size_t) act[p]->nr2);
709                         }
710                 }
711                 if (sa_read(act[p]->buf[curr],
712                             (size_t) act[p]->fsize * (size_t) act[p]->nr[curr] * (size_t) act[p]->nr2)) {
713 #ifdef DEBUG
714                         fprintf(stderr, "%s: Statistics\n", __FUNCTION__);
715 #endif
716                         print_read_error(END_OF_DATA_UNEXPECTED);
717                 }
718         }
719 }
720
721 /*
722  ***************************************************************************
723  * Read current activity's statistics (located between two consecutive
724  * LINUX RESTART messages) from file and display them.
725  *
726  * IN:
727  * @ifd         Input file descriptor.
728  * @fpos        Position in file where reading must start.
729  * @curr        Index in array for current sample statistics.
730  * @rows        Number of rows of screen.
731  * @act_id      Activity to display.
732  * @file_actlst List of activities in file.
733  * @file        Name of file being read.
734  * @file_magic  file_magic structure filled with file magic header data.
735  * @rec_hdr_tmp Temporary buffer where current record header will be saved.
736  * @endian_mismatch
737  *              TRUE if file's data don't match current machine's endianness.
738  * @arch_64     TRUE if file's data come from a 64 bit machine.
739  * @b_size      Size of @rec_hdr_tmp buffer.
740  *
741  * OUT:
742  * @curr        Index in array for next sample statistics.
743  * @cnt         Number of remaining lines of stats to write.
744  * @eosaf       Set to TRUE if EOF (end of file) has been reached.
745  * @reset       Set to TRUE if last_uptime variable should be reinitialized
746  *              (used in next_slice() function).
747  ***************************************************************************
748  */
749 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
750                            int rows, unsigned int act_id, int *reset,
751                            struct file_activity *file_actlst, char *file,
752                            struct file_magic *file_magic, void *rec_hdr_tmp,
753                            int endian_mismatch, int arch_64, size_t b_size)
754 {
755         int p, reset_cd;
756         unsigned long lines = 0;
757         unsigned char rtype;
758         int davg = 0, next, inc = 0;
759
760         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
761                 perror("lseek");
762                 exit(2);
763         }
764
765         /*
766          * Restore the first stats collected.
767          * Used to compute the rate displayed on the first line.
768          */
769         copy_structures(act, id_seq, record_hdr, !*curr, 2);
770
771         *cnt  = count;
772
773         /* Assess number of lines printed when a bitmap is used */
774         p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
775         if (act[p]->bitmap) {
776                 inc = count_bits(act[p]->bitmap->b_array,
777                                  BITMAP_SIZE(act[p]->bitmap->b_size));
778         }
779         reset_cd = 1;
780
781         do {
782                 /*
783                  * Display <count> lines of stats.
784                  * Start with reading current sample's record header.
785                  */
786                 *eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[*curr],
787                                          &file_hdr, arch_64, endian_mismatch, UEOF_STOP, b_size,
788                                          flags, &sar_fmt);
789                 rtype = record_hdr[*curr].record_type;
790
791                 if ((lines >= rows) || !lines) {
792                         lines = 0;
793                         dish = TRUE;
794                 }
795                 else
796                         dish = FALSE;
797
798                 if (*eosaf || (rtype == R_RESTART))
799                         /* This is EOF or we have met a LINUX RESTART record: Stop now */
800                         break;
801
802                 if (rtype != R_COMMENT) {
803                         /* Read the extra fields since it's not a special record */
804                         if (read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst,
805                                                  endian_mismatch, arch_64, file, file_magic, UEOF_STOP))
806                                 /* Error or unexpected EOF */
807                                 break;
808                 }
809                 else {
810                         /* Display comment */
811                         next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
812                                                     &tm_start, &tm_end, R_COMMENT, ifd,
813                                                     &rectime, file, 0,
814                                                     file_magic, &file_hdr, act, &sar_fmt,
815                                                     endian_mismatch, arch_64);
816                         if (next && lines) {
817                                 /*
818                                  * A line of comment was actually displayed: Count it in the
819                                  * total number of displayed lines.
820                                  * If no lines of stats had been previously displayed, ignore it
821                                  * to make sure the header line will be displayed.
822                                  */
823                                 lines++;
824                         }
825                         continue;
826                 }
827
828                 /* next is set to 1 when we were close enough to desired interval */
829                 next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
830                                    *reset, act_id, reset_cd);
831                 reset_cd = 0;
832                 if (next && (*cnt > 0)) {
833                         (*cnt)--;
834                 }
835
836                 if (next) {
837                         davg++;
838                         *curr ^= 1;
839
840                         if (inc) {
841                                 lines += inc;
842                         }
843                         else {
844                                 lines += act[p]->nr[*curr];
845                         }
846                 }
847                 *reset = FALSE;
848         }
849         while (*cnt);
850
851         /*
852          * At this moment, if we had a R_RESTART record, we still haven't read
853          * the number of CPU following it (nor the possible extra structures).
854          * But in this case, we always have @cnt != 0.
855          */
856
857         if (davg) {
858                 write_stats_avg(!*curr, USE_SA_FILE, act_id);
859         }
860
861         *reset = TRUE;
862 }
863
864 /*
865  ***************************************************************************
866  * Read header data sent by sadc.
867  ***************************************************************************
868  */
869 void read_header_data(void)
870 {
871         struct file_magic file_magic;
872         struct file_activity file_act;
873         int rc, i, p;
874         char version[16];
875
876         /* Read magic header */
877         rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
878
879         sprintf(version, "%d.%d.%d.%d",
880                 file_magic.sysstat_version,
881                 file_magic.sysstat_patchlevel,
882                 file_magic.sysstat_sublevel,
883                 file_magic.sysstat_extraversion);
884         if (!file_magic.sysstat_extraversion) {
885                 version[strlen(version) - 2] = '\0';
886         }
887
888         if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
889             (file_magic.format_magic != FORMAT_MAGIC) ||
890             strcmp(version, VERSION)) {
891
892                 /* sar and sadc commands are not consistent */
893                 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
894                         fprintf(stderr,
895                                 _("Using a wrong data collector from a different sysstat version\n"));
896                 }
897
898 #ifdef DEBUG
899                 fprintf(stderr, "%s: sysstat_magic=%x format_magic=%x version=%s\n",
900                         __FUNCTION__, file_magic.sysstat_magic, file_magic.format_magic, version);
901 #endif
902                 if (rc == FILE_MAGIC_SIZE) {
903                         /*
904                          * No data (0 byte) have been sent by sadc.
905                          * This is probably because no activities have been collected
906                          * ("Requested activities not available"). In this case, don't
907                          * display an error message: Exit now.
908                          */
909                         exit(3);
910                 }
911                 print_read_error(INCONSISTENT_INPUT_DATA);
912         }
913
914         /*
915          * Read header data.
916          * No need to take into account file_magic.header_size. We are sure that
917          * sadc and sar are from the same version (we have checked FORMAT_MAGIC
918          * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
919          */
920         if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
921 #ifdef DEBUG
922                 fprintf(stderr, "%s: File header\n", __FUNCTION__);
923 #endif
924                 print_read_error(END_OF_DATA_UNEXPECTED);
925         }
926
927         /* All activities are not necessarily selected, but NR_ACT is a max */
928         if (file_hdr.sa_act_nr > NR_ACT) {
929 #ifdef DEBUG
930                 fprintf(stderr, "%s: sa_act_nr=%d\n", __FUNCTION__, file_hdr.sa_act_nr);
931 #endif
932                 print_read_error(INCONSISTENT_INPUT_DATA);
933         }
934
935         if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
936             (file_hdr.rec_size != RECORD_HEADER_SIZE)) {
937 #ifdef DEBUG
938                 fprintf(stderr, "%s: act_size=%u/%zu rec_size=%u/%zu\n", __FUNCTION__,
939                         file_hdr.act_size, FILE_ACTIVITY_SIZE, file_hdr.rec_size, RECORD_HEADER_SIZE);
940 #endif
941                 print_read_error(INCONSISTENT_INPUT_DATA);
942         }
943
944         /* Read activity list */
945         for (i = 0; i < file_hdr.sa_act_nr; i++) {
946
947                 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
948 #ifdef DEBUG
949                         fprintf(stderr, "%s: File activity (%d)\n", __FUNCTION__, i);
950 #endif
951                         print_read_error(END_OF_DATA_UNEXPECTED);
952                 }
953
954                 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
955
956                 if ((p < 0) || (act[p]->fsize != file_act.size)
957                             || (act[p]->gtypes_nr[0] != file_act.types_nr[0])
958                             || (act[p]->gtypes_nr[1] != file_act.types_nr[1])
959                             || (act[p]->gtypes_nr[2] != file_act.types_nr[2])
960                             || (file_act.nr <= 0)
961                             || (file_act.nr2 <= 0)
962                             || (act[p]->magic != file_act.magic)) {
963 #ifdef DEBUG
964                         if (p < 0) {
965                                 fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
966                         }
967                         else {
968                                 fprintf(stderr, "%s: %s: size=%d/%d magic=%x/%x nr=%d nr2=%d types=%d,%d,%d/%d,%d,%d\n",
969                                         __FUNCTION__, act[p]->name, act[p]->fsize, file_act.size,
970                                         act[p]->magic, file_act.magic, file_act.nr, file_act.nr2,
971                                         act[p]->gtypes_nr[0], act[p]->gtypes_nr[1], act[p]->gtypes_nr[2],
972                                         file_act.types_nr[0], file_act.types_nr[1], file_act.types_nr[2]);
973                         }
974 #endif
975                         /* Remember that we are reading data from sadc and not from a file... */
976                         print_read_error(INCONSISTENT_INPUT_DATA);
977                 }
978
979                 id_seq[i]      = file_act.id;   /* We necessarily have "i < NR_ACT" */
980                 act[p]->nr_ini = file_act.nr;
981                 act[p]->nr2    = file_act.nr2;
982         }
983
984         while (i < NR_ACT) {
985                 id_seq[i++] = 0;
986         }
987
988         /* Check that all selected activties are actually sent by sadc */
989         reverse_check_act(file_hdr.sa_act_nr);
990
991         return;
992 }
993
994 /*
995  ***************************************************************************
996  * Read statistics from a system activity data file.
997  *
998  * IN:
999  * @from_file   Input file name.
1000  ***************************************************************************
1001  */
1002 void read_stats_from_file(char from_file[])
1003 {
1004         struct file_magic file_magic;
1005         struct file_activity *file_actlst = NULL;
1006         char rec_hdr_tmp[MAX_RECORD_HEADER_SIZE];
1007         int curr = 1, i, p;
1008         int ifd, rtype;
1009         int rows, eosaf = TRUE, reset = FALSE;
1010         long cnt = 1;
1011         off_t fpos;
1012
1013         /* Get window size */
1014         rows = get_win_height();
1015
1016         /* Read file headers and activity list */
1017         check_file_actlst(&ifd, from_file, act, flags, &file_magic, &file_hdr,
1018                           &file_actlst, id_seq, &endian_mismatch, &arch_64);
1019
1020         /* Perform required allocations */
1021         allocate_structures(act);
1022
1023         /* Print report header */
1024         print_report_hdr(flags, &rectime, &file_hdr);
1025
1026         /* Read system statistics from file */
1027         do {
1028                 /*
1029                  * If this record is a special (RESTART or COMMENT) one, print it and
1030                  * (try to) get another one.
1031                  */
1032                 do {
1033                         if (read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[0], &file_hdr,
1034                                             arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags, &sar_fmt)) {
1035                                 /* End of sa data file */
1036                                 return;
1037                         }
1038
1039                         rtype = record_hdr[0].record_type;
1040                         if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
1041                                 print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
1042                                                      &tm_start, &tm_end, rtype, ifd,
1043                                                      &rectime, from_file, 0, &file_magic,
1044                                                      &file_hdr, act, &sar_fmt, endian_mismatch, arch_64);
1045                         }
1046                         else {
1047                                 /*
1048                                  * OK: Previous record was not a special one.
1049                                  * So read now the extra fields.
1050                                  */
1051                                 if (read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
1052                                                          file_actlst, endian_mismatch, arch_64,
1053                                                          from_file, &file_magic, UEOF_STOP))
1054                                         /* Possible unexpected EOF */
1055                                         return;
1056
1057                                 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
1058                                                                    &record_hdr[0], &rectime))
1059                                         /*
1060                                          * An error was detected.
1061                                          * The timestamp hasn't been updated.
1062                                          */
1063                                         continue;
1064                         }
1065                 }
1066                 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1067                        (tm_start.use && (datecmp(&rectime, &tm_start, FALSE) < 0)) ||
1068                        (tm_end.use && (datecmp(&rectime, &tm_end, FALSE) >= 0)));
1069
1070                 /* Save the first stats collected. Will be used to compute the average */
1071                 copy_structures(act, id_seq, record_hdr, 2, 0);
1072
1073                 reset = TRUE;   /* Set flag to reset last_uptime variable */
1074
1075                 /* Save current file position */
1076                 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1077                         perror("lseek");
1078                         exit(2);
1079                 }
1080
1081                 /*
1082                  * Read and write stats located between two possible Linux restarts.
1083                  * Activities that should be displayed are saved in id_seq[] array.
1084                  * Since we are reading from a file, we print all the stats for an
1085                  * activity before displaying the next activity.
1086                  * id_seq[] has been created in check_file_actlst(), retaining only
1087                  * activities known by current sysstat version.
1088                  */
1089                 for (i = 0; i < NR_ACT; i++) {
1090
1091                         if (!id_seq[i])
1092                                 continue;
1093
1094                         p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1095                         if (!IS_SELECTED(act[p]->options))
1096                                 continue;
1097
1098                         if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1099                                 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
1100                                                       act[p]->id, &reset, file_actlst,
1101                                                       from_file, &file_magic, rec_hdr_tmp,
1102                                                       endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
1103                         }
1104                         else {
1105                                 unsigned int optf, msk;
1106
1107                                 optf = act[p]->opt_flags;
1108
1109                                 for (msk = 1; msk < 0x100; msk <<= 1) {
1110                                         if ((act[p]->opt_flags & 0xff) & msk) {
1111                                                 act[p]->opt_flags &= (0xffffff00 + msk);
1112
1113                                                 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
1114                                                                       rows, act[p]->id, &reset, file_actlst,
1115                                                                       from_file, &file_magic, rec_hdr_tmp,
1116                                                                       endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
1117                                                 act[p]->opt_flags = optf;
1118                                         }
1119                                 }
1120                         }
1121                 }
1122                 if (cnt == 0) {
1123                         /*
1124                          * Go to next Linux restart, if possible.
1125                          * Note: If we have @cnt == 0 then the last record we read was not a R_RESTART one
1126                          * (else we would have had @cnt != 0, i.e. we would have stopped reading previous activity
1127                          * because such a R_RESTART record would have been read, not because all the <count> lines
1128                          * had been printed).
1129                          * Remember @cnt is decremented only when a real line of stats have been displayed
1130                          * (not when a special record has been read).
1131                          */
1132                         do {
1133                                 /* Read next record header */
1134                                 eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr],
1135                                                         &file_hdr, arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags, &sar_fmt);
1136                                 rtype = record_hdr[curr].record_type;
1137
1138                                 if (eosaf || (rtype == R_RESTART))
1139                                         break;
1140
1141                                 if (rtype != R_COMMENT) {
1142                                         if (read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
1143                                                                  file_actlst, endian_mismatch, arch_64,
1144                                                                  from_file, &file_magic, UEOF_STOP))
1145                                                 /* Possible unexpected EOF */
1146                                                 break;
1147                                 }
1148                                 else {
1149                                         /* This was a COMMENT record: Print it */
1150                                         print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1151                                                              &tm_start, &tm_end, R_COMMENT, ifd,
1152                                                              &rectime, from_file, 0,
1153                                                              &file_magic, &file_hdr, act, &sar_fmt,
1154                                                              endian_mismatch, arch_64);
1155                                 }
1156                         }
1157                         while (1);
1158                 }
1159
1160                 /* The last record we read was a RESTART one: Print it */
1161                 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1162                         print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1163                                              &tm_start, &tm_end, R_RESTART, ifd,
1164                                              &rectime, from_file, 0,
1165                                              &file_magic, &file_hdr, act, &sar_fmt,
1166                                              endian_mismatch, arch_64);
1167                 }
1168         }
1169         while (!eosaf);
1170
1171         close(ifd);
1172
1173         free(file_actlst);
1174 }
1175
1176 /*
1177  ***************************************************************************
1178  * Read statistics sent by sadc, the data collector.
1179  ***************************************************************************
1180  */
1181 void read_stats(void)
1182 {
1183         int curr = 1;
1184         unsigned long lines;
1185         unsigned int rows;
1186         int dis_hdr = 0;
1187
1188         /* Don't buffer data if redirected to a pipe... */
1189         setbuf(stdout, NULL);
1190
1191         /* Read stats header */
1192         read_header_data();
1193
1194         if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1195                 /* Requested activities not available: Exit */
1196                 print_collect_error();
1197         }
1198
1199         /* Determine if a stat line header has to be displayed */
1200         dis_hdr = check_line_hdr();
1201
1202         lines = rows = get_win_height();
1203
1204         /* Perform required allocations */
1205         allocate_structures(act);
1206
1207         /* Print report header */
1208         print_report_hdr(flags, &rectime, &file_hdr);
1209
1210         /* Read system statistics sent by the data collector */
1211         read_sadc_stat_bunch(0);
1212
1213         if (!interval) {
1214                 /* Display stats since boot time and exit */
1215                 write_stats_startup(0);
1216         }
1217
1218         /* Save the first stats collected. Will be used to compute the average */
1219         copy_structures(act, id_seq, record_hdr, 2, 0);
1220
1221         /* Set a handler for SIGINT */
1222         memset(&int_act, 0, sizeof(int_act));
1223         int_act.sa_handler = int_handler;
1224         int_act.sa_flags = SA_RESTART;
1225         sigaction(SIGINT, &int_act, NULL);
1226
1227         /* Main loop */
1228         do {
1229
1230                 /* Get stats */
1231                 read_sadc_stat_bunch(curr);
1232                 if (sigint_caught) {
1233                         /*
1234                          * SIGINT signal caught (it is sent by sadc).
1235                          * => Display average stats.
1236                          */
1237                         curr ^= 1; /* No data retrieved from last read */
1238                         break;
1239                 }
1240
1241                 /* Print results */
1242                 if (!dis_hdr) {
1243                         dish = lines / rows;
1244                         if (dish) {
1245                                 lines %= rows;
1246                         }
1247                         lines++;
1248                 }
1249                 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1250                             NO_RESET, ALL_ACTIVITIES, TRUE);
1251
1252                 if (record_hdr[curr].record_type == R_LAST_STATS) {
1253                         /* File rotation is happening: Re-read header data sent by sadc */
1254                         read_header_data();
1255                         allocate_structures(act);
1256                 }
1257
1258                 if (count > 0) {
1259                         count--;
1260                 }
1261                 if (count) {
1262                         curr ^= 1;
1263                 }
1264         }
1265         while (count);
1266
1267         /*
1268          * Print statistics average.
1269          * At least one line of stats must have been displayed for this.
1270          * (There may be no lines at all if we press Ctrl/C immediately).
1271          */
1272         dish = dis_hdr;
1273         if (avg_count) {
1274                 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1275         }
1276 }
1277
1278 /*
1279  ***************************************************************************
1280  * Main entry to the sar program.
1281  ***************************************************************************
1282  */
1283 int main(int argc, char **argv)
1284 {
1285         int i, rc, opt = 1, args_idx = 1, p, q;
1286         int fd[2];
1287         int day_offset = 0;
1288         char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1289         char ltemp[1024];
1290
1291         /* Compute page shift in kB */
1292         get_kb_shift();
1293
1294         from_file[0] = to_file[0] = '\0';
1295
1296 #ifdef USE_NLS
1297         /* Init National Language Support */
1298         init_nls();
1299 #endif
1300
1301         tm_start.use = tm_end.use = FALSE;
1302
1303         /* Allocate and init activity bitmaps */
1304         allocate_bitmaps(act);
1305
1306         init_structures();
1307
1308         /* Process options */
1309         while (opt < argc) {
1310
1311                 if (!strcmp(argv[opt], "--sadc")) {
1312                         /* Locate sadc */
1313                         which_sadc();
1314                 }
1315
1316                 else if (!strncmp(argv[opt], "--dev=", 6)) {
1317                         /* Parse devices entered on the command line */
1318                         p = get_activity_position(act, A_DISK, EXIT_IF_NOT_FOUND);
1319                         parse_sa_devices(argv[opt], act[p], MAX_DEV_LEN, &opt, 6, NO_RANGE);
1320                 }
1321
1322                 else if (!strncmp(argv[opt], "--fs=", 5)) {
1323                         /* Parse devices entered on the command line */
1324                         p = get_activity_position(act, A_FS, EXIT_IF_NOT_FOUND);
1325                         parse_sa_devices(argv[opt], act[p], MAX_FS_LEN, &opt, 5, NO_RANGE);
1326                 }
1327
1328                 else if (!strncmp(argv[opt], "--iface=", 8)) {
1329                         /* Parse devices entered on the command line */
1330                         p = get_activity_position(act, A_NET_DEV, EXIT_IF_NOT_FOUND);
1331                         parse_sa_devices(argv[opt], act[p], MAX_IFACE_LEN, &opt, 8, NO_RANGE);
1332                         q = get_activity_position(act, A_NET_EDEV, EXIT_IF_NOT_FOUND);
1333                         act[q]->item_list = act[p]->item_list;
1334                         act[q]->item_list_sz = act[p]->item_list_sz;
1335                         act[q]->options |= AO_LIST_ON_CMDLINE;
1336                 }
1337
1338                 else if (!strncmp(argv[opt], "--int=", 6)) {
1339                         /* Parse interrupts names entered on the command line */
1340                         p = get_activity_position(act, A_IRQ, EXIT_IF_NOT_FOUND);
1341                         parse_sa_devices(argv[opt], act[p], MAX_SA_IRQ_LEN, &opt, 6, NR_IRQS);
1342                 }
1343
1344                 else if (!strcmp(argv[opt], "--help")) {
1345                         /* Display help message */
1346                         display_help(argv[0]);
1347                 }
1348
1349                 else if (!strcmp(argv[opt], "--human")) {
1350                         /* Display sizes in a human readable format */
1351                         flags |= S_F_UNIT;
1352                         opt++;
1353                 }
1354
1355                 else if (!strcmp(argv[opt], "--pretty")) {
1356                         /* Display an easy-to-read report */
1357                         flags |= S_F_PRETTY;
1358                         opt++;
1359                 }
1360
1361                 else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
1362                         /* Get number of decimal places */
1363                         dplaces_nr = atoi(argv[opt] + 6);
1364                         if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
1365                                 usage(argv[0]);
1366                         }
1367                         opt++;
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 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 }