2 * sadc: system activity data collector
3 * (C) 1999-2017 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 ***************************************************************************
34 #include <sys/utsname.h>
45 #define _(string) gettext(string)
47 #define _(string) (string)
51 #include "sensors/sensors.h"
52 #include "sensors/error.h"
56 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
57 char *sccsid(void) { return (SCCSID); }
61 unsigned int flags = 0;
65 char timestamp[2][TIMESTAMP_LEN];
67 struct file_header file_hdr;
68 struct record_header record_hdr;
70 char comment[MAX_COMMENT_LEN];
72 unsigned int id_seq[NR_ACT];
74 extern unsigned int hdr_types_nr[];
75 extern unsigned int act_types_nr[];
76 extern unsigned int rec_types_nr[];
78 extern struct activity *act[];
79 extern __nr_t (*f_count[]) (struct activity *);
81 struct sigaction alrm_act, int_act;
82 int sigint_caught = 0;
85 ***************************************************************************
86 * Print usage and exit.
89 * @progname Name of sysstat command
90 ***************************************************************************
92 void usage(char *progname)
94 fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ <outfile> ]\n"),
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"));
104 ***************************************************************************
105 * Collect all activities belonging to a group.
108 * @group_id Group identification number.
109 * @opt_f Optionnal flag to set.
110 ***************************************************************************
112 void collect_group_activities(unsigned int group_id, unsigned int opt_f)
116 for (i = 0; i < NR_ACT; i++) {
117 if (act[i]->group & group_id) {
118 act[i]->options |= AO_COLLECTED;
120 act[i]->opt_flags |= opt_f;
127 ***************************************************************************
128 * Parse option -S, indicating which activities are to be collected.
131 * @argv Arguments list.
132 * @opt Index in list of arguments.
133 ***************************************************************************
135 void parse_sadc_S_option(char *argv[], int opt)
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);
145 else if (!strcmp(p, K_DISK)) {
146 /* Select group of disk activities */
147 collect_group_activities(G_DISK, AO_F_NULL);
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);
153 else if (!strcmp(p, K_SNMP)) {
154 /* Select group of SNMP activities */
155 collect_group_activities(G_SNMP, AO_F_NULL);
157 else if (!strcmp(p, K_IPV6)) {
158 /* Select group of IPv6 activities */
159 collect_group_activities(G_IPV6, AO_F_NULL);
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);
165 else if (!strcmp(p, K_ALL) || !strcmp(p, K_XALL)) {
166 /* Select all activities */
167 for (i = 0; i < NR_ACT; i++) {
169 if (!strcmp(p, K_ALL) && (act[i]->group & G_XDISK))
171 * Don't select G_XDISK activities
172 * when option -S ALL is used.
176 act[i]->options |= AO_COLLECTED;
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);
183 else if (strspn(p, DIGITS) == strlen(p)) {
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
196 for (i = 0; i < NR_ACT; i++) {
197 if (act[i]->group & act_id) {
198 act[i]->options |= AO_COLLECTED;
202 else if ((act_id < 0) || (act_id > NR_ACT)) {
206 /* Unselect all activities */
207 for (i = 0; i < NR_ACT; i++) {
208 act[i]->options &= ~AO_COLLECTED;
212 /* Select chosen activity */
213 COLLECT_ACTIVITY(act_id);
223 ***************************************************************************
224 * SIGALRM signal handler. No need to reset handler here.
227 * @sig Signal number.
228 ***************************************************************************
230 void alarm_handler(int sig)
236 ***************************************************************************
237 * SIGINT signal handler.
240 * @sig Signal number.
241 ***************************************************************************
243 void int_handler(int sig)
245 pid_t ppid = getppid();
249 if (!optz || (ppid == 1)) {
250 /* sadc hasn't been called by sar or sar process is already dead */
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
259 if (kill(ppid, SIGINT) < 0) {
265 ***************************************************************************
266 * Display an error message.
267 ***************************************************************************
269 void p_write_error(void)
271 fprintf(stderr, _("Cannot write data to system activity file: %s\n"),
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 ***************************************************************************
285 void reset_stats(void)
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);
298 ***************************************************************************
299 * Allocate and init structures, according to system state.
300 ***************************************************************************
302 void sa_sys_init(void)
305 __nr_t f_count_results[NR_F_COUNT];
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;
312 for (i = 0; i < NR_ACT; i++) {
314 idx = act[i]->f_count_index;
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];
322 act[i]->nr = (f_count[idx])(act[i]);
323 f_count_results[idx] = act[i]->nr;
327 if (act[i]->nr > 0) {
328 if (act[i]->f_count2) {
329 act[i]->nr2 = (*act[i]->f_count2)(act[i]);
331 /* else act[i]->nr2 is a constant and doesn't need to be calculated */
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);
344 /* No items found: Invalidate current activity */
345 act[i]->options &= ~AO_COLLECTED;
348 /* Set default activity list */
349 id_seq[i] = act[i]->id;
354 ***************************************************************************
356 ***************************************************************************
358 void sa_sys_free(void)
362 for (i = 0; i < NR_ACT; i++) {
364 if (act[i]->nr > 0) {
367 act[i]->_buf0 = NULL;
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.
379 * @fd Output file descriptor.
381 * @nr_bytes Number of bytes to write.
384 * Number of bytes written to file, or -1 on error.
385 ***************************************************************************
387 int write_all(int fd, const void *buf, int nr_bytes)
389 int block, offset = 0;
390 char *buffer = (char *) buf;
392 while (nr_bytes > 0) {
393 block = write(fd, &buffer[offset], nr_bytes);
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.
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
423 * 0 on success, or 1 if file couldn't be locked.
424 ***************************************************************************
426 int ask_for_flock(int fd, int fatal)
428 /* Option -L may be used only if an outfile was specified on the command line */
429 if (LOCK_FILE(flags)) {
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.
436 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
437 if ((((errno == EWOULDBLOCK) || (errno == EAGAIN)) && (fatal == FATAL)) ||
438 ((errno != EWOULDBLOCK) && (errno != EAGAIN))) {
442 /* Was unable to lock file: Lock would have blocked... */
446 /* File successfully locked */
447 flags |= S_F_FILE_LOCKED;
454 ***************************************************************************
455 * Fill system activity file magic header.
458 * @file_magic System activity file magic header.
459 ***************************************************************************
461 void fill_magic_header(struct file_magic *file_magic)
465 memset(file_magic, 0, FILE_MAGIC_SIZE);
467 file_magic->sysstat_magic = SYSSTAT_MAGIC;
468 file_magic->format_magic = FORMAT_MAGIC;
470 enum_version_nr(file_magic);
472 file_magic->header_size = FILE_HEADER_SIZE;
474 for (i = 0; i < 3; i++) {
475 file_magic->hdr_types_nr[i] = hdr_types_nr[i];
480 ***************************************************************************
481 * Fill system activity file header, then write it (or print it if stdout).
484 * @fd Output file descriptor. May be stdout.
485 ***************************************************************************
487 void setup_file_hdr(int fd)
491 struct utsname header;
492 struct file_magic file_magic;
493 struct file_activity file_act;
495 /* Fill then write file magic header */
496 fill_magic_header(&file_magic);
498 if (write_all(fd, &file_magic, FILE_MAGIC_SIZE) != FILE_MAGIC_SIZE)
501 /* First reset the structure */
502 memset(&file_hdr, 0, FILE_HEADER_SIZE);
504 /* Then get current date */
505 file_hdr.sa_ust_time = (unsigned long long) get_time(&rectime, 0);
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);
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];
519 file_hdr.act_size = FILE_ACTIVITY_SIZE;
520 file_hdr.rec_size = RECORD_HEADER_SIZE;
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.
529 file_hdr.sa_cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
531 /* Get system name, release number, hostname and machine architecture */
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';
542 /* Write file header */
543 if (write_all(fd, &file_hdr, FILE_HEADER_SIZE) != FILE_HEADER_SIZE)
546 /* Write activity list */
547 for (i = 0; i < NR_ACT; i++) {
550 * Activity sequence given by id_seq array.
551 * Sequence must be the same for stdout as for output file.
555 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
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];
568 if (write_all(fd, &file_act, FILE_ACTIVITY_SIZE)
569 != FILE_ACTIVITY_SIZE)
578 fprintf(stderr, _("Cannot write system activity file header: %s\n"),
584 ***************************************************************************
585 * Write the new number of CPU after the RESTART record in file.
588 * @ofd Output file descriptor.
589 ***************************************************************************
591 void write_new_cpu_nr(int ofd)
596 p = get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND);
599 if (write_all(ofd, &cpu_nr, sizeof(__nr_t)) != sizeof(__nr_t)) {
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.
613 * @ofd Output file descriptor.
614 * @rtype Record type to write (dummy or comment).
615 ***************************************************************************
617 void write_special_record(int ofd, int rtype)
619 struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
621 /* Check if file is locked */
622 if (!FILE_LOCKED(flags)) {
623 ask_for_flock(ofd, FATAL);
626 /* Reset the structure (not compulsory, but a bit cleaner) */
627 memset(&record_hdr, 0, RECORD_HEADER_SIZE);
629 /* Set record type */
630 record_hdr.record_type = rtype;
633 record_hdr.ust_time = (unsigned long long) get_time(&rectime, 0);
635 record_hdr.hour = rectime.tm_hour;
636 record_hdr.minute = rectime.tm_min;
637 record_hdr.second = rectime.tm_sec;
639 /* Write record now */
640 if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
644 if (rtype == R_RESTART) {
645 /* Also write the new number of CPU */
646 write_new_cpu_nr(ofd);
648 else if (rtype == R_COMMENT) {
649 /* Also write the comment */
650 if (write_all(ofd, comment, MAX_COMMENT_LEN) != MAX_COMMENT_LEN) {
657 ***************************************************************************
658 * Write stats (or print them if stdout).
661 * @ofd Output file descriptor. May be stdout.
662 ***************************************************************************
664 void write_stats(int ofd)
668 /* Try to lock file */
669 if (!FILE_LOCKED(flags)) {
670 if (ask_for_flock(ofd, NON_FATAL))
672 * Unable to lock file:
673 * Wait for next iteration to try again to save data.
678 /* Write record header */
679 if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
683 /* Then write all statistics */
684 for (i = 0; i < NR_ACT; i++) {
688 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
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)) {
701 ***************************************************************************
702 * Create a system activity daily data file.
705 * @ofile Name of output file.
708 * @ofd Output file descriptor.
709 ***************************************************************************
711 void create_sa_file(int *ofd, char *ofile)
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));
719 /* Try to lock file */
720 ask_for_flock(*ofd, FATAL);
723 if (ftruncate(*ofd, 0) < 0) {
724 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
728 /* Write file header */
729 setup_file_hdr(*ofd);
733 ***************************************************************************
734 * Get descriptor for stdout.
737 * @stdfd A value >= 0 indicates that stats data should also
738 * be written to stdout.
741 * @stdfd Stdout file descriptor.
742 ***************************************************************************
744 void open_stdout(int *stdfd)
747 if ((*stdfd = dup(STDOUT_FILENO)) < 0) {
751 /* Write file header on STDOUT */
752 setup_file_hdr(*stdfd);
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.
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).
770 * @ofd Output file descriptor.
771 ***************************************************************************
773 void open_ofile(int *ofd, char ofile[], int restart_mark)
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};
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);
791 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
795 /* Read file magic header */
796 sz = read(*ofd, &file_magic, FILE_MAGIC_SIZE);
799 /* This is an empty file: Create it again */
800 create_sa_file(ofd, ofile);
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)) {
814 /* -F option used: Truncate file */
815 create_sa_file(ofd, ofile);
818 /* Display error message and exit */
819 handle_invalid_sa_file(ofd, &file_magic, ofile, sz);
822 /* Read file standard header */
823 if ((sz = read(*ofd, &file_hdr, FILE_HEADER_SIZE)) != FILE_HEADER_SIZE)
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.
831 get_time(&rectime, 0);
833 if (((file_hdr.sa_month != rectime.tm_mon) ||
834 (file_hdr.sa_year != rectime.tm_year)) &&
835 WANT_SA_ROTAT(flags)) {
837 create_sa_file(ofd, ofile);
841 /* OK: It's a true system activity file */
842 if (!file_hdr.sa_act_nr || (file_hdr.sa_act_nr > NR_ACT))
844 * No activities at all or at least one unknown activity:
845 * Cannot append data to such a file.
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))
860 for (i = 0; i < file_hdr.sa_act_nr; i++) {
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);
867 p = get_activity_position(act, file_act[i].id, RESUME_IF_NOT_FOUND);
869 if ((p < 0) || (act[p]->fsize != file_act[i].size) ||
870 (act[p]->magic != file_act[i].magic))
872 * Unknown activity in list or item size has changed or
873 * unknown activity format: Cannot append data to such a file.
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))
881 * Number of items and subitems should never be zero (or negative)
882 * or greater than their upper limit.
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]))
890 * Composition of structure containing statsitics cannot
891 * be different from that known by current version.
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.
901 for (i = 0; i < NR_ACT; i++) {
902 act[i]->options &= ~AO_COLLECTED;
906 for (i = 0; i < file_hdr.sa_act_nr; i++) {
908 p = get_activity_position(act, file_act[i].id, EXIT_IF_NOT_FOUND);
911 * Force number of items (serial lines, network interfaces...)
912 * and sub-items to that of the file, and reallocate structures.
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);
919 /* Save activity sequence */
920 id_seq[i] = file_act[i].id;
921 act[p]->options |= AO_COLLECTED;
929 if (FORCE_FILE(flags)) {
931 create_sa_file(ofd, ofile);
934 fprintf(stderr, _("Cannot append data to that file (%s)\n"), ofile);
940 ***************************************************************************
941 * Read statistics from various system files.
942 ***************************************************************************
944 void read_stats(void)
948 /* Read system uptime in 1/100th of a second */
949 read_uptime(&(record_hdr.uptime_cs));
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]);
960 ***************************************************************************
961 * Main loop: Read stats from the relevant sources and display them.
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
970 ***************************************************************************
972 void rw_sa_stat_loop(long count, int stdfd, int ofd, char ofile[],
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};
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);
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.
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;
1001 /* Set record type */
1003 record_hdr.record_type = R_LAST_STATS;
1006 record_hdr.record_type = R_STATS;
1009 /* Read then write stats */
1014 flags &= ~S_F_LOCK_FILE;
1019 /* If the record type was R_LAST_STATS, tag it R_STATS before writing it */
1020 record_hdr.record_type = R_STATS;
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).
1031 do_sa_rotat = FALSE;
1033 if (fdatasync(ofd) < 0) {
1034 /* Flush previous file */
1035 perror("fdatasync");
1039 strcpy(ofile, new_ofile);
1041 /* Recalculate number of system items and reallocate structures */
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.
1049 open_ofile(&ofd, ofile, FALSE);
1052 * Rewrite header and activity sequence to stdout since
1053 * number of items may have changed.
1056 setup_file_hdr(stdfd);
1059 /* Write stats to file again */
1071 /* Wait for a signal (probably SIGALRM or SIGINT) */
1076 /* SIGINT caught: Stop now */
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));
1086 if (strcmp(ofile, new_ofile)) {
1093 /* Close file descriptors if they have actually been used */
1099 ***************************************************************************
1100 * Main entry to the program.
1101 ***************************************************************************
1103 int main(int argc, char **argv)
1106 char ofile[MAX_FILE_LEN], sa_dir[MAX_FILE_LEN];
1107 int stdfd = 0, ofd = -1;
1114 /* Compute page shift in kB */
1117 ofile[0] = sa_dir[0] = comment[0] = '\0';
1120 /* Initialize sensors, let it use the default cfg file */
1121 int err = sensors_init(NULL);
1123 fprintf(stderr, "sensors_init: %s\n", sensors_strerror(err));
1125 #endif /* HAVE_SENSORS */
1128 /* Init National Language Support */
1132 while (++opt < argc) {
1134 if (!strcmp(argv[opt], "-S")) {
1138 parse_sadc_S_option(argv, opt);
1141 else if (!strcmp(argv[opt], "-D")) {
1142 flags |= S_F_SA_YYYYMMDD;
1145 else if (!strcmp(argv[opt], "-F")) {
1146 flags |= S_F_FORCE_FILE;
1149 else if (!strcmp(argv[opt], "-L")) {
1150 flags |= S_F_LOCK_FILE;
1153 else if (!strcmp(argv[opt], "-V")) {
1157 else if (!strcmp(argv[opt], "-z")) {
1158 /* Set by sar command */
1162 else if (!strcmp(argv[opt], "-C")) {
1166 strncpy(comment, argv[opt], MAX_COMMENT_LEN);
1167 comment[MAX_COMMENT_LEN - 1] = '\0';
1168 if (!strlen(comment)) {
1173 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1174 if (ofile[0] || WANT_SA_ROTAT(flags)) {
1175 /* Outfile already specified */
1178 stdfd = -1; /* Don't write to STDOUT */
1179 if (!strcmp(argv[opt], "-")) {
1180 /* File name set to '-' */
1181 flags |= S_F_SA_ROTAT;
1183 else if (!strncmp(argv[opt], "-", 1)) {
1188 /* Write data to file */
1189 strncpy(ofile, argv[opt], MAX_FILE_LEN);
1190 ofile[MAX_FILE_LEN - 1] = '\0';
1194 else if (!interval) {
1196 interval = atol(argv[opt]);
1203 else if (count <= 0) {
1204 /* Get count value */
1205 count = atol(argv[opt]);
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);
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));
1226 else if (ofile[0]) {
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.
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))) {
1237 * Yes, it was a directory.
1238 * ofile now contains the full path to current
1239 * standard daily data file.
1241 flags |= S_F_SA_ROTAT;
1244 /* No: So we can clear sa_dir */
1250 * If option -z used, write to STDOUT even if a filename
1251 * has been entered on the command line.
1258 /* -L option ignored when writing to STDOUT */
1259 flags &= ~S_F_LOCK_FILE;
1262 /* Init structures according to machine architecture */
1265 if (!interval && !comment[0]) {
1267 * Interval (and count) not set, and no comment given
1268 * => We are going to insert a restart mark.
1270 restart_mark = TRUE;
1273 restart_mark = FALSE;
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.
1283 open_ofile(&ofd, ofile, restart_mark);
1284 open_stdout(&stdfd);
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.
1295 write_special_record(ofd, R_COMMENT);
1298 write_special_record(ofd, R_RESTART);
1301 /* Close file descriptor */
1305 /* Free structures */
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);
1317 rw_sa_stat_loop(count, stdfd, ofd, ofile, sa_dir);
1320 /* Cleanup sensors */
1322 #endif /* HAVE_SENSORS */
1324 /* Free structures */