procps/proc/vmstat.c
Jim Warner faf6d4dc93 library: the uref functions were insufficiently robust
The earlier attempt at protecting these functions from
already freed memory worked just fine until the memory
was, in fact, reused by the OS. At that point, the ref
count would most likely fail an existing a test for 0.

So this commit will take control of the 'info' pointer
and force it to NULL when a reference count reaches 0.

Plus, since it makes little sense returning an address
that a caller already has, henceforth we will return a
reference count out of the 'ref' and 'unref functions.

Reference(s):
commit 74beff80ff

Signed-off-by: Jim Warner <james.warner@comcast.net>
2015-07-01 21:30:17 +10:00

198 lines
4.4 KiB
C

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <proc/vmstat.h>
#include "procps-private.h"
#define VMSTAT_FILE "/proc/vmstat"
#define ROW_NAME_LEN 32
struct vmstat_data {
unsigned long pgpgin;
unsigned long pgpgout;
unsigned long pswpin;
unsigned long pswpout;
};
struct mem_table_struct {
const char *name;
unsigned long *slot;
};
struct procps_vmstat {
int refcount;
int vmstat_fd;
struct vmstat_data data;
};
/*
* procps_vmstat_new:
*
* Create a new container to hold the vmstat information
*
* The initial refcount is 1, and needs to be decremented
* to release the resources of the structure.
*
* Returns: a new procps_vmstat container
*/
PROCPS_EXPORT int procps_vmstat_new (
struct procps_vmstat **info)
{
struct procps_vmstat *v;
v = calloc(1, sizeof(struct procps_vmstat));
if (!v)
return -ENOMEM;
v->refcount = 1;
v->vmstat_fd = -1;
*info = v;
return 0;
}
/*
* procps_vmstat_read:
*
* Read the data out of /proc/vmstat putting the information
* into the supplied info structure
*
* Returns: 0 on success, negative on error
*/
PROCPS_EXPORT int procps_vmstat_read (
struct procps_vmstat *info)
{
char buf[8192];
char *head, *tail;
int size;
unsigned long *valptr;
if (info == NULL)
return -1;
memset(&(info->data), 0, sizeof(struct vmstat_data));
/* read in the data */
if (-1 == info->vmstat_fd && (info->vmstat_fd = open(VMSTAT_FILE, O_RDONLY)) == -1) {
return -errno;
}
if (lseek(info->vmstat_fd, 0L, SEEK_SET) == -1) {
return -errno;
}
for (;;) {
if ((size = read(info->vmstat_fd, buf, sizeof(buf)-1)) < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return -errno;
}
break;
}
if (size == 0)
return 0;
buf[size] = '\0';
/* Scan the file */
head = buf;
do {
tail = strchr(head, ' ');
if (!tail)
break;
*tail = '\0';
valptr = NULL;
if (0 == strcmp(head, "pgpgin")) {
valptr = &(info->data.pgpgin);
}else if (0 == strcmp(head, "pgpgout")) {
valptr = &(info->data.pgpgout);
}else if (0 == strcmp(head, "pswpin")) {
valptr = &(info->data.pswpin);
}else if (0 == strcmp(head, "pswpout")) {
valptr = &(info->data.pswpout);
}
head = tail+1;
if (valptr) {
*valptr = strtoul(head, &tail, 10);
}
tail = strchr(head, '\n');
if (!tail)
break;
head = tail + 1;
} while(tail);
return 0;
}
PROCPS_EXPORT int procps_vmstat_ref (
struct procps_vmstat *info)
{
if (info == NULL)
return -EINVAL;
info->refcount++;
return info->refcount;
}
PROCPS_EXPORT int procps_vmstat_unref (
struct procps_vmstat **info)
{
if (info == NULL || *info == NULL)
return -EINVAL;
(*info)->refcount--;
if ((*info)->refcount == 0) {
free(*info);
*info = NULL;
return 0;
}
return (*info)->refcount;
}
/* Accessor functions */
PROCPS_EXPORT unsigned long procps_vmstat_get (
struct procps_vmstat *info,
enum vmstat_item item)
{
switch (item) {
case PROCPS_VMSTAT_PGPGIN:
return info->data.pgpgin;
case PROCPS_VMSTAT_PGPGOUT:
return info->data.pgpgout;
case PROCPS_VMSTAT_PSWPIN:
return info->data.pswpin;
case PROCPS_VMSTAT_PSWPOUT:
return info->data.pswpout;
}
return 0;
}
PROCPS_EXPORT int procps_vmstat_get_chain (
struct procps_vmstat *info,
struct vmstat_result *item)
{
if (item == NULL)
return -EINVAL;
do {
switch (item->item) {
case PROCPS_VMSTAT_PGPGIN:
item->result = info->data.pgpgin;
break;
case PROCPS_VMSTAT_PGPGOUT:
item->result = info->data.pgpgout;
break;
case PROCPS_VMSTAT_PSWPIN:
item->result = info->data.pswpin;
break;
case PROCPS_VMSTAT_PSWPOUT:
item->result = info->data.pswpout;
break;
default:
return -EINVAL;
}
item = item->next;
} while (item);
return 0;
}