2 * sadc: system activity data collector
3 * (C) 1999-2016 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"
55 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
56 char *sccsid(void) { return (SCCSID); }
59 unsigned int flags = 0;
63 char timestamp[2][TIMESTAMP_LEN];
65 struct file_header file_hdr;
66 struct record_header record_hdr;
68 char comment[MAX_COMMENT_LEN];
70 unsigned int id_seq[NR_ACT];
71 unsigned int vol_id_seq[NR_ACT];
73 extern struct activity *act[];
74 extern __nr_t (*f_count[]) (struct activity *);
76 struct sigaction alrm_act, int_act;
77 int sigint_caught = 0;
80 ***************************************************************************
81 * Print usage and exit.
84 * @progname Name of sysstat command
85 ***************************************************************************
87 void usage(char *progname)
89 fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ <outfile> ]\n"),
92 fprintf(stderr, _("Options are:\n"
93 "[ -C <comment> ] [ -D ] [ -F ] [ -L ] [ -V ]\n"
94 "[ -S { INT | DISK | IPV6 | POWER | SNMP | XDISK | ALL | XALL } ]\n"));
99 ***************************************************************************
100 * Collect all activities belonging to a group.
103 * @group_id Group identification number.
104 * @opt_f Optionnal flag to set.
105 ***************************************************************************
107 void collect_group_activities(unsigned int group_id, unsigned int opt_f)
111 for (i = 0; i < NR_ACT; i++) {
112 if (act[i]->group & group_id) {
113 act[i]->options |= AO_COLLECTED;
115 act[i]->opt_flags |= opt_f;
122 ***************************************************************************
123 * Parse option -S, indicating which activities are to be collected.
126 * @argv Arguments list.
127 * @opt Index in list of arguments.
128 ***************************************************************************
130 void parse_sadc_S_option(char *argv[], int opt)
135 for (p = strtok(argv[opt], ","); p; p = strtok(NULL, ",")) {
136 if (!strcmp(p, K_INT)) {
137 /* Select group of interrupt activities */
138 collect_group_activities(G_INT, AO_F_NULL);
140 else if (!strcmp(p, K_DISK)) {
141 /* Select group of disk activities */
142 collect_group_activities(G_DISK, AO_F_NULL);
144 else if (!strcmp(p, K_XDISK)) {
145 /* Select group of disk and partition/filesystem activities */
146 collect_group_activities(G_DISK + G_XDISK, AO_F_DISK_PART);
148 else if (!strcmp(p, K_SNMP)) {
149 /* Select group of SNMP activities */
150 collect_group_activities(G_SNMP, AO_F_NULL);
152 else if (!strcmp(p, K_IPV6)) {
153 /* Select group of IPv6 activities */
154 collect_group_activities(G_IPV6, AO_F_NULL);
156 else if (!strcmp(p, K_POWER)) {
157 /* Select group of activities related to power management */
158 collect_group_activities(G_POWER, AO_F_NULL);
160 else if (!strcmp(p, K_ALL) || !strcmp(p, K_XALL)) {
161 /* Select all activities */
162 for (i = 0; i < NR_ACT; i++) {
164 if (!strcmp(p, K_ALL) && (act[i]->group & G_XDISK))
166 * Don't select G_XDISK activities
167 * when option -S ALL is used.
171 act[i]->options |= AO_COLLECTED;
173 if (!strcmp(p, K_XALL)) {
174 /* Tell sadc to also collect partition statistics */
175 collect_group_activities(G_DISK + G_XDISK, AO_F_DISK_PART);
178 else if (strspn(p, DIGITS) == strlen(p)) {
180 * Although undocumented, option -S followed by a numerical value
181 * enables the user to select each activity that should be
182 * collected. "-S 0" unselects all activities but CPU.
183 * A value greater than 255 enables the user to select groups
191 for (i = 0; i < NR_ACT; i++) {
192 if (act[i]->group & act_id) {
193 act[i]->options |= AO_COLLECTED;
197 else if ((act_id < 0) || (act_id > NR_ACT)) {
201 /* Unselect all activities but CPU */
202 for (i = 0; i < NR_ACT; i++) {
203 act[i]->options &= ~AO_COLLECTED;
205 COLLECT_ACTIVITY(A_CPU);
208 /* Select chosen activity */
209 COLLECT_ACTIVITY(act_id);
219 ***************************************************************************
220 * SIGALRM signal handler. No need to reset handler here.
223 * @sig Signal number.
224 ***************************************************************************
226 void alarm_handler(int sig)
232 ***************************************************************************
233 * SIGINT signal handler.
236 * @sig Signal number.
237 ***************************************************************************
239 void int_handler(int sig)
241 pid_t ppid = getppid();
245 if (!optz || (ppid == 1)) {
246 /* sadc hasn't been called by sar or sar process is already dead */
251 * When starting sar then pressing ctrl/c, SIGINT is received
252 * by sadc, not sar. So send SIGINT to sar so that average stats
255 if (kill(ppid, SIGINT) < 0) {
261 ***************************************************************************
262 * Display an error message.
263 ***************************************************************************
265 void p_write_error(void)
267 fprintf(stderr, _("Cannot write data to system activity file: %s\n"),
273 ***************************************************************************
274 * Init structures. All of them are init'ed first when they are allocated
275 * (done by SREALLOC() macro in sa_sys_init() function).
276 * Then, they are init'ed again each time before reading the various system
277 * stats to make sure that no stats from a previous reading will remain (eg.
278 * if some network interfaces or block devices have been unregistered).
279 ***************************************************************************
281 void reset_stats(void)
285 for (i = 0; i < NR_ACT; i++) {
286 if ((act[i]->nr > 0) && act[i]->_buf0) {
287 memset(act[i]->_buf0, 0,
288 (size_t) act[i]->msize * (size_t) act[i]->nr * (size_t) act[i]->nr2);
294 ***************************************************************************
295 * Allocate and init structures, according to system state.
296 ***************************************************************************
298 void sa_sys_init(void)
301 __nr_t f_count_results[NR_F_COUNT];
303 /* Init array. Means that no items have been counted yet */
304 for (i = 0; i < NR_F_COUNT; i++) {
305 f_count_results[i] = -1;
308 for (i = 0; i < NR_ACT; i++) {
310 idx = act[i]->f_count_index;
313 /* Number of items is not a constant and should be calculated */
314 if (f_count_results[idx] >= 0) {
315 act[i]->nr = f_count_results[idx];
318 act[i]->nr = (f_count[idx])(act[i]);
319 f_count_results[idx] = act[i]->nr;
323 if (act[i]->nr > 0) {
324 if (act[i]->f_count2) {
325 act[i]->nr2 = (*act[i]->f_count2)(act[i]);
327 /* else act[i]->nr2 is a constant and doesn't need to be calculated */
334 if (act[i]->nr > 0) {
335 /* Allocate structures for current activity */
336 SREALLOC(act[i]->_buf0, void,
337 (size_t) act[i]->msize * (size_t) act[i]->nr * (size_t) act[i]->nr2);
340 /* No items found: Invalidate current activity */
341 act[i]->options &= ~AO_COLLECTED;
344 /* Set default activity list */
345 id_seq[i] = act[i]->id;
350 ***************************************************************************
352 ***************************************************************************
354 void sa_sys_free(void)
358 for (i = 0; i < NR_ACT; i++) {
360 if (act[i]->nr > 0) {
363 act[i]->_buf0 = NULL;
370 ***************************************************************************
371 * Write data to file. If the write() call was interrupted by a signal, try
372 * again so that the whole buffer can be written.
375 * @fd Output file descriptor.
377 * @nr_bytes Number of bytes to write.
380 * Number of bytes written to file, or -1 on error.
381 ***************************************************************************
383 int write_all(int fd, const void *buf, int nr_bytes)
385 int block, offset = 0;
386 char *buffer = (char *) buf;
388 while (nr_bytes > 0) {
389 block = write(fd, &buffer[offset], nr_bytes);
407 ***************************************************************************
408 * If -L option used, request a non-blocking, exclusive lock on the file.
409 * If lock would block, then another process (possibly sadc) has already
410 * opened that file => exit.
413 * @fd Output file descriptor.
414 * @fatal Indicate if failing to lock file should be fatal or not.
415 * If it's not fatal then we'll wait for next iteration and
419 * 0 on success, or 1 if file couldn't be locked.
420 ***************************************************************************
422 int ask_for_flock(int fd, int fatal)
424 /* Option -L may be used only if an outfile was specified on the command line */
425 if (LOCK_FILE(flags)) {
427 * Yes: Try to lock file. To make code portable, check for both EWOULDBLOCK
428 * and EAGAIN return codes, and treat them the same (glibc documentation).
429 * Indeed, some Linux ports (e.g. hppa-linux) do not equate EWOULDBLOCK and
430 * EAGAIN like every other Linux port.
432 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
433 if ((((errno == EWOULDBLOCK) || (errno == EAGAIN)) && (fatal == FATAL)) ||
434 ((errno != EWOULDBLOCK) && (errno != EAGAIN))) {
438 /* Was unable to lock file: Lock would have blocked... */
442 /* File successfully locked */
443 flags |= S_F_FILE_LOCKED;
450 ***************************************************************************
451 * Fill system activity file magic header.
454 * @file_magic System activity file magic header.
455 ***************************************************************************
457 void fill_magic_header(struct file_magic *file_magic)
459 memset(file_magic, 0, FILE_MAGIC_SIZE);
461 file_magic->header_size = FILE_HEADER_SIZE;
463 file_magic->sysstat_magic = SYSSTAT_MAGIC;
464 file_magic->format_magic = FORMAT_MAGIC;
466 enum_version_nr(file_magic);
470 ***************************************************************************
471 * Fill system activity file header, then write it (or print it if stdout).
474 * @fd Output file descriptor. May be stdout.
475 ***************************************************************************
477 void setup_file_hdr(int fd)
481 struct utsname header;
482 struct file_magic file_magic;
483 struct file_activity file_act;
485 /* Fill then write file magic header */
486 fill_magic_header(&file_magic);
488 if (write_all(fd, &file_magic, FILE_MAGIC_SIZE) != FILE_MAGIC_SIZE)
491 /* First reset the structure */
492 memset(&file_hdr, 0, FILE_HEADER_SIZE);
494 /* Then get current date */
495 file_hdr.sa_ust_time = get_time(&rectime, 0);
497 /* OK, now fill the header */
498 file_hdr.sa_act_nr = get_activity_nr(act, AO_COLLECTED, COUNT_ACTIVITIES);
499 file_hdr.sa_vol_act_nr = get_activity_nr(act, AO_COLLECTED + AO_VOLATILE,
501 file_hdr.sa_day = rectime.tm_mday;
502 file_hdr.sa_month = rectime.tm_mon;
503 file_hdr.sa_year = rectime.tm_year;
504 file_hdr.sa_sizeof_long = sizeof(long);
507 * This is a new file (or stdout): Field sa_last_cpu_nr is set to the number
508 * of CPU items of the machine (1 .. CPU_NR + 1).
509 * A_CPU activity is always collected, hence its number of items is
510 * always counted (in sa_sys_init()).
512 file_hdr.sa_last_cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
514 /* Get system name, release number, hostname and machine architecture */
516 strncpy(file_hdr.sa_sysname, header.sysname, UTSNAME_LEN);
517 file_hdr.sa_sysname[UTSNAME_LEN - 1] = '\0';
518 strncpy(file_hdr.sa_nodename, header.nodename, UTSNAME_LEN);
519 file_hdr.sa_nodename[UTSNAME_LEN - 1] = '\0';
520 strncpy(file_hdr.sa_release, header.release, UTSNAME_LEN);
521 file_hdr.sa_release[UTSNAME_LEN - 1] = '\0';
522 strncpy(file_hdr.sa_machine, header.machine, UTSNAME_LEN);
523 file_hdr.sa_machine[UTSNAME_LEN - 1] = '\0';
525 /* Write file header */
526 if (write_all(fd, &file_hdr, FILE_HEADER_SIZE) != FILE_HEADER_SIZE)
529 /* Write activity list */
530 for (i = 0; i < NR_ACT; i++) {
533 * Activity sequence given by id_seq array.
534 * Sequence must be the same for stdout as for output file.
538 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
541 if (IS_COLLECTED(act[p]->options)) {
542 file_act.id = act[p]->id;
543 file_act.magic = act[p]->magic;
544 file_act.nr = act[p]->nr;
545 file_act.nr2 = act[p]->nr2;
546 file_act.size = act[p]->fsize;
548 if (write_all(fd, &file_act, FILE_ACTIVITY_SIZE)
549 != FILE_ACTIVITY_SIZE)
552 /* Create sequence of volatile activities */
553 if (IS_VOLATILE(act[p]->options)) {
554 vol_id_seq[i] = act[p]->id;
563 fprintf(stderr, _("Cannot write system activity file header: %s\n"),
569 ***************************************************************************
570 * Write the volatile activity structures following each restart mark.
571 * sa_vol_act_nr structures have to be written.
572 * Note that volatile activities written after the restart marks may be
573 * different within the same file if different versions of sysstat have been
574 * used to create the file and then to append data to it.
577 * @ofd Output file descriptor.
578 ***************************************************************************
580 void write_vol_act_structures(int ofd)
582 struct file_activity file_act;
585 memset(&file_act, 0, FILE_ACTIVITY_SIZE);
587 for (i = 0; i < file_hdr.sa_vol_act_nr; i++) {
589 if (!vol_id_seq[i]) {
591 * Write an empty structure when current sysstat
592 * version knows fewer volatile activities than
593 * the number saved in file's header.
595 file_act.id = file_act.nr = 0;
598 p = get_activity_position(act, vol_id_seq[i], EXIT_IF_NOT_FOUND);
601 * All the fields in file_activity structure are not used.
602 * In particular, act[p]->nr2 is left unmodified.
604 file_act.id = act[p]->id;
605 file_act.nr = act[p]->nr;
608 if (write_all(ofd, &file_act, FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
615 ***************************************************************************
616 * sadc called with interval and count parameters not set:
617 * Write a dummy record notifying a system restart, or insert a comment in
618 * binary data file if option -C has been used.
619 * Writing a dummy record should typically be done at boot time,
620 * before the cron daemon is started to avoid conflict with sa1/sa2 scripts.
623 * @ofd Output file descriptor.
624 * @rtype Record type to write (dummy or comment).
625 ***************************************************************************
627 void write_special_record(int ofd, int rtype)
629 struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
631 /* Check if file is locked */
632 if (!FILE_LOCKED(flags)) {
633 ask_for_flock(ofd, FATAL);
636 /* Reset the structure (not compulsory, but a bit cleaner) */
637 memset(&record_hdr, 0, RECORD_HEADER_SIZE);
639 /* Set record type */
640 record_hdr.record_type = rtype;
643 record_hdr.ust_time = get_time(&rectime, 0);
645 record_hdr.hour = rectime.tm_hour;
646 record_hdr.minute = rectime.tm_min;
647 record_hdr.second = rectime.tm_sec;
649 /* Write record now */
650 if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
654 if (rtype == R_RESTART) {
655 /* Also write the volatile activities structures */
656 write_vol_act_structures(ofd);
658 else if (rtype == R_COMMENT) {
659 /* Also write the comment */
660 if (write_all(ofd, comment, MAX_COMMENT_LEN) != MAX_COMMENT_LEN) {
667 ***************************************************************************
668 * Write stats (or print them if stdout).
671 * @ofd Output file descriptor. May be stdout.
672 ***************************************************************************
674 void write_stats(int ofd)
678 /* Try to lock file */
679 if (!FILE_LOCKED(flags)) {
680 if (ask_for_flock(ofd, NON_FATAL))
682 * Unable to lock file:
683 * Wait for next iteration to try again to save data.
688 /* Write record header */
689 if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
693 /* Then write all statistics */
694 for (i = 0; i < NR_ACT; i++) {
698 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
701 if (IS_COLLECTED(act[p]->options)) {
702 if (write_all(ofd, act[p]->_buf0, act[p]->fsize * act[p]->nr * act[p]->nr2) !=
703 (act[p]->fsize * act[p]->nr * act[p]->nr2)) {
711 ***************************************************************************
712 * Rewrite file's header. Done when number of CPU has changed.
715 * @ofd Output file descriptor.
716 * @fpos Position in file where header structure has to be written.
717 * @file_magic File magic structure.
718 ***************************************************************************
720 void rewrite_file_hdr(int *ofd, off_t fpos, struct file_magic *file_magic)
724 /* Remove O_APPEND status flag */
725 if (fcntl(*ofd, F_SETFL, 0) < 0) {
730 /* Now rewrite file's header with its new CPU number value */
731 if (lseek(*ofd, fpos, SEEK_SET) < fpos) {
736 n = MINIMUM(file_magic->header_size, FILE_HEADER_SIZE);
737 if (write_all(*ofd, &file_hdr, n) != n) {
741 /* Restore O_APPEND status flag */
742 if (fcntl(*ofd, F_SETFL, O_APPEND) < 0) {
749 ***************************************************************************
750 * Create a system activity daily data file.
753 * @ofile Name of output file.
756 * @ofd Output file descriptor.
757 ***************************************************************************
759 void create_sa_file(int *ofd, char *ofile)
761 if ((*ofd = open(ofile, O_CREAT | O_WRONLY,
762 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
763 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
767 /* Try to lock file */
768 ask_for_flock(*ofd, FATAL);
771 if (ftruncate(*ofd, 0) < 0) {
772 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
776 /* Write file header */
777 setup_file_hdr(*ofd);
781 ***************************************************************************
782 * Get descriptor for stdout.
785 * @stdfd A value >= 0 indicates that stats data should also
786 * be written to stdout.
789 * @stdfd Stdout file descriptor.
790 ***************************************************************************
792 void open_stdout(int *stdfd)
795 if ((*stdfd = dup(STDOUT_FILENO)) < 0) {
799 /* Write file header on STDOUT */
800 setup_file_hdr(*stdfd);
805 ***************************************************************************
806 * Get descriptor for output file and write its header.
807 * We may enter this function several times (when we rotate a file).
810 * @ofile Name of output file.
811 * @restart_mark TRUE if sadc called with interval (and count) not
812 * set, and no comment given (so we are going to insert
813 * a restart mark into the file).
816 * @ofd Output file descriptor.
817 ***************************************************************************
819 void open_ofile(int *ofd, char ofile[], int restart_mark)
821 struct file_magic file_magic;
822 struct file_activity file_act[NR_ACT];
823 struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
832 /* Try to open file and check that data can be appended to it */
833 if ((*ofd = open(ofile, O_APPEND | O_RDWR)) < 0) {
834 if (errno == ENOENT) {
835 /* File doesn't exist: Create it */
836 create_sa_file(ofd, ofile);
839 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
843 /* Read file magic header */
844 sz = read(*ofd, &file_magic, FILE_MAGIC_SIZE);
847 /* This is an empty file: Create it again */
848 create_sa_file(ofd, ofile);
851 if ((sz != FILE_MAGIC_SIZE) ||
852 (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
853 (file_magic.format_magic != FORMAT_MAGIC) ||
854 (file_magic.header_size > MAX_FILE_HEADER_SIZE)) {
855 if (FORCE_FILE(flags)) {
857 /* -F option used: Truncate file */
858 create_sa_file(ofd, ofile);
861 /* Display error message and exit */
862 handle_invalid_sa_file(ofd, &file_magic, ofile, sz);
865 SREALLOC(buffer, char, file_magic.header_size);
868 * Save current file position.
869 * Needed later to update sa_last_cpu_nr.
871 if ((fpos = lseek(*ofd, 0, SEEK_CUR)) < 0) {
876 /* Read file standard header */
877 n = read(*ofd, buffer, file_magic.header_size);
878 memcpy(&file_hdr, buffer, MINIMUM(file_magic.header_size, FILE_HEADER_SIZE));
881 if (n != file_magic.header_size) {
882 /* Display error message and exit */
883 handle_invalid_sa_file(ofd, &file_magic, ofile, 0);
887 * If we are using the standard daily data file (file specified
888 * as "-" on the command line) and it is from a past month,
889 * then overwrite (truncate) it.
891 get_time(&rectime, 0);
893 if (((file_hdr.sa_month != rectime.tm_mon) ||
894 (file_hdr.sa_year != rectime.tm_year)) &&
895 WANT_SA_ROTAT(flags)) {
897 create_sa_file(ofd, ofile);
901 /* OK: It's a true system activity file */
902 if (!file_hdr.sa_act_nr || (file_hdr.sa_act_nr > NR_ACT) ||
903 (file_hdr.sa_vol_act_nr > NR_ACT))
905 * No activities at all or at least one unknown activity,
906 * or too many volatile activities:
907 * Cannot append data to such a file.
911 for (i = 0; i < file_hdr.sa_act_nr; i++) {
913 /* Read current activity in list */
914 if (read(*ofd, &file_act[i], FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
915 handle_invalid_sa_file(ofd, &file_magic, ofile, 0);
918 p = get_activity_position(act, file_act[i].id, RESUME_IF_NOT_FOUND);
920 if ((p < 0) || (act[p]->fsize != file_act[i].size) ||
921 (act[p]->magic != file_act[i].magic))
923 * Unknown activity in list or item size has changed or
924 * unknown activity format: Cannot append data to such a file.
928 if ((file_act[i].nr <= 0) || (file_act[i].nr2 <= 0) ||
929 (file_act[i].nr > act[p]->nr_max) ||
930 (file_act[i].nr2 > NR2_MAX)) {
932 * Number of items and subitems should never be zero (or negative)
933 * or greater than their upper limit.
940 * OK: (Almost) all tests successfully passed.
941 * List of activities from the file prevails over that of the user.
942 * So unselect all of them. And reset activity sequence.
944 for (i = 0; i < NR_ACT; i++) {
945 act[i]->options &= ~AO_COLLECTED;
951 for (i = 0; i < file_hdr.sa_act_nr; i++) {
953 p = get_activity_position(act, file_act[i].id, EXIT_IF_NOT_FOUND);
956 * Force number of items (serial lines, network interfaces...)
957 * and sub-items to that of the file, and reallocate structures.
958 * Exceptions are volatile activities, for which number of items
959 * is kept unmodified unless its value was zero (in this case,
960 * it is also forced to the value of the file).
961 * Also keep in mind that the file cannot contain more than
962 * sa_vol_act_nr volatile activities.
964 if (!IS_VOLATILE(act[p]->options) || !act[p]->nr || (j >= file_hdr.sa_vol_act_nr)) {
965 act[p]->nr = file_act[i].nr;
968 vol_id_seq[j++] = file_act[i].id;
970 act[p]->nr2 = file_act[i].nr2;
971 SREALLOC(act[p]->_buf0, void,
972 (size_t) act[p]->msize * (size_t) act[p]->nr * (size_t) act[p]->nr2);
974 /* Save activity sequence */
975 id_seq[i] = file_act[i].id;
976 act[p]->options |= AO_COLLECTED;
979 while (j < file_hdr.sa_vol_act_nr) {
983 p = get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND);
984 if (!IS_COLLECTED(act[p]->options)) {
985 /* A_CPU activity should always exist in file */
989 if (act[p]->nr != file_hdr.sa_last_cpu_nr) {
992 * We are inserting a restart mark, and current machine
993 * has a different number of CPU than that saved in file,
994 * so update sa_last_cpu_nr in file's header and rewrite it.
996 file_hdr.sa_last_cpu_nr = act[p]->nr;
997 rewrite_file_hdr(ofd, fpos, &file_magic);
1001 * Current number of cpu items (for current system)
1002 * doesn't match number of cpu items of the last sample
1014 if (FORCE_FILE(flags)) {
1016 create_sa_file(ofd, ofile);
1019 fprintf(stderr, _("Cannot append data to that file (%s)\n"), ofile);
1025 ***************************************************************************
1026 * Read statistics from various system files.
1027 ***************************************************************************
1029 void read_stats(void)
1032 __nr_t cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
1035 * Init uptime0. So if /proc/uptime cannot fill it,
1036 * this will be done by /proc/stat.
1037 * If cpu_nr = 2, force /proc/stat to fill it.
1038 * If cpu_nr = 1, uptime0 and uptime are equal.
1039 * NB: uptime0 is always filled.
1040 * Remember that cpu_nr = 1 means one CPU and no SMP kernel
1041 * (one structure for CPU "all") and cpu_nr = 2 means one CPU
1042 * and an SMP kernel (two structures for CPUs "all" and "0").
1044 record_hdr.uptime0 = 0;
1046 read_uptime(&(record_hdr.uptime0));
1049 for (i = 0; i < NR_ACT; i++) {
1050 if (IS_COLLECTED(act[i]->options)) {
1051 /* Read statistics for current activity */
1052 (*act[i]->f_read)(act[i]);
1058 * uptime has been filled by read_uptime()
1059 * or when reading CPU stats from /proc/stat.
1061 record_hdr.uptime0 = record_hdr.uptime;
1066 ***************************************************************************
1067 * Main loop: Read stats from the relevant sources and display them.
1070 * @count Number of lines of stats to display.
1071 * @stdfd Stdout file descriptor.
1072 * @ofd Output file descriptor.
1073 * @ofile Name of output file.
1074 * @sa_dir If not an empty string, contains the alternate location of
1076 ***************************************************************************
1078 void rw_sa_stat_loop(long count, int stdfd, int ofd, char ofile[],
1081 int do_sa_rotat = 0;
1082 unsigned int save_flags;
1083 char new_ofile[MAX_FILE_LEN] = "";
1084 struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
1086 /* Set a handler for SIGINT */
1087 memset(&int_act, 0, sizeof(int_act));
1088 int_act.sa_handler = int_handler;
1089 sigaction(SIGINT, &int_act, NULL);
1095 * Init all structures.
1096 * Exception for individual CPUs structures which must not be
1097 * init'ed to keep values for CPU before they were disabled.
1102 record_hdr.ust_time = get_time(&rectime, 0);
1103 record_hdr.hour = rectime.tm_hour;
1104 record_hdr.minute = rectime.tm_min;
1105 record_hdr.second = rectime.tm_sec;
1107 /* Set record type */
1109 record_hdr.record_type = R_LAST_STATS;
1112 record_hdr.record_type = R_STATS;
1115 /* Read then write stats */
1120 flags &= ~S_F_LOCK_FILE;
1125 /* If the record type was R_LAST_STATS, tag it R_STATS before writing it */
1126 record_hdr.record_type = R_STATS;
1133 * Stats are written at the end of previous file *and* at the
1134 * beginning of the new one (outfile must have been specified
1135 * as '-' on the command line).
1137 do_sa_rotat = FALSE;
1139 if (fdatasync(ofd) < 0) {
1140 /* Flush previous file */
1141 perror("fdatasync");
1145 strcpy(ofile, new_ofile);
1147 /* Recalculate number of system items and reallocate structures */
1151 * Open and init new file.
1152 * This is also used to set activity sequence to that of the file
1153 * if the file already exists.
1155 open_ofile(&ofd, ofile, FALSE);
1158 * Rewrite header and activity sequence to stdout since
1159 * number of items may have changed.
1162 setup_file_hdr(stdfd);
1165 /* Write stats to file again */
1177 /* Wait for a signal (probably SIGALRM or SIGINT) */
1182 /* SIGINT caught: Stop now */
1185 /* Rotate activity file if necessary */
1186 if (WANT_SA_ROTAT(flags)) {
1187 /* The user specified '-' as the filename to use */
1188 strncpy(new_ofile, sa_dir, MAX_FILE_LEN - 1);
1189 new_ofile[MAX_FILE_LEN - 1] = '\0';
1190 set_default_file(new_ofile, 0, USE_SA_YYYYMMDD(flags));
1192 if (strcmp(ofile, new_ofile)) {
1199 /* Close file descriptors if they have actually been used */
1205 ***************************************************************************
1206 * Main entry to the program.
1207 ***************************************************************************
1209 int main(int argc, char **argv)
1212 char ofile[MAX_FILE_LEN], sa_dir[MAX_FILE_LEN];
1213 int stdfd = 0, ofd = -1;
1220 /* Compute page shift in kB */
1223 ofile[0] = sa_dir[0] = comment[0] = '\0';
1226 /* Initialize sensors, let it use the default cfg file */
1227 int err = sensors_init(NULL);
1229 fprintf(stderr, "sensors_init: %s\n", sensors_strerror(err));
1231 #endif /* HAVE_SENSORS */
1234 /* Init National Language Support */
1238 while (++opt < argc) {
1240 if (!strcmp(argv[opt], "-S")) {
1242 parse_sadc_S_option(argv, opt);
1249 else if (!strcmp(argv[opt], "-D")) {
1250 flags |= S_F_SA_YYYYMMDD;
1253 else if (!strcmp(argv[opt], "-F")) {
1254 flags |= S_F_FORCE_FILE;
1257 else if (!strcmp(argv[opt], "-L")) {
1258 flags |= S_F_LOCK_FILE;
1261 else if (!strcmp(argv[opt], "-V")) {
1265 else if (!strcmp(argv[opt], "-z")) {
1266 /* Set by sar command */
1270 else if (!strcmp(argv[opt], "-C")) {
1272 strncpy(comment, argv[opt], MAX_COMMENT_LEN);
1273 comment[MAX_COMMENT_LEN - 1] = '\0';
1274 if (!strlen(comment)) {
1283 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1284 if (ofile[0] || WANT_SA_ROTAT(flags)) {
1285 /* Outfile already specified */
1288 stdfd = -1; /* Don't write to STDOUT */
1289 if (!strcmp(argv[opt], "-")) {
1290 /* File name set to '-' */
1291 flags |= S_F_SA_ROTAT;
1293 else if (!strncmp(argv[opt], "-", 1)) {
1298 /* Write data to file */
1299 strncpy(ofile, argv[opt], MAX_FILE_LEN);
1300 ofile[MAX_FILE_LEN - 1] = '\0';
1304 else if (!interval) {
1306 interval = atol(argv[opt]);
1313 else if (count <= 0) {
1314 /* Get count value */
1315 count = atol(argv[opt]);
1326 /* Process file entered on the command line */
1327 if (WANT_SA_ROTAT(flags)) {
1328 /* File name set to '-' */
1329 set_default_file(ofile, 0, USE_SA_YYYYMMDD(flags));
1331 else if (ofile[0]) {
1333 * A file (or directory) has been explicitly entered
1334 * on the command line.
1335 * Should ofile be a directory, it will be the alternate
1336 * location for sa files. So save it.
1338 strcpy(sa_dir, ofile);
1339 /* Check if this is an alternate directory for sa files */
1340 if (check_alt_sa_dir(ofile, 0, USE_SA_YYYYMMDD(flags))) {
1342 * Yes, it was a directory.
1343 * ofile now contains the full path to current
1344 * standard daily data file.
1346 flags |= S_F_SA_ROTAT;
1349 /* No: So we can clear sa_dir */
1355 * If option -z used, write to STDOUT even if a filename
1356 * has been entered on the command line.
1363 /* -L option ignored when writing to STDOUT */
1364 flags &= ~S_F_LOCK_FILE;
1367 /* Init structures according to machine architecture */
1370 if (!interval && !comment[0]) {
1372 * Interval (and count) not set, and no comment given
1373 * => We are going to insert a restart mark.
1375 restart_mark = TRUE;
1378 restart_mark = FALSE;
1382 * Open output file then STDOUT. Write header for each of them.
1383 * NB: Output file must be opened first, because we may change
1384 * the activities collected AND the activity sequence to that
1385 * of the file, and the activities collected and activity sequence
1386 * written on STDOUT must be consistent to those of the file.
1388 open_ofile(&ofd, ofile, restart_mark);
1389 open_stdout(&stdfd);
1394 * Interval (and count) not set:
1395 * Write a dummy record, or insert a comment, then exit.
1396 * NB: Never write such a dummy record on stdout since
1397 * sar never expects it.
1400 write_special_record(ofd, R_COMMENT);
1403 write_special_record(ofd, R_RESTART);
1406 /* Close file descriptor */
1410 /* Free structures */
1415 /* Set a handler for SIGALRM */
1416 memset(&alrm_act, 0, sizeof(alrm_act));
1417 alrm_act.sa_handler = alarm_handler;
1418 sigaction(SIGALRM, &alrm_act, NULL);
1422 rw_sa_stat_loop(count, stdfd, ofd, ofile, sa_dir);
1425 /* Cleanup sensors */
1427 #endif /* HAVE_SENSORS */
1429 /* Free structures */