]> granicus.if.org Git - sysstat/blob - sar.c
sar/sadf: Merge functions used to print special records
[sysstat] / sar.c
1 /*
2  * sar: report system activity
3  * (C) 1999-2015 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  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 null.
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         /* Get then set previous timestamp */
445         if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[!curr],
446                                            &rectime, NULL))
447                 return 0;
448         set_record_timestamp_string(S_F_PREFD_TIME_OUTPUT, &record_hdr[!curr],
449                                     NULL, timestamp[!curr], 16, &rectime);
450
451         /* Get then set current timestamp */
452         if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[curr],
453                                            &rectime, NULL))
454                 return 0;
455         set_record_timestamp_string(S_F_PREFD_TIME_OUTPUT, &record_hdr[curr],
456                                     NULL, timestamp[curr], 16, &rectime);
457
458         /* Check if we are beginning a new day */
459         if (use_tm_start && record_hdr[!curr].ust_time &&
460             (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
461             (record_hdr[curr].hour < record_hdr[!curr].hour)) {
462                 cross_day = 1;
463         }
464
465         if (cross_day) {
466                 /*
467                  * This is necessary if we want to properly handle something like:
468                  * sar -s time_start -e time_end with
469                  * time_start(day D) > time_end(day D+1)
470                  */
471                 rectime.tm_hour +=24;
472         }
473
474         /* Check time (2) */
475         if (use_tm_start && (datecmp(&rectime, &tm_start) < 0))
476                 /* it's too soon... */
477                 return 0;
478
479         /* Get interval values */
480         get_itv_value(&record_hdr[curr], &record_hdr[!curr],
481                       cpu_nr, &itv, &g_itv);
482
483         /* Check time (3) */
484         if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) {
485                 /* It's too late... */
486                 *cnt = 0;
487                 return 0;
488         }
489
490         avg_count++;
491
492         /* Test stdout */
493         TEST_STDOUT(STDOUT_FILENO);
494
495         for (i = 0; i < NR_ACT; i++) {
496
497                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
498                         continue;
499
500                 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
501                         /* Display current activity statistics */
502                         (*act[i]->f_print)(act[i], !curr, curr,
503                                            NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
504                 }
505         }
506
507         return 1;
508 }
509
510 /*
511  ***************************************************************************
512  * Display stats since system startup.
513  *
514  * IN:
515  * @curr        Index in array for current sample statistics.
516  ***************************************************************************
517  */
518 void write_stats_startup(int curr)
519 {
520         int i;
521
522         /* Set to 0 previous structures corresponding to boot time */
523         memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE);
524         record_hdr[!curr].record_type = R_STATS;
525         record_hdr[!curr].hour        = record_hdr[curr].hour;
526         record_hdr[!curr].minute      = record_hdr[curr].minute;
527         record_hdr[!curr].second      = record_hdr[curr].second;
528         record_hdr[!curr].ust_time    = record_hdr[curr].ust_time;
529
530         for (i = 0; i < NR_ACT; i++) {
531                 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
532                         memset(act[i]->buf[!curr], 0,
533                                (size_t) act[i]->msize * (size_t) act[i]->nr * (size_t) act[i]->nr2);
534                 }
535         }
536
537         flags |= S_F_SINCE_BOOT;
538         dis = TRUE;
539
540         write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
541                     ALL_ACTIVITIES, TRUE);
542
543         exit(0);
544 }
545
546 /*
547  ***************************************************************************
548  * Read data sent by the data collector.
549  *
550  * IN:
551  * @size        Number of bytes of data to read.
552  *
553  * OUT:
554  * @buffer      Buffer where data will be saved.
555  *
556  * RETURNS:
557  * 1 if end of file has been reached, 0 otherwise.
558  ***************************************************************************
559  */
560 int sa_read(void *buffer, int size)
561 {
562         int n;
563
564         while (size) {
565
566                 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
567                         perror("read");
568                         exit(2);
569                 }
570
571                 if (!n)
572                         return 1;       /* EOF */
573
574                 size -= n;
575                 buffer = (char *) buffer + n;
576         }
577
578         return 0;
579 }
580
581 /*
582  ***************************************************************************
583  * Display a restart message (contents of a R_RESTART record).
584  *
585  * IN:
586  * @tab         Number of tabulations (unused here).
587  * @action      Action expected from current function (unused here).
588  * @cur_date    Date string of current restart message (unused here).
589  * @cur_time    Time string of current restart message.
590  * @utc         True if @cur_time is expressed in UTC (unused here).
591  * @file_hdr    System activity file standard header (unused here).
592  * @cpu_nr      CPU count associated with restart mark.
593  ***************************************************************************
594  */
595 __print_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time, int utc,
596                                   struct file_header *file_hdr, unsigned int cpu_nr)
597 {
598         char restart[64];
599
600         printf("\n%-11s", cur_time);
601         sprintf(restart, "  LINUX RESTART\t(%d CPU)\n",
602                 cpu_nr > 1 ? cpu_nr - 1 : 1);
603         cprintf_s(IS_RESTART, "%s", restart);
604
605 }
606
607 /*
608  ***************************************************************************
609  * Display a comment (contents of R_COMMENT record).
610  *
611  * IN:
612  * @tab         Number of tabulations (unused here).
613  * @action      Action expected from current function (unused here).
614  * @cur_date    Date string of current comment (unused here).
615  * @cur_time    Time string of current comment.
616  * @utc         True if @cur_time is expressed in UTC (unused here).
617  * @comment     Comment to display.
618  * @file_hdr    System activity file standard header (unused here).
619  ***************************************************************************
620  */
621 __print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
622                                   char *comment, struct file_header *file_hdr)
623 {
624         printf("%-11s", cur_time);
625         cprintf_s(IS_COMMENT, "  COM %s\n", comment);
626 }
627
628 /*
629  ***************************************************************************
630  * Read the various statistics sent by the data collector (sadc).
631  *
632  * IN:
633  * @curr        Index in array for current sample statistics.
634  ***************************************************************************
635  */
636 void read_sadc_stat_bunch(int curr)
637 {
638         int i, p;
639
640         /* Read record header (type is always R_STATS since it is read from sadc) */
641         if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
642                 /*
643                  * SIGINT (sent by sadc) is likely to be received
644                  * while we are stuck in sa_read().
645                  * If this happens then no data have to be read.
646                  */
647                 if (sigint_caught)
648                         return;
649
650                 print_read_error();
651         }
652
653         for (i = 0; i < NR_ACT; i++) {
654
655                 if (!id_seq[i])
656                         continue;
657                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
658
659                 if (sa_read(act[p]->buf[curr], act[p]->fsize * act[p]->nr * act[p]->nr2)) {
660                         print_read_error();
661                 }
662         }
663 }
664
665 /*
666  ***************************************************************************
667  * Read stats for current activity from file and display them.
668  *
669  * IN:
670  * @ifd         Input file descriptor.
671  * @fpos        Position in file where reading must start.
672  * @curr        Index in array for current sample statistics.
673  * @rows        Number of rows of screen.
674  * @act_id      Activity to display.
675  * @file_actlst List of activities in file.
676  * @file        Name of file being read.
677  * @file_magic  file_magic structure filled with file magic header data.
678  *
679  * OUT:
680  * @curr        Index in array for next sample statistics.
681  * @cnt         Number of remaining lines of stats to write.
682  * @eosaf       Set to TRUE if EOF (end of file) has been reached.
683  * @reset       Set to TRUE if last_uptime variable should be
684  *              reinitialized (used in next_slice() function).
685  ***************************************************************************
686  */
687 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
688                            int rows, unsigned int act_id, int *reset,
689                            struct file_activity *file_actlst, char *file,
690                            struct file_magic *file_magic)
691 {
692         int p, reset_cd;
693         unsigned long lines = 0;
694         unsigned char rtype;
695         int davg = 0, next, inc;
696
697         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
698                 perror("lseek");
699                 exit(2);
700         }
701
702         /*
703          * Restore the first stats collected.
704          * Used to compute the rate displayed on the first line.
705          */
706         copy_structures(act, id_seq, record_hdr, !*curr, 2);
707
708         *cnt  = count;
709
710         /* Assess number of lines printed */
711         p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
712         if (act[p]->bitmap) {
713                 inc = count_bits(act[p]->bitmap->b_array,
714                                  BITMAP_SIZE(act[p]->bitmap->b_size));
715         }
716         else {
717                 inc = act[p]->nr;
718         }
719
720         reset_cd = 1;
721
722         do {
723                 /* Display count lines of stats */
724                 *eosaf = sa_fread(ifd, &record_hdr[*curr],
725                                   RECORD_HEADER_SIZE, SOFT_SIZE);
726                 rtype = record_hdr[*curr].record_type;
727
728                 if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
729                         /* Read the extra fields since it's not a special record */
730                         read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst);
731                 }
732
733                 if ((lines >= rows) || !lines) {
734                         lines = 0;
735                         dis = 1;
736                 }
737                 else
738                         dis = 0;
739
740                 if (!*eosaf && (rtype != R_RESTART)) {
741
742                         if (rtype == R_COMMENT) {
743                                 /* Display comment */
744                                 next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
745                                                             &tm_start, &tm_end, R_COMMENT, ifd,
746                                                             &rectime, NULL, file, 0,
747                                                             file_magic, &file_hdr, act, &sar_fmt);
748                                 if (next) {
749                                         /* A line of comment was actually displayed */
750                                         lines++;
751                                 }
752                                 continue;
753                         }
754
755                         /* next is set to 1 when we were close enough to desired interval */
756                         next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
757                                            *reset, act_id, reset_cd);
758                         reset_cd = 0;
759                         if (next && (*cnt > 0)) {
760                                 (*cnt)--;
761                         }
762                         if (next) {
763                                 davg++;
764                                 *curr ^=1;
765                                 lines += inc;
766                         }
767                         *reset = FALSE;
768                 }
769         }
770         while (*cnt && !*eosaf && (rtype != R_RESTART));
771
772         if (davg) {
773                 write_stats_avg(!*curr, USE_SA_FILE, act_id);
774         }
775
776         *reset = TRUE;
777 }
778
779 /*
780  ***************************************************************************
781  * Read header data sent by sadc.
782  ***************************************************************************
783  */
784 void read_header_data(void)
785 {
786         struct file_magic file_magic;
787         struct file_activity file_act;
788         int rc, i, p;
789         char version[16];
790
791         /* Read magic header */
792         rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
793
794         sprintf(version, "%d.%d.%d.%d",
795                 file_magic.sysstat_version,
796                 file_magic.sysstat_patchlevel,
797                 file_magic.sysstat_sublevel,
798                 file_magic.sysstat_extraversion);
799         if (!file_magic.sysstat_extraversion) {
800                 version[strlen(version) - 2] = '\0';
801         }
802
803         if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
804             (file_magic.format_magic != FORMAT_MAGIC) ||
805             strcmp(version, VERSION)) {
806
807                 /* sar and sadc commands are not consistent */
808                 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
809                         fprintf(stderr,
810                                 _("Using a wrong data collector from a different sysstat version\n"));
811                 }
812
813                 goto input_error;
814         }
815
816         /*
817          * Read header data.
818          * No need to take into account file_magic.header_size. We are sure that
819          * sadc and sar are from the same version (we have checked FORMAT_MAGIC
820          * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
821          */
822         if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
823                 print_read_error();
824         }
825
826         if (file_hdr.sa_act_nr > NR_ACT)
827                 goto input_error;
828
829         /* Read activity list */
830         for (i = 0; i < file_hdr.sa_act_nr; i++) {
831
832                 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
833                         print_read_error();
834                 }
835
836                 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
837
838                 if ((p < 0) || (act[p]->fsize != file_act.size)
839                             || (file_act.nr <= 0)
840                             || (file_act.nr2 <= 0)
841                             || (act[p]->magic != file_act.magic))
842                         /* Remember that we are reading data from sadc and not from a file... */
843                         goto input_error;
844
845                 id_seq[i]   = file_act.id;      /* We necessarily have "i < NR_ACT" */
846                 act[p]->nr  = file_act.nr;
847                 act[p]->nr2 = file_act.nr2;
848         }
849
850         while (i < NR_ACT) {
851                 id_seq[i++] = 0;
852         }
853
854         /* Check that all selected activties are actually sent by sadc */
855         reverse_check_act(file_hdr.sa_act_nr);
856
857         return;
858
859 input_error:
860
861         /* Strange data sent by sadc...! */
862         fprintf(stderr, _("Inconsistent input data\n"));
863
864         exit(3);
865 }
866
867 /*
868  ***************************************************************************
869  * Read statistics from a system activity data file.
870  *
871  * IN:
872  * @from_file   Input file name.
873  ***************************************************************************
874  */
875 void read_stats_from_file(char from_file[])
876 {
877         struct file_magic file_magic;
878         struct file_activity *file_actlst = NULL;
879         int curr = 1, i, p;
880         int ifd, rtype;
881         int rows, eosaf = TRUE, reset = FALSE;
882         long cnt = 1;
883         off_t fpos;
884
885         /* Get window size */
886         rows = get_win_height();
887
888         /* Read file headers and activity list */
889         check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr,
890                           &file_actlst, id_seq, FALSE);
891
892         /* Perform required allocations */
893         allocate_structures(act);
894
895         /* Print report header */
896         print_report_hdr(flags, &rectime, &file_hdr,
897                          act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
898
899         /* Read system statistics from file */
900         do {
901                 /*
902                  * If this record is a special (RESTART or COMMENT) one, print it and
903                  * (try to) get another one.
904                  */
905                 do {
906                         if (sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE))
907                                 /* End of sa data file */
908                                 return;
909
910                         rtype = record_hdr[0].record_type;
911                         if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
912                                 print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
913                                                      &tm_start, &tm_end, rtype, ifd,
914                                                      &rectime, NULL, from_file, 0, &file_magic,
915                                                      &file_hdr, act, &sar_fmt);
916                         }
917                         else {
918                                 /*
919                                  * OK: Previous record was not a special one.
920                                  * So read now the extra fields.
921                                  */
922                                 read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
923                                                      file_actlst);
924                                 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
925                                                                    &record_hdr[0],
926                                                                    &rectime, NULL))
927                                         /*
928                                          * An error was detected.
929                                          * The timestamp hasn't been updated.
930                                          */
931                                         continue;
932                         }
933                 }
934                 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
935                        (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) ||
936                        (tm_end.use && (datecmp(&rectime, &tm_end) >=0)));
937
938                 /* Save the first stats collected. Will be used to compute the average */
939                 copy_structures(act, id_seq, record_hdr, 2, 0);
940
941                 reset = TRUE;   /* Set flag to reset last_uptime variable */
942
943                 /* Save current file position */
944                 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
945                         perror("lseek");
946                         exit(2);
947                 }
948
949                 /*
950                  * Read and write stats located between two possible Linux restarts.
951                  * Activities that should be displayed are saved in id_seq[] array.
952                  */
953                 for (i = 0; i < NR_ACT; i++) {
954
955                         if (!id_seq[i])
956                                 continue;
957
958                         p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
959                         if (!IS_SELECTED(act[p]->options))
960                                 continue;
961
962                         if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
963                                 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
964                                                       act[p]->id, &reset, file_actlst,
965                                                       from_file, &file_magic);
966                         }
967                         else {
968                                 unsigned int optf, msk;
969
970                                 optf = act[p]->opt_flags;
971
972                                 for (msk = 1; msk < 0x100; msk <<= 1) {
973                                         if ((act[p]->opt_flags & 0xff) & msk) {
974                                                 act[p]->opt_flags &= (0xffffff00 + msk);
975
976                                                 handle_curr_act_stats(ifd, fpos, &curr, &cnt,
977                                                                       &eosaf, rows, act[p]->id,
978                                                                       &reset, file_actlst,
979                                                                       from_file, &file_magic);
980                                                 act[p]->opt_flags = optf;
981                                         }
982                                 }
983                         }
984                 }
985
986                 if (!cnt) {
987                         /* Go to next Linux restart, if possible */
988                         do {
989                                 eosaf = sa_fread(ifd, &record_hdr[curr], RECORD_HEADER_SIZE,
990                                                  SOFT_SIZE);
991                                 rtype = record_hdr[curr].record_type;
992                                 if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
993                                         read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
994                                                              file_actlst);
995                                 }
996                                 else if (!eosaf && (rtype == R_COMMENT)) {
997                                         /* This was a COMMENT record: print it */
998                                         print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
999                                                              &tm_start, &tm_end, R_COMMENT, ifd,
1000                                                              &rectime, NULL, from_file, 0,
1001                                                              &file_magic, &file_hdr, act, &sar_fmt);
1002                                 }
1003                         }
1004                         while (!eosaf && (rtype != R_RESTART));
1005                 }
1006
1007                 /* The last record we read was a RESTART one: Print it */
1008                 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1009                         print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1010                                              &tm_start, &tm_end, R_RESTART, ifd,
1011                                              &rectime, NULL, from_file, 0,
1012                                              &file_magic, &file_hdr, act, &sar_fmt);
1013                 }
1014         }
1015         while (!eosaf);
1016
1017         close(ifd);
1018
1019         free(file_actlst);
1020 }
1021
1022 /*
1023  ***************************************************************************
1024  * Read statistics sent by sadc, the data collector.
1025  ***************************************************************************
1026  */
1027 void read_stats(void)
1028 {
1029         int curr = 1;
1030         unsigned long lines;
1031         unsigned int rows;
1032         int dis_hdr = 0;
1033
1034         /* Don't buffer data if redirected to a pipe... */
1035         setbuf(stdout, NULL);
1036
1037         /* Read stats header */
1038         read_header_data();
1039
1040         if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1041                 fprintf(stderr, _("Requested activities not available\n"));
1042                 exit(1);
1043         }
1044
1045         /* Determine if a stat line header has to be displayed */
1046         dis_hdr = check_line_hdr();
1047
1048         lines = rows = get_win_height();
1049
1050         /* Perform required allocations */
1051         allocate_structures(act);
1052
1053         /* Print report header */
1054         print_report_hdr(flags, &rectime, &file_hdr,
1055                          act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
1056
1057         /* Read system statistics sent by the data collector */
1058         read_sadc_stat_bunch(0);
1059
1060         if (!interval) {
1061                 /* Display stats since boot time and exit */
1062                 write_stats_startup(0);
1063         }
1064
1065         /* Save the first stats collected. Will be used to compute the average */
1066         copy_structures(act, id_seq, record_hdr, 2, 0);
1067
1068         /* Set a handler for SIGINT */
1069         memset(&int_act, 0, sizeof(int_act));
1070         int_act.sa_handler = int_handler;
1071         int_act.sa_flags = SA_RESTART;
1072         sigaction(SIGINT, &int_act, NULL);
1073
1074         /* Main loop */
1075         do {
1076
1077                 /* Get stats */
1078                 read_sadc_stat_bunch(curr);
1079                 if (sigint_caught) {
1080                         /*
1081                          * SIGINT signal caught (it is sent by sadc).
1082                          * => Display average stats.
1083                          */
1084                         curr ^= 1; /* No data retrieved from last read */
1085                         break;
1086                 }
1087
1088                 /* Print results */
1089                 if (!dis_hdr) {
1090                         dis = lines / rows;
1091                         if (dis) {
1092                                 lines %= rows;
1093                         }
1094                         lines++;
1095                 }
1096                 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1097                             NO_RESET, ALL_ACTIVITIES, TRUE);
1098
1099                 if (record_hdr[curr].record_type == R_LAST_STATS) {
1100                         /* File rotation is happening: Re-read header data sent by sadc */
1101                         read_header_data();
1102                         allocate_structures(act);
1103                 }
1104
1105                 if (count > 0) {
1106                         count--;
1107                 }
1108                 if (count) {
1109                         curr ^= 1;
1110                 }
1111         }
1112         while (count);
1113
1114         /*
1115          * Print statistics average.
1116          * At least one line of stats must have been displayed for this.
1117          * (There may be no lines at all if we press Ctrl/C immediately).
1118          */
1119         dis = dis_hdr;
1120         if (avg_count) {
1121                 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1122         }
1123 }
1124
1125 /*
1126  ***************************************************************************
1127  * Main entry to the sar program.
1128  ***************************************************************************
1129  */
1130 int main(int argc, char **argv)
1131 {
1132         int i, rc, opt = 1, args_idx = 2;
1133         int fd[2];
1134         int day_offset = 0;
1135         char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1136         char ltemp[20];
1137
1138         /* Get HZ */
1139         get_HZ();
1140
1141         /* Compute page shift in kB */
1142         get_kb_shift();
1143
1144         from_file[0] = to_file[0] = '\0';
1145
1146 #ifdef USE_NLS
1147         /* Init National Language Support */
1148         init_nls();
1149 #endif
1150
1151         /* Init color strings */
1152         init_colors();
1153
1154         tm_start.use = tm_end.use = FALSE;
1155
1156         /* Allocate and init activity bitmaps */
1157         allocate_bitmaps(act);
1158
1159         init_structures();
1160
1161         /* Process options */
1162         while (opt < argc) {
1163
1164                 if (!strcmp(argv[opt], "--sadc")) {
1165                         /* Locate sadc */
1166                         which_sadc();
1167                 }
1168
1169                 else if (!strcmp(argv[opt], "-I")) {
1170                         if (argv[++opt]) {
1171                                 /* Parse -I option */
1172                                 if (parse_sar_I_opt(argv, &opt, act)) {
1173                                         usage(argv[0]);
1174                                 }
1175                         }
1176                         else {
1177                                 usage(argv[0]);
1178                         }
1179                 }
1180
1181                 else if (!strcmp(argv[opt], "-D")) {
1182                         /* Option to tell sar to write to saYYYYMMDD data files */
1183                         flags |= S_F_SA_YYYYMMDD;
1184                         opt++;
1185                 }
1186
1187                 else if (!strcmp(argv[opt], "-P")) {
1188                         /* Parse -P option */
1189                         if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1190                                 usage(argv[0]);
1191                         }
1192                 }
1193
1194                 else if (!strcmp(argv[opt], "-o")) {
1195                         if (to_file[0]) {
1196                                 /* Output file already specified */
1197                                 usage(argv[0]);
1198                         }
1199                         /* Save stats to a file */
1200                         if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1201                             (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1202                                 strncpy(to_file, argv[opt++], MAX_FILE_LEN);
1203                                 to_file[MAX_FILE_LEN - 1] = '\0';
1204                         }
1205                         else {
1206                                 strcpy(to_file, "-");
1207                         }
1208                 }
1209
1210                 else if (!strcmp(argv[opt], "-f")) {
1211                         if (from_file[0] || day_offset) {
1212                                 /* Input file already specified */
1213                                 usage(argv[0]);
1214                         }
1215                         /* Read stats from a file */
1216                         if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1217                             (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1218                                 strncpy(from_file, argv[opt++], MAX_FILE_LEN);
1219                                 from_file[MAX_FILE_LEN - 1] = '\0';
1220                                 /* Check if this is an alternate directory for sa files */
1221                                 check_alt_sa_dir(from_file, day_offset, -1);
1222                         }
1223                         else {
1224                                 set_default_file(from_file, day_offset, -1);
1225                         }
1226                 }
1227
1228                 else if (!strcmp(argv[opt], "-s")) {
1229                         /* Get time start */
1230                         if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1231                                 usage(argv[0]);
1232                         }
1233                 }
1234
1235                 else if (!strcmp(argv[opt], "-e")) {
1236                         /* Get time end */
1237                         if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1238                                 usage(argv[0]);
1239                         }
1240                 }
1241
1242                 else if (!strcmp(argv[opt], "-h")) {
1243                         /* Display help message */
1244                         display_help(argv[0]);
1245                 }
1246
1247                 else if (!strcmp(argv[opt], "-i")) {
1248                         if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1249                                 usage(argv[0]);
1250                         }
1251                         interval = atol(argv[opt++]);
1252                         if (interval < 1) {
1253                                 usage(argv[0]);
1254                         }
1255                         flags |= S_F_INTERVAL_SET;
1256                 }
1257
1258                 else if (!strcmp(argv[opt], "-m")) {
1259                         if (argv[++opt]) {
1260                                 /* Parse option -m */
1261                                 if (parse_sar_m_opt(argv, &opt, act)) {
1262                                         usage(argv[0]);
1263                                 }
1264                         }
1265                         else {
1266                                 usage(argv[0]);
1267                         }
1268                 }
1269
1270                 else if (!strcmp(argv[opt], "-n")) {
1271                         if (argv[++opt]) {
1272                                 /* Parse option -n */
1273                                 if (parse_sar_n_opt(argv, &opt, act)) {
1274                                         usage(argv[0]);
1275                                 }
1276                         }
1277                         else {
1278                                 usage(argv[0]);
1279                         }
1280                 }
1281
1282                 else if ((strlen(argv[opt]) > 1) &&
1283                          (strlen(argv[opt]) < 4) &&
1284                          !strncmp(argv[opt], "-", 1) &&
1285                          (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1286                         if (from_file[0] || day_offset) {
1287                                 /* Input file already specified */
1288                                 usage(argv[0]);
1289                         }
1290                         day_offset = atoi(argv[opt++] + 1);
1291                 }
1292
1293                 else if (!strncmp(argv[opt], "-", 1)) {
1294                         /* Other options not previously tested */
1295                         if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) {
1296                                 if (rc == 1) {
1297                                         usage(argv[0]);
1298                                 }
1299                                 exit(1);
1300                         }
1301                         opt++;
1302                 }
1303
1304                 else if (interval < 0) {
1305                         /* Get interval */
1306                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1307                                 usage(argv[0]);
1308                         }
1309                         interval = atol(argv[opt++]);
1310                         if (interval < 0) {
1311                                 usage(argv[0]);
1312                         }
1313                 }
1314
1315                 else {
1316                         /* Get count value */
1317                         if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1318                             !interval) {
1319                                 usage(argv[0]);
1320                         }
1321                         if (count) {
1322                                 /* Count parameter already set */
1323                                 usage(argv[0]);
1324                         }
1325                         count = atol(argv[opt++]);
1326                         if (count < 1) {
1327                                 usage(argv[0]);
1328                         }
1329                 }
1330         }
1331
1332         /* 'sar' is equivalent to 'sar -f' */
1333         if ((argc == 1) ||
1334             ((interval < 0) && !from_file[0] && !to_file[0])) {
1335                 set_default_file(from_file, day_offset, -1);
1336         }
1337
1338         if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1339                 tm_end.tm_hour += 24;
1340         }
1341
1342         /*
1343          * Check option dependencies.
1344          */
1345         /* You read from a file OR you write to it... */
1346         if (from_file[0] && to_file[0]) {
1347                 fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
1348                 exit(1);
1349         }
1350         /* Use time start or option -i only when reading stats from a file */
1351         if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
1352                 fprintf(stderr,
1353                         _("Not reading from a system activity file (use -f option)\n"));
1354                 exit(1);
1355         }
1356         /* Don't print stats since boot time if -o or -f options are used */
1357         if (!interval && (from_file[0] || to_file[0])) {
1358                 usage(argv[0]);
1359         }
1360
1361         /* Cannot enter a day shift with -o option */
1362         if (to_file[0] && day_offset) {
1363                 usage(argv[0]);
1364         }
1365
1366         if (USE_PRETTY_OPTION(flags)) {
1367                 dm_major = get_devmap_major();
1368         }
1369
1370         if (!count) {
1371                 /*
1372                  * count parameter not set: Display all the contents of the file
1373                  * or generate a report continuously.
1374                  */
1375                 count = -1;
1376         }
1377
1378         /* Default is CPU activity... */
1379         select_default_activity(act);
1380
1381         /* Reading stats from file: */
1382         if (from_file[0]) {
1383                 if (interval < 0) {
1384                         interval = 1;
1385                 }
1386
1387                 /* Read stats from file */
1388                 read_stats_from_file(from_file);
1389
1390                 /* Free stuctures and activity bitmaps */
1391                 free_bitmaps(act);
1392                 free_structures(act);
1393
1394                 return 0;
1395         }
1396
1397         /* Reading stats from sadc: */
1398
1399         /* Create anonymous pipe */
1400         if (pipe(fd) == -1) {
1401                 perror("pipe");
1402                 exit(4);
1403         }
1404
1405         switch (fork()) {
1406
1407         case -1:
1408                 perror("fork");
1409                 exit(4);
1410                 break;
1411
1412         case 0: /* Child */
1413                 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1414                         perror("dup2");
1415                         exit(4);
1416                 }
1417                 CLOSE_ALL(fd);
1418
1419                 /*
1420                  * Prepare options for sadc.
1421                  */
1422                 /* Program name */
1423                 salloc(0, SADC);
1424
1425                 /* Interval value */
1426                 if (interval < 0) {
1427                         usage(argv[0]);
1428                 }
1429                 else if (!interval) {
1430                         strcpy(ltemp, "1");
1431                 }
1432                 else {
1433                         sprintf(ltemp, "%ld", interval);
1434                 }
1435                 salloc(1, ltemp);
1436
1437                 /* Count number */
1438                 if (count >= 0) {
1439                         sprintf(ltemp, "%ld", count + 1);
1440                         salloc(args_idx++, ltemp);
1441                 }
1442
1443                 /* Flags to be passed to sadc */
1444                 salloc(args_idx++, "-z");
1445
1446                 /* Writing data to a file (option -o) */
1447                 if (to_file[0]) {
1448                         /* Set option -D if entered */
1449                         if (USE_SA_YYYYMMDD(flags)) {
1450                                 salloc(args_idx++, "-D");
1451                         }
1452                         /* Collect all possible activities (option -S XALL for sadc) */
1453                         salloc(args_idx++, "-S");
1454                         salloc(args_idx++, K_XALL);
1455                         /* Outfile arg */
1456                         salloc(args_idx++, to_file);
1457                 }
1458                 else {
1459                         /*
1460                          * If option -o hasn't been used, then tell sadc
1461                          * to collect only activities that will be displayed.
1462                          */
1463                         int act_id = 0;
1464
1465                         for (i = 0; i < NR_ACT; i++) {
1466                                 if (IS_SELECTED(act[i]->options)) {
1467                                         act_id |= act[i]->group;
1468                                 }
1469                         }
1470                         if (act_id) {
1471                                 act_id <<= 8;
1472                                 snprintf(ltemp, 19, "%d", act_id);
1473                                 ltemp[19] = '\0';
1474                                 salloc(args_idx++, "-S");
1475                                 salloc(args_idx++, ltemp);
1476                         }
1477                 }
1478
1479                 /* Last arg is NULL */
1480                 args[args_idx] = NULL;
1481
1482                 /* Call now the data collector */
1483                 execv(SADC_PATH, args);
1484                 execvp(SADC, args);
1485                 /*
1486                  * Note: Don't use execl/execlp since we don't have a fixed number of
1487                  * args to give to sadc.
1488                  */
1489                 fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC);
1490                 perror("exec");
1491                 exit(4);
1492                 break;
1493
1494         default: /* Parent */
1495                 if (dup2(fd[0], STDIN_FILENO) < 0) {
1496                         perror("dup2");
1497                         exit(4);
1498                 }
1499                 CLOSE_ALL(fd);
1500
1501                 /* Get now the statistics */
1502                 read_stats();
1503
1504                 break;
1505         }
1506
1507         /* Free structures and activity bitmaps */
1508         free_bitmaps(act);
1509         free_structures(act);
1510
1511         return 0;
1512 }