[libresource-devel] [PATCH] library of interfaces to fetch system resource information

Rahul Yadav rahul.x.yadav at oracle.com
Thu Mar 1 06:58:19 UTC 2018


1: System resource Information related to  memory, CPU, stat, networking,
security etc.
2: Currently most of such information is read from /proc and /sys.
3: Add basic infrastructure and code for some of memory and network related
information.
4: Update README file with the use cases and design details.
5: Added example codes in README.

Signed-off-by: Rahul Yadav <rahul.x.yadav at oracle.com>
---
 Makefile        |  18 ++++
 README.md       | 160 ++++++++++++++++++++++++++--
 resmem.c        | 218 ++++++++++++++++++++++++++++++++++++++
 resmem.h        |  16 +++
 resnet.c        | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 resnet.h        |  14 +++
 resource.c      | 211 +++++++++++++++++++++++++++++++++++++
 resource.h      | 147 ++++++++++++++++++++++++++
 resource_impl.h |  47 +++++++++
 9 files changed, 1137 insertions(+), 10 deletions(-)
 create mode 100644 Makefile
 create mode 100644 resmem.c
 create mode 100644 resmem.h
 create mode 100644 resnet.c
 create mode 100644 resnet.h
 create mode 100644 resource.c
 create mode 100644 resource.h
 create mode 100644 resource_impl.h

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2c71328
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+CC = gcc
+CFLAGS = -g -Wall -Werror -fPIC
+DEPS = resource.h res_impl.h resmem.h resnet.h
+OBJ = resource.o resmem.o resnet.o
+LIB = libresource.so
+TEST = test
+RM = rm -rf
+CP = cp
+
+%.o: %.c $(DEPS)
+	$(CC) -c -o $@ $< $(CFLAGS)
+
+all: $(OBJ)
+	$(CC) -shared -o $(LIB) $^ $(CFLAGS)
+
+.PHONY : clean
+clean:
+	$(RM) $(LIB) $(OBJ) $(TEST)
diff --git a/README.md b/README.md
index 960468d..97b37df 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,154 @@
 # libresource
+library of interfaces through which we can get system resource information
+like memory, CPU, stat, networking, device etc.
+Currently most of such information is read from /proc and /sys.
 
-libresource is a community effort to generate a new library which tools
-like ps, top, etc can use to get information typically garnered from
-/proc.  libresource however will attempt to virtualize the data according
-to the calling task's cgroup information.
+Use case:
+1: Ease of use -
+Currently applications and tools need to read this info mostly from
+/proc and /sys file-systems. In most of the cases complex string
+parsing is involved which is needed to be done in application
+code. With the library interfaces application can get the information
+directly and all the string parsing, if any, will be done by library.
+2: Stability -
+If the format in which the information is provided in /proc or /sys
+file-system is changed then the application code is changed to align
+with those changes. Also if a better way to get information comes in
+future, like through a syscall or a sysconf then again application code
+needs to be changed to get the benefit of it. Library will take care of
+such changes.
+3: Virtualization -
+In cases where DB is running in a virtualized environment using
+cgroup or namespaces, reading from /proc and /sys file-systems
+might not give correct information as these are not cgroup
+aware. Library API will take care of this e.g. if a process
+is running in a cgroup then library should provide information
+which is local to that cgroup.
 
-# contributing
+Design:
+Each resource is identified by a resource id. User land application should
+provide the id while calling the interfaces to fetch the information.
+Following are resource IDs which are already implemented.
 
