]> granicus.if.org Git - sysstat/blob - sadc.c
sadf: Display timezone for all output formats
[sysstat] / sadc.c
1 /*
2  * sadc: system activity data collector
3  * (C) 1999-2022 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       Optional 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_index >= 0) {
345                                 idx = act[i]->f_count2_index;
346
347                                 if (f_count_results[idx] >= 0) {
348                                         act[i]->nr2 = f_count_results[idx];
349                                 }
350                                 else {
351                                         act[i]->nr2 = (f_count[idx])(act[i]);
352                                         f_count_results[idx] = act[i]->nr2;
353                                 }
354                         }
355                         /* else act[i]->nr2 is a constant and doesn't need to be calculated */
356
357                         if (!act[i]->nr2) {
358                                 act[i]->nr_ini = 0;
359                         }
360                 }
361
362                 if (IS_COLLECTED(act[i]->options) && (act[i]->nr_ini > 0)) {
363                         /* Allocate structures for current activity (using nr_ini and nr2 results) */
364                         SREALLOC(act[i]->_buf0, void,
365                                  (size_t) act[i]->msize * (size_t) act[i]->nr_ini * (size_t) act[i]->nr2);
366                         act[i]->nr_allocated = act[i]->nr_ini;
367                 }
368
369                 if (act[i]->nr_ini <= 0) {
370                         /* No items found: Invalidate current activity */
371                         act[i]->options &= ~AO_COLLECTED;
372                 }
373
374                 if (HAS_DETECT_FUNCTION(act[i]->options) && IS_COLLECTED(act[i]->options)) {
375                         idx = act[i]->f_count_index;
376
377                         /* Detect if files needed by activity exist */
378                         if (f_count_results[idx] < 0) {
379                                 f_count_results[idx] = (f_count[idx])(act[i]);
380                         }
381                         if (f_count_results[idx] == 0) {
382                                 /* Files not present */
383                                 act[i]->options &= ~AO_COLLECTED;
384                         }
385                 }
386
387                 /* Set default activity list */
388                 id_seq[i] = act[i]->id;
389         }
390 }
391
392 /*
393  ***************************************************************************
394  * Free structures.
395  ***************************************************************************
396  */
397 void sa_sys_free(void)
398 {
399         int i;
400
401         for (i = 0; i < NR_ACT; i++) {
402
403                 if (act[i]->nr_allocated > 0) {
404                         if (act[i]->_buf0) {
405                                 free(act[i]->_buf0);
406                                 act[i]->_buf0 = NULL;
407                                 act[i]->nr_allocated = 0;
408                         }
409                 }
410         }
411 }
412
413 /*
414  ***************************************************************************
415  * If -L option used, request a non-blocking, exclusive lock on the file.
416  * If lock would block, then another process (possibly sadc) has already
417  * opened that file => exit.
418  *
419  * IN:
420  * @fd          Output file descriptor.
421  * @fatal       Indicate if failing to lock file should be fatal or not.
422  *              If it's not fatal then we'll wait for next iteration and
423  *              try again.
424  *
425  * RETURNS:
426  * 0 on success, or 1 if file couldn't be locked.
427  ***************************************************************************
428  */
429 int ask_for_flock(int fd, int fatal)
430 {
431         /* Option -L may be used only if an outfile was specified on the command line */
432         if (LOCK_FILE(flags)) {
433                 /*
434                  * Yes: Try to lock file. To make code portable, check for both EWOULDBLOCK
435                  * and EAGAIN return codes, and treat them the same (glibc documentation).
436                  * Indeed, some Linux ports (e.g. hppa-linux) do not equate EWOULDBLOCK and
437                  * EAGAIN like every other Linux port.
438                  */
439                 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
440                         if ((((errno == EWOULDBLOCK) || (errno == EAGAIN)) && (fatal == FATAL)) ||
441                             ((errno != EWOULDBLOCK) && (errno != EAGAIN))) {
442                                 perror("flock");
443                                 exit(1);
444                         }
445                         /* Was unable to lock file: Lock would have blocked... */
446                         return 1;
447                 }
448                 else {
449                         /* File successfully locked */
450                         flags |= S_F_FILE_LOCKED;
451                 }
452         }
453         return 0;
454 }
455
456 /*
457  ***************************************************************************
458  * Fill system activity file magic header.
459  *
460  * IN:
461  * @file_magic  System activity file magic header.
462  ***************************************************************************
463  */
464 void fill_magic_header(struct file_magic *file_magic)
465 {
466         int i;
467
468         memset(file_magic, 0, FILE_MAGIC_SIZE);
469
470         file_magic->sysstat_magic = SYSSTAT_MAGIC;
471         file_magic->format_magic  = FORMAT_MAGIC;
472
473         enum_version_nr(file_magic);
474
475         file_magic->header_size = FILE_HEADER_SIZE;
476
477         for (i = 0; i < 3; i++) {
478                 file_magic->hdr_types_nr[i] = hdr_types_nr[i];
479         }
480 }
481
482 /*
483  ***************************************************************************
484  * Fill system activity file header, then write it (or print it if stdout).
485  *
486  * IN:
487  * @fd  Output file descriptor. May be stdout.
488  ***************************************************************************
489  */
490 void setup_file_hdr(int fd)
491 {
492         int i, j, p;
493         struct tm rectime;
494         struct utsname header;
495         struct file_magic file_magic;
496         struct file_activity file_act;
497
498         /* Fill then write file magic header */
499         fill_magic_header(&file_magic);
500
501         if (write_all(fd, &file_magic, FILE_MAGIC_SIZE) != FILE_MAGIC_SIZE) {
502                 p_write_error();
503         }
504
505         /* First reset the structure */
506         memset(&file_hdr, 0, FILE_HEADER_SIZE);
507
508         /* Then get current date */
509         file_hdr.sa_ust_time = (unsigned long long) get_time(&rectime, 0);
510
511         /* OK, now fill the header */
512         file_hdr.sa_act_nr      = get_activity_nr(act, AO_COLLECTED, COUNT_ACTIVITIES);
513         file_hdr.sa_day         = rectime.tm_mday;
514         file_hdr.sa_month       = rectime.tm_mon;
515         file_hdr.sa_year        = rectime.tm_year;
516         file_hdr.sa_sizeof_long = sizeof(long);
517         file_hdr.sa_hz          = HZ;
518
519         for (i = 0; i < 3; i++) {
520                 file_hdr.act_types_nr[i] = act_types_nr[i];
521                 file_hdr.rec_types_nr[i] = rec_types_nr[i];
522         }
523         file_hdr.act_size = FILE_ACTIVITY_SIZE;
524         file_hdr.rec_size = RECORD_HEADER_SIZE;
525
526         /*
527          * This is a new file (or stdout): Set sa_cpu_nr field to the number
528          * of CPU of the machine (1 .. CPU_NR + 1). This is the number of CPU, whether
529          * online or offline, when sadc was started.
530          * A_CPU activity is always counted in sa_sys_init(), even if it's not collected.
531          */
532         file_hdr.sa_cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr_ini;
533
534         /* Get system name, release number, hostname and machine architecture */
535         __uname(&header);
536         strncpy(file_hdr.sa_sysname, header.sysname, sizeof(file_hdr.sa_sysname));
537         file_hdr.sa_sysname[sizeof(file_hdr.sa_sysname) - 1]  = '\0';
538         strncpy(file_hdr.sa_nodename, header.nodename, sizeof(file_hdr.sa_nodename));
539         file_hdr.sa_nodename[sizeof(file_hdr.sa_nodename) - 1] = '\0';
540         strncpy(file_hdr.sa_release, header.release, sizeof(file_hdr.sa_release));
541         file_hdr.sa_release[sizeof(file_hdr.sa_release) - 1]  = '\0';
542         strncpy(file_hdr.sa_machine, header.machine, sizeof(file_hdr.sa_machine));
543         file_hdr.sa_machine[sizeof(file_hdr.sa_machine) - 1]  = '\0';
544
545         /* Get timezone value and save it */
546         tzset();
547         strncpy(file_hdr.sa_tzname, tzname[0], TZNAME_LEN);
548         file_hdr.sa_tzname[TZNAME_LEN - 1] = '\0';
549
550         /* Write file header */
551         if (write_all(fd, &file_hdr, FILE_HEADER_SIZE) != FILE_HEADER_SIZE) {
552                 p_write_error();
553         }
554
555         /* Reset file_activity structure (in case some unknown extra fields exist) */
556         memset(&file_act, 0, FILE_ACTIVITY_SIZE);
557
558         /* Write activity list */
559         for (i = 0; i < NR_ACT; i++) {
560
561                 /*
562                  * Activity sequence given by id_seq array.
563                  * Sequence must be the same for stdout as for output file.
564                  */
565                 if (!id_seq[i])
566                         continue;
567                 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
568                         continue;
569
570                 if (IS_COLLECTED(act[p]->options)) {
571                         file_act.id    = act[p]->id;
572                         file_act.magic = act[p]->magic;
573                         file_act.nr    = act[p]->nr_ini;
574                         file_act.nr2   = act[p]->nr2;
575                         file_act.size  = act[p]->fsize;
576                         for (j = 0; j < 3; j++) {
577                                 file_act.types_nr[j] = act[p]->gtypes_nr[j];
578                         }
579
580                         file_act.has_nr = HAS_COUNT_FUNCTION(act[p]->options);
581
582                         if (write_all(fd, &file_act, FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
583                                 p_write_error();
584                         }
585                 }
586         }
587
588         return;
589 }
590
591 /*
592  ***************************************************************************
593  * Write the new number of CPU after the RESTART record in file.
594  *
595  * IN:
596  * @ofd         Output file descriptor.
597  ***************************************************************************
598  */
599 void write_new_cpu_nr(int ofd)
600 {
601         int p;
602
603         p = get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND);
604
605         if (write_all(ofd, &(act[p]->nr_ini), sizeof(__nr_t)) != sizeof(__nr_t)) {
606                 p_write_error();
607         }
608 }
609
610 /*
611  ***************************************************************************
612  * sadc called with interval and count parameters not set:
613  * Write a dummy record notifying a system restart, or insert a comment in
614  * binary data file if option -C has been used.
615  * Writing a dummy record should typically be done at boot time,
616  * before the cron daemon is started to avoid conflict with sa1/sa2 scripts.
617  *
618  * IN:
619  * @ofd         Output file descriptor.
620  * @rtype       Record type to write (restart or comment).
621  ***************************************************************************
622  */
623 void write_special_record(int ofd, int rtype)
624 {
625         struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
626
627         /* Check if file is locked */
628         if (!FILE_LOCKED(flags)) {
629                 ask_for_flock(ofd, FATAL);
630         }
631
632         /* Reset the structure (sane to do it, as other fields may be added in the future) */
633         memset(&record_hdr, 0, RECORD_HEADER_SIZE);
634
635         /* Set record type */
636         record_hdr.record_type = rtype;
637
638         /* Save time */
639         record_hdr.ust_time = (unsigned long long) get_time(&rectime, 0);
640
641         record_hdr.hour   = rectime.tm_hour;
642         record_hdr.minute = rectime.tm_min;
643         record_hdr.second = rectime.tm_sec;
644
645         /* Write record now */
646         if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
647                 p_write_error();
648         }
649
650         if (rtype == R_RESTART) {
651                 /* Also write the new number of CPU */
652                 write_new_cpu_nr(ofd);
653         }
654         else if (rtype == R_COMMENT) {
655                 /* Also write the comment */
656                 if (write_all(ofd, comment, MAX_COMMENT_LEN) != MAX_COMMENT_LEN) {
657                         p_write_error();
658                 }
659         }
660 }
661
662 /*
663  ***************************************************************************
664  * Write stats (or print them if stdout).
665  *
666  * IN:
667  * @ofd         Output file descriptor. May be stdout.
668  ***************************************************************************
669  */
670 void write_stats(int ofd)
671 {
672         int i, p;
673
674         /* Try to lock file */
675         if (!FILE_LOCKED(flags)) {
676                 if (ask_for_flock(ofd, NON_FATAL))
677                         /*
678                          * Unable to lock file:
679                          * Wait for next iteration to try again to save data.
680                          */
681                         return;
682         }
683
684         /* Write record header */
685         if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
686                 p_write_error();
687         }
688
689         /* Then write all statistics */
690         for (i = 0; i < NR_ACT; i++) {
691
692                 if (!id_seq[i])
693                         continue;
694                 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
695                         continue;
696
697                 if (IS_COLLECTED(act[p]->options)) {
698                         if (HAS_COUNT_FUNCTION(act[p]->options) && (act[p]->f_count_index >= 0)) {
699                                 if (write_all(ofd, &(act[p]->_nr0), sizeof(__nr_t)) != sizeof(__nr_t)) {
700                                         p_write_error();
701                                 }
702                         }
703                         if (write_all(ofd, act[p]->_buf0, act[p]->fsize * act[p]->_nr0 * act[p]->nr2) !=
704                             (act[p]->fsize * act[p]->_nr0 * act[p]->nr2)) {
705                                 p_write_error();
706                         }
707                 }
708         }
709 }
710
711 /*
712  ***************************************************************************
713  * Create a system activity daily data file.
714  *
715  * IN:
716  * @ofile       Name of output file.
717  *
718  * OUT:
719  * @ofd         Output file descriptor.
720  ***************************************************************************
721  */
722 void create_sa_file(int *ofd, char *ofile)
723 {
724         if ((*ofd = open(ofile, O_CREAT | O_WRONLY,
725                          S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0)
726                 goto create_error;
727
728         /* Try to lock file */
729         ask_for_flock(*ofd, FATAL);
730
731         /* Truncate file */
732         if (ftruncate(*ofd, 0) >= 0) {
733
734                 /* Write file header */
735                 setup_file_hdr(*ofd);
736
737                 return;
738         }
739
740 create_error:
741         fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
742         exit(2);
743 }
744
745 /*
746  ***************************************************************************
747  * Get descriptor for stdout.
748  *
749  * IN:
750  * @stdfd       A value >= 0 indicates that stats data should also
751  *              be written to stdout.
752  *
753  * OUT:
754  * @stdfd       Stdout file descriptor.
755  ***************************************************************************
756  */
757 void open_stdout(int *stdfd)
758 {
759         if (*stdfd >= 0) {
760                 if ((*stdfd = dup(STDOUT_FILENO)) < 0) {
761                         perror("dup");
762                         exit(4);
763                 }
764                 /* Write file header on STDOUT */
765                 setup_file_hdr(*stdfd);
766         }
767 }
768
769 /*
770  ***************************************************************************
771  * Get descriptor for output file and write its header.
772  * We may enter this function several times (when we rotate a file).
773  * NB: If data are appended to an existing file then the format must be
774  * strictly that expected by current version.
775  *
776  * IN:
777  * @ofile               Name of output file.
778  * @restart_mark        TRUE if sadc called with interval (and count) not
779  *                      set, and no comment given (so we are going to insert
780  *                      a restart mark into the file).
781  *
782  * OUT:
783  * @ofd                 Output file descriptor.
784  ***************************************************************************
785  */
786 void open_ofile(int *ofd, char ofile[], int restart_mark)
787 {
788         struct file_magic file_magic;
789         struct file_activity file_act[NR_ACT];
790         struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
791         ssize_t sz;
792         int i, p;
793
794         if (!ofile[0])
795                 return;
796
797         /* Try to open file and check that data can be appended to it */
798         if ((*ofd = open(ofile, O_APPEND | O_RDWR)) < 0) {
799                 if (errno == ENOENT) {
800                         /* File doesn't exist: Create it */
801                         create_sa_file(ofd, ofile);
802                         return;
803                 }
804                 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
805                 exit(2);
806         }
807
808         /* Read file magic header */
809         sz = read(*ofd, &file_magic, FILE_MAGIC_SIZE);
810         if (!sz) {
811                 close(*ofd);
812                 /* This is an empty file: Create it again */
813                 create_sa_file(ofd, ofile);
814                 return;
815         }
816
817         /* Test various values ("strict writing" rule) */
818         if ((sz != FILE_MAGIC_SIZE) ||
819             (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
820             (file_magic.format_magic != FORMAT_MAGIC) ||
821             (file_magic.header_size != FILE_HEADER_SIZE) ||
822             (file_magic.hdr_types_nr[0] != FILE_HEADER_ULL_NR) ||
823             (file_magic.hdr_types_nr[1] != FILE_HEADER_UL_NR) ||
824             (file_magic.hdr_types_nr[2] != FILE_HEADER_U_NR)) {
825                 if (FORCE_FILE(flags)) {
826                         close(*ofd);
827                         /* -F option used: Truncate file */
828                         create_sa_file(ofd, ofile);
829                         return;
830                 }
831 #ifdef DEBUG
832                 fprintf(stderr, "%s: Size read=%zd sysstat_magic=%x format_magic=%x header_size=%u header=%d,%d,%d\n",
833                         __FUNCTION__, sz, file_magic.sysstat_magic, file_magic.format_magic, file_magic.header_size,
834                         file_magic.hdr_types_nr[0], file_magic.hdr_types_nr[1], file_magic.hdr_types_nr[2]);
835 #endif
836                 /* Display error message and exit */
837                 handle_invalid_sa_file(*ofd, &file_magic, ofile, sz);
838         }
839
840         /* Read file standard header */
841         if ((sz = read(*ofd, &file_hdr, FILE_HEADER_SIZE)) != FILE_HEADER_SIZE) {
842 #ifdef DEBUG
843                 fprintf(stderr, "%s: Size read=%zd\n",
844                         __FUNCTION__, sz);
845 #endif
846                 goto append_error;
847         }
848
849         /*
850          * If we are using the standard daily data file (file specified
851          * as "-" on the command line) and it is from a past month,
852          * then overwrite (truncate) it.
853          */
854         get_time(&rectime, 0);
855
856         if (((file_hdr.sa_month != rectime.tm_mon) ||
857             (file_hdr.sa_year != rectime.tm_year)) &&
858             WANT_SA_ROTAT(flags)) {
859                 close(*ofd);
860                 create_sa_file(ofd, ofile);
861                 return;
862         }
863
864         /* OK: It's a true system activity file */
865         if (!file_hdr.sa_act_nr || (file_hdr.sa_act_nr > NR_ACT)) {
866 #ifdef DEBUG
867                 fprintf(stderr, "%s: sa_act_nr=%d\n",
868                         __FUNCTION__, file_hdr.sa_act_nr);
869 #endif
870                 /*
871                  * No activities at all or at least one unknown activity:
872                  * Cannot append data to such a file.
873                  */
874                 goto append_error;
875         }
876
877         /* Other sanity checks ("strict writing" rule) */
878         if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
879             (file_hdr.act_types_nr[0] != FILE_ACTIVITY_ULL_NR) ||
880             (file_hdr.act_types_nr[1] != FILE_ACTIVITY_UL_NR) ||
881             (file_hdr.act_types_nr[2] != FILE_ACTIVITY_U_NR) ||
882             (file_hdr.rec_size != RECORD_HEADER_SIZE) ||
883             (file_hdr.rec_types_nr[0] != RECORD_HEADER_ULL_NR) ||
884             (file_hdr.rec_types_nr[1] != RECORD_HEADER_UL_NR) ||
885             (file_hdr.rec_types_nr[2] != RECORD_HEADER_U_NR)) {
886 #ifdef DEBUG
887                 fprintf(stderr, "%s: act_size=%u act=%d,%d,%d rec_size=%u rec=%d,%d,%d\n",
888                         __FUNCTION__, file_hdr.act_size,
889                         file_hdr.act_types_nr[0], file_hdr.act_types_nr[1], file_hdr.act_types_nr[2],
890                         file_hdr.rec_size,
891                         file_hdr.rec_types_nr[0], file_hdr.rec_types_nr[1], file_hdr.rec_types_nr[2]);
892 #endif
893                 goto append_error;
894         }
895
896         for (i = 0; i < file_hdr.sa_act_nr; i++) {
897
898                 /* Read current activity in list */
899                 if (read(*ofd, &file_act[i], FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
900 #ifdef DEBUG
901                         fprintf(stderr, "%s: Wrong size for file_activity\n",
902                                 __FUNCTION__);
903 #endif
904                         handle_invalid_sa_file(*ofd, &file_magic, ofile, 0);
905                 }
906
907                 p = get_activity_position(act, file_act[i].id, RESUME_IF_NOT_FOUND);
908
909                 if ((p < 0) || (act[p]->fsize != file_act[i].size) ||
910                     (act[p]->magic != file_act[i].magic)) {
911 #ifdef DEBUG
912                         if (p < 0) {
913                                 fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
914                         }
915                         else {
916                                 fprintf(stderr, "%s: %s: size=%d/%d magic=%x/%x\n",
917                                         __FUNCTION__, act[p]->name, act[p]->fsize, file_act[i].size,
918                                         act[p]->magic, file_act[i].magic);
919                         }
920 #endif
921                         /*
922                          * Unknown activity in list or item size has changed or
923                          * unknown activity format: Cannot append data to such a file
924                          * ("strict writing" rule).
925                          */
926                         goto append_error;
927                 }
928
929                 if ((file_act[i].nr <= 0) || (file_act[i].nr2 <= 0) ||
930                     (file_act[i].nr > act[p]->nr_max) ||
931                     (file_act[i].nr2 > NR2_MAX)) {
932 #ifdef DEBUG
933                         fprintf(stderr, "%s: %s: nr=%d nr_max=%d nr2=%d\n",
934                                 __FUNCTION__, act[p]->name, file_act[i].nr,
935                                 act[p]->nr_max, file_act[i].nr2);
936 #endif
937                         /*
938                          * Number of items and subitems should never be zero (or negative)
939                          * or greater than their upper limit.
940                          */
941                         goto append_error;
942                 }
943
944                 if ((file_act[i].types_nr[0] != act[p]->gtypes_nr[0]) ||
945                     (file_act[i].types_nr[1] != act[p]->gtypes_nr[1]) ||
946                     (file_act[i].types_nr[2] != act[p]->gtypes_nr[2])) {
947 #ifdef DEBUG
948                         fprintf(stderr, "%s: %s: types=%d,%d,%d/%d,%d,%d\n",
949                                 __FUNCTION__, act[p]->name,
950                                 file_act[i].types_nr[0], file_act[i].types_nr[1], file_act[i].types_nr[2],
951                                 act[p]->gtypes_nr[0], act[p]->gtypes_nr[1], act[p]->gtypes_nr[2]);
952 #endif
953                         /*
954                          * Composition of structure containing statsitics cannot
955                          * be different from that known by current version.
956                          */
957                         goto append_error;
958                 }
959
960                 if ((file_act[i].has_nr && (act[p]->f_count_index < 0)) ||
961                     (!file_act[i].has_nr && (act[p]->f_count_index >= 0) && HAS_COUNT_FUNCTION(act[p]->options))) {
962 #ifdef DEBUG
963                         fprintf(stderr, "%s: %s: has_nr=%d count_index=%d\n",
964                                 __FUNCTION__, act[p]->name, file_act[i].has_nr, act[p]->f_count_index);
965 #endif
966                         /*
967                          * For every activity whose number of items is not a constant,
968                          * a value giving the number of structures to read should exist.
969                          */
970                         goto append_error;
971                 }
972         }
973
974         /*
975          * OK: (Almost) all tests successfully passed.
976          * List of activities from the file prevails over that of the user.
977          * So unselect all of them. And reset activity sequence.
978          */
979         for (i = 0; i < NR_ACT; i++) {
980                 act[i]->options &= ~AO_COLLECTED;
981                 id_seq[i] = 0;
982         }
983
984         for (i = 0; i < file_hdr.sa_act_nr; i++) {
985
986                 p = get_activity_position(act, file_act[i].id, EXIT_IF_NOT_FOUND);
987
988                 /*
989                  * sar doesn't expect a number of items equal to 0.
990                  * Yet @nr_ini may be 0 if no items have been found on current machine.
991                  * Since we are appending data to a file, set @nr_ini to the value of the file.
992                  * Stats saved in file will all be 0 for that activity if no items exist on
993                  * the machine.
994                  * NB: We must preserve the value read for A_CPU when a LINUX RESTART is inserted.
995                  */
996                 if (!ALWAYS_COUNT_ITEMS(act[p]->options) || !act[p]->nr_ini) {
997                         act[p]->nr_ini = file_act[i].nr;
998                 }
999
1000                 /*
1001                  * Force number of sub-items to that of the file, and reallocate structures.
1002                  * Note: Structures have been allocated in sa_sys_init() only for activities
1003                  * that are collected. But since activities from file now prevail over them,
1004                  * we need to reallocate.
1005                  */
1006                 act[p]->nr2 = file_act[i].nr2;
1007                 if (act[p]->nr_ini > act[p]->nr_allocated) {
1008                         act[p]->nr_allocated = act[p]->nr_ini;
1009                 }
1010                 SREALLOC(act[p]->_buf0, void,
1011                          (size_t) act[p]->msize * (size_t) act[p]->nr_allocated * (size_t) act[p]->nr2);
1012
1013                 /* Save activity sequence */
1014                 id_seq[i] = file_act[i].id;
1015                 act[p]->options |= AO_COLLECTED;
1016         }
1017
1018         return;
1019
1020 append_error:
1021
1022         close(*ofd);
1023         if (FORCE_FILE(flags)) {
1024                 /* Truncate file */
1025                 create_sa_file(ofd, ofile);
1026         }
1027         else {
1028                 fprintf(stderr, _("Cannot append data to that file (%s)\n"), ofile);
1029                 exit(1);
1030         }
1031 }
1032
1033 /*
1034  ***************************************************************************
1035  * Read statistics from various system files.
1036  ***************************************************************************
1037  */
1038 void read_stats(void)
1039 {
1040         int i;
1041
1042         /* Read system uptime in 1/100th of a second */
1043         read_uptime(&(record_hdr.uptime_cs));
1044
1045         for (i = 0; i < NR_ACT; i++) {
1046                 if (IS_COLLECTED(act[i]->options)) {
1047                         /* Read statistics for current activity */
1048                         (*act[i]->f_read)(act[i]);
1049                 }
1050         }
1051 }
1052
1053 /*
1054  ***************************************************************************
1055  * Main loop: Read stats from the relevant sources and display them.
1056  *
1057  * IN:
1058  * @count       Number of lines of stats to display.
1059  * @stdfd       Stdout file descriptor.
1060  * @ofd         Output file descriptor.
1061  * @ofile       Name of output file.
1062  * @sa_dir      If not an empty string, contains the alternate location of
1063  *              daily data files.
1064  ***************************************************************************
1065  */
1066 void rw_sa_stat_loop(long count, int stdfd, int ofd, char ofile[],
1067                      char sa_dir[])
1068 {
1069         int do_sa_rotat = 0;
1070         uint64_t save_flags;
1071         char new_ofile[MAX_FILE_LEN] = "";
1072         struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
1073
1074         /* Set a handler for SIGINT */
1075         memset(&int_act, 0, sizeof(int_act));
1076         int_act.sa_handler = int_handler;
1077         sigaction(SIGINT, &int_act, NULL);
1078
1079         /* Main loop */
1080         do {
1081                 /* Init all structures */
1082                 reset_stats();
1083                 memset(&record_hdr, 0, RECORD_HEADER_SIZE);
1084
1085                 /* Save time */
1086                 record_hdr.ust_time = (unsigned long long) get_time(&rectime, 0);
1087                 record_hdr.hour     = rectime.tm_hour;
1088                 record_hdr.minute   = rectime.tm_min;
1089                 record_hdr.second   = rectime.tm_sec;
1090
1091                 /* Set record type */
1092                 if (do_sa_rotat) {
1093                         record_hdr.record_type = R_LAST_STATS;
1094                 }
1095                 else {
1096                         record_hdr.record_type = R_STATS;
1097                 }
1098
1099                 /* Read then write stats */
1100                 read_stats();
1101
1102                 if (stdfd >= 0) {
1103                         save_flags = flags;
1104                         flags &= ~S_F_LOCK_FILE;
1105                         write_stats(stdfd);
1106                         flags = save_flags;
1107                 }
1108
1109                 /* If the record type was R_LAST_STATS, tag it R_STATS before writing it */
1110                 record_hdr.record_type = R_STATS;
1111                 if (ofile[0]) {
1112                         write_stats(ofd);
1113                 }
1114
1115                 if (do_sa_rotat) {
1116                         /*
1117                          * Stats are written at the end of previous file *and* at the
1118                          * beginning of the new one (outfile must have been specified
1119                          * as '-' on the command line).
1120                          */
1121                         do_sa_rotat = FALSE;
1122
1123                         if (fdatasync(ofd) < 0) {
1124                                 /* Flush previous file */
1125                                 perror("fdatasync");
1126                                 exit(4);
1127                         }
1128                         close(ofd);
1129                         strcpy(ofile, new_ofile);
1130
1131                         /* Recalculate number of system items and reallocate structures */
1132                         sa_sys_init();
1133
1134                         /*
1135                          * Open and init new file.
1136                          * This is also used to set activity sequence to that of the file
1137                          * if the file already exists.
1138                          */
1139                         open_ofile(&ofd, ofile, FALSE);
1140
1141                         /*
1142                          * Rewrite header and activity sequence to stdout since
1143                          * number of items may have changed.
1144                          */
1145                         if (stdfd >= 0) {
1146                                 setup_file_hdr(stdfd);
1147                         }
1148
1149                         /* Write stats to file again */
1150                         write_stats(ofd);
1151                 }
1152
1153                 /* Flush data */
1154                 fflush(stdout);
1155                 if (FDATASYNC(flags)) {
1156                         /* If indicated, sync the data to media */
1157                         if (fdatasync(ofd) < 0) {
1158                                 perror("fdatasync");
1159                                 exit(4);
1160                         }
1161                 }
1162
1163                 if (count > 0) {
1164                         count--;
1165                 }
1166
1167                 if (count) {
1168                         /* Wait for a signal (probably SIGALRM or SIGINT) */
1169                         __pause();
1170                 }
1171
1172                 if (sigint_caught)
1173                         /* SIGINT caught: Stop now */
1174                         break;
1175
1176                 /* Rotate activity file if necessary */
1177                 if (WANT_SA_ROTAT(flags)) {
1178                         /* The user specified '-' as the filename to use */
1179                         strncpy(new_ofile, sa_dir, sizeof(new_ofile) - 1);
1180                         new_ofile[sizeof(new_ofile) - 1] = '\0';
1181                         set_default_file(new_ofile, 0, USE_SA_YYYYMMDD(flags));
1182
1183                         if (strcmp(ofile, new_ofile)) {
1184                                 do_sa_rotat = TRUE;
1185                         }
1186                 }
1187         }
1188         while (count);
1189
1190         /* Close file descriptors if they have actually been used */
1191         CLOSE(stdfd);
1192         CLOSE(ofd);
1193 }
1194
1195 /*
1196  ***************************************************************************
1197  * Main entry to the program.
1198  ***************************************************************************
1199  */
1200 int main(int argc, char **argv)
1201 {
1202         int opt = 0;
1203         char ofile[MAX_FILE_LEN], sa_dir[MAX_FILE_LEN];
1204         int stdfd = 0, ofd = -1;
1205         int restart_mark;
1206         long count = 0;
1207
1208 #ifdef TEST
1209         fprintf(stderr, "TEST MODE\n");
1210 #endif
1211
1212         /* Get HZ */
1213         get_HZ();
1214
1215         /* Compute page shift in kB */
1216         get_kb_shift();
1217
1218         ofile[0] = sa_dir[0] = comment[0] = '\0';
1219
1220 #if (defined(HAVE_SENSORS) && !defined(ARCH32)) || (defined(ARCH32) && defined(HAVE_SENSORS32))
1221         /* Initialize sensors, let it use the default cfg file */
1222         int err = sensors_init(NULL);
1223         if (err) {
1224                 fprintf(stderr, "sensors_init: %s\n", sensors_strerror(err));
1225         }
1226 #endif /* HAVE_SENSORS */
1227
1228 #ifdef USE_NLS
1229         /* Init National Language Support */
1230         init_nls();
1231 #endif
1232
1233         while (++opt < argc) {
1234
1235                 if (!strcmp(argv[opt], "-S")) {
1236                         if (!argv[++opt]) {
1237                                 usage(argv[0]);
1238                         }
1239                         parse_sadc_S_option(argv, opt);
1240                 }
1241
1242                 else if (!strcmp(argv[opt], "-D")) {
1243                         flags |= S_F_SA_YYYYMMDD;
1244                 }
1245
1246                 else if (!strcmp(argv[opt], "-F")) {
1247                         flags |= S_F_FORCE_FILE;
1248                 }
1249
1250                 else if (!strcmp(argv[opt], "-L")) {
1251                         flags |= S_F_LOCK_FILE;
1252                 }
1253
1254                 else if (!strcmp(argv[opt], "-V")) {
1255                         print_version();
1256                 }
1257
1258                 else if (!strcmp(argv[opt], "-Z")) {
1259                         /* Set by sar command */
1260                         optz = 1;
1261                 }
1262
1263                 else if (!strcmp(argv[opt], "-f")) {
1264                         flags |= S_F_FDATASYNC;
1265                 }
1266
1267                 else if (!strcmp(argv[opt], "-C")) {
1268                         if (!argv[++opt]) {
1269                                 usage(argv[0]);
1270                         }
1271                         strncpy(comment, argv[opt], sizeof(comment));
1272                         comment[sizeof(comment) - 1] = '\0';
1273                         if (!strlen(comment)) {
1274                                 usage(argv[0]);
1275                         }
1276                 }
1277
1278 #ifdef TEST
1279                 else if (!strncmp(argv[opt], "--getenv", 8)) {
1280                         __env = TRUE;
1281                 }
1282
1283                 else if (!strncmp(argv[opt], "--unix_time=", 12)) {
1284                         if (strspn(argv[opt] + 12, DIGITS) != strlen(argv[opt] + 12)) {
1285                                 usage(argv[0]);
1286                         }
1287                         __unix_time = atoll(argv[opt] + 12);
1288                 }
1289 #endif
1290
1291                 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1292                         if (ofile[0] || WANT_SA_ROTAT(flags)) {
1293                                 /* Outfile already specified */
1294                                 usage(argv[0]);
1295                         }
1296                         stdfd = -1;     /* Don't write to STDOUT */
1297                         if (!strcmp(argv[opt], "-")) {
1298                                 /* File name set to '-' */
1299                                 flags |= S_F_SA_ROTAT;
1300                         }
1301                         else if (!strncmp(argv[opt], "-", 1)) {
1302                                 /* Bad option */
1303                                 usage(argv[0]);
1304                         }
1305                         else {
1306                                 /* Write data to file */
1307                                 strncpy(ofile, argv[opt], sizeof(ofile));
1308                                 ofile[sizeof(ofile) - 1] = '\0';
1309                         }
1310                 }
1311
1312                 else if (interval < 0) {
1313                         /* Get interval */
1314                         interval = atol(argv[opt]);
1315                         if (interval < 1) {
1316                                 usage(argv[0]);
1317                         }
1318                         count = -1;
1319                 }
1320
1321                 else if (count <= 0) {
1322                         /* Get count value */
1323                         count = atol(argv[opt]);
1324                         if (count < 1) {
1325                                 usage(argv[0]);
1326                         }
1327                 }
1328
1329                 else {
1330                         usage(argv[0]);
1331                 }
1332         }
1333
1334         /* Process file entered on the command line */
1335         if (WANT_SA_ROTAT(flags)) {
1336                 /* File name set to '-' */
1337                 set_default_file(ofile, 0, USE_SA_YYYYMMDD(flags));
1338         }
1339         else if (ofile[0]) {
1340                 /*
1341                  * A file (or directory) has been explicitly entered
1342                  * on the command line.
1343                  * Should ofile be a directory, it will be the alternate
1344                  * location for sa files. So save it.
1345                  */
1346                 strcpy(sa_dir, ofile);
1347                 /* Check if this is an alternate directory for sa files */
1348                 if (check_alt_sa_dir(ofile, 0, USE_SA_YYYYMMDD(flags))) {
1349                         /*
1350                          * Yes, it was a directory.
1351                          * ofile now contains the full path to current
1352                          * standard daily data file.
1353                          */
1354                         flags |= S_F_SA_ROTAT;
1355                 }
1356                 else {
1357                         /* No: So we can clear sa_dir */
1358                         sa_dir[0] = '\0';
1359                 }
1360         }
1361
1362         /*
1363          * If option -Z used, write to STDOUT even if a filename
1364          * has been entered on the command line.
1365          */
1366         if (optz) {
1367                 stdfd = 0;
1368         }
1369
1370         if (!ofile[0]) {
1371                 /* -L option ignored when writing to STDOUT */
1372                 flags &= ~S_F_LOCK_FILE;
1373         }
1374
1375         /* Init structures according to machine architecture */
1376         sa_sys_init();
1377
1378         /* At least one activity must be collected */
1379         if (!get_activity_nr(act, AO_COLLECTED, COUNT_ACTIVITIES)) {
1380                 /* Requested activities not available: Exit */
1381                 print_collect_error();
1382         }
1383
1384         if ((interval < 0) && !comment[0]) {
1385                 /*
1386                  * Interval (and count) not set, and no comment given
1387                  * => We are going to insert a restart mark.
1388                  */
1389                 restart_mark = TRUE;
1390         }
1391         else {
1392                 restart_mark = FALSE;
1393         }
1394
1395         /*
1396          * Open output file then STDOUT. Write header for each of them.
1397          * NB: Output file must be opened first, because we may change
1398          * the activities collected AND the activity sequence to that
1399          * of the file, and the activities collected and activity sequence
1400          * written on STDOUT must be consistent to those of the file.
1401          */
1402         open_ofile(&ofd, ofile, restart_mark);
1403         open_stdout(&stdfd);
1404
1405         if (interval < 0) {
1406                 if (ofd >= 0) {
1407                         /*
1408                          * Interval (and count) not set:
1409                          * Write a dummy record, or insert a comment, then exit.
1410                          * NB: Never write such a dummy record on stdout since
1411                          * sar never expects it.
1412                          */
1413                         if (comment[0]) {
1414                                 write_special_record(ofd, R_COMMENT);
1415                         }
1416                         else {
1417                                 write_special_record(ofd, R_RESTART);
1418                         }
1419
1420                         /* Close file descriptor */
1421                         CLOSE(ofd);
1422                 }
1423
1424                 /* Free structures */
1425                 sa_sys_free();
1426                 exit(0);
1427         }
1428
1429         /* Set a handler for SIGALRM */
1430         memset(&alrm_act, 0, sizeof(alrm_act));
1431         alrm_act.sa_handler = alarm_handler;
1432         sigaction(SIGALRM, &alrm_act, NULL);
1433         __alarm(interval);
1434
1435         /* Main loop */
1436         rw_sa_stat_loop(count, stdfd, ofd, ofile, sa_dir);
1437
1438 #if (defined(HAVE_SENSORS) && !defined(ARCH32)) || (defined(ARCH32) && defined(HAVE_SENSORS32))
1439         /* Cleanup sensors */
1440         sensors_cleanup();
1441 #endif /* HAVE_SENSORS */
1442
1443         /* Free structures */
1444         sa_sys_free();
1445
1446         return 0;
1447 }