]> granicus.if.org Git - sysstat/blob - count.c
Update non regression tests
[sysstat] / count.c
1 /*
2  * count.c: Count items for which statistics will be collected.
3  * (C) 1999-2023 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 <errno.h>
26 #include <dirent.h>
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/statvfs.h>
31 #include <unistd.h>
32
33 #include "common.h"
34 #include "rd_stats.h"
35
36 #ifdef USE_NLS
37 #include <locale.h>
38 #include <libintl.h>
39 #define _(string) gettext(string)
40 #else
41 #define _(string) (string)
42 #endif
43
44
45 /*
46  ***************************************************************************
47  * Count number of processors in /sys.
48  *
49  * IN:
50  * @highest     If set to TRUE, then look for the highest processor number.
51  *              This is used when eg. the machine has 4 CPU numbered 0, 1, 4
52  *              and 5. In this case, this procedure will return 6.
53  *
54  * RETURNS:
55  * Number of processors (online and offline).
56  * A value of 0 means that /sys was not mounted.
57  * A value of N (!=0) means N processor(s) (cpu0 .. cpu(N-1)).
58  ***************************************************************************
59  */
60 int get_sys_cpu_nr(int highest)
61 {
62         DIR *dir;
63         struct dirent *drd;
64         struct stat buf;
65         char line[MAX_PF_NAME];
66         int num_proc, proc_nr = -1;
67
68         /* Open relevant /sys directory */
69         if ((dir = opendir(SYSFS_DEVCPU)) == NULL)
70                 return 0;
71
72         /* Get current file entry */
73         while ((drd = readdir(dir)) != NULL) {
74
75                 if (!strncmp(drd->d_name, "cpu", 3) && isdigit(drd->d_name[3])) {
76                         snprintf(line, sizeof(line), "%s/%s", SYSFS_DEVCPU, drd->d_name);
77                         line[sizeof(line) - 1] = '\0';
78                         if (stat(line, &buf) < 0)
79                                 continue;
80                         if (S_ISDIR(buf.st_mode)) {
81                                 if (highest) {
82                                         sscanf(drd->d_name + 3, "%d", &num_proc);
83                                         if (num_proc > proc_nr) {
84                                                 proc_nr = num_proc;
85                                         }
86                                 }
87                                 else {
88                                         proc_nr++;
89                                 }
90                         }
91                 }
92         }
93
94         /* Close directory */
95         closedir(dir);
96
97         return (proc_nr + 1);
98 }
99
100 /*
101  ***************************************************************************
102  * Count number of processors in /proc/stat.
103  *
104  * RETURNS:
105  * Number of processors. The returned value is greater than or equal to the
106  * number of online processors.
107  * A value of 0 means one processor and non SMP kernel.
108  * A value of N (!=0) means N processor(s) (0 .. N-1) with SMP kernel.
109  ***************************************************************************
110  */
111 int get_proc_cpu_nr(void)
112 {
113         FILE *fp;
114         char line[16];
115         int num_proc, proc_nr = -1;
116
117         if ((fp = fopen(STAT, "r")) == NULL) {
118                 fprintf(stderr, _("Cannot open %s: %s\n"), STAT, strerror(errno));
119                 exit(1);
120         }
121
122         while (fgets(line, sizeof(line), fp) != NULL) {
123
124                 if (strncmp(line, "cpu ", 4) && !strncmp(line, "cpu", 3)) {
125                         sscanf(line + 3, "%d", &num_proc);
126                         if (num_proc > proc_nr) {
127                                 proc_nr = num_proc;
128                         }
129                 }
130         }
131
132         fclose(fp);
133
134         proc_nr++;
135         return proc_nr;
136 }
137
138 /*
139  ***************************************************************************
140  * Count the number of processors on the machine, or look for the
141  * highest processor number.
142  * Try to use /sys for that, or /proc/stat if /sys doesn't exist.
143  *
144  * IN:
145  * @max_nr_cpus Maximum number of proc that sysstat can handle.
146  * @highest     If set to TRUE, then look for the highest processor number.
147  *              This is used when eg. the machine has 4 CPU numbered 0, 1, 4
148  *              and 5. In this case, this procedure will return 6.
149  *
150  * RETURNS:
151  * Number of processors.
152  * 0: one proc and non SMP kernel.
153  * 1: one proc and SMP kernel (NB: On SMP machines where all the CPUs but
154  *    one have been disabled, we get the total number of proc since we use
155  *    /sys to count them).
156  * 2: two proc...
157  *
158  * USED BY:
159  * sadc, cifsiostat, iostat, mpstat, pidstat, tapestat
160  ***************************************************************************
161  */
162 __nr_t get_cpu_nr(unsigned int max_nr_cpus, int highest)
163 {
164         __nr_t cpu_nr;
165
166         if ((cpu_nr = get_sys_cpu_nr(highest)) == 0) {
167                 /* /sys may be not mounted. Use /proc/stat instead */
168                 cpu_nr = get_proc_cpu_nr();
169         }
170
171         if (cpu_nr > max_nr_cpus) {
172                 fprintf(stderr, _("Cannot handle so many processors!\n"));
173                 exit(1);
174         }
175
176         return cpu_nr;
177 }
178
179 /*
180  ***************************************************************************
181  * Find number of interrupts available per processor (use
182  * /proc/interrupts file or /proc/softirqs).
183  *
184  * IN:
185  * @file                /proc file to read (interrupts or softirqs).
186  * @max_nr_irqcpu       Maximum number of interrupts per processor that
187  *                      sadc can handle.
188  * @cpu_nr              Number of processors.
189  *
190  * RETURNS:
191  * Number of interrupts per processor.
192  *
193  * USED BY:
194  * sadc, mpstat
195  ***************************************************************************
196  */
197 __nr_t get_irqcpu_nr(char *file, int max_nr_irqcpu, int cpu_nr)
198 {
199         FILE *fp;
200         char *line = NULL;
201         __nr_t irq = 0;
202         int p;
203
204         if ((fp = fopen(file, "r")) == NULL)
205                 return 0;       /* No interrupts file */
206
207         SREALLOC(line, char, INTERRUPTS_LINE + 11 * cpu_nr);
208
209         while ((fgets(line, INTERRUPTS_LINE + 11 * cpu_nr , fp) != NULL) &&
210                (irq < max_nr_irqcpu)) {
211
212                 p = strcspn(line, ":");
213                 if ((p > 0) && (p < 16)) {
214                         irq++;
215                 }
216         }
217
218         fclose(fp);
219
220         free(line);
221
222         return irq;
223 }
224
225 #ifdef SOURCE_SADC
226 /*---------------- BEGIN: FUNCTIONS USED BY SADC ONLY ---------------------*/
227
228 /*
229  ***************************************************************************
230  * Find number of devices and partitions available in /proc/diskstats.
231  *
232  * IN:
233  * @count_part          Set to TRUE if devices _and_ partitions are to be
234  *                      counted.
235  * @only_used_dev       When counting devices, set to TRUE if only devices
236  *                      with non zero stats must be counted.
237  *
238  * RETURNS:
239  * Number of devices (and partitions).
240  ***************************************************************************
241  */
242 __nr_t get_diskstats_dev_nr(int count_part, int only_used_dev)
243 {
244         FILE *fp;
245         char line[256];
246         char dev_name[MAX_NAME_LEN];
247         __nr_t dev = 0;
248         int i;
249         unsigned long rd_ios, wr_ios;
250
251         if ((fp = fopen(DISKSTATS, "r")) == NULL)
252                 /* File non-existent */
253                 return 0;
254
255         /*
256          * Counting devices and partitions is simply a matter of counting
257          * the number of lines...
258          */
259         while (fgets(line, sizeof(line), fp) != NULL) {
260
261                 if (!count_part) {
262                         i = sscanf(line, "%*d %*d %127s %lu %*u %*u %*u %lu",
263                                    dev_name, &rd_ios, &wr_ios);
264                         if ((i == 2) || !is_device(SLASH_SYS, dev_name, ACCEPT_VIRTUAL_DEVICES))
265                                 /* It was a partition and not a device */
266                                 continue;
267                         if (only_used_dev && !rd_ios && !wr_ios)
268                                 /* Unused device */
269                                 continue;
270                 }
271                 dev++;
272         }
273
274         fclose(fp);
275
276         return dev;
277 }
278
279 /*
280  ***************************************************************************
281  * Find number of serial lines that support tx/rx accounting
282  * in /proc/tty/driver/serial file.
283  *
284  * RETURNS:
285  * Number of serial lines supporting tx/rx accouting.
286  ***************************************************************************
287  */
288 __nr_t get_serial_nr(void)
289 {
290         FILE *fp;
291         char line[256];
292         __nr_t sl = 0;
293
294         if ((fp = fopen(SERIAL, "r")) == NULL)
295                 return 0;       /* No SERIAL file */
296
297         while (fgets(line, sizeof(line), fp) != NULL) {
298                 /*
299                  * tx/rx statistics are always present,
300                  * except when serial line is unknown.
301                  */
302                 if (strstr(line, "tx:") != NULL) {
303                         sl++;
304                 }
305         }
306
307         fclose(fp);
308
309         return sl;
310 }
311
312 /*
313  ***************************************************************************
314  * Find number of interfaces (network devices) that are in /proc/net/dev
315  * file.
316  *
317  * RETURNS:
318  * Number of network interfaces.
319  ***************************************************************************
320  */
321 __nr_t get_iface_nr(void)
322 {
323         FILE *fp;
324         char line[128];
325         __nr_t iface = 0;
326
327         if ((fp = fopen(NET_DEV, "r")) == NULL)
328                 return 0;       /* No network device file */
329
330         while (fgets(line, sizeof(line), fp) != NULL) {
331                 if (strchr(line, ':')) {
332                         iface++;
333                 }
334         }
335
336         fclose(fp);
337
338         return iface;
339 }
340
341 /*
342  ***************************************************************************
343  * Get number of devices in /proc/diskstats.
344  *
345  * IN:
346  * @f   Non zero (true) if disks *and* partitions should be counted, and
347  *      zero (false) if only disks must be counted.
348  *
349  * RETURNS:
350  * Number of devices.
351  ***************************************************************************
352  */
353 __nr_t get_disk_nr(unsigned int f)
354 {
355         __nr_t disk_nr;
356
357         /*
358          * Partitions are taken into account by sar -d only with
359          * kernels 2.6.25 and later.
360          */
361         disk_nr = get_diskstats_dev_nr(f, CNT_USED_DEV);
362
363         return disk_nr;
364 }
365
366 /*
367  ***************************************************************************
368  * Count number of possible frequencies for CPU#0.
369  *
370  * RETURNS:
371  * Number of frequencies.
372  ***************************************************************************
373  */
374 __nr_t get_freq_nr(void)
375 {
376         FILE *fp;
377         char filename[MAX_PF_NAME];
378         char line[128];
379         __nr_t freq = 0;
380
381         snprintf(filename, MAX_PF_NAME, "%s/cpu0/%s",
382                  SYSFS_DEVCPU, SYSFS_TIME_IN_STATE);
383         if ((fp = fopen(filename, "r")) == NULL)
384                 return 0;       /* No time_in_state file for CPU#0 */
385
386         while (fgets(line, sizeof(line), fp) != NULL) {
387                 freq++;
388         }
389
390         fclose(fp);
391
392         return freq;
393 }
394
395 /*
396  ***************************************************************************
397  * Count number of USB devices in /sys/bus/usb/devices.
398  *
399  * RETURNS:
400  * Number of USB devices plugged into the system.
401  * Don't count USB root hubs.
402  * Return -1 if directory doesn't exist in sysfs.
403  ***************************************************************************
404  */
405 __nr_t get_usb_nr(void)
406 {
407         DIR *dir;
408         struct dirent *drd;
409         __nr_t usb = 0;
410
411         /* Open relevant /sys directory */
412         if ((dir = opendir(SYSFS_USBDEV)) == NULL)
413                 return -1;
414
415         /* Get current file entry */
416         while ((drd = readdir(dir)) != NULL) {
417
418                 if (isdigit(drd->d_name[0]) && !strchr(drd->d_name, ':')) {
419                         usb++;
420                 }
421         }
422
423         /* Close directory */
424         closedir(dir);
425
426         return usb;
427 }
428
429 /*
430  ***************************************************************************
431  * Find number of filesystems in /etc/mtab. Pseudo-filesystems are ignored.
432  *
433  * RETURNS:
434  * Number of filesystems.
435  ***************************************************************************
436  */
437 __nr_t get_filesystem_nr(void)
438 {
439         FILE *fp;
440         char line[512], fs_name[MAX_FS_LEN], mountp[256], type[128];
441         char *pos = 0, *pos2 = 0;
442         __nr_t fs = 0;
443         int skip, skip_next = 0;
444         struct statvfs buf;
445
446         if ((fp = fopen(MTAB, "r")) == NULL)
447                 /* File non-existent */
448                 return 0;
449
450         /* Get current filesystem */
451         while (fgets(line, sizeof(line), fp) != NULL) {
452                 /*
453                  * Ignore line if the preceding line did not contain '\n'.
454                  * (Some very long lines may be found for instance when
455                  * overlay2 filesystem with docker is used).
456                  */
457                 skip = skip_next;
458                 skip_next = (strchr(line, '\n') == NULL);
459                 if (skip)
460                         continue;
461
462                 if (line[0] == '/') {
463                         /* Find field separator position */
464                         pos = strchr(line, ' ');
465                         if (pos == NULL)
466                                 continue;
467
468                         /*
469                          * Find second field separator position,
470                          * read filesystem type,
471                          * if filesystem type is autofs, skip it
472                         */
473                         pos2 = strchr(pos + 1, ' ');
474                         if (pos2 == NULL)
475                                 continue;
476
477                         sscanf(pos2 + 1, "%127s", type);
478                         if (strcmp(type, "autofs") == 0)
479                                 continue;
480
481                         /* Read filesystem name and mount point */
482                         sscanf(line, "%127s", fs_name);
483                         sscanf(pos + 1, "%255s", mountp);
484
485                         /* Replace octal codes */
486                         oct2chr(mountp);
487
488                         /* Check that total size is not zero */
489                         if (__statvfs(mountp, &buf) < 0)
490                                 continue;
491
492                         if (buf.f_blocks) {
493                                 fs++;
494                         }
495                 }
496         }
497
498         fclose(fp);
499
500         return fs;
501 }
502
503 /*
504  ***************************************************************************
505  * Find number of fibre channel hosts in /sys/class/fc_host/.
506  *
507  * RETURNS:
508  * Number of FC hosts.
509  * Return -1 if directory doesn't exist in sysfs.
510  ***************************************************************************
511  */
512 __nr_t get_fchost_nr(void)
513 {
514         DIR *dir;
515         struct dirent *drd;
516         __nr_t fc = 0;
517
518         if ((dir = opendir(SYSFS_FCHOST)) == NULL) {
519                 /* Directory non-existent */
520                 return -1;
521         }
522
523         while ((drd = readdir(dir)) != NULL) {
524
525                 if (!strncmp(drd->d_name, "host", 4)) {
526                         fc++;
527                 }
528         }
529
530         /* Close directory */
531         closedir(dir);
532
533         return fc;
534 }
535
536 /*
537  * **************************************************************************
538  * Find number of batteries in /sys/class/power_supply/.
539  * Assume that batteries keep their id number (0, 1...) as long as the
540  * computer is not restarted.
541  *
542  * RETURNS:
543  * Number of batteries.
544  * Return -1 if directory doesn't exist in sysfs.
545  ***************************************************************************
546  */
547 __nr_t get_bat_nr(void)
548 {
549         DIR *dir;
550         struct dirent *drd;
551         __nr_t bat = 0;
552
553         if ((dir = opendir(SYSFS_PWR_SUPPLY)) == NULL) {
554                 /* Directory non-existent */
555                 return -1;
556         }
557
558         while ((drd = readdir(dir)) != NULL) {
559
560                 if (!strncmp(drd->d_name, "BAT", 3) && isdigit(drd->d_name[3])) {
561                         bat++;
562                 }
563         }
564
565         /* Close directory */
566         closedir(dir);
567
568         return bat;
569 }
570
571 /*------------------ END: FUNCTIONS USED BY SADC ONLY ---------------------*/
572 #endif /* SOURCE_SADC */