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