-We will interact through pull requests and the issue tracker.  I think we
-should start with pull requests editing the CONTRIBUTORS so we can have a
-feel whether enough people are able to contribute or interested in following
-for the project to be successful.  We can also start pull requests against
-the specs/api.md file to discuss what that should look like.
+RES_MEM_ACTIVE
+RES_MEM_INACTIVE
+RES_MEM_AVAILABLE
+RES_MEM_FREE
+RES_MEM_TOTAL
+RES_MEM_PAGESIZE
+RES_MEM_SWAPFREE
+RES_MEM_SWAPTOTAL
+RES_KERN_COMPILE_TIME
+RES_KERN_RELEASE
+RES_NET_ALLIFSTAT
+RES_NET_IFSTAT
+RES_MEM_INFOALL
+
+Interfaces:
+1:
+int res_read(int res_id, void *out, void *hint, int pid, int flags);
+
+This is to read a resource information. A valid resource id should be provided
+in res_id, out should be properly allocated on the basis of size of resource
+information, hint should be given where needed. Currently pid and flags are
+not used, they are for future extensions.
+
+2:
+
+If an application wants to read multiple resource information in one call, it
+can call res_*_blk APIs to do so.
+
+2.1 structs
+Following struct holds information about one resource in such case.
+typedef struct res_unit {
+        int status;
+        int res_id;
+        void *hint;
+        union r_data data;
+} res_unit_t;
+
+An array of these form res_blk_t structure as below.
+typedef struct res_blk {
+        int res_count;
+        res_unit_t *res_unit[0];
+} res_blk_t;
+
+res_blk_t strcut is used in all res_*_blk interfaces.
+
+2.2 functions
+res_blk_t *res_build_blk(int *res_ids, int res_count);
+
+This allocates memory for resources and initiates them properly. res_ids
+holds and array of valid resource ids and res_count holds number of
+resource ids. It also initiates struct fields properly.
+
+int res_read_blk(res_blk_t *resblk, int pid, int flags);
+Reading bulk resource information. Memory must be properly allocated and
+all fields should be properly filled to return error free resource
+information. res_build_blk call is suggested to allocate build res_blk_t
+structure.
+
+/* Free allocated memory from res_build_blk */
+extern void res_destroy_blk(res_blk_t *resblk);
+
+Example
+1:
+Reading total memory
+size_t stemp = 0;
+res_read(RES_MEM_TOTAL,&stemp,NULL, 0, 0);
+printf("MEMTOTAL is: %zu\n", stemp);
+
+
+2:
+reading network interface related statistics for interface named "lo"
+res_net_ifstat_t ifstat;
+res_read(RES_NET_IFSTAT,&ifstat, (void *)"lo",0, 0);
+printf("status for %s: %llu %llu\n", ifstat.ifname,
+	ifstat.rx_bytes,
+	ifstat.rx_packets
+	);
+3:
+Reading multiple resouce information in one call.
+
+    res_blk_t *b = NULL;
+    int a[NUM] = {RES_MEM_PAGESIZE,
+                RES_MEM_TOTAL,
+                RES_MEM_AVAILABLE,
+                RES_MEM_INFOALL,
+                RES_KERN_RELEASE,
+                RES_NET_IFSTAT,
+                RES_NET_ALLIFSTAT,
+                RES_KERN_COMPILE_TIME
+                };
+    b = res_build_blk(a, NUM);
+    b->res_unit[5]->hint = (void *)"lo";
+
+    res_read_blk(b, 0, 0);
+
+    printf("pagesize %ld bytes,\n memtotal %ld kb,\n memavailable %ld kb,\n"
+            " mefree %ld kb,\n release %s,\n compile time %s\n",
+            b->res_unit[0]->data.sz,
+            b->res_unit[1]->data.sz,
+            b->res_unit[2]->data.sz,
+            ((res_mem_infoall_t *)(b->res_unit[3]->data.ptr))->memfree,
+            b->res_unit[4]->data.str,
+            b->res_unit[7]->data.str
+    );
+
+    res_net_ifstat_t *ip = (res_net_ifstat_t *)b->res_unit[5]->data.ptr;
+    printf("stat for interface %s: %llu %llu\n", ip->ifname,
+        ip->rx_bytes,
+        ip->rx_packets
+        );
+
+    int k = (int)(long long)b->res_unit[6]->hint;
+    res_net_ifstat_t *ipp = (res_net_ifstat_t *)b->res_unit[6]->data.ptr;
+    for (int j=0; j< k; j++) {
+        printf("stat for interface %s: %llu %llu\n", ipp[j].ifname,
+            ipp[j].rx_bytes,
+            ipp[j].rx_packets
+        );
+    }
+
+    free(ipp);
+    res_destroy_blk(b);
diff --git a/resmem.c b/resmem.c
new file mode 100644
index 0000000..df7d261
--- /dev/null
+++ b/resmem.c
@@ -0,0 +1,218 @@
+#ifndef _RESOURCE_H
+#include "resource.h"
+#endif
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "resmem.h"
+#include "resource_impl.h"
+#include <errno.h>
+
+int meminfo_fd = -1;
+
+/* read a specific information from file on the basis of a string.
+ * String should tell what information is being read.
+ */
+static inline int get_info_infile(char *fname, char *res, void *out)
+{
+	const char *loc;
+	char buf[MEMBUF_2048];
+
+	if (file_to_buf(fname, buf) == -1)
+		return -1;
+
+	loc = strstr(buf, res);
+	if (loc == NULL) {
+		eprintf("%s is not found in buffer of file %s", res, fname);
+		errno = ENODATA;
+		return -1;
+	}
+
+	sscanf(loc, "%*s%zu", (size_t *)out);
+	return 0;
+}
+
+/* Read resource information corresponding to res_id */
+int getmeminfo(int res_id, void *out, void *hint, int pid, int flags)
+{
+	char buf[MEMBUF_128];
+	FILE *fp;
+	int err = 0;
+
+	switch (res_id) {
+
+	case RES_MEM_FREE:
+		return get_info_infile(MEMINFO_FILE, "MemFree:", out);
+
+	case RES_MEM_AVAILABLE:
+		return get_info_infile(MEMINFO_FILE, "MemAvailable:", out);
+
+	case RES_MEM_TOTAL:
+		return get_info_infile(MEMINFO_FILE, "MemTotal:", out);
+
+	case RES_MEM_ACTIVE:
+		return get_info_infile(MEMINFO_FILE, "Active:", out);
+
+	case RES_MEM_INACTIVE:
+		return get_info_infile(MEMINFO_FILE, "Inactive:", out);
+
+	case RES_MEM_SWAPTOTAL:
+		return get_info_infile(MEMINFO_FILE, "SwapTotal:", out);
+
+	case RES_MEM_SWAPFREE:
+		return get_info_infile(MEMINFO_FILE, "SwapFree:", out);
+
+	case RES_MEM_INFOALL:
+		fp = fopen(MEMINFO_FILE, "r");
+		if (fp == NULL) {
+			err = errno;
+			eprintf("while opening File %s with errno: %d",
+				MEMINFO_FILE, errno);
+			errno = err;
+			return -1;
+		}
+		/* Read through file and populate all information which
+		 * is required.
+		 */
+		while (fgets(buf, sizeof(buf), fp) != NULL) {
+			if (strncmp("MemTotal:", buf, MEMBUF_8) == 0)
+				sscanf(buf, "%*s%zu",
+				&((res_mem_infoall_t *)out)->memtotal);
+
+			else if (strncmp("MemFree:", buf, MEMBUF_8) == 0)
+				sscanf(buf, "%*s%zu",
+				&((res_mem_infoall_t *)out)->memfree);
+
+			else if (strncmp("MemAvailable:", buf, MEMBUF_8) == 0)
+				sscanf(buf, "%*s%zu",
+				&((res_mem_infoall_t *)out)->memavailable);
+
+			else if (strncmp("Active:", buf, MEMBUF_8) == 0)
+				sscanf(buf, "%*s%zu",
+				&((res_mem_infoall_t *)out)->active);
+
+			else if (strncmp("Inactive:", buf, MEMBUF_8) == 0)
+				sscanf(buf, "%*s%zu",
+				&((res_mem_infoall_t *)out)->inactive);
+
+			else if (strncmp("SwapTotal:", buf, MEMBUF_8) == 0)
+				sscanf(buf, "%*s%zu",
+				&((res_mem_infoall_t *)out)->swaptotal);
+
+			else if (strncmp("SwapFree:", buf, MEMBUF_8) == 0)
+				sscanf(buf, "%*s%zu",
+				&((res_mem_infoall_t *)out)->swapfree);
+		}
+		fclose(fp);
+		break;
+
+	case RES_MEM_PAGESIZE:
+		*(size_t *)out = sysconf(_SC_PAGESIZE);
+		break;
+
+	default:
+		eprintf("Resource Id is invalid");
+		errno = EINVAL;
+		return -1;
+	}
+
+	return 0;
+}
+
+int populate_meminfo(res_blk_t *res, int pid, int flags)
+{
+	size_t temp;
+	const char *loc;
+	char buf[MEMBUF_2048];
+
+	if (file_to_buf(MEMINFO_FILE, buf) == -1) {
+		for (int i = 0 ; i < res->res_count; i++)
+			res->res_unit[i]->status = errno;
+		return -1;
+	}
+
+/* Macro to read memory related information corresponding to a string
+ * from buffer.
+ */
+#define SCANMEMSTR(str) do {\
+	loc = strstr(buf, str);\
+	if (loc == NULL) {\
+		eprintf("%s is not found in file %s", str, MEMINFO_FILE);\
+			res->res_unit[i]->status = ENODATA;\
+	} else {\
+		sscanf(loc, "%*s%zu", &temp);\
+		(res->res_unit[i]->data).sz = temp;\
+		res->res_unit[i]->status = RES_STATUS_FILLED;\
+	} \
+} while (0)\
+
+
+	for (int i = 0; i < res->res_count; i++) {
+		loc = NULL;
+		switch (res->res_unit[i]->res_id) {
+		case RES_MEM_FREE:
+			SCANMEMSTR("MemFree:");
+			break;
+
+		case RES_MEM_AVAILABLE:
+			SCANMEMSTR("MemAvailable:");
+			break;
+
+		case RES_MEM_TOTAL:
+			SCANMEMSTR("MemTotal:");
+			break;
+
+		case RES_MEM_ACTIVE:
+			SCANMEMSTR("Active:");
+			break;
+
+		case RES_MEM_INACTIVE:
+			SCANMEMSTR("Inactive:");
+			break;
+
+		case RES_MEM_SWAPTOTAL:
+			SCANMEMSTR("SwapTotal:");
+			break;
+
+		case RES_MEM_SWAPFREE:
+			SCANMEMSTR("SwapFree:");
+			break;
+
+		case RES_MEM_PAGESIZE:
+			(res->res_unit[i]->data).sz = sysconf(_SC_PAGESIZE);
+			break;
+
+		case RES_MEM_INFOALL:
+			loc = strstr(buf, "MemTotal:");
+			sscanf(loc, "%*s%zu", &((res_mem_infoall_t *)
+				(res->res_unit[i]->data).ptr)->memtotal);
+			loc = strstr(buf, "MemFree:");
+			sscanf(loc, "%*s%zu", &((res_mem_infoall_t *)
+				(res->res_unit[i]->data).ptr)->memfree);
+			loc = strstr(buf, "MemAvailable:");
+			sscanf(loc, "%*s%zu", &((res_mem_infoall_t *)
+				(res->res_unit[i]->data).ptr)->memavailable);
+			loc = strstr(buf, "Active:");
+			sscanf(loc, "%*s%zu", &((res_mem_infoall_t *)
+				(res->res_unit[i]->data).ptr)->active);
+			loc = strstr(buf, "Inactive:");
+			sscanf(loc, "%*s%zu", &((res_mem_infoall_t *)
+				(res->res_unit[i]->data).ptr)->inactive);
+			loc = strstr(buf, "SwapTotal:");
+			sscanf(loc, "%*s%zu", &((res_mem_infoall_t *)
+				(res->res_unit[i]->data).ptr)->swaptotal);
+			loc = strstr(buf, "SwapFree:");
+			sscanf(loc, "%*s%zu", &((res_mem_infoall_t *)
+				(res->res_unit[i]->data).ptr)->swapfree);
+
+			res->res_unit[i]->status = RES_STATUS_FILLED;
+			break;
+		default:
+			res->res_unit[i]->status = RES_STATUS_NOTSUPPORTED;
+		}
+	}
+	return 0;
+}
+
diff --git a/resmem.h b/resmem.h
new file mode 100644
index 0000000..817b9ec
--- /dev/null
+++ b/resmem.h
@@ -0,0 +1,16 @@
+#ifndef _RESMEM_H
+#define _RESMEM_H
+
+#define MEMINFO_FILE "/proc/meminfo"
+extern int meminfo_fd;
+
+#define VMINFO_FILE "/proc/vmstat"
+
+#define MEMBUF_8	8
+#define MEMBUF_128	128
+#define MEMBUF_2048	2048
+
+extern int populate_meminfo(struct res_blk *res, int pid, int flags);
+extern int getmeminfo(int res_id, void *out, void *hint, int pid, int flags);
+
+#endif /* _RESMEM_H */
diff --git a/resnet.c b/resnet.c
new file mode 100644
index 0000000..56269bf
--- /dev/null
+++ b/resnet.c
@@ -0,0 +1,316 @@
+#ifndef _RESOURCE_H
+#include "resource.h"
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <ctype.h>
+#include <net/if.h>
+#include "resnet.h"
+#include "resource_impl.h"
+
+int netdev_fd = -1;
+
+static inline int procnetdev_ver(char *buf)
+{
+	if (strstr(buf, "compressed"))
+		return 3;
+	if (strstr(buf, "bytes"))
+		return 2;
+	return 1;
+}
+
+static inline char *get_ifname(char *name, char *p)
+{
+	char *d;
+	char *dname;
+
+	while (isspace(*p))
+		p++;
+
+	while (*p) {
+		if (isspace(*p))
+			break;
+		if (*p == ':') {
+			d = p, dname = name;
+			*name++ = *p++;
+			while (isdigit(*p))
+				*name++ = *p++;
+			if (*p != ':') {
+				p = d;
+				name = dname;
+			}
+			if (*p == '\0')
+				return NULL;
+			p++;
+			break;
+		}
+		*name++ = *p++;
+	}
+	*name++ = '\0';
+	return p;
+}
+
+static inline int scan_net_stat(char *buf, res_net_ifstat_t *i, int ver)
+{
+	switch (ver) {
+	case 3:
+		sscanf(buf,
+			"%llu %llu %lu %lu %lu %lu %lu %lu %llu %llu %lu %lu %lu %lu %lu %lu",
+			&i->rx_bytes,
+			&i->rx_packets,
+			&i->rx_errors,
+			&i->rx_dropped,
+			&i->rx_fifo_err,
+			&i->rx_frame_err,
+			&i->rx_compressed,
+			&i->rx_multicast,
+
+			&i->tx_bytes,
+			&i->tx_packets,
+			&i->tx_errors,
+			&i->tx_dropped,
+			&i->tx_fifo_err,
+			&i->tx_collisions,
+			&i->tx_carrier_err,
+			&i->tx_compressed);
+		break;
+	case 2:
+		sscanf(buf,
+			"%llu %llu %lu %lu %lu %lu %llu %llu %lu %lu %lu %lu %lu",
+			&i->rx_bytes,
+			&i->rx_packets,
+			&i->rx_errors,
+			&i->rx_dropped,
+			&i->rx_fifo_err,
+			&i->rx_frame_err,
+
+			&i->tx_bytes,
+			&i->tx_packets,
+			&i->tx_errors,
+			&i->tx_dropped,
+			&i->tx_fifo_err,
+			&i->tx_collisions,
+			&i->tx_carrier_err);
+		i->rx_multicast = 0;
+		break;
+	case 1:
+		sscanf(buf,
+			"%llu %lu %lu %lu %lu %llu %lu %lu %lu %lu %lu",
+			&i->rx_packets,
+			&i->rx_errors,
+			&i->rx_dropped,
+			&i->rx_fifo_err,
+			&i->rx_frame_err,
+
+			&i->tx_packets,
+			&i->tx_errors,
+			&i->tx_dropped,
+			&i->tx_fifo_err,
+			&i->tx_collisions,
+			&i->tx_carrier_err);
+		i->rx_bytes = 0;
+		i->tx_bytes = 0;
+		i->rx_multicast = 0;
+		break;
+	}
+	return 0;
+}
+
+static inline int getallnetinfo(void *out, void *hint)
+{	char buf[NETBUF_1024];
+	FILE *fp;
+	char ifname[IFNAMSIZ];
+	int ver = 0;
+	char *p;
+	int inum = 0;
+	int i;
+	res_net_ifstat_t *lst;
+	size_t msz = 0;
+	int err;
+
+	/* Hint should not be null */
+	if (hint == NULL) {
+		eprintf(
+			"Hint should be provided for RES_NET_ALLIFSTAT");
+		errno = EINVAL;
+		return -1;
+	}
+
+	fp = fopen(NETDEV_FILE, "r");
+	if (fp == NULL) {
+		err = errno;
+		eprintf("while opening File %s with errno: %d",
+			NETDEV_FILE, errno);
+		errno = err;
+		return -1;
+	}
+
+	fgets(buf, sizeof(buf), fp);
+	fgets(buf, sizeof(buf), fp);
+	ver = procnetdev_ver(buf);
+	inum = *(int *)hint;
+
+	/* Hint should hold number of interfaces for which memory is
+	 * allocated in out. If number of interfaces is 0 then allocated
+	 * memory for all interfaces and fill it with interface stat.
+	 */
+	if (inum == 0) {
+		i = 0;
+
+		/* Memory is allocated for NET_ALLIFSTAT_SZ interfaces
+		 * initially and then reallocated in chunks of
+		 * NET_ALLIFSTAT_SZ till all information is read.
+		 */
+		msz = sizeof(res_net_ifstat_t) * NET_ALLIFSTAT_SZ;
+		lst = (res_net_ifstat_t *)malloc(msz);
+
+		while (fgets(buf, sizeof(buf), fp) != NULL) {
+			if (i == NET_ALLIFSTAT_SZ) {
+				msz = (sizeof(res_net_ifstat_t) * inum) +
+					NET_ALLIFSTAT_SZ;
+				lst = (res_net_ifstat_t *)realloc(lst, msz);
+				i = 0;
+			}
+			p = get_ifname(ifname, buf);
+			strcpy(lst[inum].ifname, ifname);
+			scan_net_stat(p, &lst[inum], ver);
+			inum++;
+			i++;
+		}
+		*((res_net_ifstat_t **)out) = lst;
+		*(int *)hint = inum;
+	} else {
+		/* If number of interfaces is not 0 then read interfaces
+		 * stat sequentially and put it in memory allocated
+		 * by user in out. Memory might be allocated for more interfaces
+		 * than there are on system. So read sequentially till either
+		 * all interfaces are read or memory allocated is totally
+		 * filled.
+		 */
+		if (out == NULL) {
+			errno = EINVAL;
+			return -1;
+		}
+
+		for (i = 0;
+			fgets(buf, sizeof(buf), fp) != NULL && i < inum;
+			i++) {
+			p = get_ifname(ifname, buf);
+			strcpy(((res_net_ifstat_t *)out)[i].ifname, ifname);
+			scan_net_stat(p, &(((res_net_ifstat_t *)out)[i]), ver);
+		}
+
+		/* Write how many interfaces are read */
+		*(int *)hint = i;
+	}
+	fclose(fp);
+	return 0;
+}
+
+/* Get resource information related to network */
+int getnetinfo(int res_id, void *out, void *hint, int pid, int flags)
+{
+	char buf[NETBUF_1024];
+	FILE *fp;
+	int err = 0;
+	char ifname[IFNAMSIZ];
+	int ver = 0;
+	char *p;
+
+	switch (res_id) {
+	case RES_NET_IFSTAT:
+		/* Interface name should be provided */
+		if (hint == NULL) {
+			eprintf(
+				"Interface name is not provided for RES_NET_IFSTAT");
+			errno = EINVAL;
+			return -1;
+		}
+
+		/* Interface name is provided. Open /proc/net/dev and read
+		 * through it.
+		 */
+		fp = fopen(NETDEV_FILE, "r");
+		if (fp == NULL) {
+			err = errno;
+			eprintf("while opening File %s with errno: %d",
+				NETDEV_FILE, errno);
+			errno = err;
+			return -1;
+		}
+
+		/* Skip first line */
+		fgets(buf, sizeof(buf), fp);
+
+		fgets(buf, sizeof(buf), fp);
+		/* Get what information /proc/net/dev holds so that we can
+		 * read it in correct format.
+		 */
+		ver = procnetdev_ver(buf);
+
+		/* Read each interface till we find the interface which we
+		 * are looking for.
+		 */
+		while (fgets(buf, sizeof(buf), fp) != NULL) {
+			/* get interface name in the line */
+			p = get_ifname(ifname, buf);
+
+			if ((strncasecmp((char *)hint, ifname,
+				sizeof(*(char *)hint))) == 0) {
+				/* Interface found, scan information */
+				strcpy(((res_net_ifstat_t *)out)->ifname,
+					ifname);
+				scan_net_stat(p, (res_net_ifstat_t *)out, ver);
+				break;
+			}
+		}
+
+		fclose(fp);
+		break;
+
+	/* Information for all interfaces is asked for */
+	case RES_NET_ALLIFSTAT:
+		return getallnetinfo(out, hint);
+
+	default:
+		eprintf("Resource Id is invalid");
+		errno = EINVAL;
+		return -1;
+	}
+
+	return 0;
+}
+
+int populate_netinfo(res_blk_t *res, int pid, int flags)
+{
+	void *p = NULL;
+
+	for (int i = 0; i < res->res_count; i++) {
+		switch (res->res_unit[i]->res_id) {
+		case RES_NET_IFSTAT:
+			if (getnetinfo(RES_NET_IFSTAT,
+				res->res_unit[i]->data.ptr,
+				res->res_unit[i]->hint, 0, 0) == -1) {
+				res->res_unit[i]->status = errno;
+			} else
+				res->res_unit[i]->status = RES_STATUS_FILLED;
+			break;
+		case RES_NET_ALLIFSTAT:
+			res->res_unit[i]->hint = (int)0;
+
+			if (getnetinfo(RES_NET_ALLIFSTAT, &p,
+				&(res->res_unit[i]->hint), 0, 0) == -1) {
+				res->res_unit[i]->status = errno;
+			} else {
+				res->res_unit[i]->status = RES_STATUS_FILLED;
+				res->res_unit[i]->data.ptr = p;
+			}
+			break;
+		default:
+			res->res_unit[i]->status = RES_STATUS_NOTSUPPORTED;
+		}
+	}
+	return 0;
+}
diff --git a/resnet.h b/resnet.h
new file mode 100644
index 0000000..164ce16
--- /dev/null
+++ b/resnet.h
@@ -0,0 +1,14 @@
+#ifndef _RESNET_H
+#define _RESNET_H
+
+#define NETDEV_FILE "/proc/net/dev"
+
+#define NET_ALLIFSTAT_SZ    4
+#define NETBUF_1024		1024
+
+extern int netdev_fd;
+
+extern int populate_netinfo(struct res_blk *res, int pid, int flags);
+extern int getnetinfo(int res_id, void *out, void *hint, int pid, int flags);
+
+#endif /* _RESNET_H */
diff --git a/resource.c b/resource.c
new file mode 100644
index 0000000..e0c4e42
--- /dev/null
+++ b/resource.c
@@ -0,0 +1,211 @@
+#include "resource.h"
+#include <malloc.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "resource_impl.h"
+#include "resmem.h"
+#include "resnet.h"
+
+/* Allocate memory for bulk resource information and initiate it
+ * properly.
+ */
+res_blk_t *res_build_blk(int *res_ids, int res_count)
+{
+	res_unit_t *temp;
+	res_blk_t *res = NULL;
+
+	if (!res_ids || res_count <= 0) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	/* Allocate memory to hold addresses of individual resource
+	 * information.
+	 */
+	res = (res_blk_t *)
+		malloc(sizeof(res_blk_t) + (sizeof(res_unit_t *)) * res_count);
+	res->res_count = res_count;
+
+	/* Allocate and set each resource information properly */
+	for (int i = 0; i < res_count; i++) {
+		/* Allocate memory for resource info */
+		temp = (res_unit_t *)malloc(sizeof(res_unit_t));
+		memset(temp, 0, sizeof(res_unit_t));
+		temp->status = RES_STATUS_EMPTY;
+
+		/* Some resource information are big and need extra allocation.
+		 * In these cases an address is returned which hold actual
+		 * resource information.
+		 */
+		switch (res_ids[i]) {
+		case RES_MEM_ACTIVE:
+		case RES_MEM_INACTIVE:
+		case RES_MEM_AVAILABLE:
+		case RES_MEM_FREE:
+		case RES_MEM_TOTAL:
+		case RES_MEM_PAGESIZE:
+		case RES_MEM_SWAPFREE:
+		case RES_MEM_SWAPTOTAL:
+		case RES_KERN_COMPILE_TIME:
+		case RES_KERN_RELEASE:
+		case RES_NET_ALLIFSTAT:
+			break;
+
+		case RES_NET_IFSTAT:
+			temp->data.ptr = (res_net_ifstat_t *)
+				malloc(sizeof(res_net_ifstat_t));
+			break;
+
+		case RES_MEM_INFOALL:
+			temp->data.ptr = (res_mem_infoall_t *)
+				malloc(sizeof(res_mem_infoall_t));
+			break;
+
+		default:
+			eprintf("Invalid resource ID: %d", res_ids[i]);
+			while (--i >= 0)
+				free(res->res_unit[i]);
+			free(temp);
+			free(res);
+			errno = EINVAL;
+			return NULL;
+		}
+
+		temp->res_id = res_ids[i];
+		res->res_unit[i] = temp;
+	}
+
+	return res;
+}
+
+/* Free resources allocated in res_build */
+void res_destroy_blk(res_blk_t *res)
+{
+	for (int i = 0; i < res->res_count; i++) {
+		/* Some resource information are big and needed extra
+		 * Free memory allocated for that.
+		 */
+		switch (res->res_unit[i]->res_id) {
+		case RES_NET_IFSTAT:
+		case RES_MEM_INFOALL:
+			free((res->res_unit[i])->data.ptr);
+			break;
+		}
+
+		/* Free memory tho hold this resource info */
+		free(res->res_unit[i]);
+	}
+	free(res);
+}
+
+/* read resource information corresponding to res_id, out should have been
+ * properly allocated by caller if required.
+ */
+int res_read(int res_id, void *out, void *hint, int pid, int flags)
+{
+	struct utsname t;
+
+	if (out == NULL) {
+		switch (res_id) {
+		/* In case of RES_NET_ALLIFSTAT memory is allocated on the
+		 * basis of number of interfaces available on system.
+		 * So out can be NULL in that case.
+		 */
+		case RES_NET_ALLIFSTAT:
+			break;
+
+		default:
+			eprintf("out argument cannot be NULL");
+			errno = EINVAL;
+			return -1;
+		}
+	}
+
+	/* Check if memory proc file is needed to open */
+	if (res_id >= MEM_MIN && res_id < MEM_MAX)
+		return getmeminfo(res_id, out, hint, pid, flags);
+
+	/* Check if net proc file is needed to open */
+	if (res_id >= NET_MIN && res_id < NET_MAX)
+		return getnetinfo(res_id, out, hint, pid, flags);
+
+	switch (res_id) {
+	case RES_KERN_RELEASE:
+		uname(&t);
+		strcpy(out, t.release);
+		break;
+
+	case RES_KERN_COMPILE_TIME:
+		uname(&t);
+		sscanf(t.version, "%*s%*s%*s%[^\t\n]", (char *) out);
+		break;
+
+	default:
+		eprintf("Resource Id is invalid");
+		errno = EINVAL;
+		return -1;
+	}
+	return 0;
+}
+
+/* Read bulk resource information */
+int res_read_blk(res_blk_t *res, int pid, int flags)
+{
+	int ismeminforeq = 0;
+	int isnetdevreq = 0;
+	struct utsname t;
+
+	/* Loop through all resource information. If it can be filled through
+	 * a syscall or such method then fill it. Else set flags which tell
+	 * what files might have the information.
+	 */
+	for (int i = 0; i < res->res_count; i++) {
+		switch (res->res_unit[i]->res_id) {
+		case RES_MEM_TOTAL:
+		case RES_MEM_FREE:
+		case RES_MEM_AVAILABLE:
+		case RES_MEM_ACTIVE:
+		case RES_MEM_INACTIVE:
+		case RES_MEM_SWAPFREE:
+		case RES_MEM_SWAPTOTAL:
+		case RES_MEM_INFOALL:
+			ismeminforeq = 1;
+			break;
+
+		case RES_MEM_PAGESIZE:
+			(res->res_unit[i]->data).sz = sysconf(_SC_PAGESIZE);
+			res->res_unit[i]->status = RES_STATUS_FILLED;
+			break;
+
+		case RES_KERN_RELEASE:
+			uname(&t);
+			strcpy((res->res_unit[i]->data).str, t.release);
+			res->res_unit[i]->status = RES_STATUS_FILLED;
+			break;
+
+		case RES_KERN_COMPILE_TIME:
+			uname(&t);
+			sscanf(t.version, "%*s%*s%*s%[^\t\n]",
+				(res->res_unit[i]->data).str);
+			break;
+
+		case RES_NET_IFSTAT:
+		case RES_NET_ALLIFSTAT:
+			isnetdevreq = 1;
+			break;
+
+		default:
+			res->res_unit[i]->status = RES_STATUS_NOTSUPPORTED;
+		}
+	}
+
+	if (ismeminforeq)
+		populate_meminfo(res, pid, flags);
+
+	if (isnetdevreq)
+		populate_netinfo(res, pid, flags);
+
+	return 0;
+}
diff --git a/resource.h b/resource.h
new file mode 100644
index 0000000..963877d
--- /dev/null
+++ b/resource.h
@@ -0,0 +1,147 @@
+#ifndef	_RESOURCE_H
+#define	_RESOURCE_H
+
+#include <stdio.h>
+#include <net/if.h>
+
+/* libresource version */
+#define LIBRESOURCE_API_VERSION 1
+
+/* Possible status for libresource information returned */
+/* libresource information was fetched correctly */
+#define RES_STATUS_FILLED 0
+/* There was some error in fetching libresource information. In most cases
+ * errno will be set.
+ */
+#define RES_STATUS_EMPTY -1
+/* Resource information is not supported yet, or Invalid resource
+ * information.
+ */
+#define RES_STATUS_NOTSUPPORTED	-2
+/* If partial information was read for a libresource information. For example
+ * a string was read partially.
+ */
+#define RES_STATUS_TRUNCATED -3
+
+
+/* Maximum size of a resource information data type which can be returned
+ * without explicitly allocating memory for it. If resource information
+ * size is larger than this this a pointer to allocated memory will be
+ * returned.
+ */
+#define RES_UNIT_OUT_SIZE	256
+
+/* This union is used to return resource information of various types */
+union r_data {
+	int i;
+	size_t sz;
+	long l;
+	char str[RES_UNIT_OUT_SIZE];
+	void *ptr;
+};
+
+/* In case of res_read_blk, each resource information will be represented by
+ * following structure.
+ */
+typedef struct res_unit {
+	int status;
+	int res_id;
+	void *hint;
+	union r_data data;
+} res_unit_t;
+
+/* In case of bulk read (res_read_blk), this structure will hold all required
+ * information needed to do so.
+ */
+typedef struct res_blk {
+	int res_count;
+	res_unit_t *res_unit[0];
+} res_blk_t;
+
+/* Resource information is divided in broad categories and each
+ * category is assigned a number range for its resource information
+ * Memory related			(RES_MEM_*)		1024-
+ * Network related			(RES_NET_*)		2048-
+ * General kernel related		(RES_KERN_*)		3072-
+ * This is done to facilitate any future optimization which can be made
+ * on the basis of resource information (hashing etc ?)
+ */
+#define MEM_MIN				1024
+#define RES_MEM_HUGEPAGEALL		1025
+#define RES_MEM_HUGEPAGESIZE		1026
+#define RES_MEM_INACTIVE		1027
+#define RES_MEM_INFOALL			1028
+#define RES_MEM_AVAILABLE		1029
+#define RES_MEM_FREE			1030
+#define RES_MEM_TOTAL			1031
+#define RES_MEM_PAGESIZE		1032
+#define RES_MEM_SHMALL			1033
+#define RES_MEM_SHMINFOALL		1034
+#define RES_MEM_SHMMAX			1035
+#define RES_MEM_SHMMNI			1036
+#define RES_MEM_SWAPFREE		1037
+#define RES_MEM_SWAPTOTAL		1038
+#define RES_MEM_ACTIVE			1039
+#define MEM_MAX				1040
+
+#define NET_MIN				2048
+#define RES_NET_IFSTAT			2049
+#define RES_NET_ALLIFSTAT		2050
+#define NET_MAX				2051
+
+#define KERN_MIN			3072
+#define RES_KERN_COMPILE_TIME		3073
+#define RES_KERN_RELEASE		3074
+#define KERN_MAX			3075
+
+/* Structure to return RES_MEM_INFOALL resource information */
+typedef struct res_mem_infoall {
+	size_t memfree;
+	size_t memtotal;
+	size_t memavailable;
+	size_t active;
+	size_t inactive;
+	size_t swaptotal;
+	size_t swapfree;
+} res_mem_infoall_t;
+
+/* Structure to return RES_MEM_ALLIFSTAT resource information */
+typedef struct res_net_ifstat {
+	char ifname[IFNAMSIZ];
+	unsigned long long rx_bytes;
+	unsigned long long rx_packets;
+	unsigned long rx_errors;
+	unsigned long rx_dropped;
+	unsigned long rx_fifo_err;
+	unsigned long rx_frame_err;
+	unsigned long rx_compressed;
+	unsigned long rx_multicast;
+	unsigned long long tx_bytes;
+	unsigned long long tx_packets;
+	unsigned long tx_errors;
+	unsigned long tx_dropped;
+	unsigned long tx_fifo_err;
+	unsigned long tx_collisions;
+	unsigned long tx_carrier_err;
+	unsigned long tx_compressed;
+} res_net_ifstat_t;
+
+/* Allocating memory and building a res_blk structure to return bulk
+ * resource information.
+ */
+extern res_blk_t *res_build_blk(int *res_ids, int res_count);
+
+/* Reading bulk resource information. Memory must be properly allocated and
+ * all fields should be properly filled to return error free resource
+ * information. res_build_blk call is suggested to allocate build res_blk_t
+ * structure.
+ */
+extern int res_read_blk(res_blk_t *resblk, int pid, int flags);
+
+/* Free allocated memory from res_build_blk */
+extern void res_destroy_blk(res_blk_t *resblk);
+
+/* Read a resource information. Memory for out should be properly allocated */
+extern int res_read(int res_id, void *out, void *hint, int pid, int flags);
+
+#endif /* RESOURCE_H */
diff --git a/resource_impl.h b/resource_impl.h
new file mode 100644
index 0000000..e40e1d7
--- /dev/null
+++ b/resource_impl.h
@@ -0,0 +1,47 @@
+#ifndef _RESOURCE_IMPL_H
+#define _RESOURCE_IMPL_H
+
+#include <fcntl.h>
+#include <errno.h>
+#define SIZE_OF_TEMP_BUF	2048
+#define eprintf(msg, ...)	fprintf(stderr,\
+					"Err at line %d in file %s: "msg"\n",\
+					__LINE__, __FILE__, ##__VA_ARGS__)\
+
+
+static inline int file_to_buf(char *fname, char *buf)
+{
+	int fd = 0;
+	size_t rdsz = 0;
+	int err = 0;
+
+	fd = open(fname, O_RDONLY);
+	if (fd == -1) {
+		err = errno;
+		eprintf("in opening File %s with errno: %d", fname, errno);
+		errno = err;
+		return -1;
+	}
+
+	if (lseek(fd, 0L, SEEK_SET) == -1) {
+		err = errno;
+		eprintf("in lseek for File %s with errno: %d", fname, errno);
+		close(fd);
+		errno = err;
+		return -1;
+	}
+
+	rdsz = read(fd, buf, SIZE_OF_TEMP_BUF - 1);
+	if (rdsz < 0) {
+		err = errno;
+		eprintf("in read from File %s with errno: %d", fname, errno);
+		close(fd);
+		errno = err;
+		return -1;
+	}
+	buf[rdsz] = '\0';
+	close(fd);
+	return rdsz;
+}
+
+#endif /* _RESOURCE_IMPL_H */
-- 
2.7.4



More information about the libresource-devel mailing list