]> granicus.if.org Git - sysstat/blob - sadc.c
Fix #125: pidstat omits kernel threads when using -l option
[sysstat] / sadc.c
1 /*
2  * sadc: system activity data collector
3  * (C) 1999-2016 by Sebastien GODARD (sysstat <at> orange.fr)
4  *
5  ***************************************************************************
6  * This program is free software; you can redistribute it and/or modify it *
7  * under the terms of the GNU General Public License as published  by  the *
8  * Free Software Foundation; either version 2 of the License, or (at  your *
9  * option) any later version.                                              *
10  *                                                                         *
11  * This program is distributed in the hope that it  will  be  useful,  but *
12  * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
14  * for more details.                                                       *
15  *                                                                         *
16  * You should have received a copy of the GNU General Public License along *
17  * with this program; if not, write to the Free Software Foundation, Inc., *
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
19  ***************************************************************************
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <time.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <dirent.h>
32 #include <sys/file.h>
33 #include <sys/stat.h>
34 #include <sys/utsname.h>
35
36 #include "version.h"
37 #include "sa.h"
38 #include "rd_stats.h"
39 #include "common.h"
40 #include "ioconf.h"
41
42 #ifdef USE_NLS
43 #include <locale.h>
44 #include <libintl.h>
45 #define _(string) gettext(string)
46 #else
47 #define _(string) (string)
48 #endif
49
50 #ifdef HAVE_SENSORS
51 #include "sensors/sensors.h"
52 #include "sensors/error.h"
53 #endif
54
55 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
56 char *sccsid(void) { return (SCCSID); }
57
58 long interval = 0;
59 unsigned int flags = 0;
60
61 int dis;
62 int optz = 0;
63 char timestamp[2][TIMESTAMP_LEN];
64
65 struct file_header file_hdr;
66 struct record_header record_hdr;
67
68 char comment[MAX_COMMENT_LEN];
69
70 unsigned int id_seq[NR_ACT];
71 unsigned int vol_id_seq[NR_ACT];
72
73 extern struct activity *act[];
74 extern __nr_t (*f_count[]) (struct activity *);
75
76 struct sigaction alrm_act, int_act;
77 int sigint_caught = 0;
78
79 /*
80  ***************************************************************************
81  * Print usage and exit.
82  *
83  * IN:
84  * @progname    Name of sysstat command
85  ***************************************************************************
86  */
87 void usage(char *progname)
88 {
89         fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ <outfile> ]\n"),
90                 progname);
91
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"));
95         exit(1);
96 }
97
98 /*
99  ***************************************************************************
100  * Collect all activities belonging to a group.
101  *
102  * IN:
103  * @group_id    Group identification number.
104  * @opt_f       Optionnal flag to set.
105  ***************************************************************************
106  */
107 void collect_group_activities(unsigned int group_id, unsigned int opt_f)
108 {
109         int i;
110
111         for (i = 0; i < NR_ACT; i++) {
112                 if (act[i]->group & group_id) {
113                         act[i]->options |= AO_COLLECTED;
114                         if (opt_f) {
115                                 act[i]->opt_flags |= opt_f;
116                         }
117                 }
118         }
119 }
120
121 /*
122  ***************************************************************************
123  * Parse option -S, indicating which activities are to be collected.
124  *
125  * IN:
126  * @argv        Arguments list.
127  * @opt         Index in list of arguments.
128  ***************************************************************************
129  */
130 void parse_sadc_S_option(char *argv[], int opt)
131 {
132         char *p;
133         int i;
134
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);
139                 }
140                 else if (!strcmp(p, K_DISK)) {
141                         /* Select group of disk activities */
142                         collect_group_activities(G_DISK, AO_F_NULL);
143                 }
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);
147                 }
148                 else if (!strcmp(p, K_SNMP)) {
149                         /* Select group of SNMP activities */
150                         collect_group_activities(G_SNMP, AO_F_NULL);
151                 }
152                 else if (!strcmp(p, K_IPV6)) {
153                         /* Select group of IPv6 activities */
154                         collect_group_activities(G_IPV6, AO_F_NULL);
155                 }
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);
159                 }
160                 else if (!strcmp(p, K_ALL) || !strcmp(p, K_XALL)) {
161                         /* Select all activities */
162                         for (i = 0; i < NR_ACT; i++) {
163
164                                 if (!strcmp(p, K_ALL) && (act[i]->group & G_XDISK))
165                                         /*
166                                          * Don't select G_XDISK activities
167                                          * when option -S ALL is used.
168                                          */
169                                         continue;
170
171                                 act[i]->options |= AO_COLLECTED;
172                         }
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);
176                         }
177                 }
178                 else if (strspn(p, DIGITS) == strlen(p)) {
179                         /*
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
184                          * of activities.
185                          */
186                         int act_id;
187
188                         act_id = atoi(p);
189                         if (act_id > 255) {
190                                 act_id >>= 8;
191                                 for (i = 0; i < NR_ACT; i++) {
192                                         if (act[i]->group & act_id) {
193                                                 act[i]->options |= AO_COLLECTED;
194                                         }
195                                 }
196                         }
197                         else if ((act_id < 0) || (act_id > NR_ACT)) {
198                                 usage(argv[0]);
199                         }
200                         else if (!act_id) {
201                                 /* Unselect all activities but CPU */
202                                 for (i = 0; i < NR_ACT; i++) {
203                                         act[i]->options &= ~AO_COLLECTED;
204                                 }
205                                 COLLECT_ACTIVITY(A_CPU);
206                         }
207                         else {
208                                 /* Select chosen activity */
209                                 COLLECT_ACTIVITY(act_id);
210                         }
211                 }
212                 else {
213                         usage(argv[0]);
214                 }
215         }
216 }
217
218 /*
219  ***************************************************************************
220  * SIGALRM signal handler. No need to reset handler here.
221  *
222  * IN:
223  * @sig Signal number.
224  ***************************************************************************
225  */
226 void alarm_handler(int sig)
227 {
228         alarm(interval);
229 }
230
231 /*
232  ***************************************************************************
233  * SIGINT signal handler.
234  *
235  * IN:
236  * @sig Signal number.
237  ***************************************************************************
238  */
239 void int_handler(int sig)
240 {
241         pid_t ppid = getppid();
242
243         sigint_caught = 1;
244
245         if (!optz || (ppid == 1)) {
246                 /* sadc hasn't been called by sar or sar process is already dead */
247                 exit(1);
248         }
249
250         /*
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
253          * can be displayed.
254          */
255         if (kill(ppid, SIGINT) < 0) {
256                 exit(1);
257         }
258 }
259
260 /*
261  ***************************************************************************
262  * Display an error message.
263  ***************************************************************************
264  */
265 void p_write_error(void)
266 {
267         fprintf(stderr, _("Cannot write data to system activity file: %s\n"),
268                 strerror(errno));
269         exit(2);
270 }
271
272 /*
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  ***************************************************************************
280  */
281 void reset_stats(void)
282 {
283         int i;
284
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);
289                 }
290         }
291 }
292
293 /*
294  ***************************************************************************
295  * Allocate and init structures, according to system state.
296  ***************************************************************************
297  */
298 void sa_sys_init(void)
299 {
300         int i, idx;
301         __nr_t f_count_results[NR_F_COUNT];
302
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;
306         }
307
308         for (i = 0; i < NR_ACT; i++) {
309
310                 idx = act[i]->f_count_index;
311
312                 if (idx >= 0) {
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];
316                         }
317                         else {
318                                 act[i]->nr = (f_count[idx])(act[i]);
319                                 f_count_results[idx] = act[i]->nr;
320                         }
321                 }
322
323                 if (act[i]->nr > 0) {
324                         if (act[i]->f_count2) {
325                                 act[i]->nr2 = (*act[i]->f_count2)(act[i]);
326                         }
327                         /* else act[i]->nr2 is a constant and doesn't need to be calculated */
328
329                         if (!act[i]->nr2) {
330                                 act[i]->nr = 0;
331                         }
332                 }
333
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);
338                 }
339                 else {
340                         /* No items found: Invalidate current activity */
341                         act[i]->options &= ~AO_COLLECTED;
342                 }
343
344                 /* Set default activity list */
345                 id_seq[i] = act[i]->id;
346         }
347 }
348
349 /*
350  ***************************************************************************
351  * Free structures.
352  ***************************************************************************
353  */
354 void sa_sys_free(void)
355 {
356         int i;
357
358         for (i = 0; i < NR_ACT; i++) {
359
360                 if (act[i]->nr > 0) {
361                         if (act[i]->_buf0) {
362                                 free(act[i]->_buf0);
363                                 act[i]->_buf0 = NULL;
364                         }
365                 }
366         }
367 }
368
369 /*
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.
373  *
374  * IN:
375  * @fd          Output file descriptor.
376  * @buf         Data buffer.
377  * @nr_bytes    Number of bytes to write.
378  *
379  * RETURNS:
380  * Number of bytes written to file, or -1 on error.
381  ***************************************************************************
382  */
383 int write_all(int fd, const void *buf, int nr_bytes)
384 {
385         int block, offset = 0;
386         char *buffer = (char *) buf;
387
388         while (nr_bytes > 0) {
389                 block = write(fd, &buffer[offset], nr_bytes);
390
391                 if (block < 0) {
392                         if (errno == EINTR)
393                                 continue;
394                         return block;
395                 }
396                 if (block == 0)
397                         return offset;
398
399                 offset += block;
400                 nr_bytes -= block;
401         }
402
403         return offset;
404 }
405
406 /*
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.
411  *
412  * IN:
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
416  *              try again.
417  *
418  * RETURNS:
419  * 0 on success, or 1 if file couldn't be locked.
420  ***************************************************************************
421  */
422 int ask_for_flock(int fd, int fatal)
423 {
424         /* Option -L may be used only if an outfile was specified on the command line */
425         if (LOCK_FILE(flags)) {
426                 /*
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.
431                  */
432                 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
433                         if ((((errno == EWOULDBLOCK) || (errno == EAGAIN)) && (fatal == FATAL)) ||
434                             ((errno != EWOULDBLOCK) && (errno != EAGAIN))) {
435                                 perror("flock");
436                                 exit(1);
437                         }
438                         /* Was unable to lock file: Lock would have blocked... */
439                         return 1;
440                 }
441                 else {
442                         /* File successfully locked */
443                         flags |= S_F_FILE_LOCKED;
444                 }
445         }
446         return 0;
447 }
448
449 /*
450  ***************************************************************************
451  * Fill system activity file magic header.
452  *
453  * IN:
454  * @file_magic  System activity file magic header.
455  ***************************************************************************
456  */
457 void fill_magic_header(struct file_magic *file_magic)
458 {
459         memset(file_magic, 0, FILE_MAGIC_SIZE);
460
461         file_magic->header_size = FILE_HEADER_SIZE;
462
463         file_magic->sysstat_magic = SYSSTAT_MAGIC;
464         file_magic->format_magic  = FORMAT_MAGIC;
465
466         enum_version_nr(file_magic);
467 }
468
469 /*
470  ***************************************************************************
471  * Fill system activity file header, then write it (or print it if stdout).
472  *
473  * IN:
474  * @fd  Output file descriptor. May be stdout.
475  ***************************************************************************
476  */
477 void setup_file_hdr(int fd)
478 {
479         int i, p;
480         struct tm rectime;
481         struct utsname header;
482         struct file_magic file_magic;
483         struct file_activity file_act;
484
485         /* Fill then write file magic header */
486         fill_magic_header(&file_magic);
487
488         if (write_all(fd, &file_magic, FILE_MAGIC_SIZE) != FILE_MAGIC_SIZE)
489                 goto write_error;
490
491         /* First reset the structure */
492         memset(&file_hdr, 0, FILE_HEADER_SIZE);
493
494         /* Then get current date */
495         file_hdr.sa_ust_time = get_time(&rectime, 0);
496
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,
500                                                   COUNT_ACTIVITIES);
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);
505
506         /*
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()).
511          */
512         file_hdr.sa_last_cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
513
514         /* Get system name, release number, hostname and machine architecture */
515         uname(&header);
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';
524
525         /* Write file header */
526         if (write_all(fd, &file_hdr, FILE_HEADER_SIZE) != FILE_HEADER_SIZE)
527                 goto write_error;
528
529         /* Write activity list */
530         for (i = 0; i < NR_ACT; i++) {
531
532                 /*
533                  * Activity sequence given by id_seq array.
534                  * Sequence must be the same for stdout as for output file.
535                  */
536                 if (!id_seq[i])
537                         continue;
538                 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
539                         continue;
540
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;
547
548                         if (write_all(fd, &file_act, FILE_ACTIVITY_SIZE)
549                             != FILE_ACTIVITY_SIZE)
550                                 goto write_error;
551
552                         /* Create sequence of volatile activities */
553                         if (IS_VOLATILE(act[p]->options)) {
554                                 vol_id_seq[i] = act[p]->id;
555                         }
556                 }
557         }
558
559         return;
560
561 write_error:
562
563         fprintf(stderr, _("Cannot write system activity file header: %s\n"),
564                 strerror(errno));
565         exit(2);
566 }
567
568 /*
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.
575  *
576  * IN:
577  * @ofd         Output file descriptor.
578  ***************************************************************************
579  */
580 void write_vol_act_structures(int ofd)
581 {
582         struct file_activity file_act;
583         int i, p;
584
585         memset(&file_act, 0, FILE_ACTIVITY_SIZE);
586
587         for (i = 0; i < file_hdr.sa_vol_act_nr; i++) {
588
589                 if (!vol_id_seq[i]) {
590                         /*
591                          * Write an empty structure when current sysstat
592                          * version knows fewer volatile activities than
593                          * the number saved in file's header.
594                          */
595                         file_act.id = file_act.nr = 0;
596                 }
597                 else {
598                         p = get_activity_position(act, vol_id_seq[i], EXIT_IF_NOT_FOUND);
599
600                         /*
601                          * All the fields in file_activity structure are not used.
602                          * In particular, act[p]->nr2 is left unmodified.
603                          */
604                         file_act.id = act[p]->id;
605                         file_act.nr = act[p]->nr;
606                 }
607
608                 if (write_all(ofd, &file_act, FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
609                         p_write_error();
610                 }
611         }
612 }
613
614 /*
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.
621  *
622  * IN:
623  * @ofd         Output file descriptor.
624  * @rtype       Record type to write (dummy or comment).
625  ***************************************************************************
626  */
627 void write_special_record(int ofd, int rtype)
628 {
629         struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
630
631         /* Check if file is locked */
632         if (!FILE_LOCKED(flags)) {
633                 ask_for_flock(ofd, FATAL);
634         }
635
636         /* Reset the structure (not compulsory, but a bit cleaner) */
637         memset(&record_hdr, 0, RECORD_HEADER_SIZE);
638
639         /* Set record type */
640         record_hdr.record_type = rtype;
641
642         /* Save time */
643         record_hdr.ust_time = get_time(&rectime, 0);
644
645         record_hdr.hour   = rectime.tm_hour;
646         record_hdr.minute = rectime.tm_min;
647         record_hdr.second = rectime.tm_sec;
648
649         /* Write record now */
650         if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
651                 p_write_error();
652         }
653
654         if (rtype == R_RESTART) {
655                 /* Also write the volatile activities structures */
656                 write_vol_act_structures(ofd);
657         }
658         else if (rtype == R_COMMENT) {
659                 /* Also write the comment */
660                 if (write_all(ofd, comment, MAX_COMMENT_LEN) != MAX_COMMENT_LEN) {
661                         p_write_error();
662                 }
663         }
664 }
665
666 /*
667  ***************************************************************************
668  * Write stats (or print them if stdout).
669  *
670  * IN:
671  * @ofd         Output file descriptor. May be stdout.
672  ***************************************************************************
673  */
674 void write_stats(int ofd)
675 {
676         int i, p;
677
678         /* Try to lock file */
679         if (!FILE_LOCKED(flags)) {
680                 if (ask_for_flock(ofd, NON_FATAL))
681                         /*
682                          * Unable to lock file:
683                          * Wait for next iteration to try again to save data.
684                          */
685                         return;
686         }
687
688         /* Write record header */
689         if (write_all(ofd, &record_hdr, RECORD_HEADER_SIZE) != RECORD_HEADER_SIZE) {
690                 p_write_error();
691         }
692
693         /* Then write all statistics */
694         for (i = 0; i < NR_ACT; i++) {
695
696                 if (!id_seq[i])
697                         continue;
698                 if ((p = get_activity_position(act, id_seq[i], RESUME_IF_NOT_FOUND)) < 0)
699                         continue;
700
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)) {
704                                 p_write_error();
705                         }
706                 }
707         }
708 }
709
710 /*
711  ***************************************************************************
712  * Rewrite file's header. Done when number of CPU has changed.
713  *
714  * IN:
715  * @ofd         Output file descriptor.
716  * @fpos        Position in file where header structure has to be written.
717  * @file_magic  File magic structure.
718  ***************************************************************************
719  */
720 void rewrite_file_hdr(int *ofd, off_t fpos, struct file_magic *file_magic)
721 {
722         int n;
723
724         /* Remove O_APPEND status flag */
725         if (fcntl(*ofd, F_SETFL, 0) < 0) {
726                 perror("fcntl");
727                 exit(2);
728         }
729
730         /* Now rewrite file's header with its new CPU number value */
731         if (lseek(*ofd, fpos, SEEK_SET) < fpos) {
732                 perror("lseek");
733                 exit(2);
734         }
735
736         n = MINIMUM(file_magic->header_size, FILE_HEADER_SIZE);
737         if (write_all(*ofd, &file_hdr, n) != n) {
738                 p_write_error();
739         }
740
741         /* Restore O_APPEND status flag */
742         if (fcntl(*ofd, F_SETFL, O_APPEND) < 0) {
743                 perror("fcntl");
744                 exit(2);
745         }
746 }
747
748 /*
749  ***************************************************************************
750  * Create a system activity daily data file.
751  *
752  * IN:
753  * @ofile       Name of output file.
754  *
755  * OUT:
756  * @ofd         Output file descriptor.
757  ***************************************************************************
758  */
759 void create_sa_file(int *ofd, char *ofile)
760 {
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));
764                 exit(2);
765         }
766
767         /* Try to lock file */
768         ask_for_flock(*ofd, FATAL);
769
770         /* Truncate file */
771         if (ftruncate(*ofd, 0) < 0) {
772                 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
773                 exit(2);
774         }
775
776         /* Write file header */
777         setup_file_hdr(*ofd);
778 }
779
780 /*
781  ***************************************************************************
782  * Get descriptor for stdout.
783  *
784  * IN:
785  * @stdfd       A value >= 0 indicates that stats data should also
786  *              be written to stdout.
787  *
788  * OUT:
789  * @stdfd       Stdout file descriptor.
790  ***************************************************************************
791  */
792 void open_stdout(int *stdfd)
793 {
794         if (*stdfd >= 0) {
795                 if ((*stdfd = dup(STDOUT_FILENO)) < 0) {
796                         perror("dup");
797                         exit(4);
798                 }
799                 /* Write file header on STDOUT */
800                 setup_file_hdr(*stdfd);
801         }
802 }
803
804 /*
805  ***************************************************************************
806  * Get descriptor for output file and write its header.
807  * We may enter this function several times (when we rotate a file).
808  *
809  * IN:
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).
814  *
815  * OUT:
816  * @ofd                 Output file descriptor.
817  ***************************************************************************
818  */
819 void open_ofile(int *ofd, char ofile[], int restart_mark)
820 {
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};
824         void *buffer = NULL;
825         ssize_t sz, n;
826         off_t fpos;
827         int i, j, p;
828
829         if (!ofile[0])
830                 return;
831
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);
837                         return;
838                 }
839                 fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
840                 exit(2);
841         }
842
843         /* Read file magic header */
844         sz = read(*ofd, &file_magic, FILE_MAGIC_SIZE);
845         if (!sz) {
846                 close(*ofd);
847                 /* This is an empty file: Create it again */
848                 create_sa_file(ofd, ofile);
849                 return;
850         }
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)) {
856                         close(*ofd);
857                         /* -F option used: Truncate file */
858                         create_sa_file(ofd, ofile);
859                         return;
860                 }
861                 /* Display error message and exit */
862                 handle_invalid_sa_file(ofd, &file_magic, ofile, sz);
863         }
864
865         SREALLOC(buffer, char, file_magic.header_size);
866
867         /*
868          * Save current file position.
869          * Needed later to update sa_last_cpu_nr.
870          */
871         if ((fpos = lseek(*ofd, 0, SEEK_CUR)) < 0) {
872                 perror("lseek");
873                 exit(2);
874         }
875
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));
879         free(buffer);
880
881         if (n != file_magic.header_size) {
882                 /* Display error message and exit */
883                 handle_invalid_sa_file(ofd, &file_magic, ofile, 0);
884         }
885
886         /*
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.
890          */
891         get_time(&rectime, 0);
892
893         if (((file_hdr.sa_month != rectime.tm_mon) ||
894             (file_hdr.sa_year != rectime.tm_year)) &&
895             WANT_SA_ROTAT(flags)) {
896                 close(*ofd);
897                 create_sa_file(ofd, ofile);
898                 return;
899         }
900
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))
904                 /*
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.
908                  */
909                 goto append_error;
910
911         for (i = 0; i < file_hdr.sa_act_nr; i++) {
912
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);
916                 }
917
918                 p = get_activity_position(act, file_act[i].id, RESUME_IF_NOT_FOUND);
919
920                 if ((p < 0) || (act[p]->fsize != file_act[i].size) ||
921                     (act[p]->magic != file_act[i].magic))
922                         /*
923                          * Unknown activity in list or item size has changed or
924                          * unknown activity format: Cannot append data to such a file.
925                          */
926                         goto append_error;
927
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)) {
931                         /*
932                          * Number of items and subitems should never be zero (or negative)
933                          * or greater than their upper limit.
934                          */
935                         goto append_error;
936                 }
937         }
938
939         /*
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.
943          */
944         for (i = 0; i < NR_ACT; i++) {
945                 act[i]->options &= ~AO_COLLECTED;
946                 id_seq[i] = 0;
947         }
948
949         j = 0;
950
951         for (i = 0; i < file_hdr.sa_act_nr; i++) {
952
953                 p = get_activity_position(act, file_act[i].id, EXIT_IF_NOT_FOUND);
954
955                 /*
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.
963                  */
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;
966                 }
967                 else {
968                         vol_id_seq[j++] = file_act[i].id;
969                 }
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);
973
974                 /* Save activity sequence */
975                 id_seq[i] = file_act[i].id;
976                 act[p]->options |= AO_COLLECTED;
977         }
978
979         while (j < file_hdr.sa_vol_act_nr) {
980                 vol_id_seq[j++] = 0;
981         }
982
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 */
986                 goto append_error;
987         }
988
989         if (act[p]->nr != file_hdr.sa_last_cpu_nr) {
990                 if (restart_mark) {
991                         /*
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.
995                          */
996                         file_hdr.sa_last_cpu_nr = act[p]->nr;
997                         rewrite_file_hdr(ofd, fpos, &file_magic);
998                 }
999                 else {
1000                         /*
1001                          * Current number of cpu items (for current system)
1002                          * doesn't match number of cpu items of the last sample
1003                          * saved in file.
1004                          */
1005                         goto append_error;
1006                 }
1007         }
1008
1009         return;
1010
1011 append_error:
1012
1013         close(*ofd);
1014         if (FORCE_FILE(flags)) {
1015                 /* Truncate file */
1016                 create_sa_file(ofd, ofile);
1017         }
1018         else {
1019                 fprintf(stderr, _("Cannot append data to that file (%s)\n"), ofile);
1020                 exit(1);
1021         }
1022 }
1023
1024 /*
1025  ***************************************************************************
1026  * Read statistics from various system files.
1027  ***************************************************************************
1028  */
1029 void read_stats(void)
1030 {
1031         int i;
1032         __nr_t cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
1033
1034         /*
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").
1043          */
1044         record_hdr.uptime0 = 0;
1045         if (cpu_nr > 2) {
1046                 read_uptime(&(record_hdr.uptime0));
1047         }
1048
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]);
1053                 }
1054         }
1055
1056         if (cpu_nr == 1) {
1057                 /*
1058                  * uptime has been filled by read_uptime()
1059                  * or when reading CPU stats from /proc/stat.
1060                  */
1061                 record_hdr.uptime0 = record_hdr.uptime;
1062         }
1063 }
1064
1065 /*
1066  ***************************************************************************
1067  * Main loop: Read stats from the relevant sources and display them.
1068  *
1069  * IN:
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
1075  *              daily data files.
1076  ***************************************************************************
1077  */
1078 void rw_sa_stat_loop(long count, int stdfd, int ofd, char ofile[],
1079                      char sa_dir[])
1080 {
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};
1085
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);
1090
1091         /* Main loop */
1092         do {
1093
1094                 /*
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.
1098                  */
1099                 reset_stats();
1100
1101                 /* Save time */
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;
1106
1107                 /* Set record type */
1108                 if (do_sa_rotat) {
1109                         record_hdr.record_type = R_LAST_STATS;
1110                 }
1111                 else {
1112                         record_hdr.record_type = R_STATS;
1113                 }
1114
1115                 /* Read then write stats */
1116                 read_stats();
1117
1118                 if (stdfd >= 0) {
1119                         save_flags = flags;
1120                         flags &= ~S_F_LOCK_FILE;
1121                         write_stats(stdfd);
1122                         flags = save_flags;
1123                 }
1124
1125                 /* If the record type was R_LAST_STATS, tag it R_STATS before writing it */
1126                 record_hdr.record_type = R_STATS;
1127                 if (ofile[0]) {
1128                         write_stats(ofd);
1129                 }
1130
1131                 if (do_sa_rotat) {
1132                         /*
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).
1136                          */
1137                         do_sa_rotat = FALSE;
1138
1139                         if (fdatasync(ofd) < 0) {
1140                                 /* Flush previous file */
1141                                 perror("fdatasync");
1142                                 exit(4);
1143                         }
1144                         close(ofd);
1145                         strcpy(ofile, new_ofile);
1146
1147                         /* Recalculate number of system items and reallocate structures */
1148                         sa_sys_init();
1149
1150                         /*
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.
1154                          */
1155                         open_ofile(&ofd, ofile, FALSE);
1156
1157                         /*
1158                          * Rewrite header and activity sequence to stdout since
1159                          * number of items may have changed.
1160                          */
1161                         if (stdfd >= 0) {
1162                                 setup_file_hdr(stdfd);
1163                         }
1164
1165                         /* Write stats to file again */
1166                         write_stats(ofd);
1167                 }
1168
1169                 /* Flush data */
1170                 fflush(stdout);
1171
1172                 if (count > 0) {
1173                         count--;
1174                 }
1175
1176                 if (count) {
1177                         /* Wait for a signal (probably SIGALRM or SIGINT) */
1178                         pause();
1179                 }
1180
1181                 if (sigint_caught)
1182                         /* SIGINT caught: Stop now */
1183                         break;
1184
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));
1191
1192                         if (strcmp(ofile, new_ofile)) {
1193                                 do_sa_rotat = TRUE;
1194                         }
1195                 }
1196         }
1197         while (count);
1198
1199         /* Close file descriptors if they have actually been used */
1200         CLOSE(stdfd);
1201         CLOSE(ofd);
1202 }
1203
1204 /*
1205  ***************************************************************************
1206  * Main entry to the program.
1207  ***************************************************************************
1208  */
1209 int main(int argc, char **argv)
1210 {
1211         int opt = 0;
1212         char ofile[MAX_FILE_LEN], sa_dir[MAX_FILE_LEN];
1213         int stdfd = 0, ofd = -1;
1214         int restart_mark;
1215         long count = 0;
1216
1217         /* Get HZ */
1218         get_HZ();
1219
1220         /* Compute page shift in kB */
1221         get_kb_shift();
1222
1223         ofile[0] = sa_dir[0] = comment[0] = '\0';
1224
1225 #ifdef HAVE_SENSORS
1226         /* Initialize sensors, let it use the default cfg file */
1227         int err = sensors_init(NULL);
1228         if (err) {
1229                 fprintf(stderr, "sensors_init: %s\n", sensors_strerror(err));
1230         }
1231 #endif /* HAVE_SENSORS */
1232
1233 #ifdef USE_NLS
1234         /* Init National Language Support */
1235         init_nls();
1236 #endif
1237
1238         while (++opt < argc) {
1239
1240                 if (!strcmp(argv[opt], "-S")) {
1241                         if (argv[++opt]) {
1242                                 parse_sadc_S_option(argv, opt);
1243                         }
1244                         else {
1245                                 usage(argv[0]);
1246                         }
1247                 }
1248
1249                 else if (!strcmp(argv[opt], "-D")) {
1250                         flags |= S_F_SA_YYYYMMDD;
1251                 }
1252
1253                 else if (!strcmp(argv[opt], "-F")) {
1254                         flags |= S_F_FORCE_FILE;
1255                 }
1256
1257                 else if (!strcmp(argv[opt], "-L")) {
1258                         flags |= S_F_LOCK_FILE;
1259                 }
1260
1261                 else if (!strcmp(argv[opt], "-V")) {
1262                         print_version();
1263                 }
1264
1265                 else if (!strcmp(argv[opt], "-z")) {
1266                         /* Set by sar command */
1267                         optz = 1;
1268                 }
1269
1270                 else if (!strcmp(argv[opt], "-C")) {
1271                         if (argv[++opt]) {
1272                                 strncpy(comment, argv[opt], MAX_COMMENT_LEN);
1273                                 comment[MAX_COMMENT_LEN - 1] = '\0';
1274                                 if (!strlen(comment)) {
1275                                         usage(argv[0]);
1276                                 }
1277                         }
1278                         else {
1279                                 usage(argv[0]);
1280                         }
1281                 }
1282
1283                 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1284                         if (ofile[0] || WANT_SA_ROTAT(flags)) {
1285                                 /* Outfile already specified */
1286                                 usage(argv[0]);
1287                         }
1288                         stdfd = -1;     /* Don't write to STDOUT */
1289                         if (!strcmp(argv[opt], "-")) {
1290                                 /* File name set to '-' */
1291                                 flags |= S_F_SA_ROTAT;
1292                         }
1293                         else if (!strncmp(argv[opt], "-", 1)) {
1294                                 /* Bad option */
1295                                 usage(argv[0]);
1296                         }
1297                         else {
1298                                 /* Write data to file */
1299                                 strncpy(ofile, argv[opt], MAX_FILE_LEN);
1300                                 ofile[MAX_FILE_LEN - 1] = '\0';
1301                         }
1302                 }
1303
1304                 else if (!interval) {
1305                         /* Get interval */
1306                         interval = atol(argv[opt]);
1307                         if (interval < 1) {
1308                                 usage(argv[0]);
1309                         }
1310                         count = -1;
1311                 }
1312
1313                 else if (count <= 0) {
1314                         /* Get count value */
1315                         count = atol(argv[opt]);
1316                         if (count < 1) {
1317                                 usage(argv[0]);
1318                         }
1319                 }
1320
1321                 else {
1322                         usage(argv[0]);
1323                 }
1324         }
1325
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));
1330         }
1331         else if (ofile[0]) {
1332                 /*
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.
1337                  */
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))) {
1341                         /*
1342                          * Yes, it was a directory.
1343                          * ofile now contains the full path to current
1344                          * standard daily data file.
1345                          */
1346                         flags |= S_F_SA_ROTAT;
1347                 }
1348                 else {
1349                         /* No: So we can clear sa_dir */
1350                         sa_dir[0] = '\0';
1351                 }
1352         }
1353
1354         /*
1355          * If option -z used, write to STDOUT even if a filename
1356          * has been entered on the command line.
1357          */
1358         if (optz) {
1359                 stdfd = 0;
1360         }
1361
1362         if (!ofile[0]) {
1363                 /* -L option ignored when writing to STDOUT */
1364                 flags &= ~S_F_LOCK_FILE;
1365         }
1366
1367         /* Init structures according to machine architecture */
1368         sa_sys_init();
1369
1370         if (!interval && !comment[0]) {
1371                 /*
1372                  * Interval (and count) not set, and no comment given
1373                  * => We are going to insert a restart mark.
1374                  */
1375                 restart_mark = TRUE;
1376         }
1377         else {
1378                 restart_mark = FALSE;
1379         }
1380
1381         /*
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.
1387          */
1388         open_ofile(&ofd, ofile, restart_mark);
1389         open_stdout(&stdfd);
1390
1391         if (!interval) {
1392                 if (ofd >= 0) {
1393                         /*
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.
1398                          */
1399                         if (comment[0]) {
1400                                 write_special_record(ofd, R_COMMENT);
1401                         }
1402                         else {
1403                                 write_special_record(ofd, R_RESTART);
1404                         }
1405
1406                         /* Close file descriptor */
1407                         CLOSE(ofd);
1408                 }
1409
1410                 /* Free structures */
1411                 sa_sys_free();
1412                 exit(0);
1413         }
1414
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);
1419         alarm(interval);
1420
1421         /* Main loop */
1422         rw_sa_stat_loop(count, stdfd, ofd, ofile, sa_dir);
1423
1424 #ifdef HAVE_SENSORS
1425         /* Cleanup sensors */
1426         sensors_cleanup();
1427 #endif /* HAVE_SENSORS */
1428
1429         /* Free structures */
1430         sa_sys_free();
1431
1432         return 0;
1433 }