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