2 * sadc: system activity data collector
3 * (C) 1999-2021 by Sebastien GODARD (sysstat <at> orange.fr)
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. *
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 *
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 ***************************************************************************
35 #include <sys/utsname.h>
43 #define _(string) gettext(string)
45 #define _(string) (string)
48 #if (defined(HAVE_SENSORS) && !defined(ARCH32)) || (defined(ARCH32) && defined(HAVE_SENSORS32))
49 #include "sensors/sensors.h"
50 #include "sensors/error.h"
54 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
55 char *sccsid(void) { return (SCCSID); }
59 extern time_t __unix_time;
63 extern char *tzname[2];
69 char timestamp[2][TIMESTAMP_LEN];
71 struct file_header file_hdr;
72 struct record_header record_hdr;
74 char comment[MAX_COMMENT_LEN];
76 unsigned int id_seq[NR_ACT];
78 extern unsigned int hdr_types_nr[];
79 extern unsigned int act_types_nr[];
80 extern unsigned int rec_types_nr[];
82 extern struct activity *act[];
83 extern __nr_t (*f_count[]) (struct activity *);
85 struct sigaction alrm_act, int_act;
86 int sigint_caught = 0;
89 ***************************************************************************
90 * Print usage and exit.
93 * @progname Name of sysstat command
94 ***************************************************************************
96 void usage(char *progname)
98 fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ <outfile> ]\n"),
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"));
108 ***************************************************************************
109 * Collect all activities belonging to a group.
112 * @group_id Group identification number.
113 * @opt_f Optional flag to set.
114 ***************************************************************************
116 void collect_group_activities(unsigned int group_id, unsigned int opt_f)
120 for (i = 0; i < NR_ACT; i++) {
121 if (act[i]->group & group_id) {
122 act[i]->options |= AO_COLLECTED;
124 act[i]->opt_flags |= opt_f;
131 ***************************************************************************
132 * Parse option -S, indicating which activities are to be collected.
135 * @argv Arguments list.
136 * @opt Index in list of arguments.
137 ***************************************************************************
139 void parse_sadc_S_option(char *argv[], int opt)
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);
149 else if (!strcmp(p, K_DISK)) {
150 /* Select group of disk activities */
151 collect_group_activities(G_DISK, AO_F_NULL);
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);
157 else if (!strcmp(p, K_SNMP)) {
158 /* Select group of SNMP activities */
159 collect_group_activities(G_SNMP, AO_F_NULL);
161 else if (!strcmp(p, K_IPV6)) {
162 /* Select group of IPv6 activities */
163 collect_group_activities(G_IPV6, AO_F_NULL);
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);
169 else if (!strcmp(p, K_ALL) || !strcmp(p, K_XALL)) {
170 /* Select all activities */
171 for (i = 0; i < NR_ACT; i++) {
173 if (!strcmp(p, K_ALL) && (act[i]->group & G_XDISK))
175 * Don't select G_XDISK activities
176 * when option -S ALL is used.
180 act[i]->options |= AO_COLLECTED;
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);
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;
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;
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;
224 ***************************************************************************
225 * SIGALRM signal handler. No need to reset handler here.
228 * @sig Signal number.
229 ***************************************************************************
231 void alarm_handler(int sig)
237 ***************************************************************************
238 * SIGINT signal handler.
241 * @sig Signal number.
242 ***************************************************************************
244 void int_handler(int sig)
246 pid_t ppid = getppid();
250 if (!optz || (ppid == 1)) {
251 /* sadc hasn't been called by sar or sar process is already dead */
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
260 if (kill(ppid, SIGINT) < 0) {
266 ***************************************************************************
267 * Display an error message.
268 ***************************************************************************
270 void p_write_error(void)
272 fprintf(stderr, _("Cannot write data to system activity file: %s\n"),
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 ***************************************************************************
292 void reset_stats(void)
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);
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()
315 ***************************************************************************
317 void sa_sys_init(void)
320 __nr_t f_count_results[NR_F_COUNT];
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;
327 for (i = 0; i < NR_ACT; i++) {
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;
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];
338 act[i]->nr_ini = (f_count[idx])(act[i]);
339 f_count_results[idx] = act[i]->nr_ini;
343 if (act[i]->nr_ini > 0) {
344 if (act[i]->f_count2_index >= 0) {
345 idx = act[i]->f_count2_index;
347 if (f_count_results[idx] >= 0) {
348 act[i]->nr2 = f_count_results[idx];
351 act[i]->nr2 = (f_count[idx])(act[i]);
352 f_count_results[idx] = act[i]->nr2;
355 /* else act[i]->nr2 is a constant and doesn't need to be calculated */
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;
369 if (act[i]->nr_ini <= 0) {
370 /* No items found: Invalidate current activity */
371 act[i]->options &= ~AO_COLLECTED;
374 if (HAS_DETECT_FUNCTION(act[i]->options) && IS_COLLECTED(act[i]->options)) {
375 idx = act[i]->f_count_index;
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]);
381 if (f_count_results[idx] == 0) {
382 /* Files not present */
383 act[i]->options &= ~AO_COLLECTED;
387 /* Set default activity list */
388 id_seq[i] = act[i]->id;
393 ***************************************************************************
395 ***************************************************************************
397 void sa_sys_free(void)
401 for (i = 0; i < NR_ACT; i++) {
403 if (act[i]->nr_allocated > 0) {
406 act[i]->_buf0 = NULL;
407 act[i]->nr_allocated = 0;
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.
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
426 * 0 on success, or 1 if file couldn't be locked.
427 ***************************************************************************
429 int ask_for_flock(int fd, int fatal)
431 /* Option -L may be used only if an outfile was specified on the command line */
432 if (LOCK_FILE(flags)) {
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.
439 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
440 if ((((errno == EWOULDBLOCK) || (errno == EAGAIN)) && (fatal == FATAL)) ||
441 ((errno != EWOULDBLOCK) && (errno != EAGAIN))) {
445 /* Was unable to lock file: Lock would have blocked... */
449 /* File successfully locked */
450 flags |= S_F_FILE_LOCKED;
457 ***************************************************************************
458 * Fill system activity file magic header.
461 * @file_magic System activity file magic header.
462 ***************************************************************************
464 void fill_magic_header(struct file_magic *file_magic)
468 memset(file_magic, 0, FILE_MAGIC_SIZE);
470 file_magic->sysstat_magic = SYSSTAT_MAGIC;
471 file_magic->format_magic = FORMAT_MAGIC;
473 enum_version_nr(file_magic);
475 file_magic->header_size = FILE_HEADER_SIZE;
477 for (i = 0; i < 3; i++) {
478 file_magic->hdr_types_nr[i] = hdr_types_nr[i];
483 ***************************************************************************
484 * Fill system activity file header, then write it (or print it if stdout).
487 * @fd Output file descriptor. May be stdout.
488 ***************************************************************************
490 void setup_file_hdr(int fd)
494 struct utsname header;
495 struct file_magic file_magic;
496 struct file_activity file_act;
498 /* Fill then write file magic header */
499 fill_magic_header(&file_magic);
501 if (write_all(fd, &file_magic, FILE_MAGIC_SIZE) != FILE_MAGIC_SIZE) {
505 /* First reset the structure */
506 memset(&file_hdr, 0, FILE_HEADER_SIZE);
508 /* Then get current date */
509 file_hdr.sa_ust_time = (unsigned long long) get_time(&rectime, 0);
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);
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];
523 file_hdr.act_size = FILE_ACTIVITY_SIZE;
524 file_hdr.rec_size = RECORD_HEADER_SIZE;
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.
532 file_hdr.sa_cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr_ini;
534 /* Get system name, release number, hostname and machine architecture */
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';
545 /* Get timezone value and save it */
547 strncpy(file_hdr.sa_tzname, tzname[0], TZNAME_LEN);
548 file_hdr.sa_tzname[TZNAME_LEN - 1] = '\0';
550 /* Write file header */
551 if (write_all(fd, &file_hdr, FILE_HEADER_SIZE) != FILE_HEADER_SIZE) {
555 /* Reset file_activity structure (in case some unknown extra fields exist) */
556 memset(&file_act, 0, FILE_ACTIVITY_SIZE);
558 /* Write activity list */
559 for (i = 0; i < NR_ACT; i++) {
562 * Activity sequence given by id_seq array.
563 * Sequence must be the same for stdout as for output file.
567 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
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];
580 file_act.has_nr = HAS_COUNT_FUNCTION(act[p]->options);
582 if (write_all(fd, &file_act, FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
592 ***************************************************************************
593 * Write the new number of CPU after the RESTART record in file.
596 * @ofd Output file descriptor.
597 ***************************************************************************
599 void write_new_cpu_nr(int ofd)
603 p = get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND);
605 if (write_all(ofd, &(act[p]->nr_ini), sizeof(__nr_t)) != sizeof(__nr_t)) {
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.
619 * @ofd Output file descriptor.
620 * @rtype Record type to write (restart or comment).
621 ***************************************************************************
623 void write_special_record(int ofd, int rtype)
625 struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
627 /* Check if file is locked */
628 if (!FILE_LOCKED(flags)) {
629 ask_for_flock(ofd, FATAL);
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);
635 /* Set record type */
636 record_hdr.record_type = rtype;
639 record_hdr.ust_time = (unsigned long long) get_time(&rectime, 0);
641 record_hdr.hour = rectime.tm_hour;
642 record_hdr.minute = rectime.tm_min;
643 record_hdr.second = rectime.tm_sec;
645 /* Write record now */
646 if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
650 if (rtype == R_RESTART) {
651 /* Also write the new number of CPU */
652 write_new_cpu_nr(ofd);
654 else if (rtype == R_COMMENT) {
655 /* Also write the comment */
656 if (write_all(ofd, comment, MAX_COMMENT_LEN) != MAX_COMMENT_LEN) {
663 ***************************************************************************
664 * Write stats (or print them if stdout).
667 * @ofd Output file descriptor. May be stdout.
668 ***************************************************************************
670 void write_stats(int ofd)
674 /* Try to lock file */
675 if (!FILE_LOCKED(flags)) {
676 if (ask_for_flock(ofd, NON_FATAL))
678 * Unable to lock file:
679 * Wait for next iteration to try again to save data.
684 /* Write record header */
685 if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
689 /* Then write all statistics */
690 for (i = 0; i < NR_ACT; i++) {
694 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
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)) {
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)) {
712 ***************************************************************************
713 * Create a system activity daily data file.
716 * @ofile Name of output file.
719 * @ofd Output file descriptor.
720 ***************************************************************************
722 void create_sa_file(int *ofd, char *ofile)
724 if ((*ofd = open(ofile, O_CREAT | O_WRONLY,
725 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0)
728 /* Try to lock file */
729 ask_for_flock(*ofd, FATAL);
732 if (ftruncate(*ofd, 0) >= 0) {
734 /* Write file header */
735 setup_file_hdr(*ofd);
741 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
746 ***************************************************************************
747 * Get descriptor for stdout.
750 * @stdfd A value >= 0 indicates that stats data should also
751 * be written to stdout.
754 * @stdfd Stdout file descriptor.
755 ***************************************************************************
757 void open_stdout(int *stdfd)
760 if ((*stdfd = dup(STDOUT_FILENO)) < 0) {
764 /* Write file header on STDOUT */
765 setup_file_hdr(*stdfd);
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.
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).
783 * @ofd Output file descriptor.
784 ***************************************************************************
786 void open_ofile(int *ofd, char ofile[], int restart_mark)
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};
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);
804 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
808 /* Read file magic header */
809 sz = read(*ofd, &file_magic, FILE_MAGIC_SIZE);
812 /* This is an empty file: Create it again */
813 create_sa_file(ofd, ofile);
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)) {
827 /* -F option used: Truncate file */
828 create_sa_file(ofd, ofile);
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]);
836 /* Display error message and exit */
837 handle_invalid_sa_file(*ofd, &file_magic, ofile, sz);
840 /* Read file standard header */
841 if ((sz = read(*ofd, &file_hdr, FILE_HEADER_SIZE)) != FILE_HEADER_SIZE) {
843 fprintf(stderr, "%s: Size read=%zd\n",
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.
854 get_time(&rectime, 0);
856 if (((file_hdr.sa_month != rectime.tm_mon) ||
857 (file_hdr.sa_year != rectime.tm_year)) &&
858 WANT_SA_ROTAT(flags)) {
860 create_sa_file(ofd, ofile);
864 /* OK: It's a true system activity file */
865 if (!file_hdr.sa_act_nr || (file_hdr.sa_act_nr > NR_ACT)) {
867 fprintf(stderr, "%s: sa_act_nr=%d\n",
868 __FUNCTION__, file_hdr.sa_act_nr);
871 * No activities at all or at least one unknown activity:
872 * Cannot append data to such a file.
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)) {
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],
891 file_hdr.rec_types_nr[0], file_hdr.rec_types_nr[1], file_hdr.rec_types_nr[2]);
896 for (i = 0; i < file_hdr.sa_act_nr; i++) {
898 /* Read current activity in list */
899 if (read(*ofd, &file_act[i], FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
901 fprintf(stderr, "%s: Wrong size for file_activity\n",
904 handle_invalid_sa_file(*ofd, &file_magic, ofile, 0);
907 p = get_activity_position(act, file_act[i].id, RESUME_IF_NOT_FOUND);
909 if ((p < 0) || (act[p]->fsize != file_act[i].size) ||
910 (act[p]->magic != file_act[i].magic)) {
913 fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
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);
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).
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)) {
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);
938 * Number of items and subitems should never be zero (or negative)
939 * or greater than their upper limit.
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])) {
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]);
954 * Composition of structure containing statsitics cannot
955 * be different from that known by current version.
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))) {
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);
967 * For every activity whose number of items is not a constant,
968 * a value giving the number of structures to read should exist.
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.
979 for (i = 0; i < NR_ACT; i++) {
980 act[i]->options &= ~AO_COLLECTED;
984 for (i = 0; i < file_hdr.sa_act_nr; i++) {
986 p = get_activity_position(act, file_act[i].id, EXIT_IF_NOT_FOUND);
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
994 * NB: We must preserve the value read for A_CPU when a LINUX RESTART is inserted.
996 if (!ALWAYS_COUNT_ITEMS(act[p]->options) || !act[p]->nr_ini) {
997 act[p]->nr_ini = file_act[i].nr;
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.
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;
1010 SREALLOC(act[p]->_buf0, void,
1011 (size_t) act[p]->msize * (size_t) act[p]->nr_allocated * (size_t) act[p]->nr2);
1013 /* Save activity sequence */
1014 id_seq[i] = file_act[i].id;
1015 act[p]->options |= AO_COLLECTED;
1023 if (FORCE_FILE(flags)) {
1025 create_sa_file(ofd, ofile);
1028 fprintf(stderr, _("Cannot append data to that file (%s)\n"), ofile);
1034 ***************************************************************************
1035 * Read statistics from various system files.
1036 ***************************************************************************
1038 void read_stats(void)
1042 /* Read system uptime in 1/100th of a second */
1043 read_uptime(&(record_hdr.uptime_cs));
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]);
1054 ***************************************************************************
1055 * Main loop: Read stats from the relevant sources and display them.
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
1064 ***************************************************************************
1066 void rw_sa_stat_loop(long count, int stdfd, int ofd, char ofile[],
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};
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);
1081 /* Init all structures */
1083 memset(&record_hdr, 0, RECORD_HEADER_SIZE);
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;
1091 /* Set record type */
1093 record_hdr.record_type = R_LAST_STATS;
1096 record_hdr.record_type = R_STATS;
1099 /* Read then write stats */
1104 flags &= ~S_F_LOCK_FILE;
1109 /* If the record type was R_LAST_STATS, tag it R_STATS before writing it */
1110 record_hdr.record_type = R_STATS;
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).
1121 do_sa_rotat = FALSE;
1123 if (fdatasync(ofd) < 0) {
1124 /* Flush previous file */
1125 perror("fdatasync");
1129 strcpy(ofile, new_ofile);
1131 /* Recalculate number of system items and reallocate structures */
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.
1139 open_ofile(&ofd, ofile, FALSE);
1142 * Rewrite header and activity sequence to stdout since
1143 * number of items may have changed.
1146 setup_file_hdr(stdfd);
1149 /* Write stats to file again */
1155 if (FDATASYNC(flags)) {
1156 /* If indicated, sync the data to media */
1157 if (fdatasync(ofd) < 0) {
1158 perror("fdatasync");
1168 /* Wait for a signal (probably SIGALRM or SIGINT) */
1173 /* SIGINT caught: Stop now */
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));
1183 if (strcmp(ofile, new_ofile)) {
1190 /* Close file descriptors if they have actually been used */
1196 ***************************************************************************
1197 * Main entry to the program.
1198 ***************************************************************************
1200 int main(int argc, char **argv)
1203 char ofile[MAX_FILE_LEN], sa_dir[MAX_FILE_LEN];
1204 int stdfd = 0, ofd = -1;
1209 fprintf(stderr, "TEST MODE\n");
1215 /* Compute page shift in kB */
1218 ofile[0] = sa_dir[0] = comment[0] = '\0';
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);
1224 fprintf(stderr, "sensors_init: %s\n", sensors_strerror(err));
1226 #endif /* HAVE_SENSORS */
1229 /* Init National Language Support */
1233 while (++opt < argc) {
1235 if (!strcmp(argv[opt], "-S")) {
1239 parse_sadc_S_option(argv, opt);
1242 else if (!strcmp(argv[opt], "-D")) {
1243 flags |= S_F_SA_YYYYMMDD;
1246 else if (!strcmp(argv[opt], "-F")) {
1247 flags |= S_F_FORCE_FILE;
1250 else if (!strcmp(argv[opt], "-L")) {
1251 flags |= S_F_LOCK_FILE;
1254 else if (!strcmp(argv[opt], "-V")) {
1258 else if (!strcmp(argv[opt], "-Z")) {
1259 /* Set by sar command */
1263 else if (!strcmp(argv[opt], "-f")) {
1264 flags |= S_F_FDATASYNC;
1267 else if (!strcmp(argv[opt], "-C")) {
1271 strncpy(comment, argv[opt], sizeof(comment));
1272 comment[sizeof(comment) - 1] = '\0';
1273 if (!strlen(comment)) {
1279 else if (!strncmp(argv[opt], "--getenv", 8)) {
1283 else if (!strncmp(argv[opt], "--unix_time=", 12)) {
1284 if (strspn(argv[opt] + 12, DIGITS) != strlen(argv[opt] + 12)) {
1287 __unix_time = atoll(argv[opt] + 12);
1291 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1292 if (ofile[0] || WANT_SA_ROTAT(flags)) {
1293 /* Outfile already specified */
1296 stdfd = -1; /* Don't write to STDOUT */
1297 if (!strcmp(argv[opt], "-")) {
1298 /* File name set to '-' */
1299 flags |= S_F_SA_ROTAT;
1301 else if (!strncmp(argv[opt], "-", 1)) {
1306 /* Write data to file */
1307 strncpy(ofile, argv[opt], sizeof(ofile));
1308 ofile[sizeof(ofile) - 1] = '\0';
1312 else if (interval < 0) {
1314 interval = atol(argv[opt]);
1321 else if (count <= 0) {
1322 /* Get count value */
1323 count = atol(argv[opt]);
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));
1339 else if (ofile[0]) {
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.
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))) {
1350 * Yes, it was a directory.
1351 * ofile now contains the full path to current
1352 * standard daily data file.
1354 flags |= S_F_SA_ROTAT;
1357 /* No: So we can clear sa_dir */
1363 * If option -Z used, write to STDOUT even if a filename
1364 * has been entered on the command line.
1371 /* -L option ignored when writing to STDOUT */
1372 flags &= ~S_F_LOCK_FILE;
1375 /* Init structures according to machine architecture */
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();
1384 if ((interval < 0) && !comment[0]) {
1386 * Interval (and count) not set, and no comment given
1387 * => We are going to insert a restart mark.
1389 restart_mark = TRUE;
1392 restart_mark = FALSE;
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.
1402 open_ofile(&ofd, ofile, restart_mark);
1403 open_stdout(&stdfd);
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.
1414 write_special_record(ofd, R_COMMENT);
1417 write_special_record(ofd, R_RESTART);
1420 /* Close file descriptor */
1424 /* Free structures */
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);
1436 rw_sa_stat_loop(count, stdfd, ofd, ofile, sa_dir);
1438 #if (defined(HAVE_SENSORS) && !defined(ARCH32)) || (defined(ARCH32) && defined(HAVE_SENSORS32))
1439 /* Cleanup sensors */
1441 #endif /* HAVE_SENSORS */
1443 /* Free structures */