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