#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <unistd.h>
#include <sys/param.h>
#include <stdlib.h>
#if !defined(PAGE_SIZE) && !defined(PAGESIZE)
#include <sys/user.h>
#endif
#include "gskprocessinfo.h"
#include "gskerror.h"
#include "gskerrno.h"
#include "gskstdio.h"
#include "gskmacros.h"
#define LPAREN '('
#define RPAREN ')'
/* we assume that PAGE_SIZE is defined by <limits.h>.
just in case, define it to the value of PAGESIZE,
if that'll fix things. */
#if !defined(PAGE_SIZE) && defined(PAGESIZE)
#define PAGE_SIZE PAGESIZE
#endif
/* Define 'get_page_size()' */
#ifdef PAGE_SIZE
# define get_page_size() PAGE_SIZE
#else
/* Define SYSCONF_PAGESIZE_MACRO */
# ifdef _SC_PAGESIZE
# define SYSCONF_PAGESIZE_MACRO _SC_PAGESIZE
# elif defined(_SC_PAGE_SIZE)
# define SYSCONF_PAGESIZE_MACRO _SC_PAGE_SIZE
# else
# error no way to find PAGE_SIZE
# endif
static guint get_page_size (void)
{
static guint rv = 0;
if (rv == 0)
{
rv = sysconf (SYSCONF_PAGESIZE_MACRO);
g_assert (rv != 0);
}
return rv;
}
#endif /* !defined(PAGE_SIZE) */
GskProcessInfo *
gsk_process_info_get (guint pid,
GError **error)
{
GskProcessInfo *rv;
char *filename = g_strdup_printf ("/proc/%u/stat", pid);
FILE *fp = fopen (filename, "r");
char *content;
const char *at;
const char *end_name;
gint dummy_int;
guint dummy_uint;
if (fp == NULL)
{
/* TODO: remap some error codes,
like ENOENT to GSK_ERROR_PROCESS_NOT_FOUND. */
g_set_error (error, GSK_G_ERROR_DOMAIN,
gsk_error_code_from_errno (errno),
"error opening %s: %s",
filename, g_strerror (errno));
g_free (filename);
return NULL;
}
content = gsk_stdio_readline (fp);
if (content == NULL)
{
fclose (fp);
g_set_error (error, GSK_G_ERROR_DOMAIN,
GSK_ERROR_NO_DOCUMENT,
"expected line of text in %s", filename);
g_free (filename);
return NULL;
}
fclose (fp);
fp = NULL;
rv = g_new (GskProcessInfo, 1);
rv->exe_filename = NULL;
at = content;
#define ERROR_CLEANUP_AND_RETURN() \
G_STMT_START{ \
g_free (rv); \
g_free (rv->exe_filename); \
g_free (filename); \
g_free (content); \
return NULL; \
}G_STMT_END
#define PARSE_ERROR(type) \
G_STMT_START{ \
g_set_error (error, GSK_G_ERROR_DOMAIN, \
GSK_ERROR_END_OF_FILE, \
"error parsing %s from %s", \
type, filename); \
ERROR_CLEANUP_AND_RETURN(); \
}G_STMT_END
#define PARSE_ERROR_EXPECTED(what) \
G_STMT_START{ \
g_set_error (error, GSK_G_ERROR_DOMAIN, \
GSK_ERROR_END_OF_FILE, \
"expected %s in %s", \
what, filename); \
ERROR_CLEANUP_AND_RETURN(); \
}G_STMT_END
#define PARSE_INT(lvalue, base) \
G_STMT_START{ \
char *end; \
lvalue = strtol (at, &end, base); \
if (at == end) \
PARSE_ERROR ("int"); \
at = end; \
GSK_SKIP_WHITESPACE (at); \
}G_STMT_END
#define PARSE_UINT(lvalue, base) \
G_STMT_START{ \
char *end; \
lvalue = strtoul (at, &end, base); \
if (at == end) \
PARSE_ERROR ("uint"); \
at = end; \
GSK_SKIP_WHITESPACE (at); \
}G_STMT_END
/* 'pid' */
PARSE_INT (rv->process_id, 0);
/* 'comm' */
if (*at != LPAREN)
PARSE_ERROR_EXPECTED ("left-paren");
at++;
end_name = strchr (at, RPAREN);
if (end_name == NULL)
PARSE_ERROR_EXPECTED ("right-paren");
rv->exe_filename = g_strndup (at, end_name - at);
at = end_name + 1;
GSK_SKIP_WHITESPACE (at);
/* 'state' */
if (*at == 0 || strchr ("RSDZTW", *at) == NULL)
PARSE_ERROR_EXPECTED ("a valid process state, one of R,S,D,Z,T,W");
rv->state = *at;
at++;
GSK_SKIP_WHITESPACE (at);
/* 'ppid' */
PARSE_INT (rv->parent_process_id, 0);
/* 'pgrp' */
PARSE_INT (rv->process_group_id, 0);
/* 'session' */
PARSE_INT (rv->session_id, 0);
/* 'tty_nr' */
PARSE_INT (rv->tty_id, 0);
/* 'tpgid' */
PARSE_INT (dummy_int, 0);
/* 'flags' */
PARSE_INT (dummy_int, 8);
/* 'minflt' */
PARSE_UINT (dummy_uint, 0);
/* 'cminflt' */
PARSE_UINT (dummy_uint, 0);
/* 'majflt' */
PARSE_UINT (dummy_uint, 0);
/* 'cmajflt' */
PARSE_UINT (dummy_uint, 0);
/* 'utime' */
PARSE_UINT (rv->user_runtime, 0);
/* 'stime' */
PARSE_UINT (rv->kernel_runtime, 0);
/* 'cutime' */
PARSE_UINT (rv->child_user_runtime, 0);
/* 'cstime' */
PARSE_UINT (rv->child_kernel_runtime, 0);
/* 'priority' */
PARSE_INT (dummy_int, 0);
/* 'nice' */
PARSE_INT (rv->nice, 0);
/* '0' */
PARSE_INT (dummy_int, 0);
/* 'itrealvalue */
PARSE_INT (dummy_int, 0);
/* 'starttime' */
PARSE_INT (dummy_int, 0);
/* 'vsize' */
PARSE_UINT (rv->virtual_memory_size, 0);
/* 'rss' */
PARSE_UINT (dummy_uint, 0);
rv->resident_memory_size = dummy_uint * get_page_size ();
/* 'rlim' */
PARSE_UINT (dummy_uint, 0);
/* TODO: there's more fields, but we don't much care.
also the info in 'statm' is worth checking out */
g_free (filename);
g_free (content);
return rv;
}
void
gsk_process_info_free(GskProcessInfo *info)
{
g_free (info->exe_filename);
g_free (info);
}
const char *gsk_process_info_state_name (GskProcessInfoState state)
{
switch (state)
{
case GSK_PROCESS_INFO_RUNNING:
return "running";
case GSK_PROCESS_INFO_SLEEPING:
return "sleeping";
case GSK_PROCESS_INFO_DISK:
return "disk-wait";
case GSK_PROCESS_INFO_ZOMBIE:
return "zombie";
case GSK_PROCESS_INFO_TRACED:
return "traced";
case GSK_PROCESS_INFO_PAGING:
return "paging";
default:
return "**unknown process state**";
}
}
syntax highlighted by Code2HTML, v. 0.9.1