/*
 * rd_stats.c: Read system statistics
 * (C) 1999-2017 by Sebastien GODARD (sysstat <at> orange.fr)
 *
 ***************************************************************************
 * This program is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU General Public License as published  by  the *
 * Free Software Foundation; either version 2 of the License, or (at  your *
 * option) any later version.                                              *
 *                                                                         *
 * This program is distributed in the hope that it  will  be  useful,  but *
 * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
 * for more details.                                                       *
 *                                                                         *
 * You should have received a copy of the GNU General Public License along *
 * with this program; if not, write to the Free Software Foundation, Inc., *
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
 ***************************************************************************
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>

#include "common.h"
#include "rd_stats.h"
#include "ioconf.h"

#ifdef USE_NLS
#include <locale.h>
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif

/*
 ***************************************************************************
 * Read CPU statistics and machine uptime.
 *
 * IN:
 * @st_cpu	Structure where stats will be saved.
 * @nbr		Total number of CPU (including cpu "all").
 *
 * OUT:
 * @st_cpu	Structure with statistics.
 * @uptime	Machine uptime multiplied by the number of processors.
 * @uptime0	Machine uptime. Filled only if previously set to zero.
 ***************************************************************************
 */
void read_stat_cpu(struct stats_cpu *st_cpu, int nbr,
		   unsigned long long *uptime, unsigned long long *uptime0)
{
	FILE *fp;
	struct stats_cpu *st_cpu_i;
	struct stats_cpu sc;
	char line[8192];
	int proc_nb;

	if ((fp = fopen(STAT, "r")) == NULL) {
		fprintf(stderr, _("Cannot open %s: %s\n"), STAT, strerror(errno));
		exit(2);
	}

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "cpu ", 4)) {

			/*
			 * All the fields don't necessarily exist,
			 * depending on the kernel version used.
			 */
			memset(st_cpu, 0, STATS_CPU_SIZE);

			/*
			 * Read the number of jiffies spent in the different modes
			 * (user, nice, etc.) among all proc. CPU usage is not reduced
			 * to one processor to avoid rounding problems.
			 */
			sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
			       &st_cpu->cpu_user,
			       &st_cpu->cpu_nice,
			       &st_cpu->cpu_sys,
			       &st_cpu->cpu_idle,
			       &st_cpu->cpu_iowait,
			       &st_cpu->cpu_hardirq,
			       &st_cpu->cpu_softirq,
			       &st_cpu->cpu_steal,
			       &st_cpu->cpu_guest,
			       &st_cpu->cpu_guest_nice);

			/*
			 * Compute the uptime of the system in jiffies (1/100ths of a second
			 * if HZ=100).
			 * Machine uptime is multiplied by the number of processors here.
			 *
			 * NB: Don't add cpu_guest/cpu_guest_nice because cpu_user/cpu_nice
			 * already include them.
			 */
			*uptime = st_cpu->cpu_user + st_cpu->cpu_nice    +
				st_cpu->cpu_sys    + st_cpu->cpu_idle    +
				st_cpu->cpu_iowait + st_cpu->cpu_hardirq +
				st_cpu->cpu_steal  + st_cpu->cpu_softirq;
		}

		else if (!strncmp(line, "cpu", 3)) {
			if (nbr > 1) {
				/* All the fields don't necessarily exist */
				memset(&sc, 0, STATS_CPU_SIZE);
				/*
				 * Read the number of jiffies spent in the different modes
				 * (user, nice, etc) for current proc.
				 * This is done only on SMP machines.
				 */
				sscanf(line + 3, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
				       &proc_nb,
				       &sc.cpu_user,
				       &sc.cpu_nice,
				       &sc.cpu_sys,
				       &sc.cpu_idle,
				       &sc.cpu_iowait,
				       &sc.cpu_hardirq,
				       &sc.cpu_softirq,
				       &sc.cpu_steal,
				       &sc.cpu_guest,
				       &sc.cpu_guest_nice);

				if (proc_nb < (nbr - 1)) {
					st_cpu_i = st_cpu + proc_nb + 1;
					*st_cpu_i = sc;
				}
				/*
				 * else additional CPUs have been dynamically registered
				 * in /proc/stat.
				 */

				if (!proc_nb && !*uptime0) {
					/*
					 * Compute uptime reduced to one proc using proc#0.
					 * Done if /proc/uptime was unavailable.
					 *
					 * NB: Don't add cpu_guest/cpu_guest_nice because cpu_user/cpu_nice
					 * already include them.
					 */
					*uptime0 = sc.cpu_user + sc.cpu_nice  +
						sc.cpu_sys     + sc.cpu_idle  +
						sc.cpu_iowait  + sc.cpu_steal +
						sc.cpu_hardirq + sc.cpu_softirq;
				}
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read interrupts statistics from /proc/stat.
 *
 * IN:
 * @st_irq	Structure where stats will be saved.
 * @nbr		Number of interrupts to read, including the total number
 *		of interrupts.
 *
 * OUT:
 * @st_irq	Structure with statistics.
 ***************************************************************************
 */
void read_stat_irq(struct stats_irq *st_irq, int nbr)
{
	FILE *fp;
	struct stats_irq *st_irq_i;
	char line[8192];
	int i, pos;

	if ((fp = fopen(STAT, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "intr ", 5)) {
			/* Read total number of interrupts received since system boot */
			sscanf(line + 5, "%llu", &st_irq->irq_nr);
			pos = strcspn(line + 5, " ") + 5;

			for (i = 1; i < nbr; i++) {
				st_irq_i = st_irq + i;
				sscanf(line + pos, " %llu", &st_irq_i->irq_nr);
				pos += strcspn(line + pos + 1, " ") + 1;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read memory statistics from /proc/meminfo.
 *
 * IN:
 * @st_memory	Structure where stats will be saved.
 *
 * OUT:
 * @st_memory	Structure with statistics.
 ***************************************************************************
 */
void read_meminfo(struct stats_memory *st_memory)
{
	FILE *fp;
	char line[128];

	if ((fp = fopen(MEMINFO, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "MemTotal:", 9)) {
			/* Read the total amount of memory in kB */
			sscanf(line + 9, "%lu", &st_memory->tlmkb);
		}
		else if (!strncmp(line, "MemFree:", 8)) {
			/* Read the amount of free memory in kB */
			sscanf(line + 8, "%lu", &st_memory->frmkb);
		}
		else if (!strncmp(line, "MemAvailable:", 13)) {
			/* Read the amount of available memory in kB */
			sscanf(line + 13, "%lu", &st_memory->availablekb);
		}
		else if (!strncmp(line, "Buffers:", 8)) {
			/* Read the amount of buffered memory in kB */
			sscanf(line + 8, "%lu", &st_memory->bufkb);
		}
		else if (!strncmp(line, "Cached:", 7)) {
			/* Read the amount of cached memory in kB */
			sscanf(line + 7, "%lu", &st_memory->camkb);
		}
		else if (!strncmp(line, "SwapCached:", 11)) {
			/* Read the amount of cached swap in kB */
			sscanf(line + 11, "%lu", &st_memory->caskb);
		}
		else if (!strncmp(line, "Active:", 7)) {
			/* Read the amount of active memory in kB */
			sscanf(line + 7, "%lu", &st_memory->activekb);
		}
		else if (!strncmp(line, "Inactive:", 9)) {
			/* Read the amount of inactive memory in kB */
			sscanf(line + 9, "%lu", &st_memory->inactkb);
		}
		else if (!strncmp(line, "SwapTotal:", 10)) {
			/* Read the total amount of swap memory in kB */
			sscanf(line + 10, "%lu", &st_memory->tlskb);
		}
		else if (!strncmp(line, "SwapFree:", 9)) {
			/* Read the amount of free swap memory in kB */
			sscanf(line + 9, "%lu", &st_memory->frskb);
		}
		else if (!strncmp(line, "Dirty:", 6)) {
			/* Read the amount of dirty memory in kB */
			sscanf(line + 6, "%lu", &st_memory->dirtykb);
		}
		else if (!strncmp(line, "Committed_AS:", 13)) {
			/* Read the amount of commited memory in kB */
			sscanf(line + 13, "%lu", &st_memory->comkb);
		}
		else if (!strncmp(line, "AnonPages:", 10)) {
			/* Read the amount of pages mapped into userspace page tables in kB */
			sscanf(line + 10, "%lu", &st_memory->anonpgkb);
		}
		else if (!strncmp(line, "Slab:", 5)) {
			/* Read the amount of in-kernel data structures cache in kB */
			sscanf(line + 5, "%lu", &st_memory->slabkb);
		}
		else if (!strncmp(line, "KernelStack:", 12)) {
			/* Read the kernel stack utilization in kB */
			sscanf(line + 12, "%lu", &st_memory->kstackkb);
		}
		else if (!strncmp(line, "PageTables:", 11)) {
			/* Read the amount of memory dedicated to the lowest level of page tables in kB */
			sscanf(line + 11, "%lu", &st_memory->pgtblkb);
		}
		else if (!strncmp(line, "VmallocUsed:", 12)) {
			/* Read the amount of vmalloc area which is used in kB */
			sscanf(line + 12, "%lu", &st_memory->vmusedkb);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read machine uptime, independently of the number of processors.
 *
 * OUT:
 * @uptime	Uptime value in jiffies.
 ***************************************************************************
 */
void read_uptime(unsigned long long *uptime)
{
	FILE *fp;
	char line[128];
	unsigned long up_sec, up_cent;

	if ((fp = fopen(UPTIME, "r")) == NULL)
		return;

	if (fgets(line, sizeof(line), fp) == NULL) {
		fclose(fp);
		return;
	}

	sscanf(line, "%lu.%lu", &up_sec, &up_cent);
	*uptime = (unsigned long long) up_sec * HZ +
	          (unsigned long long) up_cent * HZ / 100;

	fclose(fp);

}

#ifdef SOURCE_SADC
/*---------------- BEGIN: FUNCTIONS USED BY SADC ONLY ---------------------*/

/*
 ***************************************************************************
 * Replace octal codes in string with their corresponding characters.
 *
 * IN:
 * @str		String to parse.
 *
 * OUT:
 * @str		String with octal codes replaced with characters.
 ***************************************************************************
 */
void oct2chr(char *str)
{
	int i = 0;
	int j, len;

	len = strlen(str);

	while (i < len - 3) {
		if ((str[i] == '\\') &&
		    (str[i + 1] >= '0') && (str[i + 1] <= '3') &&
		    (str[i + 2] >= '0') && (str[i + 2] <= '7') &&
		    (str[i + 3] >= '0') && (str[i + 3] <= '7')) {
			/* Octal code found */
			str[i] = (str[i + 1] - 48) * 64 +
			         (str[i + 2] - 48) * 8  +
			         (str[i + 3] - 48);
			for (j = i + 4; j <= len; j++) {
				str[j - 3] = str[j];
			}
			len -= 3;
		}
		i++;
	}
}

/*
 ***************************************************************************
 * Read processes (tasks) creation and context switches statistics
 * from /proc/stat.
 *
 * IN:
 * @st_pcsw	Structure where stats will be saved.
 *
 * OUT:
 * @st_pcsw	Structure with statistics.
 ***************************************************************************
 */
void read_stat_pcsw(struct stats_pcsw *st_pcsw)
{
	FILE *fp;
	char line[8192];

	if ((fp = fopen(STAT, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "ctxt ", 5)) {
			/* Read number of context switches */
			sscanf(line + 5, "%llu", &st_pcsw->context_switch);
		}

		else if (!strncmp(line, "processes ", 10)) {
			/* Read number of processes created since system boot */
			sscanf(line + 10, "%lu", &st_pcsw->processes);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read queue and load statistics from /proc/loadavg and /proc/stat.
 *
 * IN:
 * @st_queue	Structure where stats will be saved.
 *
 * OUT:
 * @st_queue	Structure with statistics.
 ***************************************************************************
 */
void read_loadavg(struct stats_queue *st_queue)
{
	FILE *fp;
	char line[8192];
	int load_tmp[3];
	int rc;

	if ((fp = fopen(LOADAVG, "r")) == NULL)
		return;

	/* Read load averages and queue length */
	rc = fscanf(fp, "%d.%u %d.%u %d.%u %lu/%u %*d\n",
		    &load_tmp[0], &st_queue->load_avg_1,
		    &load_tmp[1], &st_queue->load_avg_5,
		    &load_tmp[2], &st_queue->load_avg_15,
		    &st_queue->nr_running,
		    &st_queue->nr_threads);

	fclose(fp);

	if (rc < 8)
		return;

	st_queue->load_avg_1  += load_tmp[0] * 100;
	st_queue->load_avg_5  += load_tmp[1] * 100;
	st_queue->load_avg_15 += load_tmp[2] * 100;

	if (st_queue->nr_running) {
		/* Do not take current process into account */
		st_queue->nr_running--;
	}

	/* Read nr of tasks blocked from /proc/stat */
	if ((fp = fopen(STAT, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "procs_blocked ", 14)) {
			/* Read number of processes blocked */
			sscanf(line + 14, "%lu", &st_queue->procs_blocked);
			break;
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read swapping statistics from /proc/vmstat.
 *
 * IN:
 * @st_swap	Structure where stats will be saved.
 *
 * OUT:
 * @st_swap	Structure with statistics.
 ***************************************************************************
 */
void read_vmstat_swap(struct stats_swap *st_swap)
{
	FILE *fp;
	char line[128];

	if ((fp = fopen(VMSTAT, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "pswpin ", 7)) {
			/* Read number of swap pages brought in */
			sscanf(line + 7, "%lu", &st_swap->pswpin);
		}
		else if (!strncmp(line, "pswpout ", 8)) {
			/* Read number of swap pages brought out */
			sscanf(line + 8, "%lu", &st_swap->pswpout);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read paging statistics from /proc/vmstat.
 *
 * IN:
 * @st_paging	Structure where stats will be saved.
 *
 * OUT:
 * @st_paging	Structure with statistics.
 ***************************************************************************
 */
void read_vmstat_paging(struct stats_paging *st_paging)
{
	FILE *fp;
	char line[128];
	unsigned long pgtmp;

	if ((fp = fopen(VMSTAT, "r")) == NULL)
		return;

	st_paging->pgsteal = 0;
	st_paging->pgscan_kswapd = st_paging->pgscan_direct = 0;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "pgpgin ", 7)) {
			/* Read number of pages the system paged in */
			sscanf(line + 7, "%lu", &st_paging->pgpgin);
		}
		else if (!strncmp(line, "pgpgout ", 8)) {
			/* Read number of pages the system paged out */
			sscanf(line + 8, "%lu", &st_paging->pgpgout);
		}
		else if (!strncmp(line, "pgfault ", 8)) {
			/* Read number of faults (major+minor) made by the system */
			sscanf(line + 8, "%lu", &st_paging->pgfault);
		}
		else if (!strncmp(line, "pgmajfault ", 11)) {
			/* Read number of faults (major only) made by the system */
			sscanf(line + 11, "%lu", &st_paging->pgmajfault);
		}
		else if (!strncmp(line, "pgfree ", 7)) {
			/* Read number of pages freed by the system */
			sscanf(line + 7, "%lu", &st_paging->pgfree);
		}
		else if (!strncmp(line, "pgsteal_", 8)) {
			/* Read number of pages stolen by the system */
			sscanf(strchr(line, ' '), "%lu", &pgtmp);
			st_paging->pgsteal += pgtmp;
		}
		else if (!strncmp(line, "pgscan_kswapd", 13)) {
			/* Read number of pages scanned by the kswapd daemon */
			sscanf(strchr(line, ' '), "%lu", &pgtmp);
			st_paging->pgscan_kswapd += pgtmp;
		}
		else if (!strncmp(line, "pgscan_direct", 13)) {
			/* Read number of pages scanned directly */
			sscanf(strchr(line, ' '), "%lu", &pgtmp);
			st_paging->pgscan_direct += pgtmp;
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read I/O and transfer rates statistics from /proc/diskstats.
 *
 * IN:
 * @st_io	Structure where stats will be saved.
 *
 * OUT:
 * @st_io	Structure with statistics.
 ***************************************************************************
 */
void read_diskstats_io(struct stats_io *st_io)
{
	FILE *fp;
	char line[256];
	char dev_name[MAX_NAME_LEN];
	unsigned int major, minor;
	unsigned long rd_ios, wr_ios, rd_sec, wr_sec;

	if ((fp = fopen(DISKSTATS, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (sscanf(line, "%u %u %s %lu %*u %lu %*u %lu %*u %lu",
			   &major, &minor, dev_name,
			   &rd_ios, &rd_sec, &wr_ios, &wr_sec) == 7) {

			if (is_device(dev_name, IGNORE_VIRTUAL_DEVICES)) {
				/*
				 * OK: It's a (real) device and not a partition.
				 * Note: Structure should have been initialized first!
				 */
				st_io->dk_drive      += (unsigned long long) rd_ios + (unsigned long long) wr_ios;
				st_io->dk_drive_rio  += rd_ios;
				st_io->dk_drive_rblk += rd_sec;
				st_io->dk_drive_wio  += wr_ios;
				st_io->dk_drive_wblk += wr_sec;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read block devices statistics from /proc/diskstats.
 *
 * IN:
 * @st_disk	Structure where stats will be saved.
 * @nbr		Maximum number of block devices.
 * @read_part	True if disks *and* partitions should be read; False if only
 * 		disks are read.
 *
 * OUT:
 * @st_disk	Structure with statistics.
 ***************************************************************************
 */
void read_diskstats_disk(struct stats_disk *st_disk, int nbr, int read_part)
{
	FILE *fp;
	char line[256];
	char dev_name[MAX_NAME_LEN];
	int dsk = 0;
	struct stats_disk *st_disk_i;
	unsigned int major, minor, rd_ticks, wr_ticks, tot_ticks, rq_ticks;
	unsigned long rd_ios, wr_ios, rd_sec, wr_sec;

	if ((fp = fopen(DISKSTATS, "r")) == NULL)
		return;

	while ((fgets(line, sizeof(line), fp) != NULL) && (dsk < nbr)) {

		if (sscanf(line, "%u %u %s %lu %*u %lu %u %lu %*u %lu"
			   " %u %*u %u %u",
			   &major, &minor, dev_name,
			   &rd_ios, &rd_sec, &rd_ticks, &wr_ios, &wr_sec, &wr_ticks,
			   &tot_ticks, &rq_ticks) == 11) {

			if (!rd_ios && !wr_ios)
				/* Unused device: Ignore it */
				continue;
			if (read_part || is_device(dev_name, ACCEPT_VIRTUAL_DEVICES)) {
				st_disk_i = st_disk + dsk++;
				st_disk_i->major     = major;
				st_disk_i->minor     = minor;
				st_disk_i->nr_ios    = (unsigned long long) rd_ios + (unsigned long long) wr_ios;
				st_disk_i->rd_sect   = rd_sec;
				st_disk_i->wr_sect   = wr_sec;
				st_disk_i->rd_ticks  = rd_ticks;
				st_disk_i->wr_ticks  = wr_ticks;
				st_disk_i->tot_ticks = tot_ticks;
				st_disk_i->rq_ticks  = rq_ticks;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read serial lines statistics from /proc/tty/driver/serial.
 *
 * IN:
 * @st_serial	Structure where stats will be saved.
 * @nbr		Maximum number of serial lines.
 *
 * OUT:
 * @st_serial	Structure with statistics.
 ***************************************************************************
 */
void read_tty_driver_serial(struct stats_serial *st_serial, int nbr)
{
	FILE *fp;
	struct stats_serial *st_serial_i;
	int sl = 0;
	char line[256];
	char *p;

	if ((fp = fopen(SERIAL, "r")) == NULL)
		return;

	while ((fgets(line, sizeof(line), fp) != NULL) && (sl < nbr)) {

		if ((p = strstr(line, "tx:")) != NULL) {
			st_serial_i = st_serial + sl;
			sscanf(line, "%u", &st_serial_i->line);
			/*
			 * A value of 0 means an unused structure.
			 * So increment it to make sure it is not zero.
			 */
			(st_serial_i->line)++;
			/*
			 * Read the number of chars transmitted and received by
			 * current serial line.
			 */
			sscanf(p + 3, "%u", &st_serial_i->tx);
			if ((p = strstr(line, "rx:")) != NULL) {
				sscanf(p + 3, "%u", &st_serial_i->rx);
			}
			if ((p = strstr(line, "fe:")) != NULL) {
				sscanf(p + 3, "%u", &st_serial_i->frame);
			}
			if ((p = strstr(line, "pe:")) != NULL) {
				sscanf(p + 3, "%u", &st_serial_i->parity);
			}
			if ((p = strstr(line, "brk:")) != NULL) {
				sscanf(p + 4, "%u", &st_serial_i->brk);
			}
			if ((p = strstr(line, "oe:")) != NULL) {
				sscanf(p + 3, "%u", &st_serial_i->overrun);
			}

			sl++;
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read kernel tables statistics from various system files.
 *
 * IN:
 * @st_ktables	Structure where stats will be saved.
 *
 * OUT:
 * @st_ktables	Structure with statistics.
 ***************************************************************************
 */
void read_kernel_tables(struct stats_ktables *st_ktables)
{
	FILE *fp;
	unsigned int parm;
	int rc = 0;

	/* Open /proc/sys/fs/dentry-state file */
	if ((fp = fopen(FDENTRY_STATE, "r")) != NULL) {
		rc = fscanf(fp, "%*d %u",
			    &st_ktables->dentry_stat);
		fclose(fp);
		if (rc == 0) {
			st_ktables->dentry_stat = 0;
		}
	}

	/* Open /proc/sys/fs/file-nr file */
	if ((fp = fopen(FFILE_NR, "r")) != NULL) {
		rc = fscanf(fp, "%u %u",
			    &st_ktables->file_used, &parm);
		fclose(fp);
		/*
		 * The number of used handles is the number of allocated ones
		 * minus the number of free ones.
		 */
		if (rc == 2) {
			st_ktables->file_used -= parm;
		}
		else {
			st_ktables->file_used = 0;
		}
	}

	/* Open /proc/sys/fs/inode-state file */
	if ((fp = fopen(FINODE_STATE, "r")) != NULL) {
		rc = fscanf(fp, "%u %u",
			    &st_ktables->inode_used, &parm);
		fclose(fp);
		/*
		 * The number of inuse inodes is the number of allocated ones
		 * minus the number of free ones.
		 */
		if (rc == 2) {
			st_ktables->inode_used -= parm;
		}
		else {
			st_ktables->inode_used = 0;
		}
	}

	/* Open /proc/sys/kernel/pty/nr file */
	if ((fp = fopen(PTY_NR, "r")) != NULL) {
		rc = fscanf(fp, "%u",
			    &st_ktables->pty_nr);
		fclose(fp);
		if (rc == 0) {
			st_ktables->pty_nr = 0;
		}
	}
}

/*
 ***************************************************************************
 * Read network interfaces statistics from /proc/net/dev.
 *
 * IN:
 * @st_net_dev	Structure where stats will be saved.
 * @nbr		Maximum number of network interfaces.
 *
 * OUT:
 * @st_net_dev	Structure with statistics.
 *
 * RETURNS:
 * Number of interfaces for which stats have been read.
 ***************************************************************************
 */
int read_net_dev(struct stats_net_dev *st_net_dev, int nbr)
{
	FILE *fp;
	struct stats_net_dev *st_net_dev_i;
	char line[256];
	char iface[MAX_IFACE_LEN];
	int dev = 0;
	int pos;

	if ((fp = fopen(NET_DEV, "r")) == NULL)
		return 0;

	while ((fgets(line, sizeof(line), fp) != NULL) && (dev < nbr)) {

		pos = strcspn(line, ":");
		if (pos < strlen(line)) {
			st_net_dev_i = st_net_dev + dev;
			strncpy(iface, line, MINIMUM(pos, MAX_IFACE_LEN - 1));
			iface[MINIMUM(pos, MAX_IFACE_LEN - 1)] = '\0';
			sscanf(iface, "%s", st_net_dev_i->interface); /* Skip heading spaces */
			sscanf(line + pos + 1, "%llu %llu %*u %*u %*u %*u %llu %llu %llu %llu "
			       "%*u %*u %*u %*u %*u %llu",
			       &st_net_dev_i->rx_bytes,
			       &st_net_dev_i->rx_packets,
			       &st_net_dev_i->rx_compressed,
			       &st_net_dev_i->multicast,
			       &st_net_dev_i->tx_bytes,
			       &st_net_dev_i->tx_packets,
			       &st_net_dev_i->tx_compressed);
			dev++;
		}
	}

	fclose(fp);

	return dev;
}

/*
 ***************************************************************************
 * Read duplex and speed data for network interface cards.
 *
 * IN:
 * @st_net_dev	Structure where stats will be saved.
 * @nbr		Real number of network interfaces available.
 *
 * OUT:
 * @st_net_dev	Structure with statistics.
 ***************************************************************************
 */
void read_if_info(struct stats_net_dev *st_net_dev, int nbr)
{
	FILE *fp;
	struct stats_net_dev *st_net_dev_i;
	char filename[128], duplex[32];
	int dev, n;

	for (dev = 0; dev < nbr; dev++) {

		st_net_dev_i = st_net_dev + dev;

		/* Read speed info */
		sprintf(filename, IF_DUPLEX, st_net_dev_i->interface);

		if ((fp = fopen(filename, "r")) == NULL)
			/* Cannot read NIC duplex */
			continue;

		n = fscanf(fp, "%31s", duplex);

		fclose(fp);

		if (n != 1)
			/* Cannot read NIC duplex */
			continue;

		if (!strcmp(duplex, K_DUPLEX_FULL)) {
			st_net_dev_i->duplex = C_DUPLEX_FULL;
		}
		else if (!strcmp(duplex, K_DUPLEX_HALF)) {
			st_net_dev_i->duplex = C_DUPLEX_HALF;
		}
		else
			continue;

		/* Read speed info */
		sprintf(filename, IF_SPEED, st_net_dev_i->interface);

		if ((fp = fopen(filename, "r")) == NULL)
			/* Cannot read NIC speed */
			continue;

		n = fscanf(fp, "%u", &st_net_dev_i->speed);

		fclose(fp);

		if (n != 1) {
			st_net_dev_i->speed = 0;
		}
	}
}


/*
 ***************************************************************************
 * Read network interfaces errors statistics from /proc/net/dev.
 *
 * IN:
 * @st_net_edev	Structure where stats will be saved.
 * @nbr		Maximum number of network interfaces.
 *
 * OUT:
 * @st_net_edev	Structure with statistics.
 ***************************************************************************
 */
void read_net_edev(struct stats_net_edev *st_net_edev, int nbr)
{
	FILE *fp;
	struct stats_net_edev *st_net_edev_i;
	static char line[256];
	char iface[MAX_IFACE_LEN];
	int dev = 0;
	int pos;

	if ((fp = fopen(NET_DEV, "r")) == NULL)
		return;

	while ((fgets(line, sizeof(line), fp) != NULL) && (dev < nbr)) {

		pos = strcspn(line, ":");
		if (pos < strlen(line)) {
			st_net_edev_i = st_net_edev + dev;
			strncpy(iface, line, MINIMUM(pos, MAX_IFACE_LEN - 1));
			iface[MINIMUM(pos, MAX_IFACE_LEN - 1)] = '\0';
			sscanf(iface, "%s", st_net_edev_i->interface); /* Skip heading spaces */
			sscanf(line + pos + 1, "%*u %*u %llu %llu %llu %llu %*u %*u %*u %*u "
			       "%llu %llu %llu %llu %llu",
			       &st_net_edev_i->rx_errors,
			       &st_net_edev_i->rx_dropped,
			       &st_net_edev_i->rx_fifo_errors,
			       &st_net_edev_i->rx_frame_errors,
			       &st_net_edev_i->tx_errors,
			       &st_net_edev_i->tx_dropped,
			       &st_net_edev_i->tx_fifo_errors,
			       &st_net_edev_i->collisions,
			       &st_net_edev_i->tx_carrier_errors);
			dev++;
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read NFS client statistics from /proc/net/rpc/nfs.
 *
 * IN:
 * @st_net_nfs	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_nfs	Structure with statistics.
 ***************************************************************************
 */
void read_net_nfs(struct stats_net_nfs *st_net_nfs)
{
	FILE *fp;
	char line[256];
	unsigned int getattcnt = 0, accesscnt = 0, readcnt = 0, writecnt = 0;

	if ((fp = fopen(NET_RPC_NFS, "r")) == NULL)
		return;

	memset(st_net_nfs, 0, STATS_NET_NFS_SIZE);

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "rpc ", 4)) {
			sscanf(line + 4, "%u %u",
			       &st_net_nfs->nfs_rpccnt, &st_net_nfs->nfs_rpcretrans);
		}
		else if (!strncmp(line, "proc3 ", 6)) {
			sscanf(line + 6, "%*u %*u %u %*u %*u %u %*u %u %u",
			       &getattcnt, &accesscnt, &readcnt, &writecnt);

			st_net_nfs->nfs_getattcnt += getattcnt;
			st_net_nfs->nfs_accesscnt += accesscnt;
			st_net_nfs->nfs_readcnt   += readcnt;
			st_net_nfs->nfs_writecnt  += writecnt;
		}
		else if (!strncmp(line, "proc4 ", 6)) {
			sscanf(line + 6, "%*u %*u %u %u "
			       "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u %u",
			       &readcnt, &writecnt, &accesscnt, &getattcnt);

			st_net_nfs->nfs_getattcnt += getattcnt;
			st_net_nfs->nfs_accesscnt += accesscnt;
			st_net_nfs->nfs_readcnt   += readcnt;
			st_net_nfs->nfs_writecnt  += writecnt;
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read NFS server statistics from /proc/net/rpc/nfsd.
 *
 * IN:
 * @st_net_nfsd	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_nfsd	Structure with statistics.
 ***************************************************************************
 */
void read_net_nfsd(struct stats_net_nfsd *st_net_nfsd)
{
	FILE *fp;
	char line[256];
	unsigned int getattcnt = 0, accesscnt = 0, readcnt = 0, writecnt = 0;

	if ((fp = fopen(NET_RPC_NFSD, "r")) == NULL)
		return;

	memset(st_net_nfsd, 0, STATS_NET_NFSD_SIZE);

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "rc ", 3)) {
			sscanf(line + 3, "%u %u",
			       &st_net_nfsd->nfsd_rchits, &st_net_nfsd->nfsd_rcmisses);
		}
		else if (!strncmp(line, "net ", 4)) {
			sscanf(line + 4, "%u %u %u",
			       &st_net_nfsd->nfsd_netcnt, &st_net_nfsd->nfsd_netudpcnt,
			       &st_net_nfsd->nfsd_nettcpcnt);
		}
		else if (!strncmp(line, "rpc ", 4)) {
			sscanf(line + 4, "%u %u",
			       &st_net_nfsd->nfsd_rpccnt, &st_net_nfsd->nfsd_rpcbad);
		}
		else if (!strncmp(line, "proc3 ", 6)) {
			sscanf(line + 6, "%*u %*u %u %*u %*u %u %*u %u %u",
			       &getattcnt, &accesscnt, &readcnt, &writecnt);

			st_net_nfsd->nfsd_getattcnt += getattcnt;
			st_net_nfsd->nfsd_accesscnt += accesscnt;
			st_net_nfsd->nfsd_readcnt   += readcnt;
			st_net_nfsd->nfsd_writecnt  += writecnt;

		}
		else if (!strncmp(line, "proc4ops ", 9)) {
			sscanf(line + 9, "%*u %*u %*u %*u %u "
			       "%*u %*u %*u %*u %*u %u "
			       "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u "
			       "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u",
			       &accesscnt, &getattcnt, &readcnt, &writecnt);

			st_net_nfsd->nfsd_getattcnt += getattcnt;
			st_net_nfsd->nfsd_accesscnt += accesscnt;
			st_net_nfsd->nfsd_readcnt   += readcnt;
			st_net_nfsd->nfsd_writecnt  += writecnt;
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read network sockets statistics from /proc/net/sockstat.
 *
 * IN:
 * @st_net_sock	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_sock	Structure with statistics.
 ***************************************************************************
 */
void read_net_sock(struct stats_net_sock *st_net_sock)
{
	FILE *fp;
	char line[96];
	char *p;

	if ((fp = fopen(NET_SOCKSTAT, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "sockets:", 8)) {
			/* Sockets */
			sscanf(line + 14, "%u", &st_net_sock->sock_inuse);
		}
		else if (!strncmp(line, "TCP:", 4)) {
			/* TCP sockets */
			sscanf(line + 11, "%u", &st_net_sock->tcp_inuse);
			if ((p = strstr(line, "tw")) != NULL) {
				sscanf(p + 2, "%u", &st_net_sock->tcp_tw);
			}
		}
		else if (!strncmp(line, "UDP:", 4)) {
			/* UDP sockets */
			sscanf(line + 11, "%u", &st_net_sock->udp_inuse);
		}
		else if (!strncmp(line, "RAW:", 4)) {
			/* RAW sockets */
			sscanf(line + 11, "%u", &st_net_sock->raw_inuse);
		}
		else if (!strncmp(line, "FRAG:", 5)) {
			/* FRAGments */
			sscanf(line + 12, "%u", &st_net_sock->frag_inuse);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read IP network traffic statistics from /proc/net/snmp.
 *
 * IN:
 * @st_net_ip	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_ip	Structure with statistics.
 ***************************************************************************
 */
void read_net_ip(struct stats_net_ip *st_net_ip)
{
	FILE *fp;
	char line[1024];
	int sw = FALSE;

	if ((fp = fopen(NET_SNMP, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Ip:", 3)) {
			if (sw) {
				sscanf(line + 3, "%*u %*u %llu %*u %*u %llu %*u %*u "
				       "%llu %llu %*u %*u %*u %llu %llu %*u %llu %*u %llu",
				       &st_net_ip->InReceives,
				       &st_net_ip->ForwDatagrams,
				       &st_net_ip->InDelivers,
				       &st_net_ip->OutRequests,
				       &st_net_ip->ReasmReqds,
				       &st_net_ip->ReasmOKs,
				       &st_net_ip->FragOKs,
				       &st_net_ip->FragCreates);

				break;
			}
			else {
				sw = TRUE;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read IP network errors statistics from /proc/net/snmp.
 *
 * IN:
 * @st_net_eip	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_eip	Structure with statistics.
 ***************************************************************************
 */
void read_net_eip(struct stats_net_eip *st_net_eip)
{
	FILE *fp;
	char line[1024];
	int sw = FALSE;

	if ((fp = fopen(NET_SNMP, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Ip:", 3)) {
			if (sw) {
				sscanf(line + 3, "%*u %*u %*u %llu %llu %*u %llu %llu "
				       "%*u %*u %llu %llu %*u %*u %*u %llu %*u %llu",
				       &st_net_eip->InHdrErrors,
				       &st_net_eip->InAddrErrors,
				       &st_net_eip->InUnknownProtos,
				       &st_net_eip->InDiscards,
				       &st_net_eip->OutDiscards,
				       &st_net_eip->OutNoRoutes,
				       &st_net_eip->ReasmFails,
				       &st_net_eip->FragFails);

				break;
			}
			else {
				sw = TRUE;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read ICMP network traffic statistics from /proc/net/snmp.
 *
 * IN:
 * @st_net_icmp	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_icmp	Structure with statistics.
 ***************************************************************************
 */
void read_net_icmp(struct stats_net_icmp *st_net_icmp)
{
	FILE *fp;
	char line[1024];
	static char format[256] = "";
	int sw = FALSE;

	if ((fp = fopen(NET_SNMP, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Icmp:", 5)) {
			if (sw) {
				sscanf(line + 5, format,
				       &st_net_icmp->InMsgs,
				       &st_net_icmp->InEchos,
				       &st_net_icmp->InEchoReps,
				       &st_net_icmp->InTimestamps,
				       &st_net_icmp->InTimestampReps,
				       &st_net_icmp->InAddrMasks,
				       &st_net_icmp->InAddrMaskReps,
				       &st_net_icmp->OutMsgs,
				       &st_net_icmp->OutEchos,
				       &st_net_icmp->OutEchoReps,
				       &st_net_icmp->OutTimestamps,
				       &st_net_icmp->OutTimestampReps,
				       &st_net_icmp->OutAddrMasks,
				       &st_net_icmp->OutAddrMaskReps);

				break;
			}
			else {
				if (!strlen(format)) {
					if (strstr(line, "InCsumErrors")) {
						/*
						 * New format: InCsumErrors field exists at position #3.
						 * Capture: 1,9,10,11,12,13,14,15,22,23,24,25,26,27.
						 */
						strcpy(format, "%lu %*u %*u %*u %*u %*u %*u %*u "
							       "%lu %lu %lu %lu %lu %lu %lu %*u %*u %*u %*u "
							       "%*u %*u %lu %lu %lu %lu %lu %lu");
					}
					else {
						/*
						 * Old format: InCsumErrors field doesn't exist.
						 * Capture: 1,8,9,10,11,12,13,14,21,22,23,24,25,26.
						 */
						strcpy(format, "%lu %*u %*u %*u %*u %*u %*u "
							       "%lu %lu %lu %lu %lu %lu %lu %*u %*u %*u %*u "
							       "%*u %*u %lu %lu %lu %lu %lu %lu");
					}
				}
				sw = TRUE;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read ICMP network errors statistics from /proc/net/snmp.
 *
 * IN:
 * @st_net_eicmp	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_eicmp	Structure with statistics.
 ***************************************************************************
 */
void read_net_eicmp(struct stats_net_eicmp *st_net_eicmp)
{
	FILE *fp;
	char line[1024];
	int sw = FALSE;

	if ((fp = fopen(NET_SNMP, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Icmp:", 5)) {
			if (sw) {
				sscanf(line + 5, "%*u %lu %lu %lu %lu %lu %lu %*u %*u "
				       "%*u %*u %*u %*u %*u %lu %lu %lu %lu %lu %lu",
				       &st_net_eicmp->InErrors,
				       &st_net_eicmp->InDestUnreachs,
				       &st_net_eicmp->InTimeExcds,
				       &st_net_eicmp->InParmProbs,
				       &st_net_eicmp->InSrcQuenchs,
				       &st_net_eicmp->InRedirects,
				       &st_net_eicmp->OutErrors,
				       &st_net_eicmp->OutDestUnreachs,
				       &st_net_eicmp->OutTimeExcds,
				       &st_net_eicmp->OutParmProbs,
				       &st_net_eicmp->OutSrcQuenchs,
				       &st_net_eicmp->OutRedirects);

				break;
			}
			else {
				sw = TRUE;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read TCP network traffic statistics from /proc/net/snmp.
 *
 * IN:
 * @st_net_tcp	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_tcp	Structure with statistics.
 ***************************************************************************
 */
void read_net_tcp(struct stats_net_tcp *st_net_tcp)
{
	FILE *fp;
	char line[1024];
	int sw = FALSE;

	if ((fp = fopen(NET_SNMP, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Tcp:", 4)) {
			if (sw) {
				sscanf(line + 4, "%*u %*u %*u %*d %lu %lu "
				       "%*u %*u %*u %lu %lu",
				       &st_net_tcp->ActiveOpens,
				       &st_net_tcp->PassiveOpens,
				       &st_net_tcp->InSegs,
				       &st_net_tcp->OutSegs);

				break;
			}
			else {
				sw = TRUE;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read TCP network errors statistics from /proc/net/snmp.
 *
 * IN:
 * @st_net_etcp	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_etcp	Structure with statistics.
 ***************************************************************************
 */
void read_net_etcp(struct stats_net_etcp *st_net_etcp)
{
	FILE *fp;
	char line[1024];
	int sw = FALSE;

	if ((fp = fopen(NET_SNMP, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Tcp:", 4)) {
			if (sw) {
				sscanf(line + 4, "%*u %*u %*u %*d %*u %*u "
				       "%lu %lu %*u %*u %*u %lu %lu %lu",
				       &st_net_etcp->AttemptFails,
				       &st_net_etcp->EstabResets,
				       &st_net_etcp->RetransSegs,
				       &st_net_etcp->InErrs,
				       &st_net_etcp->OutRsts);

				break;
			}
			else {
				sw = TRUE;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read UDP network traffic statistics from /proc/net/snmp.
 *
 * IN:
 * @st_net_udp	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_udp	Structure with statistics.
 ***************************************************************************
 */
void read_net_udp(struct stats_net_udp *st_net_udp)
{
	FILE *fp;
	char line[1024];
	int sw = FALSE;

	if ((fp = fopen(NET_SNMP, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Udp:", 4)) {
			if (sw) {
				sscanf(line + 4, "%lu %lu %lu %lu",
				       &st_net_udp->InDatagrams,
				       &st_net_udp->NoPorts,
				       &st_net_udp->InErrors,
				       &st_net_udp->OutDatagrams);

				break;
			}
			else {
				sw = TRUE;
			}
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read IPv6 network sockets statistics from /proc/net/sockstat6.
 *
 * IN:
 * @st_net_sock6	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_sock6	Structure with statistics.
 ***************************************************************************
 */
void read_net_sock6(struct stats_net_sock6 *st_net_sock6)
{
	FILE *fp;
	char line[96];

	if ((fp = fopen(NET_SOCKSTAT6, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "TCP6:", 5)) {
			/* TCPv6 sockets */
			sscanf(line + 12, "%u", &st_net_sock6->tcp6_inuse);
		}
		else if (!strncmp(line, "UDP6:", 5)) {
			/* UDPv6 sockets */
			sscanf(line + 12, "%u", &st_net_sock6->udp6_inuse);
		}
		else if (!strncmp(line, "RAW6:", 5)) {
			/* IPv6 RAW sockets */
			sscanf(line + 12, "%u", &st_net_sock6->raw6_inuse);
		}
		else if (!strncmp(line, "FRAG6:", 6)) {
			/* IPv6 FRAGments */
			sscanf(line + 13, "%u", &st_net_sock6->frag6_inuse);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read IPv6 network traffic statistics from /proc/net/snmp6.
 *
 * IN:
 * @st_net_ip6	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_ip6	Structure with statistics.
 ***************************************************************************
 */
void read_net_ip6(struct stats_net_ip6 *st_net_ip6)
{
	FILE *fp;
	char line[128];

	if ((fp = fopen(NET_SNMP6, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Ip6InReceives ", 14)) {
			sscanf(line + 14, "%llu", &st_net_ip6->InReceives6);
		}
		else if (!strncmp(line, "Ip6OutForwDatagrams ", 20)) {
			sscanf(line + 20, "%llu", &st_net_ip6->OutForwDatagrams6);
		}
		else if (!strncmp(line, "Ip6InDelivers ", 14)) {
			sscanf(line + 14, "%llu", &st_net_ip6->InDelivers6);
		}
		else if (!strncmp(line, "Ip6OutRequests ", 15)) {
			sscanf(line + 15, "%llu", &st_net_ip6->OutRequests6);
		}
		else if (!strncmp(line, "Ip6ReasmReqds ", 14)) {
			sscanf(line + 14, "%llu", &st_net_ip6->ReasmReqds6);
		}
		else if (!strncmp(line, "Ip6ReasmOKs ", 12)) {
			sscanf(line + 12, "%llu", &st_net_ip6->ReasmOKs6);
		}
		else if (!strncmp(line, "Ip6InMcastPkts ", 15)) {
			sscanf(line + 15, "%llu", &st_net_ip6->InMcastPkts6);
		}
		else if (!strncmp(line, "Ip6OutMcastPkts ", 16)) {
			sscanf(line + 16, "%llu", &st_net_ip6->OutMcastPkts6);
		}
		else if (!strncmp(line, "Ip6FragOKs ", 11)) {
			sscanf(line + 11, "%llu", &st_net_ip6->FragOKs6);
		}
		else if (!strncmp(line, "Ip6FragCreates ", 15)) {
			sscanf(line + 15, "%llu", &st_net_ip6->FragCreates6);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read IPv6 network errors statistics from /proc/net/snmp6.
 *
 * IN:
 * @st_net_eip6	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_eip6	Structure with statistics.
 ***************************************************************************
 */
void read_net_eip6(struct stats_net_eip6 *st_net_eip6)
{
	FILE *fp;
	char line[128];

	if ((fp = fopen(NET_SNMP6, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Ip6InHdrErrors ", 15)) {
			sscanf(line + 15, "%llu", &st_net_eip6->InHdrErrors6);
		}
		else if (!strncmp(line, "Ip6InAddrErrors ", 16)) {
			sscanf(line + 16, "%llu", &st_net_eip6->InAddrErrors6);
		}
		else if (!strncmp(line, "Ip6InUnknownProtos ", 19)) {
			sscanf(line + 19, "%llu", &st_net_eip6->InUnknownProtos6);
		}
		else if (!strncmp(line, "Ip6InTooBigErrors ", 18)) {
			sscanf(line + 18, "%llu", &st_net_eip6->InTooBigErrors6);
		}
		else if (!strncmp(line, "Ip6InDiscards ", 14)) {
			sscanf(line + 14, "%llu", &st_net_eip6->InDiscards6);
		}
		else if (!strncmp(line, "Ip6OutDiscards ", 15)) {
			sscanf(line + 15, "%llu", &st_net_eip6->OutDiscards6);
		}
		else if (!strncmp(line, "Ip6InNoRoutes ", 14)) {
			sscanf(line + 14, "%llu", &st_net_eip6->InNoRoutes6);
		}
		else if (!strncmp(line, "Ip6OutNoRoutes ", 15)) {
			sscanf(line + 15, "%llu", &st_net_eip6->OutNoRoutes6);
		}
		else if (!strncmp(line, "Ip6ReasmFails ", 14)) {
			sscanf(line + 14, "%llu", &st_net_eip6->ReasmFails6);
		}
		else if (!strncmp(line, "Ip6FragFails ", 13)) {
			sscanf(line + 13, "%llu", &st_net_eip6->FragFails6);
		}
		else if (!strncmp(line, "Ip6InTruncatedPkts ", 19)) {
			sscanf(line + 19, "%llu", &st_net_eip6->InTruncatedPkts6);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read ICMPv6 network traffic statistics from /proc/net/snmp6.
 *
 * IN:
 * @st_net_icmp6	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_icmp6	Structure with statistics.
 ***************************************************************************
 */
void read_net_icmp6(struct stats_net_icmp6 *st_net_icmp6)
{
	FILE *fp;
	char line[128];

	if ((fp = fopen(NET_SNMP6, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Icmp6InMsgs ", 12)) {
			sscanf(line + 12, "%lu", &st_net_icmp6->InMsgs6);
		}
		else if (!strncmp(line, "Icmp6OutMsgs ", 13)) {
			sscanf(line + 13, "%lu", &st_net_icmp6->OutMsgs6);
		}
		else if (!strncmp(line, "Icmp6InEchos ", 13)) {
			sscanf(line + 13, "%lu", &st_net_icmp6->InEchos6);
		}
		else if (!strncmp(line, "Icmp6InEchoReplies ", 19)) {
			sscanf(line + 19, "%lu", &st_net_icmp6->InEchoReplies6);
		}
		else if (!strncmp(line, "Icmp6OutEchoReplies ", 20)) {
			sscanf(line + 20, "%lu", &st_net_icmp6->OutEchoReplies6);
		}
		else if (!strncmp(line, "Icmp6InGroupMembQueries ", 24)) {
			sscanf(line + 24, "%lu", &st_net_icmp6->InGroupMembQueries6);
		}
		else if (!strncmp(line, "Icmp6InGroupMembResponses ", 26)) {
			sscanf(line + 26, "%lu", &st_net_icmp6->InGroupMembResponses6);
		}
		else if (!strncmp(line, "Icmp6OutGroupMembResponses ", 27)) {
			sscanf(line + 27, "%lu", &st_net_icmp6->OutGroupMembResponses6);
		}
		else if (!strncmp(line, "Icmp6InGroupMembReductions ", 27)) {
			sscanf(line + 27, "%lu", &st_net_icmp6->InGroupMembReductions6);
		}
		else if (!strncmp(line, "Icmp6OutGroupMembReductions ", 28)) {
			sscanf(line + 28, "%lu", &st_net_icmp6->OutGroupMembReductions6);
		}
		else if (!strncmp(line, "Icmp6InRouterSolicits ", 22)) {
			sscanf(line + 22, "%lu", &st_net_icmp6->InRouterSolicits6);
		}
		else if (!strncmp(line, "Icmp6OutRouterSolicits ", 23)) {
			sscanf(line + 23, "%lu", &st_net_icmp6->OutRouterSolicits6);
		}
		else if (!strncmp(line, "Icmp6InRouterAdvertisements ", 28)) {
			sscanf(line + 28, "%lu", &st_net_icmp6->InRouterAdvertisements6);
		}
		else if (!strncmp(line, "Icmp6InNeighborSolicits ", 24)) {
			sscanf(line + 24, "%lu", &st_net_icmp6->InNeighborSolicits6);
		}
		else if (!strncmp(line, "Icmp6OutNeighborSolicits ", 25)) {
			sscanf(line + 25, "%lu", &st_net_icmp6->OutNeighborSolicits6);
		}
		else if (!strncmp(line, "Icmp6InNeighborAdvertisements ", 30)) {
			sscanf(line + 30, "%lu", &st_net_icmp6->InNeighborAdvertisements6);
		}
		else if (!strncmp(line, "Icmp6OutNeighborAdvertisements ", 31)) {
			sscanf(line + 31, "%lu", &st_net_icmp6->OutNeighborAdvertisements6);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read ICMPv6 network errors statistics from /proc/net/snmp6.
 *
 * IN:
 * @st_net_eicmp6	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_eicmp6	Structure with statistics.
 ***************************************************************************
 */
void read_net_eicmp6(struct stats_net_eicmp6 *st_net_eicmp6)
{
	FILE *fp;
	char line[128];

	if ((fp = fopen(NET_SNMP6, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Icmp6InErrors ", 14)) {
			sscanf(line + 14, "%lu", &st_net_eicmp6->InErrors6);
		}
		else if (!strncmp(line, "Icmp6InDestUnreachs ", 20)) {
			sscanf(line + 20, "%lu", &st_net_eicmp6->InDestUnreachs6);
		}
		else if (!strncmp(line, "Icmp6OutDestUnreachs ", 21)) {
			sscanf(line + 21, "%lu", &st_net_eicmp6->OutDestUnreachs6);
		}
		else if (!strncmp(line, "Icmp6InTimeExcds ", 17)) {
			sscanf(line + 17, "%lu", &st_net_eicmp6->InTimeExcds6);
		}
		else if (!strncmp(line, "Icmp6OutTimeExcds ", 18)) {
			sscanf(line + 18, "%lu", &st_net_eicmp6->OutTimeExcds6);
		}
		else if (!strncmp(line, "Icmp6InParmProblems ", 20)) {
			sscanf(line + 20, "%lu", &st_net_eicmp6->InParmProblems6);
		}
		else if (!strncmp(line, "Icmp6OutParmProblems ", 21)) {
			sscanf(line + 21, "%lu", &st_net_eicmp6->OutParmProblems6);
		}
		else if (!strncmp(line, "Icmp6InRedirects ", 17)) {
			sscanf(line + 17, "%lu", &st_net_eicmp6->InRedirects6);
		}
		else if (!strncmp(line, "Icmp6OutRedirects ", 18)) {
			sscanf(line + 18, "%lu", &st_net_eicmp6->OutRedirects6);
		}
		else if (!strncmp(line, "Icmp6InPktTooBigs ", 18)) {
			sscanf(line + 18, "%lu", &st_net_eicmp6->InPktTooBigs6);
		}
		else if (!strncmp(line, "Icmp6OutPktTooBigs ", 19)) {
			sscanf(line + 19, "%lu", &st_net_eicmp6->OutPktTooBigs6);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read UDPv6 network traffic statistics from /proc/net/snmp6.
 *
 * IN:
 * @st_net_udp6	Structure where stats will be saved.
 *
 * OUT:
 * @st_net_udp6	Structure with statistics.
 ***************************************************************************
 */
void read_net_udp6(struct stats_net_udp6 *st_net_udp6)
{
	FILE *fp;
	char line[128];

	if ((fp = fopen(NET_SNMP6, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Udp6InDatagrams ", 16)) {
			sscanf(line + 16, "%lu", &st_net_udp6->InDatagrams6);
		}
		else if (!strncmp(line, "Udp6OutDatagrams ", 17)) {
			sscanf(line + 17, "%lu", &st_net_udp6->OutDatagrams6);
		}
		else if (!strncmp(line, "Udp6NoPorts ", 12)) {
			sscanf(line + 12, "%lu", &st_net_udp6->NoPorts6);
		}
		else if (!strncmp(line, "Udp6InErrors ", 13)) {
			sscanf(line + 13, "%lu", &st_net_udp6->InErrors6);
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read CPU frequency statistics.
 *
 * IN:
 * @st_pwr_cpufreq	Structure where stats will be saved.
 * @nbr			Total number of CPU (including cpu "all").
 *
 * OUT:
 * @st_pwr_cpufreq	Structure with statistics.
 ***************************************************************************
 */
void read_cpuinfo(struct stats_pwr_cpufreq *st_pwr_cpufreq, int nbr)
{
	FILE *fp;
	struct stats_pwr_cpufreq *st_pwr_cpufreq_i;
	char line[1024];
	int nr = 0;
	unsigned int proc_nb = 0, ifreq, dfreq;

	if ((fp = fopen(CPUINFO, "r")) == NULL)
		return;

	st_pwr_cpufreq->cpufreq = 0;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "processor\t", 10)) {
			sscanf(strchr(line, ':') + 1, "%u", &proc_nb);
		}

		/* Entry in /proc/cpuinfo is different between Intel and Power architectures */
		else if (!strncmp(line, "cpu MHz\t", 8) ||
			 !strncmp(line, "clock\t", 6)) {
			sscanf(strchr(line, ':') + 1, "%u.%u", &ifreq, &dfreq);

			if (proc_nb < (nbr - 1)) {
				/* Save current CPU frequency */
				st_pwr_cpufreq_i = st_pwr_cpufreq + proc_nb + 1;
				st_pwr_cpufreq_i->cpufreq = ifreq * 100 + dfreq / 10;

				/* Also save it to compute an average CPU frequency */
				st_pwr_cpufreq->cpufreq += st_pwr_cpufreq_i->cpufreq;
				nr++;
			}
			else if (!proc_nb && (nbr == 1)) {
				/*
				 * We are reading freq for "Processor 0" and we have a machine
				 * with only one processor and not an SMP kernel, with /sys not mounted
				 * (the nr of proc has been counted using /proc/stat and there was
				 * only one line with global CPU stats here).
				 * This is a very specific case, I must admit...
				 */
				st_pwr_cpufreq->cpufreq = ifreq * 100 + dfreq / 10;
			}
		}
	}

	fclose(fp);

	if (nr) {
		/* Compute average CPU frequency for this machine */
		st_pwr_cpufreq->cpufreq /= nr;
	}
}

/*
 ***************************************************************************
 * Read hugepages statistics from /proc/meminfo.
 *
 * IN:
 * @st_huge	Structure where stats will be saved.
 *
 * OUT:
 * @st_huge	Structure with statistics.
 ***************************************************************************
 */
void read_meminfo_huge(struct stats_huge *st_huge)
{
	FILE *fp;
	char line[128];
	unsigned long szhkb = 0;

	if ((fp = fopen(MEMINFO, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "HugePages_Total:", 16)) {
			/* Read the total number of huge pages */
			sscanf(line + 16, "%lu", &st_huge->tlhkb);
		}
		else if (!strncmp(line, "HugePages_Free:", 15)) {
			/* Read the number of free huge pages */
			sscanf(line + 15, "%lu", &st_huge->frhkb);
		}
		else if (!strncmp(line, "Hugepagesize:", 13)) {
			/* Read the default size of a huge page in kB */
			sscanf(line + 13, "%lu", &szhkb);
		}
	}

	fclose(fp);

	/* We want huge pages stats in kB and not expressed in a number of pages */
	st_huge->tlhkb *= szhkb;
	st_huge->frhkb *= szhkb;
}

/*
 ***************************************************************************
 * Read CPU average frequencies statistics.
 *
 * IN:
 * @st_pwr_wghfreq	Structure where stats will be saved.
 * @cpu_nr		CPU number for which time_in_state date will be read.
 * @nbr			Total number of states (frequencies).
 *
 * OUT:
 * @st_pwr_wghfreq	Structure with statistics.
 ***************************************************************************
 */
void read_time_in_state(struct stats_pwr_wghfreq *st_pwr_wghfreq, int cpu_nr, int nbr)
{
	FILE *fp;
	struct stats_pwr_wghfreq *st_pwr_wghfreq_j;
	char filename[MAX_PF_NAME];
	char line[128];
	int j = 0;
	unsigned long freq;
	unsigned long long time_in_state;

	snprintf(filename, MAX_PF_NAME, "%s/cpu%d/%s",
		 SYSFS_DEVCPU, cpu_nr, SYSFS_TIME_IN_STATE);
	if ((fp = fopen(filename, "r")) == NULL)
		return;

	while (fgets(line, sizeof(line), fp) != NULL) {

		sscanf(line, "%lu %llu", &freq, &time_in_state);

		if (j < nbr) {
			/* Save current frequency and time */
			st_pwr_wghfreq_j = st_pwr_wghfreq + j;
			st_pwr_wghfreq_j->freq = freq;
			st_pwr_wghfreq_j->time_in_state = time_in_state;
			j++;
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read current USB device data.
 *
 * IN:
 * @st_pwr_usb		Structure where stats will be saved.
 * @usb_device		File name for current USB device.
 *
 * OUT:
 * @st_pwr_usb		Structure with statistics.
 ***************************************************************************
 */
void read_usb_stats(struct stats_pwr_usb *st_pwr_usb, char *usb_device)
{
	int l, rc;
	FILE *fp;
	char * rs;
	char filename[MAX_PF_NAME];

	/* Get USB device bus number */
	sscanf(usb_device, "%u", &st_pwr_usb->bus_nr);

	/* Read USB device vendor ID */
	snprintf(filename, MAX_PF_NAME, "%s/%s/%s",
		 SYSFS_USBDEV, usb_device, SYSFS_IDVENDOR);
	if ((fp = fopen(filename, "r")) != NULL) {
		rc = fscanf(fp, "%x",
			    &st_pwr_usb->vendor_id);
		fclose(fp);
		if (rc == 0) {
			st_pwr_usb->vendor_id = 0;
		}
	}

	/* Read USB device product ID */
	snprintf(filename, MAX_PF_NAME, "%s/%s/%s",
		 SYSFS_USBDEV, usb_device, SYSFS_IDPRODUCT);
	if ((fp = fopen(filename, "r")) != NULL) {
		rc = fscanf(fp, "%x",
			    &st_pwr_usb->product_id);
		fclose(fp);
		if (rc == 0) {
			st_pwr_usb->product_id = 0;
		}
	}

	/* Read USB device max power consumption */
	snprintf(filename, MAX_PF_NAME, "%s/%s/%s",
		 SYSFS_USBDEV, usb_device, SYSFS_BMAXPOWER);
	if ((fp = fopen(filename, "r")) != NULL) {
		rc = fscanf(fp, "%u",
			    &st_pwr_usb->bmaxpower);
		fclose(fp);
		if (rc == 0) {
			st_pwr_usb->bmaxpower = 0;
		}
	}

	/* Read USB device manufacturer */
	snprintf(filename, MAX_PF_NAME, "%s/%s/%s",
		 SYSFS_USBDEV, usb_device, SYSFS_MANUFACTURER);
	if ((fp = fopen(filename, "r")) != NULL) {
		rs = fgets(st_pwr_usb->manufacturer,
			   MAX_MANUF_LEN - 1, fp);
		fclose(fp);
		if ((rs != NULL) &&
		    (l = strlen(st_pwr_usb->manufacturer)) > 0) {
			/* Remove trailing CR */
			st_pwr_usb->manufacturer[l - 1] = '\0';
		}
	}

	/* Read USB device product */
	snprintf(filename, MAX_PF_NAME, "%s/%s/%s",
		 SYSFS_USBDEV, usb_device, SYSFS_PRODUCT);
	if ((fp = fopen(filename, "r")) != NULL) {
		rs = fgets(st_pwr_usb->product,
			   MAX_PROD_LEN - 1, fp);
		fclose(fp);
		if ((rs != NULL) &&
		    (l = strlen(st_pwr_usb->product)) > 0) {
			/* Remove trailing CR */
			st_pwr_usb->product[l - 1] = '\0';
		}
	}
}

/*
 ***************************************************************************
 * Read USB devices statistics.
 *
 * IN:
 * @st_pwr_usb		Structure where stats will be saved.
 * @nbr			Total number of USB devices.
 *
 * OUT:
 * @st_pwr_usb		Structure with statistics.
 ***************************************************************************
 */
void read_bus_usb_dev(struct stats_pwr_usb *st_pwr_usb, int nbr)
{
	DIR *dir;
	struct dirent *drd;
	struct stats_pwr_usb *st_pwr_usb_j;
	int j = 0;

	/* Open relevant /sys directory */
	if ((dir = opendir(SYSFS_USBDEV)) == NULL)
		return;

	/* Get current file entry */
	while ((drd = readdir(dir)) != NULL) {

		if (isdigit(drd->d_name[0]) && !strchr(drd->d_name, ':')) {
			if (j < nbr) {
				/* Read current USB device data */
				st_pwr_usb_j = st_pwr_usb + j;
				read_usb_stats(st_pwr_usb_j, drd->d_name);
				j++;
			}
			else
				break;
		}
	}

	/* Close directory */
	closedir(dir);
}

/*
 ***************************************************************************
 * Read filesystems statistics.
 *
 * IN:
 * @st_filesystem	Structure where stats will be saved.
 * @nbr			Total number of filesystems.
 *
 * OUT:
 * @st_filesystem	Structure with statistics.
 ***************************************************************************
 */
void read_filesystem(struct stats_filesystem *st_filesystem, int nbr)
{
	FILE *fp;
	char line[512], fs_name[128], mountp[256];
	int fs = 0, skip = 0, skip_next = 0;
	char *pos = 0;
	struct stats_filesystem *st_filesystem_i;
	struct statvfs buf;

	if ((fp = fopen(MTAB, "r")) == NULL)
		return;

	while ((fgets(line, sizeof(line), fp) != NULL) && (fs < nbr)) {
		/*
		 * Ignore line if the preceding line did not contain '\n'.
		 * (Some very long lines may be found for instance when
		 * overlay2 filesystem with docker is used).
		 */
		skip = skip_next;
		skip_next = (strchr(line, '\n') == NULL);
		if (skip)
			continue;

		if (line[0] == '/') {
			/* Find field separator position */
			pos = strchr(line, ' ');
			if (pos == NULL)
				continue;

			/* Read current filesystem name */
			sscanf(line, "%127s", fs_name);
			/*
			 * And now read the corresponding mount point.
			 * Read fs name and mount point in two distinct operations.
			 * Indeed, if fs name length is greater than 127 chars,
			 * previous scanf() will read only the first 127 chars, and
			 * mount point name will be read using the remaining chars
			 * from the fs name. This will result in a bogus name
			 * and following statvfs() function will always fail.
			 */
			sscanf(pos + 1, "%255s", mountp);

			/* Replace octal codes */
			oct2chr(mountp);

			/*
			 * It's important to have read the whole mount point name
			 * for statvfs() to work properly (see above).
			 */
			if ((statvfs(mountp, &buf) < 0) || (!buf.f_blocks))
				continue;

			st_filesystem_i = st_filesystem + fs++;
			st_filesystem_i->f_blocks = (unsigned long long) buf.f_blocks * (unsigned long long) buf.f_frsize;
			st_filesystem_i->f_bfree  = (unsigned long long) buf.f_bfree * (unsigned long long) buf.f_frsize;
			st_filesystem_i->f_bavail = (unsigned long long) buf.f_bavail * (unsigned long long) buf.f_frsize;
			st_filesystem_i->f_files  = (unsigned long long) buf.f_files;
			st_filesystem_i->f_ffree  = (unsigned long long) buf.f_ffree;
			strncpy(st_filesystem_i->fs_name, fs_name, MAX_FS_LEN);
			st_filesystem_i->fs_name[MAX_FS_LEN - 1] = '\0';
			strncpy(st_filesystem_i->mountp, mountp, MAX_FS_LEN);
			st_filesystem_i->mountp[MAX_FS_LEN - 1] = '\0';
		}
	}

	fclose(fp);
}

/*
 ***************************************************************************
 * Read Fibre Channel HBA statistics.
 *
 * IN:
 * @st_fc	Structure where stats will be saved.
 * @nbr		Total number of HBAs.
 *
 * OUT:
 * @st_fc	Structure with statistics.
 ***************************************************************************
 */
void read_fchost(struct stats_fchost *st_fc, int nbr)
{
	DIR *dir;
	FILE *fp;
	struct dirent *drd;
	struct stats_fchost *st_fc_i;
	int fch = 0;
	char fcstat_filename[MAX_PF_NAME];
	char line[256];
	unsigned long rx_frames, tx_frames, rx_words, tx_words;

	/* Each host, if present, will have its own hostX entry within SYSFS_FCHOST */
	if ((dir = opendir(SYSFS_FCHOST)) == NULL)
		return; /* No FC hosts */

	/*
	 * Read each of the counters via sysfs, where they are
	 * returned as hex values (e.g. 0x72400).
	 */
	while (((drd = readdir(dir)) != NULL) && (fch < nbr)) {
		rx_frames = tx_frames = rx_words = tx_words = 0;

		if (!strncmp(drd->d_name, "host", 4)) {

			snprintf(fcstat_filename, MAX_PF_NAME, FC_RX_FRAMES,
				 SYSFS_FCHOST, drd->d_name);
			if ((fp = fopen(fcstat_filename, "r"))) {
				if (fgets(line, sizeof(line), fp)) {
					sscanf(line, "%lx", &rx_frames);
				}
				fclose(fp);
			}

			snprintf(fcstat_filename, MAX_PF_NAME, FC_TX_FRAMES,
				 SYSFS_FCHOST, drd->d_name);
			if ((fp = fopen(fcstat_filename, "r"))) {
				if (fgets(line, sizeof(line), fp)) {
					sscanf(line, "%lx", &tx_frames);
				}
				fclose(fp);
			}

			snprintf(fcstat_filename, MAX_PF_NAME, FC_RX_WORDS,
				 SYSFS_FCHOST, drd->d_name);
			if ((fp = fopen(fcstat_filename, "r"))) {
				if (fgets(line, sizeof(line), fp)) {
					sscanf(line, "%lx", &rx_words);
				}
				fclose(fp);
			}

			snprintf(fcstat_filename, MAX_PF_NAME, FC_TX_WORDS,
				 SYSFS_FCHOST, drd->d_name);
			if ((fp = fopen(fcstat_filename, "r"))) {
				if (fgets(line, sizeof(line), fp)) {
					sscanf(line, "%lx", &tx_words);
				}
				fclose(fp);
			}

			st_fc_i = st_fc + fch++;
			st_fc_i->f_rxframes = rx_frames;
			st_fc_i->f_txframes = tx_frames;
			st_fc_i->f_rxwords  = rx_words;
			st_fc_i->f_txwords  = tx_words;
			strncpy(st_fc_i->fchost_name, drd->d_name, MAX_FCH_LEN);
			st_fc_i->fchost_name[MAX_FCH_LEN - 1] = '\0';
		}

	}
	closedir(dir);
}

/*
 ***************************************************************************
 * Read softnet statistics.
 *
 * IN:
 * @st_softnet	Structure where stats will be saved.
 * @nbr		Total number of CPU (including cpu "all").
 *
 * OUT:
 * @st_softnet	Structure with statistics.
 ***************************************************************************
 */
void read_softnet(struct stats_softnet *st_softnet, int nbr)
{
	FILE *fp;
	struct stats_softnet *st_softnet_i;
	char line[1024];
	unsigned int proc_nb = 1;

	/* Open /proc/net/softnet_stat file */
	if ((fp = fopen(NET_SOFTNET, "r")) == NULL)
		return;

	/*
	 * Init a structure that will contain the values for CPU "all".
	 * CPU "all" doesn't exist in /proc/net/softnet_stat file, so
	 * we compute its values as the sum of the values of each CPU.
	 */
	memset(st_softnet, 0, sizeof(struct stats_softnet));

	while ((fgets(line, sizeof(line), fp) != NULL) && (proc_nb < nbr)) {

		st_softnet_i = st_softnet + proc_nb++;
		sscanf(line, "%x %x %x %*x %*x %*x %*x %*x %*x %x %x",
		       &st_softnet_i->processed,
		       &st_softnet_i->dropped,
		       &st_softnet_i->time_squeeze,
		       &st_softnet_i->received_rps,
		       &st_softnet_i->flow_limit);

		st_softnet->processed += st_softnet_i->processed;
		st_softnet->dropped += st_softnet_i->dropped;
		st_softnet->time_squeeze += st_softnet_i->time_squeeze;
		st_softnet->received_rps += st_softnet_i->received_rps;
		st_softnet->flow_limit += st_softnet_i->flow_limit;
	}

	fclose(fp);
}

/*------------------ END: FUNCTIONS USED BY SADC ONLY ---------------------*/
#endif /* SOURCE_SADC */
