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