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