2 * count.c: Count items for which statistics will be collected.
3 * (C) 1999-2023 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 ***************************************************************************
28 #include <sys/types.h>
30 #include <sys/statvfs.h>
39 #define _(string) gettext(string)
41 #define _(string) (string)
46 ***************************************************************************
47 * Count number of processors in /sys.
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.
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 ***************************************************************************
60 int get_sys_cpu_nr(int highest)
65 char line[MAX_PF_NAME];
66 int num_proc, proc_nr = -1;
68 /* Open relevant /sys directory */
69 if ((dir = opendir(SYSFS_DEVCPU)) == NULL)
72 /* Get current file entry */
73 while ((drd = readdir(dir)) != NULL) {
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)
80 if (S_ISDIR(buf.st_mode)) {
82 sscanf(drd->d_name + 3, "%d", &num_proc);
83 if (num_proc > proc_nr) {
101 ***************************************************************************
102 * Count number of processors in /proc/stat.
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 ***************************************************************************
111 int get_proc_cpu_nr(void)
115 int num_proc, proc_nr = -1;
117 if ((fp = fopen(STAT, "r")) == NULL) {
118 fprintf(stderr, _("Cannot open %s: %s\n"), STAT, strerror(errno));
122 while (fgets(line, sizeof(line), fp) != NULL) {
124 if (strncmp(line, "cpu ", 4) && !strncmp(line, "cpu", 3)) {
125 sscanf(line + 3, "%d", &num_proc);
126 if (num_proc > proc_nr) {
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.
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.
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).
159 * sadc, cifsiostat, iostat, mpstat, pidstat, tapestat
160 ***************************************************************************
162 __nr_t get_cpu_nr(unsigned int max_nr_cpus, int highest)
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();
171 if (cpu_nr > max_nr_cpus) {
172 fprintf(stderr, _("Cannot handle so many processors!\n"));
180 ***************************************************************************
181 * Find number of interrupts available per processor (use
182 * /proc/interrupts file or /proc/softirqs).
185 * @file /proc file to read (interrupts or softirqs).
186 * @max_nr_irqcpu Maximum number of interrupts per processor that
188 * @cpu_nr Number of processors.
191 * Number of interrupts per processor.
195 ***************************************************************************
197 __nr_t get_irqcpu_nr(char *file, int max_nr_irqcpu, int cpu_nr)
204 if ((fp = fopen(file, "r")) == NULL)
205 return 0; /* No interrupts file */
207 SREALLOC(line, char, INTERRUPTS_LINE + 11 * cpu_nr);
209 while ((fgets(line, INTERRUPTS_LINE + 11 * cpu_nr , fp) != NULL) &&
210 (irq < max_nr_irqcpu)) {
212 p = strcspn(line, ":");
213 if ((p > 0) && (p < 16)) {
226 /*---------------- BEGIN: FUNCTIONS USED BY SADC ONLY ---------------------*/
229 ***************************************************************************
230 * Find number of devices and partitions available in /proc/diskstats.
233 * @count_part Set to TRUE if devices _and_ partitions are to be
235 * @only_used_dev When counting devices, set to TRUE if only devices
236 * with non zero stats must be counted.
239 * Number of devices (and partitions).
240 ***************************************************************************
242 __nr_t get_diskstats_dev_nr(int count_part, int only_used_dev)
246 char dev_name[MAX_NAME_LEN];
249 unsigned long rd_ios, wr_ios;
251 if ((fp = fopen(DISKSTATS, "r")) == NULL)
252 /* File non-existent */
256 * Counting devices and partitions is simply a matter of counting
257 * the number of lines...
259 while (fgets(line, sizeof(line), fp) != NULL) {
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 */
267 if (only_used_dev && !rd_ios && !wr_ios)
280 ***************************************************************************
281 * Find number of serial lines that support tx/rx accounting
282 * in /proc/tty/driver/serial file.
285 * Number of serial lines supporting tx/rx accouting.
286 ***************************************************************************
288 __nr_t get_serial_nr(void)
294 if ((fp = fopen(SERIAL, "r")) == NULL)
295 return 0; /* No SERIAL file */
297 while (fgets(line, sizeof(line), fp) != NULL) {
299 * tx/rx statistics are always present,
300 * except when serial line is unknown.
302 if (strstr(line, "tx:") != NULL) {
313 ***************************************************************************
314 * Find number of interfaces (network devices) that are in /proc/net/dev
318 * Number of network interfaces.
319 ***************************************************************************
321 __nr_t get_iface_nr(void)
327 if ((fp = fopen(NET_DEV, "r")) == NULL)
328 return 0; /* No network device file */
330 while (fgets(line, sizeof(line), fp) != NULL) {
331 if (strchr(line, ':')) {
342 ***************************************************************************
343 * Get number of devices in /proc/diskstats.
346 * @f Non zero (true) if disks *and* partitions should be counted, and
347 * zero (false) if only disks must be counted.
351 ***************************************************************************
353 __nr_t get_disk_nr(unsigned int f)
358 * Partitions are taken into account by sar -d only with
359 * kernels 2.6.25 and later.
361 disk_nr = get_diskstats_dev_nr(f, CNT_USED_DEV);
367 ***************************************************************************
368 * Count number of possible frequencies for CPU#0.
371 * Number of frequencies.
372 ***************************************************************************
374 __nr_t get_freq_nr(void)
377 char filename[MAX_PF_NAME];
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 */
386 while (fgets(line, sizeof(line), fp) != NULL) {
396 ***************************************************************************
397 * Count number of USB devices in /sys/bus/usb/devices.
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 ***************************************************************************
405 __nr_t get_usb_nr(void)
411 /* Open relevant /sys directory */
412 if ((dir = opendir(SYSFS_USBDEV)) == NULL)
415 /* Get current file entry */
416 while ((drd = readdir(dir)) != NULL) {
418 if (isdigit(drd->d_name[0]) && !strchr(drd->d_name, ':')) {
423 /* Close directory */
430 ***************************************************************************
431 * Find number of filesystems in /etc/mtab. Pseudo-filesystems are ignored.
434 * Number of filesystems.
435 ***************************************************************************
437 __nr_t get_filesystem_nr(void)
440 char line[512], fs_name[MAX_FS_LEN], mountp[256], type[128];
441 char *pos = 0, *pos2 = 0;
443 int skip, skip_next = 0;
446 if ((fp = fopen(MTAB, "r")) == NULL)
447 /* File non-existent */
450 /* Get current filesystem */
451 while (fgets(line, sizeof(line), fp) != NULL) {
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).
458 skip_next = (strchr(line, '\n') == NULL);
462 if (line[0] == '/') {
463 /* Find field separator position */
464 pos = strchr(line, ' ');
469 * Find second field separator position,
470 * read filesystem type,
471 * if filesystem type is autofs, skip it
473 pos2 = strchr(pos + 1, ' ');
477 sscanf(pos2 + 1, "%127s", type);
478 if (strcmp(type, "autofs") == 0)
481 /* Read filesystem name and mount point */
482 sscanf(line, "%127s", fs_name);
483 sscanf(pos + 1, "%255s", mountp);
485 /* Replace octal codes */
488 /* Check that total size is not zero */
489 if (__statvfs(mountp, &buf) < 0)
504 ***************************************************************************
505 * Find number of fibre channel hosts in /sys/class/fc_host/.
508 * Number of FC hosts.
509 * Return -1 if directory doesn't exist in sysfs.
510 ***************************************************************************
512 __nr_t get_fchost_nr(void)
518 if ((dir = opendir(SYSFS_FCHOST)) == NULL) {
519 /* Directory non-existent */
523 while ((drd = readdir(dir)) != NULL) {
525 if (!strncmp(drd->d_name, "host", 4)) {
530 /* Close directory */
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.
543 * Number of batteries.
544 * Return -1 if directory doesn't exist in sysfs.
545 ***************************************************************************
547 __nr_t get_bat_nr(void)
553 if ((dir = opendir(SYSFS_PWR_SUPPLY)) == NULL) {
554 /* Directory non-existent */
558 while ((drd = readdir(dir)) != NULL) {
560 if (!strncmp(drd->d_name, "BAT", 3) && isdigit(drd->d_name[3])) {
565 /* Close directory */
571 /*------------------ END: FUNCTIONS USED BY SADC ONLY ---------------------*/
572 #endif /* SOURCE_SADC */