]> granicus.if.org Git - sysstat/blob - sar.c
sar/sadc: Use %z format specifier for size_t values
[sysstat] / sar.c
1 /*
2  * sar: report system activity
3  * (C) 1999-2020 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;
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_start && (datecmp(&rectime, &tm_start, cross_day) < 0))
491                 /* it's too soon... */
492                 return 0;
493
494         /* Get interval value in 1/100th of a second */
495         get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv);
496
497         /* Check time (3) */
498         if (use_tm_end && (datecmp(&rectime, &tm_end, cross_day) > 0)) {
499                 /* It's too late... */
500                 *cnt = 0;
501                 return 0;
502         }
503
504         avg_count++;
505
506         /* Test stdout */
507         TEST_STDOUT(STDOUT_FILENO);
508
509         for (i = 0; i < NR_ACT; i++) {
510
511                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
512                         continue;
513
514                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
515                         /* Display current activity statistics */
516                         (*act[i]->f_print)(act[i], !curr, curr, itv);
517                 }
518         }
519
520         return 1;
521 }
522
523 /*
524  ***************************************************************************
525  * Display stats since system startup.
526  *
527  * IN:
528  * @curr        Index in array for current sample statistics.
529  ***************************************************************************
530  */
531 void write_stats_startup(int curr)
532 {
533         int i;
534
535         /* Set to 0 previous structures corresponding to boot time */
536         memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE);
537         record_hdr[!curr].record_type = R_STATS;
538         record_hdr[!curr].hour        = record_hdr[curr].hour;
539         record_hdr[!curr].minute      = record_hdr[curr].minute;
540         record_hdr[!curr].second      = record_hdr[curr].second;
541         record_hdr[!curr].ust_time    = record_hdr[curr].ust_time;
542
543         for (i = 0; i < NR_ACT; i++) {
544                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
545                         /*
546                          * Using nr[curr] and not nr[!curr] below because we initialize
547                          * reference structures for each structure that has been
548                          * currently read in memory.
549                          * No problem with buffers allocation since they all have the
550                          * same size.
551                          */
552                         memset(act[i]->buf[!curr], 0,
553                                (size_t) act[i]->msize * (size_t) act[i]->nr[curr] * (size_t) act[i]->nr2);
554                 }
555         }
556
557         flags |= S_F_SINCE_BOOT;
558         dish = TRUE;
559
560         write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
561                     ALL_ACTIVITIES, TRUE);
562
563         exit(0);
564 }
565
566 /*
567  ***************************************************************************
568  * Read data sent by the data collector.
569  *
570  * IN:
571  * @size        Number of bytes of data to read.
572  *
573  * OUT:
574  * @buffer      Buffer where data will be saved.
575  *
576  * RETURNS:
577  * 0 if all the data have been successfully read.
578  * Otherwise, return the number of bytes left to be read.
579  ***************************************************************************
580  */
581 size_t sa_read(void *buffer, size_t size)
582 {
583         ssize_t n;
584
585         while (size) {
586
587                 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
588                         perror("read");
589                         exit(2);
590                 }
591
592                 if (!n)
593                         return size;    /* EOF */
594
595                 size -= n;
596                 buffer = (char *) buffer + n;
597         }
598
599         return 0;
600 }
601
602 /*
603  ***************************************************************************
604  * Display a restart message (contents of a R_RESTART record).
605  *
606  * IN:
607  * @tab         Number of tabulations (unused here).
608  * @action      Action expected from current function (unused here).
609  * @cur_date    Date string of current restart message (unused here).
610  * @cur_time    Time string of current restart message.
611  * @utc         True if @cur_time is expressed in UTC (unused here).
612  * @file_hdr    System activity file standard header.
613  * @record_hdr  Current record header (unused here).
614  ***************************************************************************
615  */
616 __printf_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time,
617                                   int utc, struct file_header *file_hdr,
618                                   struct record_header *record_hdr)
619 {
620         char restart[64];
621
622         printf("\n%-11s", cur_time);
623         sprintf(restart, "  LINUX RESTART\t(%d CPU)\n",
624                 file_hdr->sa_cpu_nr > 1 ? file_hdr->sa_cpu_nr - 1 : 1);
625         cprintf_s(IS_RESTART, "%s", restart);
626
627 }
628
629 /*
630  ***************************************************************************
631  * Display a comment (contents of R_COMMENT record).
632  *
633  * IN:
634  * @tab         Number of tabulations (unused here).
635  * @action      Action expected from current function (unused here).
636  * @cur_date    Date string of current comment (unused here).
637  * @cur_time    Time string of current comment.
638  * @utc         True if @cur_time is expressed in UTC (unused here).
639  * @comment     Comment to display.
640  * @file_hdr    System activity file standard header (unused here).
641  * @record_hdr  Current record header (unused here).
642  ***************************************************************************
643  */
644 __print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
645                                   char *comment, struct file_header *file_hdr,
646                                   struct record_header *record_hdr)
647 {
648         printf("%-11s", cur_time);
649         cprintf_s(IS_COMMENT, "  COM %s\n", comment);
650 }
651
652 /*
653  ***************************************************************************
654  * Read the various statistics sent by the data collector (sadc).
655  *
656  * IN:
657  * @curr        Index in array for current sample statistics.
658  ***************************************************************************
659  */
660 void read_sadc_stat_bunch(int curr)
661 {
662         int i, p;
663
664         /* Read record header (type is always R_STATS since it is read from sadc) */
665         if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
666                 /*
667                  * SIGINT (sent by sadc) is likely to be received
668                  * while we are stuck in sa_read().
669                  * If this happens then no data have to be read.
670                  */
671                 if (sigint_caught)
672                         return;
673
674 #ifdef DEBUG
675                 fprintf(stderr, "%s: Record header\n", __FUNCTION__);
676 #endif
677                 print_read_error(END_OF_DATA_UNEXPECTED);
678         }
679
680         for (i = 0; i < NR_ACT; i++) {
681
682                 if (!id_seq[i])
683                         continue;
684                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
685
686                 if (HAS_COUNT_FUNCTION(act[p]->options)) {
687                         if (sa_read(&(act[p]->nr[curr]), sizeof(__nr_t))) {
688 #ifdef DEBUG
689                                 fprintf(stderr, "%s: Nb of items\n", __FUNCTION__);
690 #endif
691                                 print_read_error(END_OF_DATA_UNEXPECTED);
692                         }
693                         if ((act[p]->nr[curr] > act[p]->nr_max) || (act[p]->nr[curr] < 0)) {
694 #ifdef DEBUG
695                                 fprintf(stderr, "%s: %s: nr=%d nr_max=%d\n",
696                                         __FUNCTION__, act[p]->name, act[p]->nr[curr], act[p]->nr_max);
697 #endif
698                                 print_read_error(INCONSISTENT_INPUT_DATA);
699                         }
700                         if (act[p]->nr[curr] > act[p]->nr_allocated) {
701                                 reallocate_all_buffers(act[p], act[p]->nr[curr]);
702                         }
703
704
705                         /*
706                          * For persistent activities, we must make sure that no statistics
707                          * from a previous iteration remain, especially if the number
708                          * of structures read is smaller than @nr_ini.
709                          */
710                         if (HAS_PERSISTENT_VALUES(act[p]->options)) {
711                             memset(act[p]->buf[curr], 0,
712                                    (size_t) act[p]->fsize * (size_t) act[p]->nr_ini * (size_t) act[p]->nr2);
713                         }
714                 }
715                 if (sa_read(act[p]->buf[curr],
716                             (size_t) act[p]->fsize * (size_t) act[p]->nr[curr] * (size_t) act[p]->nr2)) {
717 #ifdef DEBUG
718                         fprintf(stderr, "%s: Statistics\n", __FUNCTION__);
719 #endif
720                         print_read_error(END_OF_DATA_UNEXPECTED);
721                 }
722         }
723 }
724
725 /*
726  ***************************************************************************
727  * Read current activity's statistics (located between two consecutive
728  * LINUX RESTART messages) from file and display them.
729  *
730  * IN:
731  * @ifd         Input file descriptor.
732  * @fpos        Position in file where reading must start.
733  * @curr        Index in array for current sample statistics.
734  * @rows        Number of rows of screen.
735  * @act_id      Activity to display.
736  * @file_actlst List of activities in file.
737  * @file        Name of file being read.
738  * @file_magic  file_magic structure filled with file magic header data.
739  * @rec_hdr_tmp Temporary buffer where current record header will be saved.
740  * @endian_mismatch
741  *              TRUE if file's data don't match current machine's endianness.
742  * @arch_64     TRUE if file's data come from a 64 bit machine.
743  * @b_size      Size of @rec_hdr_tmp buffer.
744  *
745  * OUT:
746  * @curr        Index in array for next sample statistics.
747  * @cnt         Number of remaining lines of stats to write.
748  * @eosaf       Set to TRUE if EOF (end of file) has been reached.
749  * @reset       Set to TRUE if last_uptime variable should be reinitialized
750  *              (used in next_slice() function).
751  ***************************************************************************
752  */
753 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
754                            int rows, unsigned int act_id, int *reset,
755                            struct file_activity *file_actlst, char *file,
756                            struct file_magic *file_magic, void *rec_hdr_tmp,
757                            int endian_mismatch, int arch_64, size_t b_size)
758 {
759         int p, reset_cd;
760         unsigned long lines = 0;
761         unsigned char rtype;
762         int davg = 0, next, inc = 0;
763
764         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
765                 perror("lseek");
766                 exit(2);
767         }
768
769         /*
770          * Restore the first stats collected.
771          * Used to compute the rate displayed on the first line.
772          */
773         copy_structures(act, id_seq, record_hdr, !*curr, 2);
774
775         *cnt  = count;
776
777         /* Assess number of lines printed when a bitmap is used */
778         p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
779         if (act[p]->bitmap) {
780                 inc = count_bits(act[p]->bitmap->b_array,
781                                  BITMAP_SIZE(act[p]->bitmap->b_size));
782         }
783         reset_cd = 1;
784
785         do {
786                 /*
787                  * Display <count> lines of stats.
788                  * Start with reading current sample's record header.
789                  */
790                 *eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[*curr],
791                                          &file_hdr, arch_64, endian_mismatch, UEOF_STOP, b_size, flags);
792                 rtype = record_hdr[*curr].record_type;
793
794                 if ((lines >= rows) || !lines) {
795                         lines = 0;
796                         dish = TRUE;
797                 }
798                 else
799                         dish = FALSE;
800
801                 if (*eosaf || (rtype == R_RESTART))
802                         /* This is EOF or we have met a LINUX RESTART record: Stop now */
803                         break;
804
805                 if (rtype != R_COMMENT) {
806                         /* Read the extra fields since it's not a special record */
807                         read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst,
808                                              endian_mismatch, arch_64, file, file_magic, UEOF_STOP);
809                 }
810                 else {
811                         /* Display comment */
812                         next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
813                                                     &tm_start, &tm_end, R_COMMENT, ifd,
814                                                     &rectime, file, 0,
815                                                     file_magic, &file_hdr, act, &sar_fmt,
816                                                     endian_mismatch, arch_64);
817                         if (next && lines) {
818                                 /*
819                                  * A line of comment was actually displayed: Count it in the
820                                  * total number of displayed lines.
821                                  * If no lines of stats had been previously displayed, ignore it
822                                  * to make sure the header line will be displayed.
823                                  */
824                                 lines++;
825                         }
826                         continue;
827                 }
828
829                 /* next is set to 1 when we were close enough to desired interval */
830                 next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
831                                    *reset, act_id, reset_cd);
832                 reset_cd = 0;
833                 if (next && (*cnt > 0)) {
834                         (*cnt)--;
835                 }
836
837                 if (next) {
838                         davg++;
839                         *curr ^= 1;
840
841                         if (inc) {
842                                 lines += inc;
843                         }
844                         else {
845                                 lines += act[p]->nr[*curr];
846                         }
847                 }
848                 *reset = FALSE;
849         }
850         while (*cnt);
851
852         /*
853          * At this moment, if we had a R_RESTART record, we still haven't read
854          * the number of CPU following it (nor the possible extra structures).
855          * But in this case, we always have @cnt != 0.
856          */
857
858         if (davg) {
859                 write_stats_avg(!*curr, USE_SA_FILE, act_id);
860         }
861
862         *reset = TRUE;
863 }
864
865 /*
866  ***************************************************************************
867  * Read header data sent by sadc.
868  ***************************************************************************
869  */
870 void read_header_data(void)
871 {
872         struct file_magic file_magic;
873         struct file_activity file_act;
874         int rc, i, p;
875         char version[16];
876
877         /* Read magic header */
878         rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
879
880         sprintf(version, "%d.%d.%d.%d",
881                 file_magic.sysstat_version,
882                 file_magic.sysstat_patchlevel,
883                 file_magic.sysstat_sublevel,
884                 file_magic.sysstat_extraversion);
885         if (!file_magic.sysstat_extraversion) {
886                 version[strlen(version) - 2] = '\0';
887         }
888
889         if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
890             (file_magic.format_magic != FORMAT_MAGIC) ||
891             strcmp(version, VERSION)) {
892
893                 /* sar and sadc commands are not consistent */
894                 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
895                         fprintf(stderr,
896                                 _("Using a wrong data collector from a different sysstat version\n"));
897                 }
898
899 #ifdef DEBUG
900                 fprintf(stderr, "%s: sysstat_magic=%x format_magic=%x version=%s\n",
901                         __FUNCTION__, file_magic.sysstat_magic, file_magic.format_magic, version);
902 #endif
903                 if (rc == FILE_MAGIC_SIZE) {
904                         /*
905                          * No data (0 byte) have been sent by sadc.
906                          * This is probably because no activities have been collected
907                          * ("Requested activities not available"). In this case, don't
908                          * display an error message: Exit now.
909                          */
910                         exit(3);
911                 }
912                 print_read_error(INCONSISTENT_INPUT_DATA);
913         }
914
915         /*
916          * Read header data.
917          * No need to take into account file_magic.header_size. We are sure that
918          * sadc and sar are from the same version (we have checked FORMAT_MAGIC
919          * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
920          */
921         if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
922 #ifdef DEBUG
923                 fprintf(stderr, "%s: File header\n", __FUNCTION__);
924 #endif
925                 print_read_error(END_OF_DATA_UNEXPECTED);
926         }
927
928         /* All activities are not necessarily selected, but NR_ACT is a max */
929         if (file_hdr.sa_act_nr > NR_ACT) {
930 #ifdef DEBUG
931                 fprintf(stderr, "%s: sa_act_nr=%d\n", __FUNCTION__, file_hdr.sa_act_nr);
932 #endif
933                 print_read_error(INCONSISTENT_INPUT_DATA);
934         }
935
936         if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
937             (file_hdr.rec_size != RECORD_HEADER_SIZE)) {
938 #ifdef DEBUG
939                 fprintf(stderr, "%s: act_size=%u/%zu rec_size=%u/%zu\n", __FUNCTION__,
940                         file_hdr.act_size, FILE_ACTIVITY_SIZE, file_hdr.rec_size, RECORD_HEADER_SIZE);
941 #endif
942                 print_read_error(INCONSISTENT_INPUT_DATA);
943         }
944
945         /* Read activity list */
946         for (i = 0; i < file_hdr.sa_act_nr; i++) {
947
948                 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
949 #ifdef DEBUG
950                         fprintf(stderr, "%s: File activity (%d)\n", __FUNCTION__, i);
951 #endif
952                         print_read_error(END_OF_DATA_UNEXPECTED);
953                 }
954
955                 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
956
957                 if ((p < 0) || (act[p]->fsize != file_act.size)
958                             || (act[p]->gtypes_nr[0] != file_act.types_nr[0])
959                             || (act[p]->gtypes_nr[1] != file_act.types_nr[1])
960                             || (act[p]->gtypes_nr[2] != file_act.types_nr[2])
961                             || (file_act.nr <= 0)
962                             || (file_act.nr2 <= 0)
963                             || (act[p]->magic != file_act.magic)) {
964 #ifdef DEBUG
965                         if (p < 0) {
966                                 fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
967                         }
968                         else {
969                                 fprintf(stderr, "%s: %s: size=%d/%d magic=%x/%x nr=%d nr2=%d types=%d,%d,%d/%d,%d,%d\n",
970                                         __FUNCTION__, act[p]->name, act[p]->fsize, file_act.size,
971                                         act[p]->magic, file_act.magic, file_act.nr, file_act.nr2,
972                                         act[p]->gtypes_nr[0], act[p]->gtypes_nr[1], act[p]->gtypes_nr[2],
973                                         file_act.types_nr[0], file_act.types_nr[1], file_act.types_nr[2]);
974                         }
975 #endif
976                         /* Remember that we are reading data from sadc and not from a file... */
977                         print_read_error(INCONSISTENT_INPUT_DATA);
978                 }
979
980                 id_seq[i]      = file_act.id;   /* We necessarily have "i < NR_ACT" */
981                 act[p]->nr_ini = file_act.nr;
982                 act[p]->nr2    = file_act.nr2;
983         }
984
985         while (i < NR_ACT) {
986                 id_seq[i++] = 0;
987         }
988
989         /* Check that all selected activties are actually sent by sadc */
990         reverse_check_act(file_hdr.sa_act_nr);
991
992         return;
993 }
994
995 /*
996  ***************************************************************************
997  * Read statistics from a system activity data file.
998  *
999  * IN:
1000  * @from_file   Input file name.
1001  ***************************************************************************
1002  */
1003 void read_stats_from_file(char from_file[])
1004 {
1005         struct file_magic file_magic;
1006         struct file_activity *file_actlst = NULL;
1007         char rec_hdr_tmp[MAX_RECORD_HEADER_SIZE];
1008         int curr = 1, i, p;
1009         int ifd, rtype;
1010         int rows, eosaf = TRUE, reset = FALSE;
1011         long cnt = 1;
1012         off_t fpos;
1013
1014         /* Get window size */
1015         rows = get_win_height();
1016
1017         /* Read file headers and activity list */
1018         check_file_actlst(&ifd, from_file, act, flags, &file_magic, &file_hdr,
1019                           &file_actlst, id_seq, &endian_mismatch, &arch_64);
1020
1021         /* Perform required allocations */
1022         allocate_structures(act);
1023
1024         /* Print report header */
1025         print_report_hdr(flags, &rectime, &file_hdr);
1026
1027         /* Read system statistics from file */
1028         do {
1029                 /*
1030                  * If this record is a special (RESTART or COMMENT) one, print it and
1031                  * (try to) get another one.
1032                  */
1033                 do {
1034                         if (read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[0], &file_hdr,
1035                                             arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags)) {
1036                                 /* End of sa data file */
1037                                 return;
1038                         }
1039
1040                         rtype = record_hdr[0].record_type;
1041                         if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
1042                                 print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
1043                                                      &tm_start, &tm_end, rtype, ifd,
1044                                                      &rectime, from_file, 0, &file_magic,
1045                                                      &file_hdr, act, &sar_fmt, endian_mismatch, arch_64);
1046                         }
1047                         else {
1048                                 /*
1049                                  * OK: Previous record was not a special one.
1050                                  * So read now the extra fields.
1051                                  */
1052                                 read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
1053                                                      file_actlst, endian_mismatch, arch_64,
1054                                                      from_file, &file_magic, UEOF_STOP);
1055                                 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
1056                                                                    &record_hdr[0], &rectime))
1057                                         /*
1058                                          * An error was detected.
1059                                          * The timestamp hasn't been updated.
1060                                          */
1061                                         continue;
1062                         }
1063                 }
1064                 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1065                        (tm_start.use && (datecmp(&rectime, &tm_start, FALSE) < 0)) ||
1066                        (tm_end.use && (datecmp(&rectime, &tm_end, FALSE) >= 0)));
1067
1068                 /* Save the first stats collected. Will be used to compute the average */
1069                 copy_structures(act, id_seq, record_hdr, 2, 0);
1070
1071                 reset = TRUE;   /* Set flag to reset last_uptime variable */
1072
1073                 /* Save current file position */
1074                 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1075                         perror("lseek");
1076                         exit(2);
1077                 }
1078
1079                 /*
1080                  * Read and write stats located between two possible Linux restarts.
1081                  * Activities that should be displayed are saved in id_seq[] array.
1082                  * Since we are reading from a file, we print all the stats for an
1083                  * activity before displaying the next activity.
1084                  * id_seq[] has been created in check_file_actlst(), retaining only
1085                  * activities known by current sysstat version.
1086                  */
1087                 for (i = 0; i < NR_ACT; i++) {
1088
1089                         if (!id_seq[i])
1090                                 continue;
1091
1092                         p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1093                         if (!IS_SELECTED(act[p]->options))
1094                                 continue;
1095
1096                         if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1097                                 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
1098                                                       act[p]->id, &reset, file_actlst,
1099                                                       from_file, &file_magic, rec_hdr_tmp,
1100                                                       endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
1101                         }
1102                         else {
1103                                 unsigned int optf, msk;
1104
1105                                 optf = act[p]->opt_flags;
1106
1107                                 for (msk = 1; msk < 0x100; msk <<= 1) {
1108                                         if ((act[p]->opt_flags & 0xff) & msk) {
1109                                                 act[p]->opt_flags &= (0xffffff00 + msk);
1110
1111                                                 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
1112                                                                       rows, act[p]->id, &reset, file_actlst,
1113                                                                       from_file, &file_magic, rec_hdr_tmp,
1114                                                                       endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
1115                                                 act[p]->opt_flags = optf;
1116                                         }
1117                                 }
1118                         }
1119                 }
1120                 if (cnt == 0) {
1121                         /*
1122                          * Go to next Linux restart, if possible.
1123                          * Note: If we have @cnt == 0 then the last record we read was not a R_RESTART one
1124                          * (else we would have had @cnt != 0, i.e. we would have stopped reading previous activity
1125                          * because such a R_RESTART record would have been read, not because all the <count> lines
1126                          * had been printed).
1127                          * Remember @cnt is decremented only when a real line of stats have been displayed
1128                          * (not when a special record has been read).
1129                          */
1130                         do {
1131                                 /* Read next record header */
1132                                 eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr],
1133                                                         &file_hdr, arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags);
1134                                 rtype = record_hdr[curr].record_type;
1135
1136                                 if (eosaf || (rtype == R_RESTART))
1137                                         break;
1138
1139                                 if (rtype != R_COMMENT) {
1140                                         read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
1141                                                              file_actlst, endian_mismatch, arch_64,
1142                                                              from_file, &file_magic, UEOF_STOP);
1143                                 }
1144                                 else {
1145                                         /* This was a COMMENT record: print it */
1146                                         print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1147                                                              &tm_start, &tm_end, R_COMMENT, ifd,
1148                                                              &rectime, from_file, 0,
1149                                                              &file_magic, &file_hdr, act, &sar_fmt,
1150                                                              endian_mismatch, arch_64);
1151                                 }
1152                         }
1153                         while (1);
1154                 }
1155
1156                 /* The last record we read was a RESTART one: Print it */
1157                 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1158                         print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1159                                              &tm_start, &tm_end, R_RESTART, ifd,
1160                                              &rectime, from_file, 0,
1161                                              &file_magic, &file_hdr, act, &sar_fmt,
1162                                              endian_mismatch, arch_64);
1163                 }
1164         }
1165         while (!eosaf);
1166
1167         close(ifd);
1168
1169         free(file_actlst);
1170 }
1171
1172 /*
1173  ***************************************************************************
1174  * Read statistics sent by sadc, the data collector.
1175  ***************************************************************************
1176  */
1177 void read_stats(void)
1178 {
1179         int curr = 1;
1180         unsigned long lines;
1181         unsigned int rows;
1182         int dis_hdr = 0;
1183
1184         /* Don't buffer data if redirected to a pipe... */
1185         setbuf(stdout, NULL);
1186
1187         /* Read stats header */
1188         read_header_data();
1189
1190         if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1191                 /* Requested activities not available: Exit */
1192                 print_collect_error();
1193         }
1194
1195         /* Determine if a stat line header has to be displayed */
1196         dis_hdr = check_line_hdr();
1197
1198         lines = rows = get_win_height();
1199
1200         /* Perform required allocations */
1201         allocate_structures(act);
1202
1203         /* Print report header */
1204         print_report_hdr(flags, &rectime, &file_hdr);
1205
1206         /* Read system statistics sent by the data collector */
1207         read_sadc_stat_bunch(0);
1208
1209         if (!interval) {
1210                 /* Display stats since boot time and exit */
1211                 write_stats_startup(0);
1212         }
1213
1214         /* Save the first stats collected. Will be used to compute the average */
1215         copy_structures(act, id_seq, record_hdr, 2, 0);
1216
1217         /* Set a handler for SIGINT */
1218         memset(&int_act, 0, sizeof(int_act));
1219         int_act.sa_handler = int_handler;
1220         int_act.sa_flags = SA_RESTART;
1221         sigaction(SIGINT, &int_act, NULL);
1222
1223         /* Main loop */
1224         do {
1225
1226                 /* Get stats */
1227                 read_sadc_stat_bunch(curr);
1228                 if (sigint_caught) {
1229                         /*
1230                          * SIGINT signal caught (it is sent by sadc).
1231                          * => Display average stats.
1232                          */
1233                         curr ^= 1; /* No data retrieved from last read */
1234                         break;
1235                 }
1236
1237                 /* Print results */
1238                 if (!dis_hdr) {
1239                         dish = lines / rows;
1240                         if (dish) {
1241                                 lines %= rows;
1242                         }
1243                         lines++;
1244                 }
1245                 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1246                             NO_RESET, ALL_ACTIVITIES, TRUE);
1247
1248                 if (record_hdr[curr].record_type == R_LAST_STATS) {
1249                         /* File rotation is happening: Re-read header data sent by sadc */
1250                         read_header_data();
1251                         allocate_structures(act);
1252                 }
1253
1254                 if (count > 0) {
1255                         count--;
1256                 }
1257                 if (count) {
1258                         curr ^= 1;
1259                 }
1260         }
1261         while (count);
1262
1263         /*
1264          * Print statistics average.
1265          * At least one line of stats must have been displayed for this.
1266          * (There may be no lines at all if we press Ctrl/C immediately).
1267          */
1268         dish = dis_hdr;
1269         if (avg_count) {
1270                 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1271         }
1272 }
1273
1274 /*
1275  ***************************************************************************
1276  * Main entry to the sar program.
1277  ***************************************************************************
1278  */
1279 int main(int argc, char **argv)
1280 {
1281         int i, rc, opt = 1, args_idx = 1, p, q;
1282         int fd[2];
1283         int day_offset = 0;
1284         char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1285         char ltemp[1024];
1286
1287         /* Compute page shift in kB */
1288         get_kb_shift();
1289
1290         from_file[0] = to_file[0] = '\0';
1291
1292 #ifdef USE_NLS
1293         /* Init National Language Support */
1294         init_nls();
1295 #endif
1296
1297         tm_start.use = tm_end.use = FALSE;
1298
1299         /* Allocate and init activity bitmaps */
1300         allocate_bitmaps(act);
1301
1302         init_structures();
1303
1304         /* Process options */
1305         while (opt < argc) {
1306
1307                 if (!strcmp(argv[opt], "--sadc")) {
1308                         /* Locate sadc */
1309                         which_sadc();
1310                 }
1311
1312                 else if (!strncmp(argv[opt], "--dev=", 6)) {
1313                         /* Parse devices entered on the command line */
1314                         p = get_activity_position(act, A_DISK, EXIT_IF_NOT_FOUND);
1315                         parse_sa_devices(argv[opt], act[p], MAX_DEV_LEN, &opt, 6);
1316                 }
1317
1318                 else if (!strncmp(argv[opt], "--fs=", 5)) {
1319                         /* Parse devices entered on the command line */
1320                         p = get_activity_position(act, A_FS, EXIT_IF_NOT_FOUND);
1321                         parse_sa_devices(argv[opt], act[p], MAX_FS_LEN, &opt, 5);
1322                 }
1323
1324                 else if (!strncmp(argv[opt], "--iface=", 8)) {
1325                         /* Parse devices entered on the command line */
1326                         p = get_activity_position(act, A_NET_DEV, EXIT_IF_NOT_FOUND);
1327                         parse_sa_devices(argv[opt], act[p], MAX_IFACE_LEN, &opt, 8);
1328                         q = get_activity_position(act, A_NET_EDEV, EXIT_IF_NOT_FOUND);
1329                         act[q]->item_list = act[p]->item_list;
1330                         act[q]->item_list_sz = act[p]->item_list_sz;
1331                         act[q]->options |= AO_LIST_ON_CMDLINE;
1332                 }
1333
1334                 else if (!strcmp(argv[opt], "--help")) {
1335                         /* Display help message */
1336                         display_help(argv[0]);
1337                 }
1338
1339                 else if (!strcmp(argv[opt], "--human")) {
1340                         /* Display sizes in a human readable format */
1341                         flags |= S_F_UNIT;
1342                         opt++;
1343                 }
1344
1345                 else if (!strcmp(argv[opt], "--pretty")) {
1346                         /* Display an easy-to-read report */
1347                         flags |= S_F_PRETTY;
1348                         opt++;
1349                 }
1350
1351                 else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
1352                         /* Get number of decimal places */
1353                         dplaces_nr = atoi(argv[opt] + 6);
1354                         if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
1355                                 usage(argv[0]);
1356                         }
1357                         opt++;
1358                 }
1359
1360                 else if (!strcmp(argv[opt], "-I")) {
1361                         /* Parse -I option */
1362                         if (parse_sar_I_opt(argv, &opt, &flags, act)) {
1363                                 usage(argv[0]);
1364                         }
1365                 }
1366
1367                 else if (!strcmp(argv[opt], "-D")) {
1368                         /* Option to tell sar to write to saYYYYMMDD data files */
1369                         flags |= S_F_SA_YYYYMMDD;
1370                         opt++;
1371                 }
1372
1373                 else if (!strcmp(argv[opt], "-P")) {
1374                         /* Parse -P option */
1375                         if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1376                                 usage(argv[0]);
1377                         }
1378                 }
1379
1380                 else if (!strcmp(argv[opt], "-o")) {
1381                         if (to_file[0]) {
1382                                 /* Output file already specified */
1383                                 usage(argv[0]);
1384                         }
1385                         /* Save stats to a file */
1386                         if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1387                             (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1388                                 strncpy(to_file, argv[opt++], sizeof(to_file));
1389                                 to_file[sizeof(to_file) - 1] = '\0';
1390                         }
1391                         else {
1392                                 strcpy(to_file, "-");
1393                         }
1394                 }
1395
1396                 else if (!strcmp(argv[opt], "-f")) {
1397                         if (from_file[0] || day_offset) {
1398                                 /* Input file already specified */
1399                                 usage(argv[0]);
1400                         }
1401                         /* Read stats from a file */
1402                         if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1403                             (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1404                                 strncpy(from_file, argv[opt++], sizeof(from_file));
1405                                 from_file[sizeof(from_file) - 1] = '\0';
1406                                 /* Check if this is an alternate directory for sa files */
1407                                 check_alt_sa_dir(from_file, day_offset, -1);
1408                         }
1409                         else {
1410                                 set_default_file(from_file, day_offset, -1);
1411                         }
1412                 }
1413
1414                 else if (!strcmp(argv[opt], "-s")) {
1415                         /* Get time start */
1416                         if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1417                                 usage(argv[0]);
1418                         }
1419                 }
1420
1421                 else if (!strcmp(argv[opt], "-e")) {
1422                         /* Get time end */
1423                         if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1424                                 usage(argv[0]);
1425                         }
1426                 }
1427
1428                 else if (!strcmp(argv[opt], "-i")) {
1429                         if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1430                                 usage(argv[0]);
1431                         }
1432                         interval = atol(argv[opt++]);
1433                         if (interval < 1) {
1434                                 usage(argv[0]);
1435                         }
1436                         flags |= S_F_INTERVAL_SET;
1437                 }
1438
1439                 else if (!strcmp(argv[opt], "-m")) {
1440                         if (!argv[++opt]) {
1441                                 usage(argv[0]);
1442                         }
1443                         /* Parse option -m */
1444                         if (parse_sar_m_opt(argv, &opt, act)) {
1445                                 usage(argv[0]);
1446                         }
1447                 }
1448
1449                 else if (!strcmp(argv[opt], "-n")) {
1450                         if (!argv[++opt]) {
1451                                 usage(argv[0]);
1452                         }
1453                         /* Parse option -n */
1454                         if (parse_sar_n_opt(argv, &opt, act)) {
1455                                 usage(argv[0]);
1456                         }
1457                 }
1458
1459                 else if (!strcmp(argv[opt], "-q")) {
1460                         if (!argv[++opt]) {
1461                                 SELECT_ACTIVITY(A_QUEUE);
1462                         }
1463                         /* Parse option -q */
1464                         else if (parse_sar_q_opt(argv, &opt, act)) {
1465                                 SELECT_ACTIVITY(A_QUEUE);
1466                         }
1467                 }
1468
1469 #ifdef TEST
1470                 else if (!strncmp(argv[opt], "--getenv", 8)) {
1471                         __env = TRUE;
1472                         opt++;
1473                 }
1474
1475                 else if (!strncmp(argv[opt], "--unix_time=", 12)) {
1476                         if (strspn(argv[opt] + 12, DIGITS) != strlen(argv[opt] + 12)) {
1477                                 usage(argv[0]);
1478                         }
1479                         __unix_time = atoll(argv[opt++] + 12);
1480                 }
1481 #endif
1482                 else if ((strlen(argv[opt]) > 1) &&
1483                          (strlen(argv[opt]) < 4) &&
1484                          !strncmp(argv[opt], "-", 1) &&
1485                          (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1486                         if (from_file[0] || day_offset) {
1487                                 /* Input file already specified */
1488                                 usage(argv[0]);
1489                         }
1490                         day_offset = atoi(argv[opt++] + 1);
1491                 }
1492
1493                 else if (!strncmp(argv[opt], "-", 1)) {
1494                         /* Other options not previously tested */
1495                         if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) {
1496                                 if (rc == 1) {
1497                                         usage(argv[0]);
1498                                 }
1499                                 exit(1);
1500                         }
1501                         opt++;
1502                 }
1503
1504                 else if (interval < 0) {
1505                         /* Get interval */
1506                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1507                                 usage(argv[0]);
1508                         }
1509                         interval = atol(argv[opt++]);
1510                         if (interval < 0) {
1511                                 usage(argv[0]);
1512                         }
1513                 }
1514
1515                 else {
1516                         /* Get count value */
1517                         if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1518                             !interval) {
1519                                 usage(argv[0]);
1520                         }
1521                         if (count) {
1522                                 /* Count parameter already set */
1523                                 usage(argv[0]);
1524                         }
1525                         count = atol(argv[opt++]);
1526                         if (count < 1) {
1527                                 usage(argv[0]);
1528                         }
1529                 }
1530         }
1531
1532         /* Init color strings */
1533         init_colors();
1534
1535         /* 'sar' is equivalent to 'sar -f' */
1536         if ((argc == 1) ||
1537             (((interval < 0) || INTERVAL_SET(flags)) && !from_file[0] && !to_file[0])) {
1538                 set_default_file(from_file, day_offset, -1);
1539         }
1540
1541         if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1542                 tm_end.tm_hour += 24;
1543         }
1544
1545         /*
1546          * Check option dependencies.
1547          */
1548         /* You read from a file OR you write to it... */
1549         if (from_file[0] && to_file[0]) {
1550                 fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
1551                 exit(1);
1552         }
1553         if (USE_OPTION_A(flags)) {
1554                 /* Set -P ALL -I ALL if needed */
1555                 set_bitmaps(act, &flags);
1556         }
1557         /* Use time start or option -i only when reading stats from a file */
1558         if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
1559                 fprintf(stderr,
1560                         _("Not reading from a system activity file (use -f option)\n"));
1561                 exit(1);
1562         }
1563         /* Don't print stats since boot time if -o or -f options are used */
1564         if (!interval && (from_file[0] || to_file[0])) {
1565                 usage(argv[0]);
1566         }
1567
1568         /* Cannot enter a day shift with -o option */
1569         if (to_file[0] && day_offset) {
1570                 usage(argv[0]);
1571         }
1572
1573         if (!count) {
1574                 /*
1575                  * count parameter not set: Display all the contents of the file
1576                  * or generate a report continuously.
1577                  */
1578                 count = -1;
1579         }
1580
1581         /* Default is CPU activity... */
1582         select_default_activity(act);
1583
1584         /* Check S_TIME_FORMAT variable contents */
1585         if (!is_iso_time_fmt())
1586                 flags |= S_F_PREFD_TIME_OUTPUT;
1587
1588         /* Reading stats from file: */
1589         if (from_file[0]) {
1590                 if (interval < 0) {
1591                         interval = 1;
1592                 }
1593
1594                 /* Read stats from file */
1595                 read_stats_from_file(from_file);
1596
1597                 /* Free structures and activity bitmaps */
1598                 free_bitmaps(act);
1599                 free_structures(act);
1600
1601                 return 0;
1602         }
1603
1604         /* Reading stats from sadc: */
1605
1606         /* Create anonymous pipe */
1607         if (pipe(fd) == -1) {
1608                 perror("pipe");
1609                 exit(4);
1610         }
1611
1612         switch (fork()) {
1613
1614         case -1:
1615                 perror("fork");
1616                 exit(4);
1617                 break;
1618
1619         case 0: /* Child */
1620                 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1621                         perror("dup2");
1622                         exit(4);
1623                 }
1624                 CLOSE_ALL(fd);
1625
1626                 /*
1627                  * Prepare options for sadc.
1628                  */
1629                 /* Program name */
1630                 salloc(0, SADC);
1631
1632                 /* Interval value */
1633                 if (interval < 0) {
1634                         usage(argv[0]);
1635                 }
1636                 else if (!interval) {
1637                         strcpy(ltemp, "1");
1638                         /*
1639                          * Display stats since system startup: Set <interval> to 1.
1640                          * <count> arg will also be set to 1 below.
1641                          */
1642                         salloc(args_idx++, ltemp);
1643                 }
1644                 else {
1645                         sprintf(ltemp, "%ld", interval);
1646                 }
1647                 salloc(args_idx++, ltemp);
1648
1649                 /* Count number */
1650                 if (count >= 0) {
1651                         sprintf(ltemp, "%ld", count + 1);
1652                         salloc(args_idx++, ltemp);
1653                 }
1654
1655 #ifdef TEST
1656                 if (__unix_time) {
1657                         sprintf(ltemp, "--unix_time=%ld", __unix_time);
1658                         salloc(args_idx++, ltemp);
1659                 }
1660 #endif
1661                 /* Flags to be passed to sadc */
1662                 salloc(args_idx++, "-Z");
1663
1664                 /* Writing data to a file (option -o) */
1665                 if (to_file[0]) {
1666                         /* Set option -D if entered */
1667                         if (USE_SA_YYYYMMDD(flags)) {
1668                                 salloc(args_idx++, "-D");
1669                         }
1670                         /* Collect all possible activities (option -S XALL for sadc) */
1671                         salloc(args_idx++, "-S");
1672                         salloc(args_idx++, K_XALL);
1673                         /* Outfile arg */
1674                         salloc(args_idx++, to_file);
1675                 }
1676                 else {
1677                         /*
1678                          * If option -o hasn't been used, then tell sadc
1679                          * to collect only activities that will be displayed.
1680                          */
1681                         salloc(args_idx++, "-S");
1682                         strcpy(ltemp, K_A_NULL);
1683                         for (i = 0; i < NR_ACT; i++) {
1684                                 if (IS_SELECTED(act[i]->options)) {
1685                                         strcat(ltemp, ",");
1686                                         strcat(ltemp, act[i]->name);
1687                                 }
1688                         }
1689                         salloc(args_idx++, ltemp);
1690                 }
1691
1692                 /* Last arg is NULL */
1693                 args[args_idx] = NULL;
1694
1695                 /* Call now the data collector */
1696 #ifdef DEBUG
1697                 fprintf(stderr, "%s: 1.sadc: %s\n", __FUNCTION__, SADC_PATH);
1698 #endif
1699
1700                 execv(SADC_PATH, args);
1701 #ifdef DEBUG
1702                 fprintf(stderr, "%s: 2.sadc: %s\n", __FUNCTION__, SADC);
1703 #endif
1704                 execvp(SADC, args);
1705                 /*
1706                  * Note: Don't use execl/execlp since we don't have a fixed number of
1707                  * args to give to sadc.
1708                  */
1709                 fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC);
1710                 perror("exec");
1711                 exit(4);
1712                 break;
1713
1714         default: /* Parent */
1715                 if (dup2(fd[0], STDIN_FILENO) < 0) {
1716                         perror("dup2");
1717                         exit(4);
1718                 }
1719                 CLOSE_ALL(fd);
1720
1721                 /* Get now the statistics */
1722                 read_stats();
1723
1724                 break;
1725         }
1726
1727         /* Free structures and activity bitmaps */
1728         free_bitmaps(act);
1729         free_structures(act);
1730
1731         return 0;
1732 }