]> granicus.if.org Git - sysstat/blob - sadc.c
sysstat-12.5.3
[sysstat] / sadc.c
1 /*
2  * sadc: system activity data collector
3  * (C) 1999-2021 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  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
19  ***************************************************************************
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <ctype.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <time.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <dirent.h>
33 #include <sys/file.h>
34 #include <sys/stat.h>
35 #include <sys/utsname.h>
36
37 #include "version.h"
38 #include "sa.h"
39
40 #ifdef USE_NLS
41 #include <locale.h>
42 #include <libintl.h>
43 #define _(string) gettext(string)
44 #else
45 #define _(string) (string)
46 #endif
47
48 #if (defined(HAVE_SENSORS) && !defined(ARCH32)) || (defined(ARCH32) && defined(HAVE_SENSORS32))
49 #include "sensors/sensors.h"
50 #include "sensors/error.h"
51 #endif
52
53 #ifdef USE_SCCSID
54 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
55 char *sccsid(void) { return (SCCSID); }
56 #endif
57
58 #ifdef TEST
59 extern time_t __unix_time;
60 extern int __env;
61 #endif
62
63 extern char *tzname[2];
64
65 long interval = -1;
66 uint64_t flags = 0;
67
68 int optz = 0;
69 char timestamp[2][TIMESTAMP_LEN];
70
71 struct file_header file_hdr;
72 struct record_header record_hdr;
73
74 char comment[MAX_COMMENT_LEN];
75
76 unsigned int id_seq[NR_ACT];
77
78 extern unsigned int hdr_types_nr[];
79 extern unsigned int act_types_nr[];
80 extern unsigned int rec_types_nr[];
81
82 extern struct activity *act[];
83 extern __nr_t (*f_count[]) (struct activity *);
84
85 struct sigaction alrm_act, int_act;
86 int sigint_caught = 0;
87
88 /*
89  ***************************************************************************
90  * Print usage and exit.
91  *
92  * IN:
93  * @progname    Name of sysstat command
94  ***************************************************************************
95  */
96 void usage(char *progname)
97 {
98         fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ <outfile> ]\n"),
99                 progname);
100
101         fprintf(stderr, _("Options are:\n"
102                           "[ -C <comment> ] [ -D ] [ -F ] [ -f ] [ -L ] [ -V ]\n"
103                           "[ -S { INT | DISK | IPV6 | POWER | SNMP | XDISK | ALL | XALL } ]\n"));
104         exit(1);
105 }
106
107 /*
108  ***************************************************************************
109  * Collect all activities belonging to a group.
110  *
111  * IN:
112  * @group_id    Group identification number.
113  * @opt_f       Optionnal flag to set.
114  ***************************************************************************
115  */
116 void collect_group_activities(unsigned int group_id, unsigned int opt_f)
117 {
118         int i;
119
120         for (i = 0; i < NR_ACT; i++) {
121                 if (act[i]->group & group_id) {
122                         act[i]->options |= AO_COLLECTED;
123                         if (opt_f) {
124                                 act[i]->opt_flags |= opt_f;
125                         }
126                 }
127         }
128 }
129
130 /*
131  ***************************************************************************
132  * Parse option -S, indicating which activities are to be collected.
133  *
134  * IN:
135  * @argv        Arguments list.
136  * @opt         Index in list of arguments.
137  ***************************************************************************
138  */
139 void parse_sadc_S_option(char *argv[], int opt)
140 {
141         char *p;
142         int i;
143
144         for (p = strtok(argv[opt], ","); p; p = strtok(NULL, ",")) {
145                 if (!strcmp(p, K_INT)) {
146                         /* Select group of interrupt activities */
147                         collect_group_activities(G_INT, AO_F_NULL);
148                 }
149                 else if (!strcmp(p, K_DISK)) {
150                         /* Select group of disk activities */
151                         collect_group_activities(G_DISK, AO_F_NULL);
152                 }
153                 else if (!strcmp(p, K_XDISK)) {
154                         /* Select group of disk and partition/filesystem activities */
155                         collect_group_activities(G_DISK + G_XDISK, AO_F_DISK_PART);
156                 }
157                 else if (!strcmp(p, K_SNMP)) {
158                         /* Select group of SNMP activities */
159                         collect_group_activities(G_SNMP, AO_F_NULL);
160                 }
161                 else if (!strcmp(p, K_IPV6)) {
162                         /* Select group of IPv6 activities */
163                         collect_group_activities(G_IPV6, AO_F_NULL);
164                 }
165                 else if (!strcmp(p, K_POWER)) {
166                         /* Select group of activities related to power management */
167                         collect_group_activities(G_POWER, AO_F_NULL);
168                 }
169                 else if (!strcmp(p, K_ALL) || !strcmp(p, K_XALL)) {
170                         /* Select all activities */
171                         for (i = 0; i < NR_ACT; i++) {
172
173                                 if (!strcmp(p, K_ALL) && (act[i]->group & G_XDISK))
174                                         /*
175                                          * Don't select G_XDISK activities
176                                          * when option -S ALL is used.
177                                          */
178                                         continue;
179
180                                 act[i]->options |= AO_COLLECTED;
181                         }
182                         if (!strcmp(p, K_XALL)) {
183                                 /* Tell sadc to also collect partition statistics */
184                                 collect_group_activities(G_DISK + G_XDISK, AO_F_DISK_PART);
185                         }
186                 }
187                 else if (!strcmp(p, K_A_NULL)) {
188                         /* Unselect all activities */
189                         for (i = 0; i < NR_ACT; i++) {
190                                 act[i]->options &= ~AO_COLLECTED;
191                         }
192                 }
193                 else if (!strncmp(p, "A_", 2)) {
194                         /* Select activity by name */
195                         for (i = 0; i < NR_ACT; i++) {
196                                 if (!strcmp(p, act[i]->name)) {
197                                         act[i]->options |= AO_COLLECTED;
198                                         break;
199                                 }
200                         }
201                         if (i == NR_ACT) {
202                                 usage(argv[0]);
203                         }
204                 }
205                 else if (!strncmp(p, "-A_", 3)) {
206                         /* Unselect activity by name */
207                         for (i = 0; i < NR_ACT; i++) {
208                                 if (!strcmp(p + 1, act[i]->name)) {
209                                         act[i]->options &= ~AO_COLLECTED;
210                                         break;
211                                 }
212                         }
213                         if (i == NR_ACT) {
214                                 usage(argv[0]);
215                         }
216                 }
217                 else {
218                         usage(argv[0]);
219                 }
220         }
221 }
222
223 /*
224  ***************************************************************************
225  * SIGALRM signal handler. No need to reset handler here.
226  *
227  * IN:
228  * @sig Signal number.
229  ***************************************************************************
230  */
231 void alarm_handler(int sig)
232 {
233         __alarm(interval);
234 }
235
236 /*
237  ***************************************************************************
238  * SIGINT signal handler.
239  *
240  * IN:
241  * @sig Signal number.
242  ***************************************************************************
243  */
244 void int_handler(int sig)
245 {
246         pid_t ppid = getppid();
247
248         sigint_caught = 1;
249
250         if (!optz || (ppid == 1)) {
251                 /* sadc hasn't been called by sar or sar process is already dead */
252                 exit(1);
253         }
254
255         /*
256          * When starting sar then pressing ctrl/c, SIGINT is received
257          * by sadc, not sar. So send SIGINT to sar so that average stats
258          * can be displayed.
259          */
260         if (kill(ppid, SIGINT) < 0) {
261                 exit(1);
262         }
263 }
264
265 /*
266  ***************************************************************************
267  * Display an error message.
268  ***************************************************************************
269  */
270 void p_write_error(void)
271 {
272         fprintf(stderr, _("Cannot write data to system activity file: %s\n"),
273                 strerror(errno));
274         exit(2);
275 }
276
277 /*
278  ***************************************************************************
279  * Init structures. All of them are init'ed first when they are allocated
280  * (done by SREALLOC() macro in sa_sys_init() function).
281  * Then, they are init'ed again each time before reading the various system
282  * stats to make sure that no stats from a previous reading will remain.
283  * This is useful mainly for non sequential activities where some structures
284  * may remain unchanged. Such an activity is A_CPU, for which statistics
285  * for offline CPU won't be read and their corresponding stats structure
286  * won't be overwritten, giving the idea they are still online if we don't
287  * reset their structures to zero.
288  * Other activities may also assume that structure's fields are initialized
289  * when their stats are read.
290  ***************************************************************************
291  */
292 void reset_stats(void)
293 {
294         int i;
295
296         for (i = 0; i < NR_ACT; i++) {
297                 if ((act[i]->_nr0 > 0) && act[i]->_buf0) {
298                         memset(act[i]->_buf0, 0,
299                                (size_t) act[i]->msize * (size_t) act[i]->nr_allocated * (size_t) act[i]->nr2);
300                 }
301         }
302 }
303
304 /*
305  ***************************************************************************
306  * Count activities items then allocate and init corresponding structures.
307  * Activities such as A_CPU with AO_ALWAYS_COUNTED flag set are always
308  * counted (thus the number of CPU will always be counted even if CPU
309  * activity is not collected), but ONLY those that will be collected have
310  * allocated structures.
311  * This function is called when sadc is started, and when a file is rotated.
312  * If a file is rotated and structures are reallocated with a larger size,
313  * additional space is not initialized: It doesn't matter as reset_stats()
314  * will do it later.
315  ***************************************************************************
316  */
317 void sa_sys_init(void)
318 {
319         int i, idx;
320         __nr_t f_count_results[NR_F_COUNT];
321
322         /* Init array. Means that no items have been counted yet */
323         for (i = 0; i < NR_F_COUNT; i++) {
324                 f_count_results[i] = -1;
325         }
326
327         for (i = 0; i < NR_ACT; i++) {
328
329                 if ((HAS_COUNT_FUNCTION(act[i]->options) && IS_COLLECTED(act[i]->options)) ||
330                     ALWAYS_COUNT_ITEMS(act[i]->options)) {
331                         idx = act[i]->f_count_index;
332
333                         /* Number of items is not a constant and should be calculated */
334                         if (f_count_results[idx] >= 0) {
335                                 act[i]->nr_ini = f_count_results[idx];
336                         }
337                         else {
338                                 act[i]->nr_ini = (f_count[idx])(act[i]);
339                                 f_count_results[idx] = act[i]->nr_ini;
340                         }
341                 }
342
343                 if (act[i]->nr_ini > 0) {
344                         if (act[i]->f_count2) {
345                                 act[i]->nr2 = (*act[i]->f_count2)(act[i]);
346                         }
347                         /* else act[i]->nr2 is a constant and doesn't need to be calculated */
348
349                         if (!act[i]->nr2) {
350                                 act[i]->nr_ini = 0;
351                         }
352                 }
353
354                 if (IS_COLLECTED(act[i]->options) && (act[i]->nr_ini > 0)) {
355                         /* Allocate structures for current activity (using nr_ini and nr2 results) */
356                         SREALLOC(act[i]->_buf0, void,
357                                  (size_t) act[i]->msize * (size_t) act[i]->nr_ini * (size_t) act[i]->nr2);
358                         act[i]->nr_allocated = act[i]->nr_ini;
359                 }
360
361                 if (act[i]->nr_ini <= 0) {
362                         /* No items found: Invalidate current activity */
363                         act[i]->options &= ~AO_COLLECTED;
364                 }
365
366                 if (HAS_DETECT_FUNCTION(act[i]->options) && IS_COLLECTED(act[i]->options)) {
367                         idx = act[i]->f_count_index;
368
369                         /* Detect if files needed by activity exist */
370                         if (f_count_results[idx] < 0) {
371                                 f_count_results[idx] = (f_count[idx])(act[i]);
372                         }
373                         if (f_count_results[idx] == 0) {
374                                 /* Files not present */
375                                 act[i]->options &= ~AO_COLLECTED;
376                         }
377                 }
378
379                 /* Set default activity list */
380                 id_seq[i] = act[i]->id;
381         }
382 }
383
384 /*
385  ***************************************************************************
386  * Free structures.
387  ***************************************************************************
388  */
389 void sa_sys_free(void)
390 {
391         int i;
392
393         for (i = 0; i < NR_ACT; i++) {
394
395                 if (act[i]->nr_allocated > 0) {
396                         if (act[i]->_buf0) {
397                                 free(act[i]->_buf0);
398                                 act[i]->_buf0 = NULL;
399                                 act[i]->nr_allocated = 0;
400                         }
401                 }
402         }
403 }
404
405 /*
406  ***************************************************************************
407  * If -L option used, request a non-blocking, exclusive lock on the file.
408  * If lock would block, then another process (possibly sadc) has already
409  * opened that file => exit.
410  *
411  * IN:
412  * @fd          Output file descriptor.
413  * @fatal       Indicate if failing to lock file should be fatal or not.
414  *              If it's not fatal then we'll wait for next iteration and
415  *              try again.
416  *
417  * RETURNS:
418  * 0 on success, or 1 if file couldn't be locked.
419  ***************************************************************************
420  */
421 int ask_for_flock(int fd, int fatal)
422 {
423         /* Option -L may be used only if an outfile was specified on the command line */
424         if (LOCK_FILE(flags)) {
425                 /*
426                  * Yes: Try to lock file. To make code portable, check for both EWOULDBLOCK
427                  * and EAGAIN return codes, and treat them the same (glibc documentation).
428                  * Indeed, some Linux ports (e.g. hppa-linux) do not equate EWOULDBLOCK and
429                  * EAGAIN like every other Linux port.
430                  */
431                 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
432                         if ((((errno == EWOULDBLOCK) || (errno == EAGAIN)) && (fatal == FATAL)) ||
433                             ((errno != EWOULDBLOCK) && (errno != EAGAIN))) {
434                                 perror("flock");
435                                 exit(1);
436                         }
437                         /* Was unable to lock file: Lock would have blocked... */
438                         return 1;
439                 }
440                 else {
441                         /* File successfully locked */
442                         flags |= S_F_FILE_LOCKED;
443                 }
444         }
445         return 0;
446 }
447
448 /*
449  ***************************************************************************
450  * Fill system activity file magic header.
451  *
452  * IN:
453  * @file_magic  System activity file magic header.
454  ***************************************************************************
455  */
456 void fill_magic_header(struct file_magic *file_magic)
457 {
458         int i;
459
460         memset(file_magic, 0, FILE_MAGIC_SIZE);
461
462         file_magic->sysstat_magic = SYSSTAT_MAGIC;
463         file_magic->format_magic  = FORMAT_MAGIC;
464
465         enum_version_nr(file_magic);
466
467         file_magic->header_size = FILE_HEADER_SIZE;
468
469         for (i = 0; i < 3; i++) {
470                 file_magic->hdr_types_nr[i] = hdr_types_nr[i];
471         }
472 }
473
474 /*
475  ***************************************************************************
476  * Fill system activity file header, then write it (or print it if stdout).
477  *
478  * IN:
479  * @fd  Output file descriptor. May be stdout.
480  ***************************************************************************
481  */
482 void setup_file_hdr(int fd)
483 {
484         int i, j, p;
485         struct tm rectime;
486         struct utsname header;
487         struct file_magic file_magic;
488         struct file_activity file_act;
489
490         /* Fill then write file magic header */
491         fill_magic_header(&file_magic);
492
493         if (write_all(fd, &file_magic, FILE_MAGIC_SIZE) != FILE_MAGIC_SIZE) {
494                 p_write_error();
495         }
496
497         /* First reset the structure */
498         memset(&file_hdr, 0, FILE_HEADER_SIZE);
499
500         /* Then get current date */
501         file_hdr.sa_ust_time = (unsigned long long) get_time(&rectime, 0);
502
503         /* OK, now fill the header */
504         file_hdr.sa_act_nr      = get_activity_nr(act, AO_COLLECTED, COUNT_ACTIVITIES);
505         file_hdr.sa_day         = rectime.tm_mday;
506         file_hdr.sa_month       = rectime.tm_mon;
507         file_hdr.sa_year        = rectime.tm_year;
508         file_hdr.sa_sizeof_long = sizeof(long);
509         file_hdr.sa_hz          = HZ;
510
511         for (i = 0; i < 3; i++) {
512                 file_hdr.act_types_nr[i] = act_types_nr[i];
513                 file_hdr.rec_types_nr[i] = rec_types_nr[i];
514         }
515         file_hdr.act_size = FILE_ACTIVITY_SIZE;
516         file_hdr.rec_size = RECORD_HEADER_SIZE;
517
518         /*
519          * This is a new file (or stdout): Set sa_cpu_nr field to the number
520          * of CPU of the machine (1 .. CPU_NR + 1). This is the number of CPU, whether
521          * online or offline, when sadc was started.
522          * A_CPU activity is always counted in sa_sys_init(), even if it's not collected.
523          */
524         file_hdr.sa_cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr_ini;
525
526         /* Get system name, release number, hostname and machine architecture */
527         __uname(&header);
528         strncpy(file_hdr.sa_sysname, header.sysname, sizeof(file_hdr.sa_sysname));
529         file_hdr.sa_sysname[sizeof(file_hdr.sa_sysname) - 1]  = '\0';
530         strncpy(file_hdr.sa_nodename, header.nodename, sizeof(file_hdr.sa_nodename));
531         file_hdr.sa_nodename[sizeof(file_hdr.sa_nodename) - 1] = '\0';
532         strncpy(file_hdr.sa_release, header.release, sizeof(file_hdr.sa_release));
533         file_hdr.sa_release[sizeof(file_hdr.sa_release) - 1]  = '\0';
534         strncpy(file_hdr.sa_machine, header.machine, sizeof(file_hdr.sa_machine));
535         file_hdr.sa_machine[sizeof(file_hdr.sa_machine) - 1]  = '\0';
536
537         /* Get timezone value and save it */
538         tzset();
539         strncpy(file_hdr.sa_tzname, tzname[0], TZNAME_LEN);
540         file_hdr.sa_tzname[TZNAME_LEN - 1] = '\0';
541
542         /* Write file header */
543         if (write_all(fd, &file_hdr, FILE_HEADER_SIZE) != FILE_HEADER_SIZE) {
544                 p_write_error();
545         }
546
547         /* Reset file_activity structure (in case some unknown extra fields exist) */
548         memset(&file_act, 0, FILE_ACTIVITY_SIZE);
549
550         /* Write activity list */
551         for (i = 0; i < NR_ACT; i++) {
552
553                 /*
554                  * Activity sequence given by id_seq array.
555                  * Sequence must be the same for stdout as for output file.
556                  */
557                 if (!id_seq[i])
558                         continue;
559                 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
560                         continue;
561
562                 if (IS_COLLECTED(act[p]->options)) {
563                         file_act.id    = act[p]->id;
564                         file_act.magic = act[p]->magic;
565                         file_act.nr    = act[p]->nr_ini;
566                         file_act.nr2   = act[p]->nr2;
567                         file_act.size  = act[p]->fsize;
568                         for (j = 0; j < 3; j++) {
569                                 file_act.types_nr[j] = act[p]->gtypes_nr[j];
570                         }
571
572                         file_act.has_nr = HAS_COUNT_FUNCTION(act[p]->options);
573
574                         if (write_all(fd, &file_act, FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
575                                 p_write_error();
576                         }
577                 }
578         }
579
580         return;
581 }
582
583 /*
584  ***************************************************************************
585  * Write the new number of CPU after the RESTART record in file.
586  *
587  * IN:
588  * @ofd         Output file descriptor.
589  ***************************************************************************
590  */
591 void write_new_cpu_nr(int ofd)
592 {
593         int p;
594
595         p = get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND);
596
597         if (write_all(ofd, &(act[p]->nr_ini), sizeof(__nr_t)) != sizeof(__nr_t)) {
598                 p_write_error();
599         }
600 }
601
602 /*
603  ***************************************************************************
604  * sadc called with interval and count parameters not set:
605  * Write a dummy record notifying a system restart, or insert a comment in
606  * binary data file if option -C has been used.
607  * Writing a dummy record should typically be done at boot time,
608  * before the cron daemon is started to avoid conflict with sa1/sa2 scripts.
609  *
610  * IN:
611  * @ofd         Output file descriptor.
612  * @rtype       Record type to write (restart or comment).
613  ***************************************************************************
614  */
615 void write_special_record(int ofd, int rtype)
616 {
617         struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
618
619         /* Check if file is locked */
620         if (!FILE_LOCKED(flags)) {
621                 ask_for_flock(ofd, FATAL);
622         }
623
624         /* Reset the structure (sane to do it, as other fields may be added in the future) */
625         memset(&record_hdr, 0, RECORD_HEADER_SIZE);
626
627         /* Set record type */
628         record_hdr.record_type = rtype;
629
630         /* Save time */
631         record_hdr.ust_time = (unsigned long long) get_time(&rectime, 0);
632
633         record_hdr.hour   = rectime.tm_hour;
634         record_hdr.minute = rectime.tm_min;
635         record_hdr.second = rectime.tm_sec;
636
637         /* Write record now */
638         if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
639                 p_write_error();
640         }
641
642         if (rtype == R_RESTART) {
643                 /* Also write the new number of CPU */
644                 write_new_cpu_nr(ofd);
645         }
646         else if (rtype == R_COMMENT) {
647                 /* Also write the comment */
648                 if (write_all(ofd, comment, MAX_COMMENT_LEN) != MAX_COMMENT_LEN) {
649                         p_write_error();
650                 }
651         }
652 }
653
654 /*
655  ***************************************************************************
656  * Write stats (or print them if stdout).
657  *
658  * IN:
659  * @ofd         Output file descriptor. May be stdout.
660  ***************************************************************************
661  */
662 void write_stats(int ofd)
663 {
664         int i, p;
665
666         /* Try to lock file */
667         if (!FILE_LOCKED(flags)) {
668                 if (ask_for_flock(ofd, NON_FATAL))
669                         /*
670                          * Unable to lock file:
671                          * Wait for next iteration to try again to save data.
672                          */
673                         return;
674         }
675
676         /* Write record header */
677         if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
678                 p_write_error();
679         }
680
681         /* Then write all statistics */
682         for (i = 0; i < NR_ACT; i++) {
683
684                 if (!id_seq[i])
685                         continue;
686                 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
687                         continue;
688
689                 if (IS_COLLECTED(act[p]->options)) {
690                         if (HAS_COUNT_FUNCTION(act[p]->options) && (act[p]->f_count_index >= 0)) {
691                                 if (write_all(ofd, &(act[p]->_nr0), sizeof(__nr_t)) != sizeof(__nr_t)) {
692                                         p_write_error();
693                                 }
694                         }
695                         if (write_all(ofd, act[p]->_buf0, act[p]->fsize * act[p]->_nr0 * act[p]->nr2) !=
696                             (act[p]->fsize * act[p]->_nr0 * act[p]->nr2)) {
697                                 p_write_error();
698                         }
699                 }
700         }
701 }
702
703 /*
704  ***************************************************************************
705  * Create a system activity daily data file.
706  *
707  * IN:
708  * @ofile       Name of output file.
709  *
710  * OUT:
711  * @ofd         Output file descriptor.
712  ***************************************************************************
713  */
714 void create_sa_file(int *ofd, char *ofile)
715 {
716         if ((*ofd = open(ofile, O_CREAT | O_WRONLY,
717                          S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0)
718                 goto create_error;
719
720         /* Try to lock file */
721         ask_for_flock(*ofd, FATAL);
722
723         /* Truncate file */
724         if (ftruncate(*ofd, 0) >= 0) {
725
726                 /* Write file header */
727                 setup_file_hdr(*ofd);
728
729                 return;
730         }
731
732 create_error:
733         fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
734         exit(2);
735 }
736
737 /*
738  ***************************************************************************
739  * Get descriptor for stdout.
740  *
741  * IN:
742  * @stdfd       A value >= 0 indicates that stats data should also
743  *              be written to stdout.
744  *
745  * OUT:
746  * @stdfd       Stdout file descriptor.
747  ***************************************************************************
748  */
749 void open_stdout(int *stdfd)
750 {
751         if (*stdfd >= 0) {
752                 if ((*stdfd = dup(STDOUT_FILENO)) < 0) {
753                         perror("dup");
754                         exit(4);
755                 }
756                 /* Write file header on STDOUT */
757                 setup_file_hdr(*stdfd);
758         }
759 }
760
761 /*
762  ***************************************************************************
763  * Get descriptor for output file and write its header.
764  * We may enter this function several times (when we rotate a file).
765  * NB: If data are appended to an existing file then the format must be
766  * strictly that expected by current version.
767  *
768  * IN:
769  * @ofile               Name of output file.
770  * @restart_mark        TRUE if sadc called with interval (and count) not
771  *                      set, and no comment given (so we are going to insert
772  *                      a restart mark into the file).
773  *
774  * OUT:
775  * @ofd                 Output file descriptor.
776  ***************************************************************************
777  */
778 void open_ofile(int *ofd, char ofile[], int restart_mark)
779 {
780         struct file_magic file_magic;
781         struct file_activity file_act[NR_ACT];
782         struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
783         ssize_t sz;
784         int i, p;
785
786         if (!ofile[0])
787                 return;
788
789         /* Try to open file and check that data can be appended to it */
790         if ((*ofd = open(ofile, O_APPEND | O_RDWR)) < 0) {
791                 if (errno == ENOENT) {
792                         /* File doesn't exist: Create it */
793                         create_sa_file(ofd, ofile);
794                         return;
795                 }
796                 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
797                 exit(2);
798         }
799
800         /* Read file magic header */
801         sz = read(*ofd, &file_magic, FILE_MAGIC_SIZE);
802         if (!sz) {
803                 close(*ofd);
804                 /* This is an empty file: Create it again */
805                 create_sa_file(ofd, ofile);
806                 return;
807         }
808
809         /* Test various values ("strict writing" rule) */
810         if ((sz != FILE_MAGIC_SIZE) ||
811             (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
812             (file_magic.format_magic != FORMAT_MAGIC) ||
813             (file_magic.header_size != FILE_HEADER_SIZE) ||
814             (file_magic.hdr_types_nr[0] != FILE_HEADER_ULL_NR) ||
815             (file_magic.hdr_types_nr[1] != FILE_HEADER_UL_NR) ||
816             (file_magic.hdr_types_nr[2] != FILE_HEADER_U_NR)) {
817                 if (FORCE_FILE(flags)) {
818                         close(*ofd);
819                         /* -F option used: Truncate file */
820                         create_sa_file(ofd, ofile);
821                         return;
822                 }
823 #ifdef DEBUG
824                 fprintf(stderr, "%s: Size read=%zd sysstat_magic=%x format_magic=%x header_size=%u header=%d,%d,%d\n",
825                         __FUNCTION__, sz, file_magic.sysstat_magic, file_magic.format_magic, file_magic.header_size,
826                         file_magic.hdr_types_nr[0], file_magic.hdr_types_nr[1], file_magic.hdr_types_nr[2]);
827 #endif
828                 /* Display error message and exit */
829                 handle_invalid_sa_file(*ofd, &file_magic, ofile, sz);
830         }
831
832         /* Read file standard header */
833         if ((sz = read(*ofd, &file_hdr, FILE_HEADER_SIZE)) != FILE_HEADER_SIZE) {
834 #ifdef DEBUG
835                 fprintf(stderr, "%s: Size read=%zd\n",
836                         __FUNCTION__, sz);
837 #endif
838                 goto append_error;
839         }
840
841         /*
842          * If we are using the standard daily data file (file specified
843          * as "-" on the command line) and it is from a past month,
844          * then overwrite (truncate) it.
845          */
846         get_time(&rectime, 0);
847
848         if (((file_hdr.sa_month != rectime.tm_mon) ||
849             (file_hdr.sa_year != rectime.tm_year)) &&
850             WANT_SA_ROTAT(flags)) {
851                 close(*ofd);
852                 create_sa_file(ofd, ofile);
853                 return;
854         }
855
856         /* OK: It's a true system activity file */
857         if (!file_hdr.sa_act_nr || (file_hdr.sa_act_nr > NR_ACT)) {
858 #ifdef DEBUG
859                 fprintf(stderr, "%s: sa_act_nr=%d\n",
860                         __FUNCTION__, file_hdr.sa_act_nr);
861 #endif
862                 /*
863                  * No activities at all or at least one unknown activity:
864                  * Cannot append data to such a file.
865                  */
866                 goto append_error;
867         }
868
869         /* Other sanity checks ("strict writing" rule) */
870         if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
871             (file_hdr.act_types_nr[0] != FILE_ACTIVITY_ULL_NR) ||
872             (file_hdr.act_types_nr[1] != FILE_ACTIVITY_UL_NR) ||
873             (file_hdr.act_types_nr[2] != FILE_ACTIVITY_U_NR) ||
874             (file_hdr.rec_size != RECORD_HEADER_SIZE) ||
875             (file_hdr.rec_types_nr[0] != RECORD_HEADER_ULL_NR) ||
876             (file_hdr.rec_types_nr[1] != RECORD_HEADER_UL_NR) ||
877             (file_hdr.rec_types_nr[2] != RECORD_HEADER_U_NR)) {
878 #ifdef DEBUG
879                 fprintf(stderr, "%s: act_size=%u act=%d,%d,%d rec_size=%u rec=%d,%d,%d\n",
880                         __FUNCTION__, file_hdr.act_size,
881                         file_hdr.act_types_nr[0], file_hdr.act_types_nr[1], file_hdr.act_types_nr[2],
882                         file_hdr.rec_size,
883                         file_hdr.rec_types_nr[0], file_hdr.rec_types_nr[1], file_hdr.rec_types_nr[2]);
884 #endif
885                 goto append_error;
886         }
887
888         for (i = 0; i < file_hdr.sa_act_nr; i++) {
889
890                 /* Read current activity in list */
891                 if (read(*ofd, &file_act[i], FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
892 #ifdef DEBUG
893                         fprintf(stderr, "%s: Wrong size for file_activity\n",
894                                 __FUNCTION__);
895 #endif
896                         handle_invalid_sa_file(*ofd, &file_magic, ofile, 0);
897                 }
898
899                 p = get_activity_position(act, file_act[i].id, RESUME_IF_NOT_FOUND);
900
901                 if ((p < 0) || (act[p]->fsize != file_act[i].size) ||
902                     (act[p]->magic != file_act[i].magic)) {
903 #ifdef DEBUG
904                         if (p < 0) {
905                                 fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
906                         }
907                         else {
908                                 fprintf(stderr, "%s: %s: size=%d/%d magic=%x/%x\n",
909                                         __FUNCTION__, act[p]->name, act[p]->fsize, file_act[i].size,
910                                         act[p]->magic, file_act[i].magic);
911                         }
912 #endif
913                         /*
914                          * Unknown activity in list or item size has changed or
915                          * unknown activity format: Cannot append data to such a file
916                          * ("strict writing" rule).
917                          */
918                         goto append_error;
919                 }
920
921                 if ((file_act[i].nr <= 0) || (file_act[i].nr2 <= 0) ||
922                     (file_act[i].nr > act[p]->nr_max) ||
923                     (file_act[i].nr2 > NR2_MAX)) {
924 #ifdef DEBUG
925                         fprintf(stderr, "%s: %s: nr=%d nr_max=%d nr2=%d\n",
926                                 __FUNCTION__, act[p]->name, file_act[i].nr,
927                                 act[p]->nr_max, file_act[i].nr2);
928 #endif
929                         /*
930                          * Number of items and subitems should never be zero (or negative)
931                          * or greater than their upper limit.
932                          */
933                         goto append_error;
934                 }
935
936                 if ((file_act[i].types_nr[0] != act[p]->gtypes_nr[0]) ||
937                     (file_act[i].types_nr[1] != act[p]->gtypes_nr[1]) ||
938                     (file_act[i].types_nr[2] != act[p]->gtypes_nr[2])) {
939 #ifdef DEBUG
940                         fprintf(stderr, "%s: %s: types=%d,%d,%d/%d,%d,%d\n",
941                                 __FUNCTION__, act[p]->name,
942                                 file_act[i].types_nr[0], file_act[i].types_nr[1], file_act[i].types_nr[2],
943                                 act[p]->gtypes_nr[0], act[p]->gtypes_nr[1], act[p]->gtypes_nr[2]);
944 #endif
945                         /*
946                          * Composition of structure containing statsitics cannot
947                          * be different from that known by current version.
948                          */
949                         goto append_error;
950                 }
951
952                 if ((file_act[i].has_nr && (act[p]->f_count_index < 0)) ||
953                     (!file_act[i].has_nr && (act[p]->f_count_index >= 0) && HAS_COUNT_FUNCTION(act[p]->options))) {
954 #ifdef DEBUG
955                         fprintf(stderr, "%s: %s: has_nr=%d count_index=%d\n",
956                                 __FUNCTION__, act[p]->name, file_act[i].has_nr, act[p]->f_count_index);
957 #endif
958                         /*
959                          * For every activity whose number of items is not a constant,
960                          * a value giving the number of structures to read should exist.
961                          */
962                         goto append_error;
963                 }
964         }
965
966         /*
967          * OK: (Almost) all tests successfully passed.
968          * List of activities from the file prevails over that of the user.
969          * So unselect all of them. And reset activity sequence.
970          */
971         for (i = 0; i < NR_ACT; i++) {
972                 act[i]->options &= ~AO_COLLECTED;
973                 id_seq[i] = 0;
974         }
975
976         for (i = 0; i < file_hdr.sa_act_nr; i++) {
977
978                 p = get_activity_position(act, file_act[i].id, EXIT_IF_NOT_FOUND);
979
980                 /*
981                  * sar doesn't expect a number of items equal to 0.
982                  * Yet @nr_ini may be 0 if no items have been found on current machine.
983                  * Since we are appending data to a file, set @nr_ini to the value of the file.
984                  * Stats saved in file will all be 0 for that activity if no items exist on
985                  * the machine.
986                  * NB: We must preserve the value read for A_CPU when a LINUX RESTART is inserted.
987                  */
988                 if (!ALWAYS_COUNT_ITEMS(act[p]->options) || !act[p]->nr_ini) {
989                         act[p]->nr_ini = file_act[i].nr;
990                 }
991
992                 /*
993                  * Force number of sub-items to that of the file, and reallocate structures.
994                  * Note: Structures have been allocated in sa_sys_init() only for activities
995                  * that are collected. But since activities from file now prevail over them,
996                  * we need to reallocate.
997                  */
998                 act[p]->nr2 = file_act[i].nr2;
999                 if (act[p]->nr_ini > act[p]->nr_allocated) {
1000                         act[p]->nr_allocated = act[p]->nr_ini;
1001                 }
1002                 SREALLOC(act[p]->_buf0, void,
1003                          (size_t) act[p]->msize * (size_t) act[p]->nr_allocated * (size_t) act[p]->nr2);
1004
1005                 /* Save activity sequence */
1006                 id_seq[i] = file_act[i].id;
1007                 act[p]->options |= AO_COLLECTED;
1008         }
1009
1010         return;
1011
1012 append_error:
1013
1014         close(*ofd);
1015         if (FORCE_FILE(flags)) {
1016                 /* Truncate file */
1017                 create_sa_file(ofd, ofile);
1018         }
1019         else {
1020                 fprintf(stderr, _("Cannot append data to that file (%s)\n"), ofile);
1021                 exit(1);
1022         }
1023 }
1024
1025 /*
1026  ***************************************************************************
1027  * Read statistics from various system files.
1028  ***************************************************************************
1029  */
1030 void read_stats(void)
1031 {
1032         int i;
1033
1034         /* Read system uptime in 1/100th of a second */
1035         read_uptime(&(record_hdr.uptime_cs));
1036
1037         for (i = 0; i < NR_ACT; i++) {
1038                 if (IS_COLLECTED(act[i]->options)) {
1039                         /* Read statistics for current activity */
1040                         (*act[i]->f_read)(act[i]);
1041                 }
1042         }
1043 }
1044
1045 /*
1046  ***************************************************************************
1047  * Main loop: Read stats from the relevant sources and display them.
1048  *
1049  * IN:
1050  * @count       Number of lines of stats to display.
1051  * @stdfd       Stdout file descriptor.
1052  * @ofd         Output file descriptor.
1053  * @ofile       Name of output file.
1054  * @sa_dir      If not an empty string, contains the alternate location of
1055  *              daily data files.
1056  ***************************************************************************
1057  */
1058 void rw_sa_stat_loop(long count, int stdfd, int ofd, char ofile[],
1059                      char sa_dir[])
1060 {
1061         int do_sa_rotat = 0;
1062         uint64_t save_flags;
1063         char new_ofile[MAX_FILE_LEN] = "";
1064         struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
1065
1066         /* Set a handler for SIGINT */
1067         memset(&int_act, 0, sizeof(int_act));
1068         int_act.sa_handler = int_handler;
1069         sigaction(SIGINT, &int_act, NULL);
1070
1071         /* Main loop */
1072         do {
1073                 /* Init all structures */
1074                 reset_stats();
1075                 memset(&record_hdr, 0, RECORD_HEADER_SIZE);
1076
1077                 /* Save time */
1078                 record_hdr.ust_time = (unsigned long long) get_time(&rectime, 0);
1079                 record_hdr.hour     = rectime.tm_hour;
1080                 record_hdr.minute   = rectime.tm_min;
1081                 record_hdr.second   = rectime.tm_sec;
1082
1083                 /* Set record type */
1084                 if (do_sa_rotat) {
1085                         record_hdr.record_type = R_LAST_STATS;
1086                 }
1087                 else {
1088                         record_hdr.record_type = R_STATS;
1089                 }
1090
1091                 /* Read then write stats */
1092                 read_stats();
1093
1094                 if (stdfd >= 0) {
1095                         save_flags = flags;
1096                         flags &= ~S_F_LOCK_FILE;
1097                         write_stats(stdfd);
1098                         flags = save_flags;
1099                 }
1100
1101                 /* If the record type was R_LAST_STATS, tag it R_STATS before writing it */
1102                 record_hdr.record_type = R_STATS;
1103                 if (ofile[0]) {
1104                         write_stats(ofd);
1105                 }
1106
1107                 if (do_sa_rotat) {
1108                         /*
1109                          * Stats are written at the end of previous file *and* at the
1110                          * beginning of the new one (outfile must have been specified
1111                          * as '-' on the command line).
1112                          */
1113                         do_sa_rotat = FALSE;
1114
1115                         if (fdatasync(ofd) < 0) {
1116                                 /* Flush previous file */
1117                                 perror("fdatasync");
1118                                 exit(4);
1119                         }
1120                         close(ofd);
1121                         strcpy(ofile, new_ofile);
1122
1123                         /* Recalculate number of system items and reallocate structures */
1124                         sa_sys_init();
1125
1126                         /*
1127                          * Open and init new file.
1128                          * This is also used to set activity sequence to that of the file
1129                          * if the file already exists.
1130                          */
1131                         open_ofile(&ofd, ofile, FALSE);
1132
1133                         /*
1134                          * Rewrite header and activity sequence to stdout since
1135                          * number of items may have changed.
1136                          */
1137                         if (stdfd >= 0) {
1138                                 setup_file_hdr(stdfd);
1139                         }
1140
1141                         /* Write stats to file again */
1142                         write_stats(ofd);
1143                 }
1144
1145                 /* Flush data */
1146                 fflush(stdout);
1147                 if (FDATASYNC(flags)) {
1148                         /* If indicated, sync the data to media */
1149                         if (fdatasync(ofd) < 0) {
1150                                 perror("fdatasync");
1151                                 exit(4);
1152                         }
1153                 }
1154
1155                 if (count > 0) {
1156                         count--;
1157                 }
1158
1159                 if (count) {
1160                         /* Wait for a signal (probably SIGALRM or SIGINT) */
1161                         __pause();
1162                 }
1163
1164                 if (sigint_caught)
1165                         /* SIGINT caught: Stop now */
1166                         break;
1167
1168                 /* Rotate activity file if necessary */
1169                 if (WANT_SA_ROTAT(flags)) {
1170                         /* The user specified '-' as the filename to use */
1171                         strncpy(new_ofile, sa_dir, sizeof(new_ofile) - 1);
1172                         new_ofile[sizeof(new_ofile) - 1] = '\0';
1173                         set_default_file(new_ofile, 0, USE_SA_YYYYMMDD(flags));
1174
1175                         if (strcmp(ofile, new_ofile)) {
1176                                 do_sa_rotat = TRUE;
1177                         }
1178                 }
1179         }
1180         while (count);
1181
1182         /* Close file descriptors if they have actually been used */
1183         CLOSE(stdfd);
1184         CLOSE(ofd);
1185 }
1186
1187 /*
1188  ***************************************************************************
1189  * Main entry to the program.
1190  ***************************************************************************
1191  */
1192 int main(int argc, char **argv)
1193 {
1194         int opt = 0;
1195         char ofile[MAX_FILE_LEN], sa_dir[MAX_FILE_LEN];
1196         int stdfd = 0, ofd = -1;
1197         int restart_mark;
1198         long count = 0;
1199
1200 #ifdef TEST
1201         fprintf(stderr, "TEST MODE\n");
1202 #endif
1203
1204         /* Get HZ */
1205         get_HZ();
1206
1207         /* Compute page shift in kB */
1208         get_kb_shift();
1209
1210         ofile[0] = sa_dir[0] = comment[0] = '\0';
1211
1212 #if (defined(HAVE_SENSORS) && !defined(ARCH32)) || (defined(ARCH32) && defined(HAVE_SENSORS32))
1213         /* Initialize sensors, let it use the default cfg file */
1214         int err = sensors_init(NULL);
1215         if (err) {
1216                 fprintf(stderr, "sensors_init: %s\n", sensors_strerror(err));
1217         }
1218 #endif /* HAVE_SENSORS */
1219
1220 #ifdef USE_NLS
1221         /* Init National Language Support */
1222         init_nls();
1223 #endif
1224
1225         while (++opt < argc) {
1226
1227                 if (!strcmp(argv[opt], "-S")) {
1228                         if (!argv[++opt]) {
1229                                 usage(argv[0]);
1230                         }
1231                         parse_sadc_S_option(argv, opt);
1232                 }
1233
1234                 else if (!strcmp(argv[opt], "-D")) {
1235                         flags |= S_F_SA_YYYYMMDD;
1236                 }
1237
1238                 else if (!strcmp(argv[opt], "-F")) {
1239                         flags |= S_F_FORCE_FILE;
1240                 }
1241
1242                 else if (!strcmp(argv[opt], "-L")) {
1243                         flags |= S_F_LOCK_FILE;
1244                 }
1245
1246                 else if (!strcmp(argv[opt], "-V")) {
1247                         print_version();
1248                 }
1249
1250                 else if (!strcmp(argv[opt], "-Z")) {
1251                         /* Set by sar command */
1252                         optz = 1;
1253                 }
1254
1255                 else if (!strcmp(argv[opt], "-f")) {
1256                         flags |= S_F_FDATASYNC;
1257                 }
1258
1259                 else if (!strcmp(argv[opt], "-C")) {
1260                         if (!argv[++opt]) {
1261                                 usage(argv[0]);
1262                         }
1263                         strncpy(comment, argv[opt], sizeof(comment));
1264                         comment[sizeof(comment) - 1] = '\0';
1265                         if (!strlen(comment)) {
1266                                 usage(argv[0]);
1267                         }
1268                 }
1269
1270 #ifdef TEST
1271                 else if (!strncmp(argv[opt], "--getenv", 8)) {
1272                         __env = TRUE;
1273                 }
1274
1275                 else if (!strncmp(argv[opt], "--unix_time=", 12)) {
1276                         if (strspn(argv[opt] + 12, DIGITS) != strlen(argv[opt] + 12)) {
1277                                 usage(argv[0]);
1278                         }
1279                         __unix_time = atoll(argv[opt] + 12);
1280                 }
1281 #endif
1282
1283                 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1284                         if (ofile[0] || WANT_SA_ROTAT(flags)) {
1285                                 /* Outfile already specified */
1286                                 usage(argv[0]);
1287                         }
1288                         stdfd = -1;     /* Don't write to STDOUT */
1289                         if (!strcmp(argv[opt], "-")) {
1290                                 /* File name set to '-' */
1291                                 flags |= S_F_SA_ROTAT;
1292                         }
1293                         else if (!strncmp(argv[opt], "-", 1)) {
1294                                 /* Bad option */
1295                                 usage(argv[0]);
1296                         }
1297                         else {
1298                                 /* Write data to file */
1299                                 strncpy(ofile, argv[opt], sizeof(ofile));
1300                                 ofile[sizeof(ofile) - 1] = '\0';
1301                         }
1302                 }
1303
1304                 else if (interval < 0) {
1305                         /* Get interval */
1306                         interval = atol(argv[opt]);
1307                         if (interval < 1) {
1308                                 usage(argv[0]);
1309                         }
1310                         count = -1;
1311                 }
1312
1313                 else if (count <= 0) {
1314                         /* Get count value */
1315                         count = atol(argv[opt]);
1316                         if (count < 1) {
1317                                 usage(argv[0]);
1318                         }
1319                 }
1320
1321                 else {
1322                         usage(argv[0]);
1323                 }
1324         }
1325
1326         /* Process file entered on the command line */
1327         if (WANT_SA_ROTAT(flags)) {
1328                 /* File name set to '-' */
1329                 set_default_file(ofile, 0, USE_SA_YYYYMMDD(flags));
1330         }
1331         else if (ofile[0]) {
1332                 /*
1333                  * A file (or directory) has been explicitly entered
1334                  * on the command line.
1335                  * Should ofile be a directory, it will be the alternate
1336                  * location for sa files. So save it.
1337                  */
1338                 strcpy(sa_dir, ofile);
1339                 /* Check if this is an alternate directory for sa files */
1340                 if (check_alt_sa_dir(ofile, 0, USE_SA_YYYYMMDD(flags))) {
1341                         /*
1342                          * Yes, it was a directory.
1343                          * ofile now contains the full path to current
1344                          * standard daily data file.
1345                          */
1346                         flags |= S_F_SA_ROTAT;
1347                 }
1348                 else {
1349                         /* No: So we can clear sa_dir */
1350                         sa_dir[0] = '\0';
1351                 }
1352         }
1353
1354         /*
1355          * If option -Z used, write to STDOUT even if a filename
1356          * has been entered on the command line.
1357          */
1358         if (optz) {
1359                 stdfd = 0;
1360         }
1361
1362         if (!ofile[0]) {
1363                 /* -L option ignored when writing to STDOUT */
1364                 flags &= ~S_F_LOCK_FILE;
1365         }
1366
1367         /* Init structures according to machine architecture */
1368         sa_sys_init();
1369
1370         /* At least one activity must be collected */
1371         if (!get_activity_nr(act, AO_COLLECTED, COUNT_ACTIVITIES)) {
1372                 /* Requested activities not available: Exit */
1373                 print_collect_error();
1374         }
1375
1376         if ((interval < 0) && !comment[0]) {
1377                 /*
1378                  * Interval (and count) not set, and no comment given
1379                  * => We are going to insert a restart mark.
1380                  */
1381                 restart_mark = TRUE;
1382         }
1383         else {
1384                 restart_mark = FALSE;
1385         }
1386
1387         /*
1388          * Open output file then STDOUT. Write header for each of them.
1389          * NB: Output file must be opened first, because we may change
1390          * the activities collected AND the activity sequence to that
1391          * of the file, and the activities collected and activity sequence
1392          * written on STDOUT must be consistent to those of the file.
1393          */
1394         open_ofile(&ofd, ofile, restart_mark);
1395         open_stdout(&stdfd);
1396
1397         if (interval < 0) {
1398                 if (ofd >= 0) {
1399                         /*
1400                          * Interval (and count) not set:
1401                          * Write a dummy record, or insert a comment, then exit.
1402                          * NB: Never write such a dummy record on stdout since
1403                          * sar never expects it.
1404                          */
1405                         if (comment[0]) {
1406                                 write_special_record(ofd, R_COMMENT);
1407                         }
1408                         else {
1409                                 write_special_record(ofd, R_RESTART);
1410                         }
1411
1412                         /* Close file descriptor */
1413                         CLOSE(ofd);
1414                 }
1415
1416                 /* Free structures */
1417                 sa_sys_free();
1418                 exit(0);
1419         }
1420
1421         /* Set a handler for SIGALRM */
1422         memset(&alrm_act, 0, sizeof(alrm_act));
1423         alrm_act.sa_handler = alarm_handler;
1424         sigaction(SIGALRM, &alrm_act, NULL);
1425         __alarm(interval);
1426
1427         /* Main loop */
1428         rw_sa_stat_loop(count, stdfd, ofd, ofile, sa_dir);
1429
1430 #if (defined(HAVE_SENSORS) && !defined(ARCH32)) || (defined(ARCH32) && defined(HAVE_SENSORS32))
1431         /* Cleanup sensors */
1432         sensors_cleanup();
1433 #endif /* HAVE_SENSORS */
1434
1435         /* Free structures */
1436         sa_sys_free();
1437
1438         return 0;
1439 }