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