summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremie Koenig <jk@jk.fr.eu.org>2010-08-23 14:15:54 +0000
committerJeremie Koenig <jk@jk.fr.eu.org>2010-08-30 14:31:31 +0200
commitf522af65aa004fea09705c74115836c6acd1cddb (patch)
tree9de6222c1be5b8e1f678801233436796ea753a94
parent4646c4a3ef6171a0ddec4fcfe0c6aa34b50501cd (diff)
Revamp procfs_dir
* procfs_dir.c, procfs_dir.h: Revamp the interface to make the more complicated use cases somewhat less hackish. * process.c: Update, specify a default make_node function. * rootdir.c: Likewise; make this optional "self" link use case somewhat less hackish.
-rw-r--r--process.c16
-rw-r--r--procfs_dir.c78
-rw-r--r--procfs_dir.h48
-rw-r--r--rootdir.c36
4 files changed, 113 insertions, 65 deletions
diff --git a/process.c b/process.c
index 624f79e..61688f7 100644
--- a/process.c
+++ b/process.c
@@ -209,7 +209,7 @@ process_file_get_contents (void *hook, char **contents, ssize_t *contents_len)
}
static struct node *
-process_file_make_node (void *dir_hook, void *entry_hook)
+process_file_make_node (void *dir_hook, const void *entry_hook)
{
static const struct procfs_node_ops ops = {
.get_contents = process_file_get_contents,
@@ -245,7 +245,6 @@ process_file_make_node (void *dir_hook, void *entry_hook)
static struct procfs_dir_entry entries[] = {
{
.name = "cmdline",
- .make_node = process_file_make_node,
.hook = & (struct process_file_desc) {
.get_contents = process_file_gc_cmdline,
.needs = PSTAT_ARGS,
@@ -254,7 +253,6 @@ static struct procfs_dir_entry entries[] = {
},
{
.name = "environ",
- .make_node = process_file_make_node,
.hook = & (struct process_file_desc) {
.get_contents = process_file_gc_environ,
.needs = PSTAT_ENV,
@@ -265,7 +263,6 @@ static struct procfs_dir_entry entries[] = {
{
/* Beware of the hack below, which requires this to be entries[2]. */
.name = "stat",
- .make_node = process_file_make_node,
.hook = & (struct process_file_desc) {
.get_contents = process_file_gc_stat,
.needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO
@@ -276,7 +273,6 @@ static struct procfs_dir_entry entries[] = {
},
{
.name = "statm",
- .make_node = process_file_make_node,
.hook = & (struct process_file_desc) {
.get_contents = process_file_gc_statm,
.needs = PSTAT_TASK_BASIC,
@@ -285,7 +281,6 @@ static struct procfs_dir_entry entries[] = {
},
{
.name = "status",
- .make_node = process_file_make_node,
.hook = & (struct process_file_desc) {
.get_contents = process_file_gc_status,
.needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO
@@ -299,6 +294,13 @@ static struct procfs_dir_entry entries[] = {
error_t
process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np)
{
+ static const struct procfs_dir_ops dir_ops = {
+ .entries = entries,
+ .cleanup = (void (*)(void *)) _proc_stat_free,
+ .entry_ops = {
+ .make_node = process_file_make_node,
+ },
+ };
struct proc_stat *ps;
int owner;
error_t err;
@@ -317,7 +319,7 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np)
accessed in a more robust and straightforward way. */
((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode;
- *np = procfs_dir_make_node (entries, ps, (void (*)(void *)) _proc_stat_free);
+ *np = procfs_dir_make_node (&dir_ops, ps);
if (! *np)
return ENOMEM;
diff --git a/procfs_dir.c b/procfs_dir.c
index 8ec3f7a..dfe30a2 100644
--- a/procfs_dir.c
+++ b/procfs_dir.c
@@ -5,49 +5,71 @@
struct procfs_dir_node
{
- const struct procfs_dir_entry *entries;
+ const struct procfs_dir_ops *ops;
void *hook;
- void (*cleanup) (void *hook);
};
+static int
+entry_exists (struct procfs_dir_node *dir, const struct procfs_dir_entry *ent)
+{
+ if (ent->ops.exists)
+ return ent->ops.exists (dir->hook, ent->hook);
+ if (dir->ops->entry_ops.exists)
+ return dir->ops->entry_ops.exists (dir->hook, ent->hook);
+
+ return 1;
+}
+
static error_t
procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len)
{
static const char dot_dotdot[] = ".\0..";
- struct procfs_dir_node *dn = hook;
+ struct procfs_dir_node *dir = hook;
const struct procfs_dir_entry *ent;
- char *pos;
+ int pos;
- *contents_len = sizeof dot_dotdot;
- for (ent = dn->entries; ent->name; ent++)
- *contents_len += strlen (ent->name) + 1;
+ /* Evaluate how much space is needed. Note that we include the hidden
+ entries, just in case their status changes between now and then. */
+ pos = sizeof dot_dotdot;
+ for (ent = dir->ops->entries; ent->name; ent++)
+ pos += strlen (ent->name) + 1;
- *contents = malloc (*contents_len);
+ *contents = malloc (pos);
if (! *contents)
return ENOMEM;
memcpy (*contents, dot_dotdot, sizeof dot_dotdot);
- pos = *contents + sizeof dot_dotdot;
- for (ent = dn->entries; ent->name; ent++)
+ pos = sizeof dot_dotdot;
+ for (ent = dir->ops->entries; ent->name; ent++)
{
- strcpy (pos, ent->name);
+ if (! entry_exists (dir, ent))
+ continue;
+
+ strcpy (*contents + pos, ent->name);
pos += strlen (ent->name) + 1;
}
+ *contents_len = pos;
return 0;
}
static error_t
procfs_dir_lookup (void *hook, const char *name, struct node **np)
{
- struct procfs_dir_node *dn = hook;
+ struct procfs_dir_node *dir = hook;
const struct procfs_dir_entry *ent;
- for (ent = dn->entries; ent->name && strcmp (name, ent->name); ent++);
+ for (ent = dir->ops->entries; ent->name && strcmp (name, ent->name); ent++);
if (! ent->name)
return ENOENT;
- *np = ent->make_node (dn->hook, ent->hook);
+ if (ent->ops.make_node)
+ *np = ent->ops.make_node (dir->hook, ent->hook);
+ else if (dir->ops->entry_ops.make_node)
+ *np = dir->ops->entry_ops.make_node (dir->hook, ent->hook);
+ else
+ return EGRATUITOUS;
+
if (! *np)
return ENOMEM;
@@ -57,17 +79,16 @@ procfs_dir_lookup (void *hook, const char *name, struct node **np)
static void
procfs_dir_cleanup (void *hook)
{
- struct procfs_dir_node *dn = hook;
+ struct procfs_dir_node *dir = hook;
- if (dn->cleanup)
- dn->cleanup (dn->hook);
+ if (dir->ops->cleanup)
+ dir->ops->cleanup (dir->hook);
- free (dn);
+ free (dir);
}
struct node *
-procfs_dir_make_node (const struct procfs_dir_entry *entries,
- void *dir_hook, void (*cleanup) (void *dir_hook))
+procfs_dir_make_node (const struct procfs_dir_ops *dir_ops, void *dir_hook)
{
static const struct procfs_node_ops ops = {
.get_contents = procfs_dir_get_contents,
@@ -75,21 +96,20 @@ procfs_dir_make_node (const struct procfs_dir_entry *entries,
.cleanup_contents = procfs_cleanup_contents_with_free,
.cleanup = procfs_dir_cleanup,
};
- struct procfs_dir_node *dn;
+ struct procfs_dir_node *dir;
- dn = malloc (sizeof *dn);
- if (! dn)
+ dir = malloc (sizeof *dir);
+ if (! dir)
{
- if (cleanup)
- cleanup (dir_hook);
+ if (dir_ops->cleanup)
+ dir_ops->cleanup (dir_hook);
return NULL;
}
- dn->entries = entries;
- dn->hook = dir_hook;
- dn->cleanup = cleanup;
+ dir->ops = dir_ops;
+ dir->hook = dir_hook;
- return procfs_make_node (&ops, dn);
+ return procfs_make_node (&ops, dir);
}
diff --git a/procfs_dir.h b/procfs_dir.h
index 4eb934e..12e99d2 100644
--- a/procfs_dir.h
+++ b/procfs_dir.h
@@ -1,20 +1,44 @@
+/* This module provides an abstraction layer for implementing simple
+ directories with (mostly) static contents. The user defines the
+ contents of the directory by providing a table of entries and various
+ optional callback functions. */
-/* Each entry associates a name with a callback function for creating new
- nodes corresponding to that entry. */
+/* These operations define how a given entry will behave. Either can be
+ omitted, both from the entry-specific operations and from the
+ directory-wide defaults. */
+struct procfs_dir_entry_ops
+{
+ /* Called when this entry is looked up to create a corresponding node. */
+ struct node *(*make_node)(void *dir_hook, const void *entry_hook);
+ /* If this is provided and returns 0, this entry will be hidden. */
+ int (*exists)(void *dir_hook, const void *entry_hook);
+};
+
+/* Describes an individual directory entry, associating a NAME with
+ * arbitrary HOOK data and node-specific OPS. */
struct procfs_dir_entry
{
const char *name;
- struct node *(*make_node)(void *dir_hook, void *entry_hook);
- void *hook;
+ const void *hook;
+ struct procfs_dir_entry_ops ops;
+};
+
+/* Describes a complete directory. ENTRIES is a table terminated by a
+ null NAME field. ENTRY_OPS provides default operations for the
+ entries which don't specify them. The optional CLEANUP function
+ should release all the resources associated with the directory hook. */
+struct procfs_dir_ops
+{
+ const struct procfs_dir_entry *entries;
+ void (*cleanup)(void *dir_hook);
+ struct procfs_dir_entry_ops entry_ops;
};
-/* A simple directory is built from a table of entries. The table is
- terminated by a null NAME pointer. The DIR_HOOK is passed the
- MAKE_NODE callback function of looked up procfs_dir_entries, and to
- the provided CLEANUP function when the directory is destroyed.
- Returns the new directory node. If not enough memory can be
- allocated, CLEANUP is invoked immediately and NULL is returned. */
+/* Create and return a new node for the directory described in OPS.
+ The DIR_HOOK is passed the MAKE_NODE callback function of looked up
+ entries, as well as to the CLEANUP callback when the node is
+ destroyed. If not enough memory can be allocated, OPS->CLEANUP is
+ invoked immediately and NULL is returned. */
struct node *
-procfs_dir_make_node (const struct procfs_dir_entry *entries,
- void *dir_hook, void (*cleanup) (void *dir_hook));
+procfs_dir_make_node (const struct procfs_dir_ops *ops, void *dir_hook);
diff --git a/rootdir.c b/rootdir.c
index 364b073..dcee9a5 100644
--- a/rootdir.c
+++ b/rootdir.c
@@ -266,6 +266,12 @@ out:
return err;
}
+static int
+rootdir_fakeself_exists ()
+{
+ return opt_fake_self >= 0;
+}
+
static error_t
rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len)
{
@@ -275,13 +281,13 @@ rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len)
static struct node *
-rootdir_file_make_node (void *dir_hook, void *entry_hook)
+rootdir_file_make_node (void *dir_hook, const void *entry_hook)
{
return procfs_make_node (entry_hook, dir_hook);
}
static struct node *
-rootdir_symlink_make_node (void *dir_hook, void *entry_hook)
+rootdir_symlink_make_node (void *dir_hook, const void *entry_hook)
{
struct node *np = procfs_make_node (entry_hook, dir_hook);
if (np)
@@ -292,15 +298,17 @@ rootdir_symlink_make_node (void *dir_hook, void *entry_hook)
static const struct procfs_dir_entry rootdir_entries[] = {
{
.name = "self",
- .make_node = rootdir_symlink_make_node,
.hook = & (struct procfs_node_ops) {
.get_contents = rootdir_gc_fakeself,
.cleanup_contents = procfs_cleanup_contents_with_free,
},
+ .ops = {
+ .make_node = rootdir_symlink_make_node,
+ .exists = rootdir_fakeself_exists,
+ }
},
{
.name = "version",
- .make_node = rootdir_file_make_node,
.hook = & (struct procfs_node_ops) {
.get_contents = rootdir_gc_version,
.cleanup_contents = procfs_cleanup_contents_with_free,
@@ -308,7 +316,6 @@ static const struct procfs_dir_entry rootdir_entries[] = {
},
{
.name = "uptime",
- .make_node = rootdir_file_make_node,
.hook = & (struct procfs_node_ops) {
.get_contents = rootdir_gc_uptime,
.cleanup_contents = procfs_cleanup_contents_with_free,
@@ -316,7 +323,6 @@ static const struct procfs_dir_entry rootdir_entries[] = {
},
{
.name = "stat",
- .make_node = rootdir_file_make_node,
.hook = & (struct procfs_node_ops) {
.get_contents = rootdir_gc_stat,
.cleanup_contents = procfs_cleanup_contents_with_free,
@@ -324,7 +330,6 @@ static const struct procfs_dir_entry rootdir_entries[] = {
},
{
.name = "loadavg",
- .make_node = rootdir_file_make_node,
.hook = & (struct procfs_node_ops) {
.get_contents = rootdir_gc_loadavg,
.cleanup_contents = procfs_cleanup_contents_with_free,
@@ -332,7 +337,6 @@ static const struct procfs_dir_entry rootdir_entries[] = {
},
{
.name = "meminfo",
- .make_node = rootdir_file_make_node,
.hook = & (struct procfs_node_ops) {
.get_contents = rootdir_gc_meminfo,
.cleanup_contents = procfs_cleanup_contents_with_free,
@@ -340,7 +344,6 @@ static const struct procfs_dir_entry rootdir_entries[] = {
},
{
.name = "vmstat",
- .make_node = rootdir_file_make_node,
.hook = & (struct procfs_node_ops) {
.get_contents = rootdir_gc_vmstat,
.cleanup_contents = procfs_cleanup_contents_with_free,
@@ -348,7 +351,6 @@ static const struct procfs_dir_entry rootdir_entries[] = {
},
{
.name = "cmdline",
- .make_node = rootdir_file_make_node,
.hook = & (struct procfs_node_ops) {
.get_contents = rootdir_gc_cmdline,
.cleanup_contents = procfs_cleanup_contents_with_free,
@@ -360,12 +362,12 @@ static const struct procfs_dir_entry rootdir_entries[] = {
struct node
*rootdir_make_node (struct ps_context *pc)
{
- const struct procfs_dir_entry *entries;
-
- entries = rootdir_entries;
- if (opt_fake_self < 0)
- entries++;
-
- return procfs_dir_make_node (entries, pc, NULL);
+ static const struct procfs_dir_ops ops = {
+ .entries = rootdir_entries,
+ .entry_ops = {
+ .make_node = rootdir_file_make_node,
+ },
+ };
+ return procfs_dir_make_node (&ops, pc);
}