From f522af65aa004fea09705c74115836c6acd1cddb Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 14:15:54 +0000 Subject: 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. --- process.c | 16 +++++++------ procfs_dir.c | 78 ++++++++++++++++++++++++++++++++++++++---------------------- procfs_dir.h | 48 +++++++++++++++++++++++++++---------- rootdir.c | 36 +++++++++++++++------------- 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); } -- cgit v1.2.3