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