From 1083606b7f97f0284039aa197f7b2181f902d0bb Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 09:43:29 +0000 Subject: Basic infrastructure * procfs.h: New file; basic interfaces for procfs nodes. * procfs.c: New file; implement the basic infrastructure. * netfs.c: New file; bridge libnetfs and the procfs interfaces. * main.c: New file; mostly a "Hello, World!" for now. * Makefile: New file; standalone for now. --- main.c | 58 ++++++++++ netfs.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs.c | 90 +++++++++++++++ procfs.h | 40 +++++++ 4 files changed, 579 insertions(+) create mode 100644 main.c create mode 100644 netfs.c create mode 100644 procfs.c create mode 100644 procfs.h diff --git a/main.c b/main.c new file mode 100644 index 00000000..cafd0c9a --- /dev/null +++ b/main.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include "procfs.h" + +static error_t get_contents (void *hook, void **contents, size_t *contents_len) +{ + static const char hello[] = "Hello, World!\n"; + *contents = (void *) hello; + *contents_len = sizeof hello - 1; + return 0; +} + +static error_t get_entries (void *hook, void **contents, size_t *contents_len) +{ + static const char entries[] = "hello"; + *contents = (void *) entries; + *contents_len = sizeof entries; + return 0; +} + +static error_t lookup (void *hook, const char *name, struct node **np) +{ + static const struct procfs_node_ops ops = { .get_contents = get_contents }; + + if (strcmp (name, "hello")) + return ENOENT; + + *np = procfs_make_node (&ops, NULL); + if (! *np) + return ENOMEM; + + return 0; +} + +int main (int argc, char **argv) +{ + static const struct procfs_node_ops ops = { + .get_contents = get_entries, + .lookup = lookup, + }; + mach_port_t bootstrap; + + argp_parse (&netfs_std_startup_argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "Must be started as a translator"); + + netfs_init (); + netfs_root_node = procfs_make_node (&ops, NULL); + + netfs_startup (bootstrap, 0); + for (;;) + netfs_server_loop (); +} + diff --git a/netfs.c b/netfs.c new file mode 100644 index 00000000..58665085 --- /dev/null +++ b/netfs.c @@ -0,0 +1,391 @@ +#include +#include +#include +#include +#include +#include +#include "procfs.h" + +#define PROCFS_SERVER_NAME "procfs" +#define PROCFS_SERVER_VERSION "0.1.0" +#define PROCFS_MAXSYMLINKS 16 + + +/* Interesting libnetfs callback functions. */ + +/* The user must define this variable. Set this to the name of the + filesystem server. */ +char *netfs_server_name = PROCFS_SERVER_NAME; + +/* The user must define this variables. Set this to be the server + version number. */ +char *netfs_server_version = PROCFS_SERVER_VERSION; + +/* Maximum number of symlinks to follow before returning ELOOP. */ +int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; + +/* The user must define this function. Read from the locked file NP + for user CRED starting at OFFSET and continuing for up to *LEN + bytes. Put the data at DATA. Set *LEN to the amount successfully + read upon return. */ +error_t netfs_attempt_read (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + char *contents; + size_t contents_len; + error_t err; + + err = procfs_get_contents (np, (void **) &contents, &contents_len); + if (err) + return err; + + contents += offset; + contents_len -= offset; + + if (*len > contents_len) + *len = contents_len; + if (*len < 0) + *len = 0; + + memcpy (data, contents, *len); + return 0; +} + +/* The user must define this function. Read the contents of locked + node NP (a symlink), for USER, into BUF. */ +error_t netfs_attempt_readlink (struct iouser *user, struct node *np, + char *buf) +{ + return EIO; +} + +/* Helper function for netfs_get_dirents() below. CONTENTS is an argz + vector of directory entry names, as returned by procfs_get_contents(). + Convert at most NENTRIES of them to dirent structures, put them in + DATA (if not NULL), write the number of entries processed in *AMT and + return the required/used space in DATACNT. */ +static int putentries (char *contents, size_t contents_len, int nentries, + char *data, mach_msg_type_number_t *datacnt) +{ + int i; + + *datacnt = 0; + for (i = 0; contents_len && (nentries < 0 || i < nentries); i++) + { + int namlen = strlen (contents); + int reclen = sizeof (struct dirent) + namlen; + + if (data) + { + struct dirent *d = (struct dirent *) (data + *datacnt); + d->d_fileno = 42; /* XXX */ + d->d_namlen = namlen; + d->d_reclen = reclen; + d->d_type = DT_UNKNOWN; + strcpy (d->d_name, contents); + } + + *datacnt += reclen; + contents += namlen + 1; + contents_len -= namlen + 1; + } + + return i; +} + +/* The user must define this function. Fill the array *DATA of size + BUFSIZE with up to NENTRIES dirents from DIR (which is locked) + starting with entry ENTRY for user CRED. The number of entries in + the array is stored in *AMT and the number of bytes in *DATACNT. + If the supplied buffer is not large enough to hold the data, it + should be grown. */ +error_t netfs_get_dirents (struct iouser *cred, struct node *dir, + int entry, int nentries, char **data, + mach_msg_type_number_t *datacnt, + vm_size_t bufsize, int *amt) +{ + char *contents; + size_t contents_len; + error_t err; + + err = procfs_get_contents (dir, (void **) &contents, &contents_len); + if (err) + return err; + + /* We depend on the fact that CONTENTS is terminated. */ + assert (contents_len == 0 || contents[contents_len - 1] == '\0'); + + /* Skip to the first requested entry. */ + while (contents_len && entry--) + { + int ofs = strlen (contents) + 1; + contents += ofs; + contents_len -= ofs; + } + + /* Allocate a buffer if necessary. */ + putentries (contents, contents_len, nentries, NULL, datacnt); + if (bufsize < *datacnt) + { + void *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); + if (n == MAP_FAILED) + return ENOMEM; + + *data = n; + } + + /* Do the actual conversion. */ + *amt = putentries (contents, contents_len, nentries, *data, datacnt); + + return 0; +} + +/* The user must define this function. Lookup NAME in DIR (which is + locked) for USER; set *NP to the found name upon return. If the + name was not found, then return ENOENT. On any error, clear *NP. + (*NP, if found, should be locked and a reference to it generated. + This call should unlock DIR no matter what.) */ +error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **np) +{ + error_t err; + + err = procfs_lookup (dir, name, np); + if (! err) + mutex_lock (&(*np)->lock); + + mutex_unlock (&dir->lock); + return err; +} + +/* The user must define this function. Node NP has no more references; + free all its associated storage. */ +void netfs_node_norefs (struct node *np) +{ + procfs_cleanup (np); + free (np); +} + + +/* Libnetfs callbacks managed with libfshelp. */ + +/* The user must define this function. Locked node NP is being opened + by USER, with FLAGS. NEWNODE is nonzero if we just created this + node. Return an error if we should not permit the open to complete + because of a permission restriction. */ +error_t netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = 0; + if (!err && (flags & O_READ)) + err = fshelp_access (&np->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&np->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&np->nn_stat, S_IEXEC, user); + return err; +} + +/* The user must define this function. Return the valid access + types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for + locked file NP and user CRED. */ +error_t netfs_report_access (struct iouser *cred, struct node *np, + int *types) +{ + *types = 0; + if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + return 0; +} + + +/* Trivial or unsupported libnetfs callbacks. */ + +/* The user must define this function. Make sure that NP->nn_stat is + filled with the most current information. CRED identifies the user + responsible for the operation. NP is locked. */ +error_t netfs_validate_stat (struct node *np, struct iouser *cred) +{ + return 0; +} + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NP, to change + the owner to UID and the group to GID. */ +error_t netfs_attempt_chown (struct iouser *cred, struct node *np, + uid_t uid, uid_t gid) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chauthor + call for the user specified by CRED on locked node NP, thereby + changing the author to AUTHOR. */ +error_t netfs_attempt_chauthor (struct iouser *cred, struct node *np, + uid_t author) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NODE, to change + the mode to MODE. Unlike the normal Unix and Hurd meaning of + chmod, this function is also used to attempt to change files into + other types. If such a transition is attempted which is + impossible, then return EOPNOTSUPP. */ +error_t netfs_attempt_chmod (struct iouser *cred, struct node *np, + mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn locked node NP + (user CRED) into a symlink with target NAME. */ +error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn NODE (user + CRED) into a device. TYPE is either S_IFBLK or S_IFCHR. NP is + locked. */ +error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np, + mode_t type, dev_t indexes) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chflags + call for the user specified by CRED on locked node NP, to change + the flags to FLAGS. */ +error_t netfs_attempt_chflags (struct iouser *cred, struct node *np, + int flags) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a utimes + call for the user specified by CRED on locked node NP, to change + the atime to ATIME and the mtime to MTIME. If ATIME or MTIME is + null, then set to the current time. */ +error_t netfs_attempt_utimes (struct iouser *cred, struct node *np, + struct timespec *atime, struct timespec *mtime) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to set the + size of the locked file NP (for user CRED) to SIZE bytes long. */ +error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, + loff_t size) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to fetch + filesystem status information for the remote filesystem, for the + user CRED. NP is locked. */ +error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, + fsys_statfsbuf_t *st) +{ + return ENOSYS; +} + +/* The user must define this function. This should sync the locked + file NP completely to disk, for the user CRED. If WAIT is set, + return only after the sync is completely finished. */ +error_t netfs_attempt_sync (struct iouser *cred, struct node *np, + int wait) +{ + return 0; +} + +/* The user must define this function. This should sync the entire + remote filesystem. If WAIT is set, return only after the sync is + completely finished. */ +error_t netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + +/* The user must define this function. Delete NAME in DIR (which is + locked) for USER. */ +error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to rename the + directory FROMDIR to TODIR. Note that neither of the specific nodes + are locked. */ +error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, + char *fromname, struct node *todir, + char *toname, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a new + directory named NAME in DIR (which is locked) for USER with mode + MODE. */ +error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, + char *name, mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to remove directory + named NAME in DIR (which is locked) for USER. */ +error_t netfs_attempt_rmdir (struct iouser *user, + struct node *dir, char *name) +{ + return EROFS; +} + + +/* The user must define this function. Create a link in DIR with name + NAME to FILE for USER. Note that neither DIR nor FILE are + locked. If EXCL is set, do not delete the target. Return EEXIST if + NAME is already found in DIR. */ +error_t netfs_attempt_link (struct iouser *user, struct node *dir, + struct node *file, char *name, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create an anonymous + file related to DIR (which is locked) for USER with MODE. Set *NP + to the returned file upon success. No matter what, unlock DIR. */ +error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, + mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a file named + NAME in DIR (which is locked) for USER with MODE. Set *NP to the + new node upon return. On any error, clear *NP. *NP should be + locked on success; no matter what, unlock DIR before returning. */ +error_t netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Write to the locked file NP + for user CRED starting at OFSET and continuing for up to *LEN bytes + from DATA. Set *LEN to the amount successfully written upon + return. */ +error_t netfs_attempt_write (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + return EROFS; +} + + diff --git a/procfs.c b/procfs.c new file mode 100644 index 00000000..304befba --- /dev/null +++ b/procfs.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include "procfs.h" + +struct netnode +{ + const struct procfs_node_ops *ops; + void *hook; + + /* (cached) contents of the node */ + void *contents; + size_t contents_len; +}; + +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) +{ + struct netnode *nn; + struct node *np; + + nn = malloc (sizeof *nn); + if (! nn) + return NULL; + + memset (nn, 0, sizeof *nn); + nn->ops = ops; + nn->hook = hook; + + np = netfs_make_node (nn); + if (! np) + { + free (nn); + return NULL; + } + + np->nn = nn; + memset (&np->nn_stat, 0, sizeof np->nn_stat); + np->nn_translated = 0; + + if (np->nn->ops->lookup) + np->nn_stat.st_mode = S_IFDIR | 0555; + else + np->nn_stat.st_mode = S_IFREG | 0444; + + return np; +} + +error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) +{ + if (! np->nn->contents && np->nn->ops->get_contents) + { + void *contents; + size_t contents_len; + error_t err; + + err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); + if (err) + return err; + + np->nn->contents = contents; + np->nn->contents_len = contents_len; + } + + *data = np->nn->contents; + *data_len = np->nn->contents_len; + return 0; +} + +error_t procfs_lookup (struct node *np, const char *name, struct node **npp) +{ + error_t err = ENOENT; + + if (np->nn->ops->lookup) + err = np->nn->ops->lookup (np->nn->hook, name, npp); + + return err; +} + +void procfs_cleanup (struct node *np) +{ + if (np->nn->contents && np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->contents); + + if (np->nn->ops->cleanup) + np->nn->ops->cleanup (np->nn->hook); + + free (np->nn); +} diff --git a/procfs.h b/procfs.h new file mode 100644 index 00000000..0557b6d1 --- /dev/null +++ b/procfs.h @@ -0,0 +1,40 @@ +#include +#include + + +/* Interface for the procfs side. */ + +/* Any of these callback functions can be omitted, in which case + reasonable defaults will be used. The initial file mode and type + depend on whether a lookup function is provided, but can be + overridden in update_stat(). */ +struct procfs_node_ops +{ + /* Fetch the contents of a node. A pointer to the contents should be + returned in *CONTENTS and their length in *CONTENTS_LEN. The exact + nature of these data depends on whether the node is a regular file, + symlink or directory, as determined by the file mode in + netnode->nn_stat. For regular files and symlinks, they are what + you would expect; for directories, they are an argz vector of the + names of the entries. */ + error_t (*get_contents) (void *hook, void **contents, size_t *contents_len); + void (*cleanup_contents) (void *contents); + + /* Lookup NAME in this directory, and store the result in *np. The + returned node should be created by lookup() using procfs_make_node() + or a derived function. */ + error_t (*lookup) (void *hook, const char *name, struct node **np); + + /* Destroy this node. */ + void (*cleanup) (void *hook); +}; + +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); + + +/* Interface for the libnetfs side. */ + +error_t procfs_get_contents (struct node *np, void **data, size_t *data_len); +error_t procfs_lookup (struct node *np, const char *name, struct node **npp); +void procfs_cleanup (struct node *np); + -- cgit v1.2.3 From faf709e3898de748f4c7ea02110010b018a995a6 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 10:10:22 +0000 Subject: Add a helper module for simple regular files * procfs_file.h: New file, declares procfs_file_make_node. * procfs_file.c: New file, implements procfs_file_make_node. * main.c: Use them. * Makefile: Add the procfs_file module. --- main.c | 13 ++----------- procfs_file.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs_file.h | 6 ++++++ 3 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 procfs_file.c create mode 100644 procfs_file.h diff --git a/main.c b/main.c index cafd0c9a..457cf6a7 100644 --- a/main.c +++ b/main.c @@ -3,14 +3,7 @@ #include #include #include "procfs.h" - -static error_t get_contents (void *hook, void **contents, size_t *contents_len) -{ - static const char hello[] = "Hello, World!\n"; - *contents = (void *) hello; - *contents_len = sizeof hello - 1; - return 0; -} +#include "procfs_file.h" static error_t get_entries (void *hook, void **contents, size_t *contents_len) { @@ -22,12 +15,10 @@ static error_t get_entries (void *hook, void **contents, size_t *contents_len) static error_t lookup (void *hook, const char *name, struct node **np) { - static const struct procfs_node_ops ops = { .get_contents = get_contents }; - if (strcmp (name, "hello")) return ENOENT; - *np = procfs_make_node (&ops, NULL); + *np = procfs_file_make_node ("Hello, World!\n", -1, NULL); if (! *np) return ENOMEM; diff --git a/procfs_file.c b/procfs_file.c new file mode 100644 index 00000000..62419ee5 --- /dev/null +++ b/procfs_file.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include "procfs.h" +#include "procfs_file.h" + +struct procfs_file +{ + void *contents; + size_t len; + void (*cleanup)(void *contents); +}; + +error_t +procfs_file_getcontents (void *hook, void **contents, size_t *contents_len) +{ + struct procfs_file *f = hook; + + *contents = f->contents; + *contents_len = f->len; + return 0; +} + +void +procfs_file_cleanup (void *hook) +{ + struct procfs_file *f = hook; + + if (f->cleanup) + f->cleanup (f->contents); + + free (f); +} + +struct node * +procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)) +{ + static const struct procfs_node_ops ops = { + .get_contents = procfs_file_getcontents, + .cleanup = procfs_file_cleanup, + }; + struct procfs_file *f; + struct node *np; + + f = malloc (sizeof *f); + if (! f) + return NULL; + + f->contents = contents; + f->len = (len >= 0) ? len : strlen (f->contents); + f->cleanup = cleanup; + + np = procfs_make_node (&ops, f); + if (! np) + free (f); + + return np; +} + diff --git a/procfs_file.h b/procfs_file.h new file mode 100644 index 00000000..b615db93 --- /dev/null +++ b/procfs_file.h @@ -0,0 +1,6 @@ +/* Create a new regular file with the given CONTENTS. If LEN is negative, + CONTENTS is considered as a string and the file stops at the first + nul char. If CLEANUP is non-NULL, it is passed CONTENTS when the + node is destroyed. */ +struct node * +procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)); -- cgit v1.2.3 From e4f03466e3a31a8929e974ded8e6f6f451b3980a Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 10:26:12 +0000 Subject: Implement simple directories * procfs_dir.h: New file; declare the procfs_dir_make_node function, based on the procfs_dir_entry structure. * procfs_dir.c: New file; implement simple directories. * Makefile: Add the procfs_dir module. * main.c: Use it. --- main.c | 29 ++++++---------------- procfs_dir.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs_dir.h | 15 ++++++++++++ 3 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 procfs_dir.c create mode 100644 procfs_dir.h diff --git a/main.c b/main.c index 457cf6a7..62e440d4 100644 --- a/main.c +++ b/main.c @@ -4,32 +4,19 @@ #include #include "procfs.h" #include "procfs_file.h" +#include "procfs_dir.h" -static error_t get_entries (void *hook, void **contents, size_t *contents_len) +static struct node *make_file (void *dir_hook, void *ent_hook) { - static const char entries[] = "hello"; - *contents = (void *) entries; - *contents_len = sizeof entries; - return 0; -} - -static error_t lookup (void *hook, const char *name, struct node **np) -{ - if (strcmp (name, "hello")) - return ENOENT; - - *np = procfs_file_make_node ("Hello, World!\n", -1, NULL); - if (! *np) - return ENOMEM; - - return 0; + return procfs_file_make_node (ent_hook, -1, NULL); } int main (int argc, char **argv) { - static const struct procfs_node_ops ops = { - .get_contents = get_entries, - .lookup = lookup, + static const struct procfs_dir_entry entries[] = { + { "hello", make_file, "Hello, World!\n" }, + { "goodbye", make_file, "Goodbye, cruel World!\n" }, + { } }; mach_port_t bootstrap; @@ -40,7 +27,7 @@ int main (int argc, char **argv) error (1, 0, "Must be started as a translator"); netfs_init (); - netfs_root_node = procfs_make_node (&ops, NULL); + netfs_root_node = procfs_dir_make_node (entries, NULL); netfs_startup (bootstrap, 0); for (;;) diff --git a/procfs_dir.c b/procfs_dir.c new file mode 100644 index 00000000..4d4faa28 --- /dev/null +++ b/procfs_dir.c @@ -0,0 +1,79 @@ +#include +#include +#include "procfs.h" +#include "procfs_dir.h" + +struct procfs_dir_node +{ + const struct procfs_dir_entry *entries; + void *hook; +}; + +static error_t +procfs_dir_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct procfs_dir_node *dn = hook; + const struct procfs_dir_entry *ent; + char *pos; + + *contents_len = 0; + for (ent = dn->entries; ent->name; ent++) + *contents_len += strlen (ent->name) + 1; + + *contents = malloc (*contents_len); + if (! *contents) + return ENOMEM; + + pos = *contents; + for (ent = dn->entries; ent->name; ent++) + { + strcpy (pos, ent->name); + pos += strlen (ent->name) + 1; + } + + return 0; +} + +static error_t +procfs_dir_lookup (void *hook, const char *name, struct node **np) +{ + struct procfs_dir_node *dn = hook; + const struct procfs_dir_entry *ent; + + for (ent = dn->entries; ent->name && strcmp (name, ent->name); ent++); + if (! ent->name) + return ENOENT; + + *np = ent->make_node (dn->hook, ent->hook); + if (! *np) + return ENOMEM; + + return 0; +} + +struct node * +procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = procfs_dir_get_contents, + .lookup = procfs_dir_lookup, + .cleanup_contents = free, + .cleanup = free, + }; + struct procfs_dir_node *dn; + struct node *np; + + dn = malloc (sizeof *dn); + if (! dn) + return NULL; + + dn->entries = entries; + dn->hook = dir_hook; + + np = procfs_make_node (&ops, dn); + if (! np) + free (dn); + + return np; +} + diff --git a/procfs_dir.h b/procfs_dir.h new file mode 100644 index 00000000..1ba45ad0 --- /dev/null +++ b/procfs_dir.h @@ -0,0 +1,15 @@ + +/* Each entry associates a name with a callback function for creating new + nodes corresponding to that entry. */ +struct procfs_dir_entry +{ + const char *name; + struct node *(*make_node)(void *dir_hook, void *entry_hook); + void *hook; +}; + +/* A simple directory is built from a table of entries. The table is + terminated by a null NAME pointer. */ +struct node * +procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook); + -- cgit v1.2.3 From 1db5f258c1ce92d92aa1589762865d96cbb20971 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 12:49:33 +0000 Subject: Add the list of processes as a directory * proclist.h, proclist.c: New files. * main.c: Add a proclist directory based on them. * Makefile: Include the proclist module. --- main.c | 8 +++++++ proclist.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ proclist.h | 2 ++ 3 files changed, 91 insertions(+) create mode 100644 proclist.c create mode 100644 proclist.h diff --git a/main.c b/main.c index 62e440d4..bc5c865c 100644 --- a/main.c +++ b/main.c @@ -1,21 +1,29 @@ #include +#include #include #include #include #include "procfs.h" #include "procfs_file.h" #include "procfs_dir.h" +#include "proclist.h" static struct node *make_file (void *dir_hook, void *ent_hook) { return procfs_file_make_node (ent_hook, -1, NULL); } +static struct node *make_proclist (void *dir_hook, void *ent_hook) +{ + return proclist_make_node (getproc ()); +} + int main (int argc, char **argv) { static const struct procfs_dir_entry entries[] = { { "hello", make_file, "Hello, World!\n" }, { "goodbye", make_file, "Goodbye, cruel World!\n" }, + { "proclist", make_proclist, }, { } }; mach_port_t bootstrap; diff --git a/proclist.c b/proclist.c new file mode 100644 index 00000000..4dd6ab31 --- /dev/null +++ b/proclist.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include "procfs.h" +#include "procfs_file.h" +#include "procfs_dir.h" + +#define PID_STR_SIZE (3 * sizeof (pid_t) + 1) + +struct proclist_node +{ + process_t process; +}; + +static error_t +proclist_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct proclist_node *pl = hook; + pidarray_t pids; + mach_msg_type_number_t num_pids; + error_t err; + int i; + + num_pids = 0; + err = proc_getallpids (pl->process, &pids, &num_pids); + if (err) + return EIO; + + *contents = malloc (num_pids * PID_STR_SIZE); + if (*contents) + { + *contents_len = 0; + for (i=0; i < num_pids; i++) + { + int n = sprintf (*contents + *contents_len, "%d", pids[i]); + assert (n >= 0); + *contents_len += (n + 1); + } + } + else + err = ENOMEM; + + vm_deallocate (mach_task_self (), (vm_address_t) pids, num_pids * sizeof pids[0]); + return err; +} + +static error_t +proclist_lookup (void *hook, const char *name, struct node **np) +{ + *np = procfs_file_make_node ("Ceci n'est pas un processus\n", -1, NULL); + return *np ? 0 : ENOMEM; +} + +struct node * +proclist_make_node (process_t process) +{ + static const struct procfs_node_ops ops = { + .get_contents = proclist_get_contents, + .lookup = proclist_lookup, + .cleanup_contents = free, + .cleanup = free, + }; + struct proclist_node *pl; + struct node *np; + + pl = malloc (sizeof *pl); + if (! pl) + return NULL; + + memset (pl, 0, sizeof *pl); + pl->process = process; + + np = procfs_make_node (&ops, pl); + if (! np) + free (pl); + + return np; +} + diff --git a/proclist.h b/proclist.h new file mode 100644 index 00000000..a766d50e --- /dev/null +++ b/proclist.h @@ -0,0 +1,2 @@ +#include +struct node *proclist_make_node (process_t process); -- cgit v1.2.3 From ef3adf6d159d566d83335cfe55a72206d492d838 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 16:48:55 +0000 Subject: Fix the failure semantics of procfs_make_node * procfs.c (procfs_make_node): Invoke the cleanup callback on failure, so that callers don't have to. * procfs.h: Document the change. * procfs_dir.c (procfs_dir_make_node), procfs_file.c (procfs_file_make_node), proclist.c (proclist_make_node): Update to reflect the change. --- procfs.c | 14 +++++++++----- procfs.h | 2 ++ procfs_dir.c | 7 +------ procfs_file.c | 7 +------ proclist.c | 7 +------ 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/procfs.c b/procfs.c index 304befba..0c57686b 100644 --- a/procfs.c +++ b/procfs.c @@ -22,7 +22,7 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) nn = malloc (sizeof *nn); if (! nn) - return NULL; + goto fail; memset (nn, 0, sizeof *nn); nn->ops = ops; @@ -30,10 +30,7 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) np = netfs_make_node (nn); if (! np) - { - free (nn); - return NULL; - } + goto fail; np->nn = nn; memset (&np->nn_stat, 0, sizeof np->nn_stat); @@ -45,6 +42,13 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) np->nn_stat.st_mode = S_IFREG | 0444; return np; + +fail: + if (ops->cleanup) + ops->cleanup (hook); + + free (nn); + return NULL; } error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) diff --git a/procfs.h b/procfs.h index 0557b6d1..21b0f93d 100644 --- a/procfs.h +++ b/procfs.h @@ -29,6 +29,8 @@ struct procfs_node_ops void (*cleanup) (void *hook); }; +/* Create a new node and return it. Returns NULL if it fails to allocate + enough memory. In this case, ops->cleanup will be invoked. */ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); diff --git a/procfs_dir.c b/procfs_dir.c index 4d4faa28..62a45b1b 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -61,7 +61,6 @@ procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook) .cleanup = free, }; struct procfs_dir_node *dn; - struct node *np; dn = malloc (sizeof *dn); if (! dn) @@ -70,10 +69,6 @@ procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook) dn->entries = entries; dn->hook = dir_hook; - np = procfs_make_node (&ops, dn); - if (! np) - free (dn); - - return np; + return procfs_make_node (&ops, dn); } diff --git a/procfs_file.c b/procfs_file.c index 62419ee5..cb0488e9 100644 --- a/procfs_file.c +++ b/procfs_file.c @@ -40,7 +40,6 @@ procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)) .cleanup = procfs_file_cleanup, }; struct procfs_file *f; - struct node *np; f = malloc (sizeof *f); if (! f) @@ -50,10 +49,6 @@ procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)) f->len = (len >= 0) ? len : strlen (f->contents); f->cleanup = cleanup; - np = procfs_make_node (&ops, f); - if (! np) - free (f); - - return np; + return procfs_make_node (&ops, f); } diff --git a/proclist.c b/proclist.c index 4dd6ab31..16cef9d6 100644 --- a/proclist.c +++ b/proclist.c @@ -63,7 +63,6 @@ proclist_make_node (process_t process) .cleanup = free, }; struct proclist_node *pl; - struct node *np; pl = malloc (sizeof *pl); if (! pl) @@ -72,10 +71,6 @@ proclist_make_node (process_t process) memset (pl, 0, sizeof *pl); pl->process = process; - np = procfs_make_node (&ops, pl); - if (! np) - free (pl); - - return np; + return procfs_make_node (&ops, pl); } -- cgit v1.2.3 From 09b121d42e9ba25e103b103bf1f53e1350e084a1 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 17:27:54 +0000 Subject: More cleanup possibilities * procfs.c, procfs.h: Extend the signature of the cleanup_contents callback in the procfs_node_ops structure to include the hook and contents_len. (cleanup_contents_with_free, cleanup_contents_with_vm_deallocate): New functions, can be used as a cleanup_contents callback for simple cases. * procfs_dir.c, procfs_dir.h (procfs_dir_make_node): Update, add a cleanup callback, make sure the cleanup callback is invoked if there is an error. * proclist.c (proclist_make_node), main.c (main): Update to match the new interfaces. --- main.c | 2 +- procfs.c | 15 ++++++++++++++- procfs.h | 6 +++++- procfs_dir.c | 27 +++++++++++++++++++++++---- procfs_dir.h | 9 +++++++-- proclist.c | 2 +- 6 files changed, 51 insertions(+), 10 deletions(-) diff --git a/main.c b/main.c index bc5c865c..4350eff5 100644 --- a/main.c +++ b/main.c @@ -35,7 +35,7 @@ int main (int argc, char **argv) error (1, 0, "Must be started as a translator"); netfs_init (); - netfs_root_node = procfs_dir_make_node (entries, NULL); + netfs_root_node = procfs_dir_make_node (entries, NULL, NULL); netfs_startup (bootstrap, 0); for (;;) diff --git a/procfs.c b/procfs.c index 0c57686b..755e0519 100644 --- a/procfs.c +++ b/procfs.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "procfs.h" @@ -15,6 +16,18 @@ struct netnode size_t contents_len; }; +void +procfs_cleanup_contents_with_free (void *hook, void *cont, size_t len) +{ + free (cont); +} + +void +procfs_cleanup_contents_with_vm_deallocate (void *hook, void *cont, size_t len) +{ + vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); +} + struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) { struct netnode *nn; @@ -85,7 +98,7 @@ error_t procfs_lookup (struct node *np, const char *name, struct node **npp) void procfs_cleanup (struct node *np) { if (np->nn->contents && np->nn->ops->cleanup_contents) - np->nn->ops->cleanup_contents (np->nn->contents); + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); if (np->nn->ops->cleanup) np->nn->ops->cleanup (np->nn->hook); diff --git a/procfs.h b/procfs.h index 21b0f93d..3cb3223d 100644 --- a/procfs.h +++ b/procfs.h @@ -18,7 +18,7 @@ struct procfs_node_ops you would expect; for directories, they are an argz vector of the names of the entries. */ error_t (*get_contents) (void *hook, void **contents, size_t *contents_len); - void (*cleanup_contents) (void *contents); + void (*cleanup_contents) (void *hook, void *contents, size_t contents_len); /* Lookup NAME in this directory, and store the result in *np. The returned node should be created by lookup() using procfs_make_node() @@ -29,6 +29,10 @@ struct procfs_node_ops void (*cleanup) (void *hook); }; +/* These helper functions can be used as procfs_node_ops.cleanup_contents. */ +void procfs_cleanup_contents_with_free (void *, void *, size_t); +void procfs_cleanup_contents_with_vm_deallocate (void *, void *, size_t); + /* Create a new node and return it. Returns NULL if it fails to allocate enough memory. In this case, ops->cleanup will be invoked. */ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); diff --git a/procfs_dir.c b/procfs_dir.c index 62a45b1b..b7fb28fa 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -7,6 +7,7 @@ struct procfs_dir_node { const struct procfs_dir_entry *entries; void *hook; + void (*cleanup) (void *hook); }; static error_t @@ -51,23 +52,41 @@ procfs_dir_lookup (void *hook, const char *name, struct node **np) return 0; } +static void +procfs_dir_cleanup (void *hook) +{ + struct procfs_dir_node *dn = hook; + + if (dn->cleanup) + dn->cleanup (dn->hook); + + free (dn); +} + struct node * -procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook) +procfs_dir_make_node (const struct procfs_dir_entry *entries, + void *dir_hook, void (*cleanup) (void *dir_hook)) { static const struct procfs_node_ops ops = { .get_contents = procfs_dir_get_contents, .lookup = procfs_dir_lookup, - .cleanup_contents = free, - .cleanup = free, + .cleanup_contents = procfs_cleanup_contents_with_free, + .cleanup = procfs_dir_cleanup, }; struct procfs_dir_node *dn; dn = malloc (sizeof *dn); if (! dn) - return NULL; + { + if (cleanup) + cleanup (dir_hook); + + return NULL; + } dn->entries = entries; dn->hook = dir_hook; + dn->cleanup = cleanup; return procfs_make_node (&ops, dn); } diff --git a/procfs_dir.h b/procfs_dir.h index 1ba45ad0..4eb934e0 100644 --- a/procfs_dir.h +++ b/procfs_dir.h @@ -9,7 +9,12 @@ struct procfs_dir_entry }; /* A simple directory is built from a table of entries. The table is - terminated by a null NAME pointer. */ + 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. */ struct node * -procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook); +procfs_dir_make_node (const struct procfs_dir_entry *entries, + void *dir_hook, void (*cleanup) (void *dir_hook)); diff --git a/proclist.c b/proclist.c index 16cef9d6..e009ebdd 100644 --- a/proclist.c +++ b/proclist.c @@ -59,7 +59,7 @@ proclist_make_node (process_t process) static const struct procfs_node_ops ops = { .get_contents = proclist_get_contents, .lookup = proclist_lookup, - .cleanup_contents = free, + .cleanup_contents = procfs_cleanup_contents_with_free, .cleanup = free, }; struct proclist_node *pl; -- cgit v1.2.3 From 931c02a6bd8197b0b6334622795984463c05200b Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 20:34:58 +0000 Subject: Add real process nodes * process.c, process.h: New files, implement a process directory with cmdline and environ files. * Makefile: Add the process module. * proclist.c: Replace stub pid files with the real thing. --- process.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ process.h | 2 ++ proclist.c | 13 ++++++++--- 3 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 process.c create mode 100644 process.h diff --git a/process.c b/process.c new file mode 100644 index 00000000..90ea511b --- /dev/null +++ b/process.c @@ -0,0 +1,79 @@ +#include +#include +#include "procfs.h" +#include "procfs_dir.h" +#include "process.h" + +struct process_node { + process_t procserv; + pid_t pid; +}; + + +/* The proc_getprocargs() and proc_getprocenv() calls have the same + prototype and we use them in the same way; namely, publish the data + they return as-is. We take advantage of this to have common code and + use a function pointer as the procfs_dir "entry hook" to choose the + call to use on a file by file basis. */ + +struct process_argz_node +{ + struct process_node pn; + error_t (*getargz) (process_t, pid_t, void **, mach_msg_type_number_t *); +}; + +static error_t +process_argz_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct process_argz_node *pz = hook; + error_t err; + + *contents_len = 0; + err = pz->getargz (pz->pn.procserv, pz->pn.pid, contents, contents_len); + if (err) + return EIO; + + return 0; +} + +static struct node * +process_argz_make_node (void *dir_hook, void *entry_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = process_argz_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_vm_deallocate, + .cleanup = free, + }; + struct process_argz_node *zn; + + zn = malloc (sizeof *zn); + if (! zn) + return NULL; + + memcpy (&zn->pn, dir_hook, sizeof zn->pn); + zn->getargz = entry_hook; + + return procfs_make_node (&ops, zn); +} + + +struct node * +process_make_node (process_t procserv, pid_t pid) +{ + static const struct procfs_dir_entry entries[] = { + { "cmdline", process_argz_make_node, proc_getprocargs, }, + { "environ", process_argz_make_node, proc_getprocenv, }, + { NULL, } + }; + struct process_node *pn; + + pn = malloc (sizeof *pn); + if (! pn) + return NULL; + + pn->procserv = procserv; + pn->pid = pid; + + return procfs_dir_make_node (entries, pn, free); +} + diff --git a/process.h b/process.h new file mode 100644 index 00000000..8ca7c851 --- /dev/null +++ b/process.h @@ -0,0 +1,2 @@ +struct node * +process_make_node (process_t procserv, pid_t pid); diff --git a/proclist.c b/proclist.c index e009ebdd..148e4bc3 100644 --- a/proclist.c +++ b/proclist.c @@ -4,8 +4,7 @@ #include #include #include "procfs.h" -#include "procfs_file.h" -#include "procfs_dir.h" +#include "process.h" #define PID_STR_SIZE (3 * sizeof (pid_t) + 1) @@ -49,7 +48,15 @@ proclist_get_contents (void *hook, void **contents, size_t *contents_len) static error_t proclist_lookup (void *hook, const char *name, struct node **np) { - *np = procfs_file_make_node ("Ceci n'est pas un processus\n", -1, NULL); + struct proclist_node *pl = hook; + char *endp; + pid_t pid; + + pid = strtol (name, &endp, 10); + if (name[0] == '0' || !name[0] || *endp) + return ENOENT; + + *np = process_make_node (pl->process, pid); return *np ? 0 : ENOMEM; } -- cgit v1.2.3 From 85feec46e7d8884a9a34276c7cad8133c5fbb02e Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 22:20:32 +0000 Subject: Fetch process information and reject the non-existing ones * process.c, process.h (process_make_node): Make static, include a procinfo structure into the node information. (process_lookup_pid): New function, replaces process_make_node as the outer interface, returns an error for non-existing processes. * proclist.c (proclist_lookup): Convert to the new interface. --- process.c | 34 +++++++++++++++++++++++++++++++--- process.h | 10 ++++++++-- proclist.c | 12 +++++++++--- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/process.c b/process.c index 90ea511b..612ed493 100644 --- a/process.c +++ b/process.c @@ -7,6 +7,7 @@ struct process_node { process_t procserv; pid_t pid; + struct procinfo info; }; @@ -57,8 +58,8 @@ process_argz_make_node (void *dir_hook, void *entry_hook) } -struct node * -process_make_node (process_t procserv, pid_t pid) +static struct node * +process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) { static const struct procfs_dir_entry entries[] = { { "cmdline", process_argz_make_node, proc_getprocargs, }, @@ -73,7 +74,34 @@ process_make_node (process_t procserv, pid_t pid) pn->procserv = procserv; pn->pid = pid; + memcpy (&pn->info, info, sizeof pn->info); - return procfs_dir_make_node (entries, pn, free); + return procfs_dir_make_node (entries, pn, process_cleanup); } +error_t +process_lookup_pid (process_t procserv, pid_t pid, struct node **np) +{ + procinfo_t info; + size_t info_sz; + data_t tw; + size_t tw_sz; + int flags; + error_t err; + + tw_sz = info_sz = 0, flags = 0; + err = proc_getprocinfo (procserv, pid, &flags, &info, &info_sz, &tw, &tw_sz); + if (err == ESRCH) + return ENOENT; + if (err) + return EIO; + + assert (info_sz * sizeof *info >= sizeof (struct procinfo)); + *np = process_make_node (procserv, pid, (struct procinfo *) info); + vm_deallocate (mach_task_self (), (vm_address_t) info, info_sz); + + if (! *np) + return ENOMEM; + + return 0; +} diff --git a/process.h b/process.h index 8ca7c851..abdaaa3b 100644 --- a/process.h +++ b/process.h @@ -1,2 +1,8 @@ -struct node * -process_make_node (process_t procserv, pid_t pid); +#include + +/* Create a node for a directory representing information available at + the proc server PROC for the given PID. On success, returns the + newly created node in *NP. */ +error_t +process_lookup_pid (process_t proc, pid_t pid, struct node **np); + diff --git a/proclist.c b/proclist.c index 148e4bc3..75b61a2e 100644 --- a/proclist.c +++ b/proclist.c @@ -52,12 +52,18 @@ proclist_lookup (void *hook, const char *name, struct node **np) char *endp; pid_t pid; + /* Self-lookups should not end up here. */ + assert (name[0]); + + /* No leading zeros allowed */ + if (name[0] == '0' && name[1]) + return ENOENT; + pid = strtol (name, &endp, 10); - if (name[0] == '0' || !name[0] || *endp) + if (*endp) return ENOENT; - *np = process_make_node (pl->process, pid); - return *np ? 0 : ENOMEM; + return process_lookup_pid (pl->process, pid, np); } struct node * -- cgit v1.2.3 From f89f2ccd6f4dde2dc6c6ffabd0f784ecf2a46617 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 18 Aug 2010 20:44:54 +0000 Subject: Fuse the proclist into the root node * dircat.c, dircat.h: New files, merge directories. * Makefile: Add the dircat module. * main.c: Use dircat to merge the proclist into the root directory, instead of having it as a stand-alone one. * procfs.h, procfs.c: Add a "refresh hack" to have the contents of the root directory recreated on each request. * proclist.c (proclist_make_node): Enable the hack in question. --- dircat.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dircat.h | 9 ++++++ main.c | 41 +++++++++++++++++++------- procfs.c | 8 +++++ procfs.h | 8 +++++ proclist.c | 1 + 6 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 dircat.c create mode 100644 dircat.h diff --git a/dircat.c b/dircat.c new file mode 100644 index 00000000..857ba72b --- /dev/null +++ b/dircat.c @@ -0,0 +1,99 @@ +#include +#include +#include "procfs.h" + +struct dircat_node +{ + struct node **dirs; +}; + +static error_t +dircat_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct dircat_node *dcn = hook; + int i, sz, pos; + error_t err; + + pos = 0; + *contents = malloc (sz = 512); + + for (i=0; dcn->dirs[i]; i++) + { + void *subcon; + size_t sublen; + + err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); + if (err) + { + free (*contents); + *contents = NULL; + return err; + } + + while (pos + sublen > sz) + *contents = realloc (*contents, sz *= 2); + + memcpy (*contents + pos, subcon, sublen); + pos += sublen; + } + + *contents_len = pos; + return 0; +} + +static error_t +dircat_lookup (void *hook, const char *name, struct node **np) +{ + struct dircat_node *dcn = hook; + error_t err; + int i; + + err = ENOENT; + for (i=0; err && dcn->dirs[i]; i++) + err = procfs_lookup (dcn->dirs[i], name, np); + + return err; +} + +static void +dircat_release_dirs (struct node **dirs) +{ + int i; + + for (i=0; dirs[i]; i++) + netfs_nrele (dirs[i]); +} + +static void +dircat_cleanup (void *hook) +{ + struct dircat_node *dcn = hook; + + dircat_release_dirs (dcn->dirs); + free (dcn); +} + +struct node * +dircat_make_node (struct node **dirs) +{ + static struct procfs_node_ops ops = { + .get_contents = dircat_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_free, + .lookup = dircat_lookup, + .cleanup = dircat_cleanup, + /* necessary so that it propagates to proclist */ + .enable_refresh_hack_and_break_readdir = 1, + }; + struct dircat_node *dcn; + + dcn = malloc (sizeof *dcn); + if (! dcn) + { + dircat_release_dirs (dirs); + return NULL; + } + + dcn->dirs = dirs; + return procfs_make_node (&ops, dcn); +} + diff --git a/dircat.h b/dircat.h new file mode 100644 index 00000000..cb228526 --- /dev/null +++ b/dircat.h @@ -0,0 +1,9 @@ +/* Append the contents of multiple directories. DIRS is a + NULL-terminated array of directory nodes. One reference is consumed + for each of them, even on ENOMEM, in which case NULL is returned. + DIRS has to be static data for now, or at list remain available and + unchanged for the duration of the created node's life. Strange + things will happen if they have entries with the same name or if one + of them is not a directory. */ +struct node * +dircat_make_node (struct node **dirs); diff --git a/main.c b/main.c index 4350eff5..e08bbdb7 100644 --- a/main.c +++ b/main.c @@ -7,25 +7,46 @@ #include "procfs_file.h" #include "procfs_dir.h" #include "proclist.h" +#include "dircat.h" -static struct node *make_file (void *dir_hook, void *ent_hook) +static struct node * +make_file (void *dir_hook, void *ent_hook) { return procfs_file_make_node (ent_hook, -1, NULL); } -static struct node *make_proclist (void *dir_hook, void *ent_hook) +error_t +root_make_node (struct node **np) { - return proclist_make_node (getproc ()); + static const struct procfs_dir_entry static_entries[] = { + { "hello", make_file, "Hello, World!\n" }, + { "goodbye", make_file, "Goodbye, cruel World!\n" }, + }; + /* We never have two root nodes alive simultaneously, so it's ok to + have this as static data. */ + static struct node *root_dirs[3]; + + root_dirs[0] = procfs_dir_make_node (static_entries, NULL, NULL); + if (! root_dirs[0]) + return ENOMEM; + + root_dirs[1] = proclist_make_node (getproc ()); + if (! root_dirs[1]) + { + netfs_nrele (root_dirs[0]); + return ENOMEM; + } + + root_dirs[2] = NULL; + *np = dircat_make_node (root_dirs); + if (! *np) + return ENOMEM; + + return 0; } int main (int argc, char **argv) { - static const struct procfs_dir_entry entries[] = { - { "hello", make_file, "Hello, World!\n" }, - { "goodbye", make_file, "Goodbye, cruel World!\n" }, - { "proclist", make_proclist, }, - { } - }; mach_port_t bootstrap; argp_parse (&netfs_std_startup_argp, argc, argv, 0, 0, 0); @@ -35,7 +56,7 @@ int main (int argc, char **argv) error (1, 0, "Must be started as a translator"); netfs_init (); - netfs_root_node = procfs_dir_make_node (entries, NULL, NULL); + root_make_node (&netfs_root_node); netfs_startup (bootstrap, 0); for (;;) diff --git a/procfs.c b/procfs.c index 755e0519..573bb72f 100644 --- a/procfs.c +++ b/procfs.c @@ -66,6 +66,14 @@ fail: error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) { + if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) + { + if (np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, + np->nn->contents_len); + np->nn->contents = NULL; + } + if (! np->nn->contents && np->nn->ops->get_contents) { void *contents; diff --git a/procfs.h b/procfs.h index 3cb3223d..b44db6e3 100644 --- a/procfs.h +++ b/procfs.h @@ -27,6 +27,14 @@ struct procfs_node_ops /* Destroy this node. */ void (*cleanup) (void *hook); + + /* FIXME: This is needed because the root node is persistent, and we + want the list of processes to be updated. However, this means that + readdir() on the root node runs the risk of returning incoherent + results if done in multiple runs and processes are added/removed in + the meantime. The right way to fix this is probably to add a + getroot() user hook function to libnetfs. */ + int enable_refresh_hack_and_break_readdir; }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ diff --git a/proclist.c b/proclist.c index 75b61a2e..94a7a04d 100644 --- a/proclist.c +++ b/proclist.c @@ -74,6 +74,7 @@ proclist_make_node (process_t process) .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, .cleanup = free, + .enable_refresh_hack_and_break_readdir = 1, }; struct proclist_node *pl; -- cgit v1.2.3 From 89b154b710010b09d731d74dc94e4bc6ce795006 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 18 Aug 2010 21:06:08 +0000 Subject: Set the owner of process directories. * process.c (process_make_node): Use the owner_uid from the procinfo structure to set the owner of the created directory. --- process.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index 612ed493..411f4c4a 100644 --- a/process.c +++ b/process.c @@ -67,6 +67,7 @@ process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) { NULL, } }; struct process_node *pn; + struct node *np; pn = malloc (sizeof *pn); if (! pn) @@ -76,7 +77,10 @@ process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) pn->pid = pid; memcpy (&pn->info, info, sizeof pn->info); - return procfs_dir_make_node (entries, pn, process_cleanup); + np = procfs_dir_make_node (entries, pn, process_cleanup); + np->nn_stat.st_uid = pn->info.owner; + + return np; } error_t -- cgit v1.2.3 From 1f9c094faa066aff3e02f00f997bfc37756cdd1f Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 19 Aug 2010 03:12:27 +0000 Subject: Add a basic [pid]/stat file * process.c: Add a basic stat file. --- process.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/process.c b/process.c index 411f4c4a..2b89ef81 100644 --- a/process.c +++ b/process.c @@ -1,3 +1,4 @@ +#include #include #include #include "procfs.h" @@ -57,6 +58,113 @@ process_argz_make_node (void *dir_hook, void *entry_hook) return procfs_make_node (&ops, zn); } +/* The other files don't need any information besides the data in struct + process_node. Furthermore, their contents don't have any nul byte. + Consequently, we use a simple "multiplexer" based on the information + below. */ + +struct process_file_node +{ + struct process_node pn; + error_t (*get_contents) (struct process_node *pn, char **contents); +}; + +static char mapstate (int hurd_state) +{ + return '?'; +} + +static error_t +process_file_gc_stat (struct process_node *pn, char **contents) +{ + char *argz; + size_t argz_len; + int len; + + argz = NULL, argz_len = 0; + proc_getprocargs(pn->procserv, pn->pid, &argz, &argz_len); + + len = asprintf (contents, + "%d (%s) %c " /* pid, command, state */ + "%d %d %d " /* ppid, pgid, session */ + "%d %d " /* controling tty stuff */ + "%u " /* flags, as defined by */ + "%lu %lu %lu %lu " /* page fault counts */ + "%lu %lu %ld %ld " /* user/sys times, in sysconf(_SC_CLK_TCK) */ + "%ld %ld " /* scheduler params (priority, nice) */ + "%ld %ld " /* number of threads, [obsolete] */ + "%llu " /* start time since boot (jiffies) */ + "%lu %ld %lu " /* virtual size, rss, rss limit */ + "%lu %lu %lu %lu %lu " /* some vm addresses (code, stack, sp, pc) */ + "%lu %lu %lu %lu " /* pending, blocked, ignored and caught sigs */ + "%lu " /* wait channel */ + "%lu %lu " /* swap usage (not maintained in Linux) */ + "%d " /* exit signal, to be sent to the parent */ + "%d " /* last processor used */ + "%u %u " /* RT priority and policy */ + "%llu " /* aggregated block I/O delay */ + "\n", + pn->pid, argz ?: "", mapstate (pn->info.state), + pn->info.ppid, pn->info.pgrp, pn->info.session, + 0, 0, + 0, + 0L, 0L, 0L, 0L, + 0L, 0L, 0L, 0L, + 0L, 0L, + 0L, 0L, + 0LL, + 0L, 0L, 0L, + 0L, 0L, 0L, 0L, 0L, + 0L, 0L, 0L, 0L, + 0L, + 0L, 0L, + 0, + 0, + 0, 0, + 0LL); + + vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len); + + if (len < 0) + return ENOMEM; + + return 0; +} + +static error_t +process_file_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct process_file_node *fn = hook; + error_t err; + + err = fn->get_contents (&fn->pn, (char **) contents); + if (err) + return err; + + *contents_len = strlen (*contents); + return 0; +} + +static struct node * +process_file_make_node (void *dir_hook, void *entry_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = process_file_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_free, + .cleanup = free, + }; + struct process_file_node *fn; + + fn = malloc (sizeof *fn); + if (! fn) + return NULL; + + memcpy (&fn->pn, dir_hook, sizeof fn->pn); + fn->get_contents = entry_hook; + + return procfs_make_node (&ops, fn); +} + static struct node * process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) @@ -64,6 +172,7 @@ process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) static const struct procfs_dir_entry entries[] = { { "cmdline", process_argz_make_node, proc_getprocargs, }, { "environ", process_argz_make_node, proc_getprocenv, }, + { "stat", process_file_make_node, process_file_gc_stat, }, { NULL, } }; struct process_node *pn; -- cgit v1.2.3 From e9b239bbec8cae14a9a4d510bae7496d733e5812 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 19 Aug 2010 04:52:17 +0000 Subject: Add the dot entries to directories * procfs_dir.c (procfs_dir_get_contents): Prepend the . and .. entries to the ones from the given table. --- procfs_dir.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/procfs_dir.c b/procfs_dir.c index b7fb28fa..431fea3e 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -13,11 +13,12 @@ struct procfs_dir_node static error_t procfs_dir_get_contents (void *hook, void **contents, size_t *contents_len) { + static const char dot_dotdot[] = ".\0.."; struct procfs_dir_node *dn = hook; const struct procfs_dir_entry *ent; char *pos; - *contents_len = 0; + *contents_len = sizeof dot_dotdot; for (ent = dn->entries; ent->name; ent++) *contents_len += strlen (ent->name) + 1; @@ -25,7 +26,8 @@ procfs_dir_get_contents (void *hook, void **contents, size_t *contents_len) if (! *contents) return ENOMEM; - pos = *contents; + memcpy (*contents, dot_dotdot, sizeof dot_dotdot); + pos = *contents + sizeof dot_dotdot; for (ent = dn->entries; ent->name; ent++) { strcpy (pos, ent->name); -- cgit v1.2.3 From 87fbcc7a89559aa529eeb12472053ff869f66de9 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 19 Aug 2010 07:48:30 +0000 Subject: Implement lookup for . and .. * procfs.c (procfs_lookup): Keep track of the parent directory, implement the lookup of the dot-directories. (procfs_cleanup): Release the reference to the parent node, if applicable. * procfs.h: Add a comment about the parent reference. * netfs.c (netfs_attempt_lookup): Lock the looked up node after the directory has been unlocked, in case they are the same. --- netfs.c | 3 ++- procfs.c | 26 ++++++++++++++++++++++++-- procfs.h | 5 ++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/netfs.c b/netfs.c index 58665085..6b105f91 100644 --- a/netfs.c +++ b/netfs.c @@ -151,10 +151,11 @@ error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, error_t err; err = procfs_lookup (dir, name, np); + mutex_unlock (&dir->lock); + if (! err) mutex_lock (&(*np)->lock); - mutex_unlock (&dir->lock); return err; } diff --git a/procfs.c b/procfs.c index 573bb72f..c5f19497 100644 --- a/procfs.c +++ b/procfs.c @@ -14,6 +14,9 @@ struct netnode /* (cached) contents of the node */ void *contents; size_t contents_len; + + /* parent directory, if applicable */ + struct node *parent; }; void @@ -97,8 +100,24 @@ error_t procfs_lookup (struct node *np, const char *name, struct node **npp) { error_t err = ENOENT; - if (np->nn->ops->lookup) - err = np->nn->ops->lookup (np->nn->hook, name, npp); + if (err && ! strcmp (name, ".")) + { + netfs_nref(*npp = np); + err = 0; + } + + if (err && np->nn->parent && ! strcmp (name, "..")) + { + netfs_nref(*npp = np->nn->parent); + err = 0; + } + + if (err && np->nn->ops->lookup) + { + err = np->nn->ops->lookup (np->nn->hook, name, npp); + if (! err) + netfs_nref ((*npp)->nn->parent = np); + } return err; } @@ -111,5 +130,8 @@ void procfs_cleanup (struct node *np) if (np->nn->ops->cleanup) np->nn->ops->cleanup (np->nn->hook); + if (np->nn->parent) + netfs_nrele (np->nn->parent); + free (np->nn); } diff --git a/procfs.h b/procfs.h index b44db6e3..42eed0e2 100644 --- a/procfs.h +++ b/procfs.h @@ -22,7 +22,10 @@ struct procfs_node_ops /* Lookup NAME in this directory, and store the result in *np. The returned node should be created by lookup() using procfs_make_node() - or a derived function. */ + or a derived function. Note that the parent will be kept alive as + long as the child exists, so you can safely reference the parent's + data from the child. You may want to consider locking if there's + any mutation going on, though. */ error_t (*lookup) (void *hook, const char *name, struct node **np); /* Destroy this node. */ -- cgit v1.2.3 From 5baed27bd3b7a7d20ecb77068546b158d131fc1c Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 19 Aug 2010 23:10:11 +0000 Subject: Invent path-based inode numbers * procfs.h, procfs.c (procfs_make_ino): New function, invents an inode number by hashing the parent's and the name of an entry. (procfs_lookup): Use it to assign an inode number to child nodes at lookup time. * main.c (root_make_node): Assign an arbitrary inode number to the root directory. --- main.c | 4 ++++ procfs.c | 28 +++++++++++++++++++++++++++- procfs.h | 5 +++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index e08bbdb7..15602819 100644 --- a/main.c +++ b/main.c @@ -42,6 +42,10 @@ root_make_node (struct node **np) if (! *np) return ENOMEM; + /* Since this one is not created through proc_lookup(), we have to affect an + inode number to it. */ + (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; + return 0; } diff --git a/procfs.c b/procfs.c index c5f19497..4cce46b9 100644 --- a/procfs.c +++ b/procfs.c @@ -67,6 +67,29 @@ fail: return NULL; } +/* FIXME: possibly not the fastest hash function... */ +ino64_t +procfs_make_ino (struct node *np, const char *filename) +{ + unsigned short x[3]; + + if (! strcmp (filename, ".")) + return np->nn_stat.st_ino; + if (! strcmp (filename, "..")) + return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 42; + + assert (sizeof np->nn_stat.st_ino > sizeof x); + memcpy (x, &np->nn_stat.st_ino, sizeof x); + + while (*filename) + { + x[0] ^= *(filename++); + jrand48 (x); + } + + return jrand48 (x); +} + error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) { if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) @@ -116,7 +139,10 @@ error_t procfs_lookup (struct node *np, const char *name, struct node **npp) { err = np->nn->ops->lookup (np->nn->hook, name, npp); if (! err) - netfs_nref ((*npp)->nn->parent = np); + { + (*npp)->nn_stat.st_ino = procfs_make_ino (np, name); + netfs_nref ((*npp)->nn->parent = np); + } } return err; diff --git a/procfs.h b/procfs.h index 42eed0e2..4c9d828b 100644 --- a/procfs.h +++ b/procfs.h @@ -51,6 +51,11 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); /* Interface for the libnetfs side. */ +/* Get the inode number which will be given to a child of NP named FILENAME. + This allows us to retreive them for readdir() without creating the + corresponding child nodes. */ +ino64_t procfs_make_ino (struct node *np, const char *filename); + error_t procfs_get_contents (struct node *np, void **data, size_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); -- cgit v1.2.3 From 7eff83acaac2167ffdc7a297a0ed6a78354aa062 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 09:25:27 +0000 Subject: Handle the ref counter spinlock on cleanup * netfs.c (netfs_node_norefs): Handle the reference counters spinlock so as to avoid deadlocking on reentry. --- netfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/netfs.c b/netfs.c index 6b105f91..e98af752 100644 --- a/netfs.c +++ b/netfs.c @@ -163,8 +163,12 @@ error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, free all its associated storage. */ void netfs_node_norefs (struct node *np) { + spin_unlock (&netfs_node_refcnt_lock); + procfs_cleanup (np); free (np); + + spin_lock (&netfs_node_refcnt_lock); } -- cgit v1.2.3 From 41e9b70f37295ec16d403d7efd1e49027ab867a8 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 09:26:09 +0000 Subject: Use libps and enhance [pid]/stat * Makefile: Add libps to the $(LIBS). * proclist.c, proclist.h: Embed the proc server port in a ps_context structure. (proclist_make_node): Change to prototype to allow for the possibility of error. Rename to proclist_create_node to reflect the change and non-triviality. * process.c, process.h: Revamp. Use a full-blown procstat structure instead of just the procinfo fetched from the process server. Use the additional data to complement [pid]/stat. (process_lookup_pid): Get a ps_context structure instead of a port to the process server. * main.c (root_make_node): Convert to the new interface for proclist_create_node. --- main.c | 7 +- process.c | 267 ++++++++++++++++++++++++++++++++----------------------------- process.h | 8 +- procfs.c | 2 +- proclist.c | 35 ++++---- proclist.h | 2 +- 6 files changed, 163 insertions(+), 158 deletions(-) diff --git a/main.c b/main.c index 15602819..75e1cda3 100644 --- a/main.c +++ b/main.c @@ -25,16 +25,17 @@ root_make_node (struct node **np) /* We never have two root nodes alive simultaneously, so it's ok to have this as static data. */ static struct node *root_dirs[3]; + error_t err; root_dirs[0] = procfs_dir_make_node (static_entries, NULL, NULL); if (! root_dirs[0]) return ENOMEM; - root_dirs[1] = proclist_make_node (getproc ()); - if (! root_dirs[1]) + err = proclist_create_node (getproc (), &root_dirs[1]); + if (err) { netfs_nrele (root_dirs[0]); - return ENOMEM; + return err; } root_dirs[2] = NULL; diff --git a/process.c b/process.c index 2b89ef81..a1cc0f61 100644 --- a/process.c +++ b/process.c @@ -1,100 +1,75 @@ #include #include +#include #include +#include +#include +#include #include "procfs.h" #include "procfs_dir.h" #include "process.h" + +/* Implementations for the process_file_desc.get_contents callback. */ -struct process_node { - process_t procserv; - pid_t pid; - struct procinfo info; -}; - - -/* The proc_getprocargs() and proc_getprocenv() calls have the same - prototype and we use them in the same way; namely, publish the data - they return as-is. We take advantage of this to have common code and - use a function pointer as the procfs_dir "entry hook" to choose the - call to use on a file by file basis. */ - -struct process_argz_node +static char * +process_file_gc_cmdline (struct proc_stat *ps, size_t *len) { - struct process_node pn; - error_t (*getargz) (process_t, pid_t, void **, mach_msg_type_number_t *); -}; + *len = proc_stat_args_len(ps); + return proc_stat_args(ps); +} -static error_t -process_argz_get_contents (void *hook, void **contents, size_t *contents_len) +static char * +process_file_gc_environ (struct proc_stat *ps, size_t *len) { - struct process_argz_node *pz = hook; - error_t err; - - *contents_len = 0; - err = pz->getargz (pz->pn.procserv, pz->pn.pid, contents, contents_len); - if (err) - return EIO; - - return 0; + *len = proc_stat_args_len(ps); + return proc_stat_args(ps); } -static struct node * -process_argz_make_node (void *dir_hook, void *entry_hook) +static char state_char (struct proc_stat *ps) { - static const struct procfs_node_ops ops = { - .get_contents = process_argz_get_contents, - .cleanup_contents = procfs_cleanup_contents_with_vm_deallocate, - .cleanup = free, - }; - struct process_argz_node *zn; - - zn = malloc (sizeof *zn); - if (! zn) - return NULL; + int i; - memcpy (&zn->pn, dir_hook, sizeof zn->pn); - zn->getargz = entry_hook; + for (i = 0; (1 << i) & (PSTAT_STATE_P_STATES | PSTAT_STATE_T_STATES); i++) + if (proc_stat_state (ps) & (1 << i)) + return proc_stat_state_tags[i]; - return procfs_make_node (&ops, zn); + return '?'; } -/* The other files don't need any information besides the data in struct - process_node. Furthermore, their contents don't have any nul byte. - Consequently, we use a simple "multiplexer" based on the information - below. */ - -struct process_file_node +static long int sc_tv (time_value_t tv) { - struct process_node pn; - error_t (*get_contents) (struct process_node *pn, char **contents); -}; + double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; + return usecs * sysconf(_SC_CLK_TCK) / 1000000; +} -static char mapstate (int hurd_state) +static long long int jiff_tv (time_value_t tv) { - return '?'; + double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; + /* Let's say a jiffy is 1/100 of a second.. */ + return usecs * 100 / 1000000; } -static error_t -process_file_gc_stat (struct process_node *pn, char **contents) +static char * +process_file_gc_stat (struct proc_stat *ps, size_t *len) { - char *argz; - size_t argz_len; - int len; - - argz = NULL, argz_len = 0; - proc_getprocargs(pn->procserv, pn->pid, &argz, &argz_len); - - len = asprintf (contents, + struct procinfo *pi = proc_stat_proc_info (ps); + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); + char *contents; + + /* See proc(5) for more information about the contents of each field for the + Linux procfs. */ + *len = asprintf (&contents, "%d (%s) %c " /* pid, command, state */ "%d %d %d " /* ppid, pgid, session */ "%d %d " /* controling tty stuff */ "%u " /* flags, as defined by */ "%lu %lu %lu %lu " /* page fault counts */ "%lu %lu %ld %ld " /* user/sys times, in sysconf(_SC_CLK_TCK) */ - "%ld %ld " /* scheduler params (priority, nice) */ - "%ld %ld " /* number of threads, [obsolete] */ + "%d %d " /* scheduler params (priority, nice) */ + "%d %ld " /* number of threads, [obsolete] */ "%llu " /* start time since boot (jiffies) */ - "%lu %ld %lu " /* virtual size, rss, rss limit */ + "%lu %ld %lu " /* virtual size (bytes), rss (pages), rss lim */ "%lu %lu %lu %lu %lu " /* some vm addresses (code, stack, sp, pc) */ "%lu %lu %lu %lu " /* pending, blocked, ignored and caught sigs */ "%lu " /* wait channel */ @@ -104,44 +79,71 @@ process_file_gc_stat (struct process_node *pn, char **contents) "%u %u " /* RT priority and policy */ "%llu " /* aggregated block I/O delay */ "\n", - pn->pid, argz ?: "", mapstate (pn->info.state), - pn->info.ppid, pn->info.pgrp, pn->info.session, - 0, 0, - 0, - 0L, 0L, 0L, 0L, - 0L, 0L, 0L, 0L, - 0L, 0L, - 0L, 0L, - 0LL, - 0L, 0L, 0L, + proc_stat_pid (ps), proc_stat_args (ps), state_char (ps), + pi->ppid, pi->pgrp, pi->session, + 0, 0, /* no such thing as a major:minor for ctty */ + 0, /* no such thing as CLONE_* flags on Hurd */ + 0L, 0L, 0L, 0L, /* TASK_EVENTS_INFO is unavailable on GNU Mach */ + sc_tv (thbi->user_time), sc_tv (thbi->system_time), + 0L, 0L, /* cumulative time for children */ + MACH_PRIORITY_TO_NICE(thbi->base_priority) + 20, + MACH_PRIORITY_TO_NICE(thbi->base_priority), + pi->nthreads, 0L, + jiff_tv (thbi->creation_time), /* FIXME: ... since boot */ + (long unsigned int) tbi->virtual_size, + (long unsigned int) tbi->resident_size / PAGE_SIZE, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, - 0L, + (long unsigned int) proc_stat_thread_rpc (ps), /* close enough */ 0L, 0L, 0, 0, 0, 0, 0LL); - vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len); + return len >= 0 ? contents : NULL; +} - if (len < 0) - return ENOMEM; - return 0; -} +/* Describes a file in a process directory. This is used as an "entry hook" + * for our procfs_dir entry table, passed to process_file_make_node. */ +struct process_file_desc +{ + /* The proc_stat information required to get the contents of this file. */ + ps_flags_t needs; + + /* Once we have acquired the necessary information, there can be only + memory allocation errors, hence this simplified signature. */ + char *(*get_contents) (struct proc_stat *ps, size_t *len); + + /* The cmdline and environ contents don't need any cleaning since they are + part of a proc_stat structure. */ + int no_cleanup; +}; + +/* Information associated to an actual file node. */ +struct process_file_node +{ + const struct process_file_desc *desc; + struct proc_stat *ps; +}; static error_t process_file_get_contents (void *hook, void **contents, size_t *contents_len) { - struct process_file_node *fn = hook; + struct process_file_node *file = hook; error_t err; - err = fn->get_contents (&fn->pn, (char **) contents); + err = proc_stat_set_flags (file->ps, file->desc->needs); if (err) - return err; + return EIO; + if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) + return EIO; + + *contents = file->desc->get_contents (file->ps, contents_len); + if (! *contents) + return ENOMEM; - *contents_len = strlen (*contents); return 0; } @@ -153,68 +155,75 @@ process_file_make_node (void *dir_hook, void *entry_hook) .cleanup_contents = procfs_cleanup_contents_with_free, .cleanup = free, }; - struct process_file_node *fn; + static const struct procfs_node_ops ops_no_cleanup = { + .get_contents = process_file_get_contents, + .cleanup = free, + }; + struct process_file_node *f; - fn = malloc (sizeof *fn); - if (! fn) + f = malloc (sizeof *f); + if (! f) return NULL; - memcpy (&fn->pn, dir_hook, sizeof fn->pn); - fn->get_contents = entry_hook; + f->desc = entry_hook; + f->ps = dir_hook; - return procfs_make_node (&ops, fn); + return procfs_make_node (f->desc->no_cleanup ? &ops_no_cleanup : &ops, f); } -static struct node * -process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) -{ - static const struct procfs_dir_entry entries[] = { - { "cmdline", process_argz_make_node, proc_getprocargs, }, - { "environ", process_argz_make_node, proc_getprocenv, }, - { "stat", process_file_make_node, process_file_gc_stat, }, - { NULL, } - }; - struct process_node *pn; - struct node *np; - - pn = malloc (sizeof *pn); - if (! pn) - return NULL; - - pn->procserv = procserv; - pn->pid = pid; - memcpy (&pn->info, info, sizeof pn->info); - - np = procfs_dir_make_node (entries, pn, process_cleanup); - np->nn_stat.st_uid = pn->info.owner; - - return np; -} +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, + .no_cleanup = 1, + }, + }, + { + .name = "environ", + .make_node = process_file_make_node, + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_environ, + .needs = PSTAT_ENV, + .no_cleanup = 1, + }, + }, + { + .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 + | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC + | PSTAT_THREAD_WAIT, + }, + }, + {} +}; error_t -process_lookup_pid (process_t procserv, pid_t pid, struct node **np) +process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) { - procinfo_t info; - size_t info_sz; - data_t tw; - size_t tw_sz; - int flags; + struct proc_stat *ps; error_t err; - tw_sz = info_sz = 0, flags = 0; - err = proc_getprocinfo (procserv, pid, &flags, &info, &info_sz, &tw, &tw_sz); + err = _proc_stat_create (pid, pc, &ps); if (err == ESRCH) return ENOENT; if (err) return EIO; - assert (info_sz * sizeof *info >= sizeof (struct procinfo)); - *np = process_make_node (procserv, pid, (struct procinfo *) info); - vm_deallocate (mach_task_self (), (vm_address_t) info, info_sz); + err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); + if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) + return EIO; + *np = procfs_dir_make_node (entries, ps, (void (*)(void *)) _proc_stat_free); if (! *np) return ENOMEM; + (*np)->nn_stat.st_uid = proc_stat_owner_uid (ps); return 0; } diff --git a/process.h b/process.h index abdaaa3b..8c2ee636 100644 --- a/process.h +++ b/process.h @@ -1,8 +1,8 @@ -#include +#include -/* Create a node for a directory representing information available at - the proc server PROC for the given PID. On success, returns the +/* Create a node for a directory representing the given PID, as published by + the proc server refrenced by the libps context PC. On success, returns the newly created node in *NP. */ error_t -process_lookup_pid (process_t proc, pid_t pid, struct node **np); +process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np); diff --git a/procfs.c b/procfs.c index 4cce46b9..0a235a76 100644 --- a/procfs.c +++ b/procfs.c @@ -87,7 +87,7 @@ procfs_make_ino (struct node *np, const char *filename) jrand48 (x); } - return jrand48 (x); + return (unsigned long) jrand48 (x); } error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) diff --git a/proclist.c b/proclist.c index 94a7a04d..56a3fdf9 100644 --- a/proclist.c +++ b/proclist.c @@ -3,27 +3,23 @@ #include #include #include +#include #include "procfs.h" #include "process.h" #define PID_STR_SIZE (3 * sizeof (pid_t) + 1) -struct proclist_node -{ - process_t process; -}; - static error_t proclist_get_contents (void *hook, void **contents, size_t *contents_len) { - struct proclist_node *pl = hook; + struct ps_context *pc = hook; pidarray_t pids; mach_msg_type_number_t num_pids; error_t err; int i; num_pids = 0; - err = proc_getallpids (pl->process, &pids, &num_pids); + err = proc_getallpids (pc->server, &pids, &num_pids); if (err) return EIO; @@ -48,7 +44,7 @@ proclist_get_contents (void *hook, void **contents, size_t *contents_len) static error_t proclist_lookup (void *hook, const char *name, struct node **np) { - struct proclist_node *pl = hook; + struct ps_context *pc = hook; char *endp; pid_t pid; @@ -63,28 +59,27 @@ proclist_lookup (void *hook, const char *name, struct node **np) if (*endp) return ENOENT; - return process_lookup_pid (pl->process, pid, np); + return process_lookup_pid (pc, pid, np); } -struct node * -proclist_make_node (process_t process) +error_t +proclist_create_node (process_t procserv, struct node **np) { static const struct procfs_node_ops ops = { .get_contents = proclist_get_contents, .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, - .cleanup = free, + .cleanup = (void (*)(void *)) ps_context_free, .enable_refresh_hack_and_break_readdir = 1, }; - struct proclist_node *pl; - - pl = malloc (sizeof *pl); - if (! pl) - return NULL; + struct ps_context *pc; + error_t err; - memset (pl, 0, sizeof *pl); - pl->process = process; + err = ps_context_create (procserv, &pc); + if (err) + return err; - return procfs_make_node (&ops, pl); + *np = procfs_make_node (&ops, pc); + return 0; } diff --git a/proclist.h b/proclist.h index a766d50e..1c7ab084 100644 --- a/proclist.h +++ b/proclist.h @@ -1,2 +1,2 @@ #include -struct node *proclist_make_node (process_t process); +error_t proclist_create_node (process_t procserv, struct node **np); -- cgit v1.2.3 From f3d54d87f2abd6ff2be77e1e396bb45a2f0953a6 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 11:09:36 +0000 Subject: Implement symlinks * netfs.c (netfs_validate_stat): For symlinks, fetch the contents and propagate their length into the nn_stat.st_size field. (netfs_attempt_readlink): Implement using procfs_get_contents. --- netfs.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/netfs.c b/netfs.c index e98af752..a47861e5 100644 --- a/netfs.c +++ b/netfs.c @@ -24,6 +24,28 @@ char *netfs_server_version = PROCFS_SERVER_VERSION; /* Maximum number of symlinks to follow before returning ELOOP. */ int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; +/* The user must define this function. Make sure that NP->nn_stat is + filled with the most current information. CRED identifies the user + responsible for the operation. NP is locked. */ +error_t netfs_validate_stat (struct node *np, struct iouser *cred) +{ + void *contents; + size_t contents_len; + error_t err; + + /* Only symlinks need to have their size filled, before a read is + attempted. */ + if (! S_ISLNK (np->nn_stat.st_mode)) + return 0; + + err = procfs_get_contents (np, &contents, &contents_len); + if (err) + return err; + + np->nn_stat.st_size = contents_len; + return 0; +} + /* The user must define this function. Read from the locked file NP for user CRED starting at OFFSET and continuing for up to *LEN bytes. Put the data at DATA. Set *LEN to the amount successfully @@ -56,7 +78,17 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *np, error_t netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) { - return EIO; + char *contents; + size_t contents_len; + error_t err; + + err = procfs_get_contents (np, (void **) &contents, &contents_len); + if (err) + return err; + + assert (contents_len == np->nn_stat.st_size); + memcpy (buf, contents, contents_len); + return 0; } /* Helper function for netfs_get_dirents() below. CONTENTS is an argz @@ -210,14 +242,6 @@ error_t netfs_report_access (struct iouser *cred, struct node *np, /* Trivial or unsupported libnetfs callbacks. */ -/* The user must define this function. Make sure that NP->nn_stat is - filled with the most current information. CRED identifies the user - responsible for the operation. NP is locked. */ -error_t netfs_validate_stat (struct node *np, struct iouser *cred) -{ - return 0; -} - /* The user must define this function. This should attempt a chmod call for the user specified by CRED on locked node NP, to change the owner to UID and the group to GID. */ -- cgit v1.2.3 From bebb88600bc0fc7aa3abb86712652c0ff5c38122 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 11:21:59 +0000 Subject: Encapsulate access to node->nn_stat * procfs.c, procfs.h (procfs_node_chown, procfs_node_chmod, procfs_node_chtype): New functions, encapsulate access to some nn_stat fields. * process.c (process_lookup_pid): Use procfs_node_chown instead of direct access. --- process.c | 2 +- procfs.c | 19 +++++++++++++++++++ procfs.h | 13 +++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index a1cc0f61..779a2e18 100644 --- a/process.c +++ b/process.c @@ -224,6 +224,6 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) if (! *np) return ENOMEM; - (*np)->nn_stat.st_uid = proc_stat_owner_uid (ps); + procfs_node_chown (*np, proc_stat_owner_uid (ps)); return 0; } diff --git a/procfs.c b/procfs.c index 0a235a76..5396ecf7 100644 --- a/procfs.c +++ b/procfs.c @@ -67,6 +67,25 @@ fail: return NULL; } +void procfs_node_chown (struct node *np, uid_t owner) +{ + np->nn_stat.st_uid = owner; +} + +void procfs_node_chmod (struct node *np, mode_t mode) +{ + np->nn_stat.st_mode = (np->nn_stat.st_mode & S_IFMT) | mode; + np->nn_translated = np->nn_stat.st_mode; +} + +void procfs_node_chtype (struct node *np, mode_t type) +{ + np->nn_stat.st_mode = (np->nn_stat.st_mode & ~S_IFMT) | type; + np->nn_translated = np->nn_stat.st_mode; + if (type == S_IFLNK) + procfs_node_chmod (np, 0777); +} + /* FIXME: possibly not the fastest hash function... */ ino64_t procfs_make_ino (struct node *np, const char *filename) diff --git a/procfs.h b/procfs.h index 4c9d828b..4ab3b567 100644 --- a/procfs.h +++ b/procfs.h @@ -48,6 +48,19 @@ void procfs_cleanup_contents_with_vm_deallocate (void *, void *, size_t); enough memory. In this case, ops->cleanup will be invoked. */ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); +/* Set the owner of the node NP. Must be called right after the node + has been created. */ +void procfs_node_chown (struct node *np, uid_t owner); + +/* Set the permission bits of the node NP. Must be called right after + the node has been created. */ +void procfs_node_chmod (struct node *np, mode_t mode); + +/* Set the type of the node NP. If type is S_IFLNK, appropriate + permission bits will be set as well. Must be called right after the + node has been created. */ +void procfs_node_chtype (struct node *np, mode_t type); + /* Interface for the libnetfs side. */ -- cgit v1.2.3 From eac768767aef12d1b0b98468eacf5a12db1bf124 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 18:06:16 +0000 Subject: Set a restrictive mode on some sensitive files * process.c (process_file_make_node, entries): Set the environ and stat files as readable only by the owner of the process. --- process.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index 779a2e18..8955cf40 100644 --- a/process.c +++ b/process.c @@ -119,6 +119,9 @@ struct process_file_desc /* The cmdline and environ contents don't need any cleaning since they are part of a proc_stat structure. */ int no_cleanup; + + /* If specified, the file mode to be set with procfs_node_chmod(). */ + mode_t mode; }; /* Information associated to an actual file node. */ @@ -160,6 +163,7 @@ process_file_make_node (void *dir_hook, void *entry_hook) .cleanup = free, }; struct process_file_node *f; + struct node *np; f = malloc (sizeof *f); if (! f) @@ -168,7 +172,15 @@ process_file_make_node (void *dir_hook, void *entry_hook) f->desc = entry_hook; f->ps = dir_hook; - return procfs_make_node (f->desc->no_cleanup ? &ops_no_cleanup : &ops, f); + np = procfs_make_node (f->desc->no_cleanup ? &ops_no_cleanup : &ops, f); + if (! np) + return NULL; + + procfs_node_chown (np, proc_stat_owner_uid (f->ps)); + if (f->desc->mode) + procfs_node_chmod (np, f->desc->mode); + + return np; } @@ -189,6 +201,7 @@ static struct procfs_dir_entry entries[] = { .get_contents = process_file_gc_environ, .needs = PSTAT_ENV, .no_cleanup = 1, + .mode = 0400, }, }, { @@ -199,6 +212,7 @@ static struct procfs_dir_entry entries[] = { .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC | PSTAT_THREAD_WAIT, + .mode = 0400, }, }, {} -- cgit v1.2.3 From 90a5e8f072ba3e06508d2fe82195058d1578e345 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 18:08:17 +0000 Subject: New root files: version, uptime, stat * rootdir.c, rootdir.h: New files. * main.c: Use rootdir_create_node. * Makefile: Add the rootdir module. --- main.c | 21 ++------- rootdir.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rootdir.h | 2 + 3 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 rootdir.c create mode 100644 rootdir.h diff --git a/main.c b/main.c index 75e1cda3..15ad60f4 100644 --- a/main.c +++ b/main.c @@ -4,34 +4,23 @@ #include #include #include "procfs.h" -#include "procfs_file.h" -#include "procfs_dir.h" #include "proclist.h" +#include "rootdir.h" #include "dircat.h" -static struct node * -make_file (void *dir_hook, void *ent_hook) -{ - return procfs_file_make_node (ent_hook, -1, NULL); -} - error_t root_make_node (struct node **np) { - static const struct procfs_dir_entry static_entries[] = { - { "hello", make_file, "Hello, World!\n" }, - { "goodbye", make_file, "Goodbye, cruel World!\n" }, - }; /* We never have two root nodes alive simultaneously, so it's ok to have this as static data. */ static struct node *root_dirs[3]; error_t err; - root_dirs[0] = procfs_dir_make_node (static_entries, NULL, NULL); - if (! root_dirs[0]) - return ENOMEM; + err = proclist_create_node (getproc (), &root_dirs[0]); + if (err) + return err; - err = proclist_create_node (getproc (), &root_dirs[1]); + err = rootdir_create_node (&root_dirs[1]); if (err) { netfs_nrele (root_dirs[0]); diff --git a/rootdir.c b/rootdir.c new file mode 100644 index 00000000..fb48b932 --- /dev/null +++ b/rootdir.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include "procfs.h" +#include "procfs_dir.h" + +/* This implements a directory node with the static files in /proc */ + +#define INIT_PID 1 + +static error_t +get_boottime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps; + error_t err; + + err = _proc_stat_create (INIT_PID, pc, &ps); + if (err) + return err; + + err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); + if (err || !(proc_stat_flags (ps) & PSTAT_TASK_BASIC)) + err = EIO; + + if (! err) + { + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + tv->tv_sec = tbi->creation_time.seconds; + tv->tv_usec = tbi->creation_time.microseconds; + } + + _proc_stat_free (ps); + return err; +} + +static error_t +rootdir_gc_version (void *hook, void **contents, size_t *contents_len) +{ + struct utsname uts; + int r; + + r = uname (&uts); + if (r < 0) + return errno; + + *contents_len = asprintf ((char **) contents, + "Linux version 2.6.1 (%s %s %s %s)\n", + uts.sysname, uts.release, uts.version, uts.machine); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + +/* Uptime -- we use the start time of init to deduce it. This is probably a bit + fragile, as any clock update will make the result inaccurate. */ +static error_t +rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) +{ + struct timeval time, boottime; + double up_secs; + error_t err; + + err = gettimeofday (&time, NULL); + if (err < 0) + return errno; + + err = get_boottime (hook, &boottime); + if (err) + return err; + + timersub (&time, &boottime, &time); + up_secs = time.tv_sec + time.tv_usec / 1000000.; + + /* The second field is the total idle time. As far as I know we don't + keep track of it. */ + *contents_len = asprintf ((char **) contents, "%.2lf %.2lf\n", up_secs, 0.); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + +static error_t +rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) +{ + struct timeval boottime; + struct vm_statistics vmstats; + error_t err; + + err = get_boottime (hook, &boottime); + if (err) + return err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + *contents_len = asprintf ((char **) contents, + /* Does Mach keeps track of any of this? */ + "cpu 0 0 0 0 0 0 0 0 0\n" + "cpu0 0 0 0 0 0 0 0 0 0\n" + "intr 0\n" + /* This we know. */ + "page %d %d\n" + "btime %lu\n", + vmstats.pageins, vmstats.pageouts, + boottime.tv_sec); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + +static struct node * +rootdir_file_make_node (void *dir_hook, void *entry_hook) +{ + return procfs_make_node (entry_hook, dir_hook); +} + +static struct procfs_dir_entry rootdir_entries[] = { + { + .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, + }, + }, + { + .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, + }, + }, + { + .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, + }, + }, + {} +}; + +error_t +rootdir_create_node (struct node **np) +{ + struct ps_context *pc; + error_t err; + + err = ps_context_create (getproc (), &pc); + if (err) + return err; + + *np = procfs_dir_make_node (rootdir_entries, pc, + (void (*)(void *)) ps_context_free); + return 0; +} + diff --git a/rootdir.h b/rootdir.h new file mode 100644 index 00000000..a764b0fd --- /dev/null +++ b/rootdir.h @@ -0,0 +1,2 @@ +error_t +rootdir_create_node (struct node **np); -- cgit v1.2.3 From 880f120da999a3217079af65ce7273087e0e0eda Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 19:18:59 +0000 Subject: Add loadavg * rootdir.c: Add the root file "loadavg". --- rootdir.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/rootdir.c b/rootdir.c index fb48b932..0985b63b 100644 --- a/rootdir.c +++ b/rootdir.c @@ -108,6 +108,28 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) return *contents_len >= 0 ? 0 : ENOMEM; } +static error_t +rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) +{ + host_load_info_data_t hli; + mach_msg_type_number_t cnt; + error_t err; + + cnt = HOST_LOAD_INFO_COUNT; + err = host_info (mach_host_self (), HOST_LOAD_INFO, (host_info_t) &hli, &cnt); + if (err) + return err; + + assert (cnt == HOST_LOAD_INFO_COUNT); + *contents_len = asprintf ((char **) contents, + "%.2f %.2f %.2f 1/0 0\n", + hli.avenrun[0] / (double) LOAD_SCALE, + hli.avenrun[1] / (double) LOAD_SCALE, + hli.avenrun[2] / (double) LOAD_SCALE); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + static struct node * rootdir_file_make_node (void *dir_hook, void *entry_hook) { @@ -139,6 +161,14 @@ static struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .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, + }, + }, {} }; -- cgit v1.2.3 From 6e84b8c1182013691d3629bfc08e49f81b662a2d Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 00:55:11 +0000 Subject: Add a fake "self" symlink * rootdir.c: Add a fake "self" symlink which always points to init. --- rootdir.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/rootdir.c b/rootdir.c index 0985b63b..79928446 100644 --- a/rootdir.c +++ b/rootdir.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "procfs.h" #include "procfs_dir.h" @@ -130,12 +131,30 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) return *contents_len >= 0 ? 0 : ENOMEM; } +static error_t +rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) +{ + *contents = "1"; + *contents_len = strlen (*contents); + return 0; +} + + static struct node * rootdir_file_make_node (void *dir_hook, void *entry_hook) { return procfs_make_node (entry_hook, dir_hook); } +static struct node * +rootdir_symlink_make_node (void *dir_hook, void *entry_hook) +{ + struct node *np = procfs_make_node (entry_hook, dir_hook); + if (np) + procfs_node_chtype (np, S_IFLNK); + return np; +} + static struct procfs_dir_entry rootdir_entries[] = { { .name = "version", @@ -169,6 +188,13 @@ static struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .name = "self", + .make_node = rootdir_symlink_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_fakeself, + }, + }, {} }; -- cgit v1.2.3 From 5dc5292664522e85ea4ede00f2edbfeed929eb5e Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 00:55:29 +0000 Subject: Add an empty meminfo root file * rootdir.c: Add "meminfo", empty for now. --- rootdir.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/rootdir.c b/rootdir.c index 79928446..17fdb93a 100644 --- a/rootdir.c +++ b/rootdir.c @@ -131,6 +131,13 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) return *contents_len >= 0 ? 0 : ENOMEM; } +static error_t +rootdir_gc_empty (void *hook, void **contents, size_t *contents_len) +{ + *contents_len = 0; + return 0; +} + static error_t rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) { @@ -188,6 +195,13 @@ static struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .name = "meminfo", + .make_node = rootdir_file_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_empty, + }, + }, { .name = "self", .make_node = rootdir_symlink_make_node, -- cgit v1.2.3 From 24fa47890c0d180b807de8abb1a284755cba9c96 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sun, 22 Aug 2010 19:33:43 +0000 Subject: Play nice with the procps old_Hertz_hack * rootdir.c (rootdir_gc_uptime, rootdir_gc_stat): assume a completely idle rather than completely busy system, so that the idle seconds can be meaningfully divided by the idle jiffies by procps. --- rootdir.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/rootdir.c b/rootdir.c index 17fdb93a..50f6e071 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -74,8 +75,11 @@ rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) up_secs = time.tv_sec + time.tv_usec / 1000000.; /* The second field is the total idle time. As far as I know we don't - keep track of it. */ - *contents_len = asprintf ((char **) contents, "%.2lf %.2lf\n", up_secs, 0.); + keep track of it. However, procps uses it to compute "USER_HZ", and + proc(5) specifies that it should be equal to USER_HZ times the idle value + in ticks from /proc/stat. So we assume a completely idle system both here + and there to make that work. */ + *contents_len = asprintf ((char **) contents, "%.2lf %.2lf\n", up_secs, up_secs); return *contents_len >= 0 ? 0 : ENOMEM; } @@ -83,10 +87,15 @@ rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) static error_t rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) { - struct timeval boottime; + struct timeval boottime, time; struct vm_statistics vmstats; + unsigned long up_ticks; error_t err; + err = gettimeofday (&time, NULL); + if (err < 0) + return errno; + err = get_boottime (hook, &boottime); if (err) return err; @@ -95,14 +104,19 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) if (err) return EIO; + timersub (&time, &boottime, &time); + up_ticks = sysconf(_SC_CLK_TCK) * (time.tv_sec + time.tv_usec / 1000000.); + *contents_len = asprintf ((char **) contents, /* Does Mach keeps track of any of this? */ - "cpu 0 0 0 0 0 0 0 0 0\n" - "cpu0 0 0 0 0 0 0 0 0 0\n" + "cpu 0 0 0 %lu 0 0 0 0 0\n" + "cpu0 0 0 0 %lu 0 0 0 0 0\n" "intr 0\n" /* This we know. */ "page %d %d\n" "btime %lu\n", + up_ticks, + up_ticks, vmstats.pageins, vmstats.pageouts, boottime.tv_sec); -- cgit v1.2.3 From 9ab114b6a7e0723135555ed202886f47c032f511 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sun, 22 Aug 2010 20:17:54 +0000 Subject: Add --clk-tck to set the clock unit * main.c (argp_parser, main): Add and parse the --clk-tck option. * main.h: Publish opt_clk_tck. * process.c (sc_tc): Use the user-provided clock frequency. * rootdir.c (rootdir_gc_stat): Likewise. --- main.c | 40 +++++++++++++++++++++++++++++++++++++++- main.h | 2 ++ process.c | 3 ++- rootdir.c | 3 ++- 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 main.h diff --git a/main.c b/main.c index 15ad60f4..a59ab47f 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -7,6 +8,42 @@ #include "proclist.h" #include "rootdir.h" #include "dircat.h" +#include "main.h" + +/* Command-line options */ +int opt_clk_tck; + +static error_t +argp_parser (int key, char *arg, struct argp_state *state) +{ + char *endp; + + switch (key) + { + case 'h': + opt_clk_tck = strtol (arg, &endp, 0); + if (*endp || ! *arg || opt_clk_tck <= 0) + error (1, 0, "--clk-tck: HZ should be a positive integer"); + break; + } + + return 0; +} + +struct argp argp = { + .options = (struct argp_option []) { + { "clk-tck", 'h', "HZ", 0, + "Unit used for the values expressed in system clock ticks " + "(default: sysconf(_SC_CLK_TCK))" }, + {} + }, + .parser = argp_parser, + .doc = "A virtual filesystem emulating the Linux procfs.", + .children = (struct argp_child []) { + { &netfs_std_startup_argp, }, + {} + }, +}; error_t root_make_node (struct node **np) @@ -43,7 +80,8 @@ int main (int argc, char **argv) { mach_port_t bootstrap; - argp_parse (&netfs_std_startup_argp, argc, argv, 0, 0, 0); + opt_clk_tck = sysconf(_SC_CLK_TCK); + argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) diff --git a/main.h b/main.h new file mode 100644 index 00000000..4b2ef9a1 --- /dev/null +++ b/main.h @@ -0,0 +1,2 @@ +/* Startup options */ +extern int opt_clk_tck; diff --git a/process.c b/process.c index 8955cf40..65f0e503 100644 --- a/process.c +++ b/process.c @@ -8,6 +8,7 @@ #include "procfs.h" #include "procfs_dir.h" #include "process.h" +#include "main.h" /* Implementations for the process_file_desc.get_contents callback. */ @@ -39,7 +40,7 @@ static char state_char (struct proc_stat *ps) static long int sc_tv (time_value_t tv) { double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; - return usecs * sysconf(_SC_CLK_TCK) / 1000000; + return usecs * opt_clk_tck / 1000000; } static long long int jiff_tv (time_value_t tv) diff --git a/rootdir.c b/rootdir.c index 50f6e071..1d9c083c 100644 --- a/rootdir.c +++ b/rootdir.c @@ -7,6 +7,7 @@ #include #include "procfs.h" #include "procfs_dir.h" +#include "main.h" /* This implements a directory node with the static files in /proc */ @@ -105,7 +106,7 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) return EIO; timersub (&time, &boottime, &time); - up_ticks = sysconf(_SC_CLK_TCK) * (time.tv_sec + time.tv_usec / 1000000.); + up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); *contents_len = asprintf ((char **) contents, /* Does Mach keeps track of any of this? */ -- cgit v1.2.3 From a22115eca05fd87748aed2ffde8caeb5b71f1c6d Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sun, 22 Aug 2010 20:46:54 +0000 Subject: Add --stat-mode to override the perms for [pid]/stat * main.c (argp_parser, main): Add the --stat-mode option. * process.c (process_lookup_pid): Use it. --- main.c | 14 ++++++++++++++ main.h | 1 + process.c | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/main.c b/main.c index a59ab47f..09cffc76 100644 --- a/main.c +++ b/main.c @@ -12,6 +12,7 @@ /* Command-line options */ int opt_clk_tck; +mode_t opt_stat_mode; static error_t argp_parser (int key, char *arg, struct argp_state *state) @@ -25,6 +26,12 @@ argp_parser (int key, char *arg, struct argp_state *state) if (*endp || ! *arg || opt_clk_tck <= 0) error (1, 0, "--clk-tck: HZ should be a positive integer"); break; + + case 's': + opt_stat_mode = strtol (arg, &endp, 8); + if (*endp || ! *arg || opt_stat_mode & ~07777) + error (1, 0, "--stat-mode: MODE should be an octal mode"); + break; } return 0; @@ -35,6 +42,12 @@ struct argp argp = { { "clk-tck", 'h', "HZ", 0, "Unit used for the values expressed in system clock ticks " "(default: sysconf(_SC_CLK_TCK))" }, + { "stat-mode", 's', "MODE", 0, + "The [pid]/stat file publishes information which on Hurd is only " + "available to the process owner. " + "You can use this option to override its mode to be more permissive " + "for compatibility purposes. " + "(default: 0400)" }, {} }, .parser = argp_parser, @@ -81,6 +94,7 @@ int main (int argc, char **argv) mach_port_t bootstrap; opt_clk_tck = sysconf(_SC_CLK_TCK); + opt_stat_mode = 0400; argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); diff --git a/main.h b/main.h index 4b2ef9a1..6669d321 100644 --- a/main.h +++ b/main.h @@ -1,2 +1,3 @@ /* Startup options */ extern int opt_clk_tck; +extern mode_t opt_stat_mode; diff --git a/process.c b/process.c index 65f0e503..f7a7a577 100644 --- a/process.c +++ b/process.c @@ -206,6 +206,7 @@ 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) { @@ -235,6 +236,10 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) return EIO; + /* FIXME: have a separate proc_desc structure for each file, so this can be + 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); if (! *np) return ENOMEM; -- cgit v1.2.3 From 8b8965bcc85053857b84efd5a0ca63976870de14 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sun, 22 Aug 2010 21:23:36 +0000 Subject: Add a fake-self option to control the self symlink * main.c (argp_parse, main): Add the --fake-self option. * main.h: Publish it. * rootdir.c (rootdir_gc_fakeself, rootdir_entries, rootdir_create_node): Use it. --- main.c | 17 +++++++++++++++++ main.h | 1 + rootdir.c | 30 +++++++++++++++++------------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/main.c b/main.c index 09cffc76..26f5248f 100644 --- a/main.c +++ b/main.c @@ -13,6 +13,7 @@ /* Command-line options */ int opt_clk_tck; mode_t opt_stat_mode; +pid_t opt_fake_self; static error_t argp_parser (int key, char *arg, struct argp_state *state) @@ -32,6 +33,17 @@ argp_parser (int key, char *arg, struct argp_state *state) if (*endp || ! *arg || opt_stat_mode & ~07777) error (1, 0, "--stat-mode: MODE should be an octal mode"); break; + + case 'S': + if (arg) + { + opt_fake_self = strtol (arg, &endp, 0); + if (*endp || ! *arg) + error (1, 0, "--fake-self: PID must be an integer"); + } + else + opt_fake_self = 1; + break; } return 0; @@ -48,6 +60,10 @@ struct argp argp = { "You can use this option to override its mode to be more permissive " "for compatibility purposes. " "(default: 0400)" }, + { "fake-self", 'S', "PID", OPTION_ARG_OPTIONAL, + "Provide a fake \"self\" symlink to the given PID, for compatibility " + "purposes. If PID is omitted, \"self\" will point to init. " + "(default: no self link)" }, {} }, .parser = argp_parser, @@ -95,6 +111,7 @@ int main (int argc, char **argv) opt_clk_tck = sysconf(_SC_CLK_TCK); opt_stat_mode = 0400; + opt_fake_self = -1; argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); diff --git a/main.h b/main.h index 6669d321..361380b3 100644 --- a/main.h +++ b/main.h @@ -1,3 +1,4 @@ /* Startup options */ extern int opt_clk_tck; extern mode_t opt_stat_mode; +extern pid_t opt_fake_self; diff --git a/rootdir.c b/rootdir.c index 1d9c083c..833fc150 100644 --- a/rootdir.c +++ b/rootdir.c @@ -156,9 +156,8 @@ rootdir_gc_empty (void *hook, void **contents, size_t *contents_len) static error_t rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) { - *contents = "1"; - *contents_len = strlen (*contents); - return 0; + *contents_len = asprintf ((char **) contents, "%d", opt_fake_self); + return *contents_len >= 0 ? 0 : ENOMEM; } @@ -177,7 +176,15 @@ rootdir_symlink_make_node (void *dir_hook, void *entry_hook) return np; } -static struct procfs_dir_entry rootdir_entries[] = { +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, + }, + }, { .name = "version", .make_node = rootdir_file_make_node, @@ -217,13 +224,6 @@ static struct procfs_dir_entry rootdir_entries[] = { .get_contents = rootdir_gc_empty, }, }, - { - .name = "self", - .make_node = rootdir_symlink_make_node, - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_fakeself, - }, - }, {} }; @@ -231,14 +231,18 @@ error_t rootdir_create_node (struct node **np) { struct ps_context *pc; + const struct procfs_dir_entry *entries; error_t err; err = ps_context_create (getproc (), &pc); if (err) return err; - *np = procfs_dir_make_node (rootdir_entries, pc, - (void (*)(void *)) ps_context_free); + entries = rootdir_entries; + if (opt_fake_self < 0) + entries++; + + *np = procfs_dir_make_node (entries, pc, (void (*)(void *)) ps_context_free); return 0; } -- cgit v1.2.3 From c8d73b6e554d6bf1492b10a063e8e35a9cb002c2 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 00:52:24 +0000 Subject: Add meminfo and vmstat * rootdir.c: Add a (non-empty) meminfo and a vmstat file. --- rootdir.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/rootdir.c b/rootdir.c index 833fc150..f5b43ff0 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -9,7 +10,10 @@ #include "procfs_dir.h" #include "main.h" -/* This implements a directory node with the static files in /proc */ +/* This implements a directory node with the static files in /proc. + NB: the libps functions for host information return static storage; + using them would require locking and as a consequence it would be + more complicated, not simpler. */ #define INIT_PID 1 @@ -147,10 +151,83 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_empty (void *hook, void **contents, size_t *contents_len) +rootdir_gc_meminfo (void *hook, void **contents, size_t *contents_len) { - *contents_len = 0; - return 0; + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + struct vm_statistics vmstats; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf ((char **) contents, + "MemTotal: %14lu kB\n" + "MemFree: %14lu kB\n" + "Active: %14lu kB\n" + "Inactive: %14lu kB\n" + "Mlocked: %14lu kB\n" + , + /* TODO: check that these are really 1024-bytes kBs. */ + (long unsigned) hbi.memory_size / 1024, + (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + +static error_t +rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) +{ + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + struct vm_statistics vmstats; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf ((char **) contents, + "nr_free_pages %lu\n" + "nr_inactive_anon %lu\n" + "nr_active_anon %lu\n" + "nr_inactive_file %lu\n" + "nr_active_file %lu\n" + "nr_unevictable %lu\n" + "nr_mlock %lu\n" + "pgpgin %lu\n" + "pgpgout %lu\n" + "pgfault %lu\n", + (long unsigned) vmstats.free_count, + /* FIXME: how can we distinguish the anon/file pages? Maybe we can + ask the default pager how many it manages? */ + (long unsigned) vmstats.inactive_count, + (long unsigned) vmstats.active_count, + (long unsigned) 0, + (long unsigned) 0, + (long unsigned) vmstats.wire_count, + (long unsigned) vmstats.wire_count, + (long unsigned) vmstats.pageins, + (long unsigned) vmstats.pageouts, + (long unsigned) vmstats.faults); + + return *contents_len >= 0 ? 0 : ENOMEM; } static error_t @@ -221,7 +298,16 @@ 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_empty, + .get_contents = rootdir_gc_meminfo, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .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, }, }, {} -- cgit v1.2.3 From d87eae321df3f434ea9b6cf96c2437c8b9a7b23d Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 05:19:19 +0000 Subject: Add a global cmdline file * main.c (argp_parser, main): Add the --kernel-pid option. * main.h: Publish it. * rootdir.c (rootdir_gc_cmdline): New function. --- main.c | 11 +++++++++++ main.h | 1 + rootdir.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/main.c b/main.c index 26f5248f..fee4867c 100644 --- a/main.c +++ b/main.c @@ -14,6 +14,7 @@ int opt_clk_tck; mode_t opt_stat_mode; pid_t opt_fake_self; +pid_t opt_kernel_pid; static error_t argp_parser (int key, char *arg, struct argp_state *state) @@ -44,6 +45,12 @@ argp_parser (int key, char *arg, struct argp_state *state) else opt_fake_self = 1; break; + + case 'k': + opt_kernel_pid = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_kernel_pid < 0) + error (1, 0, "--kernel-process: PID must be a positive integer"); + break; } return 0; @@ -64,6 +71,9 @@ struct argp argp = { "Provide a fake \"self\" symlink to the given PID, for compatibility " "purposes. If PID is omitted, \"self\" will point to init. " "(default: no self link)" }, + { "kernel-process", 'k', "PID", 0, + "Process identifier for the kernel, used to retreive its command line " + "(default: 2)" }, {} }, .parser = argp_parser, @@ -112,6 +122,7 @@ int main (int argc, char **argv) opt_clk_tck = sysconf(_SC_CLK_TCK); opt_stat_mode = 0400; opt_fake_self = -1; + opt_kernel_pid = 2; argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); diff --git a/main.h b/main.h index 361380b3..6ada229a 100644 --- a/main.h +++ b/main.h @@ -2,3 +2,4 @@ extern int opt_clk_tck; extern mode_t opt_stat_mode; extern pid_t opt_fake_self; +extern pid_t opt_kernel_pid; diff --git a/rootdir.c b/rootdir.c index f5b43ff0..865342ce 100644 --- a/rootdir.c +++ b/rootdir.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "procfs.h" #include "procfs_dir.h" @@ -230,6 +231,41 @@ rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) return *contents_len >= 0 ? 0 : ENOMEM; } +static error_t +rootdir_gc_cmdline (void *hook, void **contents, size_t *contents_len) +{ + struct ps_context *pc = hook; + struct proc_stat *ps; + error_t err; + + err = _proc_stat_create (opt_kernel_pid, pc, &ps); + if (err) + return EIO; + + err = proc_stat_set_flags (ps, PSTAT_ARGS); + if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS)) + { + err = EIO; + goto out; + } + + *contents_len = proc_stat_args_len (ps); + *contents = malloc (*contents_len); + if (! *contents) + { + err = ENOMEM; + goto out; + } + + memcpy (*contents, proc_stat_args (ps), *contents_len); + argz_stringify (*contents, *contents_len, ' '); + ((char *) *contents)[*contents_len - 1] = '\n'; + +out: + _proc_stat_free (ps); + return err; +} + static error_t rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) { @@ -310,6 +346,14 @@ static const struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .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, + }, + }, {} }; -- cgit v1.2.3 From 166becc7157e51c54a51a7cb78602704256969d0 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 05:44:37 +0000 Subject: Add statm to process directories * process.c: Add the statm file. --- process.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/process.c b/process.c index f7a7a577..4404d084 100644 --- a/process.c +++ b/process.c @@ -105,6 +105,20 @@ process_file_gc_stat (struct proc_stat *ps, size_t *len) return len >= 0 ? contents : NULL; } +static char * +process_file_gc_statm (struct proc_stat *ps, size_t *len) +{ + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + char *contents; + + *len = asprintf (&contents, + "%lu %lu 0 0 0 0 0\n", + tbi->virtual_size / sysconf(_SC_PAGE_SIZE), + tbi->resident_size / sysconf(_SC_PAGE_SIZE)); + + return len >= 0 ? contents : NULL; +} + /* Describes a file in a process directory. This is used as an "entry hook" * for our procfs_dir entry table, passed to process_file_make_node. */ @@ -217,6 +231,15 @@ static struct procfs_dir_entry entries[] = { .mode = 0400, }, }, + { + .name = "statm", + .make_node = process_file_make_node, + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_statm, + .needs = PSTAT_TASK_BASIC, + .mode = 0444, + }, + }, {} }; -- cgit v1.2.3 From 41c86b73e275b632e10fc81e2c4823472dedb89f Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 07:14:45 +0000 Subject: Add a status file to process directories * process.c: Add a status file. --- process.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/process.c b/process.c index 4404d084..b2b098ff 100644 --- a/process.c +++ b/process.c @@ -37,6 +37,27 @@ static char state_char (struct proc_stat *ps) return '?'; } +static const char *state_string (struct proc_stat *ps) +{ + static const char *const state_strings[] = { + "T (stopped)", + "Z (zombie)", + "R (running)", + "H (halted)", + "D (disk sleep)", + "S (sleeping)", + "I (idle)", + NULL + }; + int i; + + for (i = 0; state_strings[i]; i++) + if (proc_stat_state (ps) & (1 << i)) + return state_strings[i]; + + return "? (unknown)"; +} + static long int sc_tv (time_value_t tv) { double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; @@ -119,6 +140,42 @@ process_file_gc_statm (struct proc_stat *ps, size_t *len) return len >= 0 ? contents : NULL; } +static char * +process_file_gc_status (struct proc_stat *ps, size_t *len) +{ + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + char *contents; + + *len = asprintf (&contents, + "Name:\t%s\n" + "State:\t%s\n" + "Tgid:\t%u\n" + "Pid:\t%u\n" + "PPid:\t%u\n" + "Uid:\t%u\t%u\t%u\t%u\n" + "VmSize:\t%8u kB\n" + "VmPeak:\t%8u kB\n" + "VmRSS:\t%8u kB\n" + "VmHWM:\t%8u kB\n" /* ie. resident peak */ + "Threads:\t%u\n", + proc_stat_args (ps), + state_string (ps), + proc_stat_pid (ps), /* XXX will need more work for threads */ + proc_stat_pid (ps), + proc_stat_proc_info (ps)->ppid, + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + tbi->virtual_size / 1024, + tbi->virtual_size / 1024, + tbi->resident_size / 1024, + tbi->resident_size / 1024, + proc_stat_num_threads (ps)); + + return len >= 0 ? contents : NULL; +} + /* Describes a file in a process directory. This is used as an "entry hook" * for our procfs_dir entry table, passed to process_file_make_node. */ @@ -240,6 +297,16 @@ static struct procfs_dir_entry entries[] = { .mode = 0444, }, }, + { + .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 + | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS, + .mode = 0444, + }, + }, {} }; -- cgit v1.2.3 From c0e9dd6d3bcad339d8a263712997f0aaa89f236e Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 07:35:59 +0000 Subject: Add a --compatible option * main.c (argp_parser): Add --compatible, which sets the options required for compatibility with the procps tools. --- main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/main.c b/main.c index fee4867c..35d05c65 100644 --- a/main.c +++ b/main.c @@ -51,6 +51,12 @@ argp_parser (int key, char *arg, struct argp_state *state) if (*endp || ! *arg || (signed) opt_kernel_pid < 0) error (1, 0, "--kernel-process: PID must be a positive integer"); break; + + case 'c': + opt_clk_tck = 100; + opt_stat_mode = 0444; + opt_fake_self = 1; + break; } return 0; @@ -74,6 +80,9 @@ struct argp argp = { { "kernel-process", 'k', "PID", 0, "Process identifier for the kernel, used to retreive its command line " "(default: 2)" }, + { "compatible", 'c', NULL, 0, + "Try to be compatible with the Linux procps utilities. " + "Currently equivalent to -h 100 -s 0444 -S 1." }, {} }, .parser = argp_parser, -- cgit v1.2.3 From 3e4b6453f821646ff7c81cd770c4f4f1f781b3a6 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 08:24:43 +0000 Subject: Handle errors in main * main.c (main): Handle errors from argp_parse and root_make_node. --- main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 35d05c65..a1db4753 100644 --- a/main.c +++ b/main.c @@ -127,19 +127,24 @@ root_make_node (struct node **np) int main (int argc, char **argv) { mach_port_t bootstrap; + error_t err; opt_clk_tck = sysconf(_SC_CLK_TCK); opt_stat_mode = 0400; opt_fake_self = -1; opt_kernel_pid = 2; - argp_parse (&argp, argc, argv, 0, 0, 0); + err = argp_parse (&argp, argc, argv, 0, 0, 0); + if (err) + error (1, err, "Could not parse command line"); task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) error (1, 0, "Must be started as a translator"); netfs_init (); - root_make_node (&netfs_root_node); + err = root_make_node (&netfs_root_node); + if (err) + error (1, err, "Could not create the root node"); netfs_startup (bootstrap, 0); for (;;) -- cgit v1.2.3 From 60f604f96c8f78320d415d92b325d0de80949043 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 08:25:07 +0000 Subject: netfs_server_loop never returns * main.c: Don't call netfs_server_loop repeatedly; it should never return. --- main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index a1db4753..4be506fb 100644 --- a/main.c +++ b/main.c @@ -147,7 +147,8 @@ int main (int argc, char **argv) error (1, err, "Could not create the root node"); netfs_startup (bootstrap, 0); - for (;;) - netfs_server_loop (); + netfs_server_loop (); + + assert (0 /* netfs_server_loop returned after all */); } -- cgit v1.2.3 From 687c9658628eb044be75df8074c5983e865b6394 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 09:06:01 +0000 Subject: Use a global ps_context * proclist.c, proclist.h (proclist_create_node): Use a ps_context passed by the caller; errors are no longer possible, rename to proclist_make_node and change the signature accordingly. * rootdir.c, rootdir.h (rootdir_create_node): Likewise. * main.c (main): Create the ps_context here and pass it to root_make_node. (root_make_node): Pass it to proclist_make_node and rootdir_make_node. --- main.c | 35 ++++++++++++++++++++++------------- proclist.c | 15 +++------------ proclist.h | 6 ++++-- rootdir.c | 13 +++---------- rootdir.h | 6 ++++-- 5 files changed, 36 insertions(+), 39 deletions(-) diff --git a/main.c b/main.c index 4be506fb..91156485 100644 --- a/main.c +++ b/main.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "procfs.h" #include "proclist.h" #include "rootdir.h" @@ -94,38 +95,42 @@ struct argp argp = { }; error_t -root_make_node (struct node **np) +root_make_node (struct ps_context *pc, struct node **np) { /* We never have two root nodes alive simultaneously, so it's ok to have this as static data. */ static struct node *root_dirs[3]; - error_t err; - err = proclist_create_node (getproc (), &root_dirs[0]); - if (err) - return err; + root_dirs[0] = proclist_make_node (pc); + if (! root_dirs[0]) + goto nomem; - err = rootdir_create_node (&root_dirs[1]); - if (err) - { - netfs_nrele (root_dirs[0]); - return err; - } + root_dirs[1] = rootdir_make_node (pc); + if (! root_dirs[1]) + goto nomem; root_dirs[2] = NULL; *np = dircat_make_node (root_dirs); if (! *np) - return ENOMEM; + goto nomem; /* Since this one is not created through proc_lookup(), we have to affect an inode number to it. */ (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; return 0; + +nomem: + if (root_dirs[1]) + netfs_nrele (root_dirs[1]); + if (root_dirs[0]) + netfs_nrele (root_dirs[0]); + return ENOMEM; } int main (int argc, char **argv) { + struct ps_context *pc; mach_port_t bootstrap; error_t err; @@ -137,12 +142,16 @@ int main (int argc, char **argv) if (err) error (1, err, "Could not parse command line"); + err = ps_context_create (getproc (), &pc); + if (err) + error (1, err, "Could not create libps context"); + task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) error (1, 0, "Must be started as a translator"); netfs_init (); - err = root_make_node (&netfs_root_node); + err = root_make_node (pc, &netfs_root_node); if (err) error (1, err, "Could not create the root node"); diff --git a/proclist.c b/proclist.c index 56a3fdf9..35422a81 100644 --- a/proclist.c +++ b/proclist.c @@ -62,24 +62,15 @@ proclist_lookup (void *hook, const char *name, struct node **np) return process_lookup_pid (pc, pid, np); } -error_t -proclist_create_node (process_t procserv, struct node **np) +struct node * +proclist_make_node (struct ps_context *pc) { static const struct procfs_node_ops ops = { .get_contents = proclist_get_contents, .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, - .cleanup = (void (*)(void *)) ps_context_free, .enable_refresh_hack_and_break_readdir = 1, }; - struct ps_context *pc; - error_t err; - - err = ps_context_create (procserv, &pc); - if (err) - return err; - - *np = procfs_make_node (&ops, pc); - return 0; + return procfs_make_node (&ops, pc); } diff --git a/proclist.h b/proclist.h index 1c7ab084..ce69dde3 100644 --- a/proclist.h +++ b/proclist.h @@ -1,2 +1,4 @@ -#include -error_t proclist_create_node (process_t procserv, struct node **np); +#include + +struct node * +proclist_make_node (struct ps_context *pc); diff --git a/rootdir.c b/rootdir.c index 865342ce..8062f0d3 100644 --- a/rootdir.c +++ b/rootdir.c @@ -357,22 +357,15 @@ static const struct procfs_dir_entry rootdir_entries[] = { {} }; -error_t -rootdir_create_node (struct node **np) +struct node +*rootdir_make_node (struct ps_context *pc) { - struct ps_context *pc; const struct procfs_dir_entry *entries; - error_t err; - - err = ps_context_create (getproc (), &pc); - if (err) - return err; entries = rootdir_entries; if (opt_fake_self < 0) entries++; - *np = procfs_dir_make_node (entries, pc, (void (*)(void *)) ps_context_free); - return 0; + return procfs_dir_make_node (entries, pc, NULL); } diff --git a/rootdir.h b/rootdir.h index a764b0fd..0caee258 100644 --- a/rootdir.h +++ b/rootdir.h @@ -1,2 +1,4 @@ -error_t -rootdir_create_node (struct node **np); +#include + +struct node * +rootdir_make_node (struct ps_context *pc); -- cgit v1.2.3 From fa82e6672499e2fb5a4191c567c6a418d1f88405 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:02:13 +0000 Subject: Fix the handling of processes without an owner * main.c (argp_parser): New option --anonymous-owner. * main.h: Publish it. * process.c (process_lookup_pid): Use it to set the file owner uid of non-owned processes. --- main.c | 22 ++++++++++++++++++++++ main.h | 1 + process.c | 4 +++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index 91156485..06c1da04 100644 --- a/main.c +++ b/main.c @@ -16,10 +16,12 @@ int opt_clk_tck; mode_t opt_stat_mode; pid_t opt_fake_self; pid_t opt_kernel_pid; +uid_t opt_anon_owner; static error_t argp_parser (int key, char *arg, struct argp_state *state) { + struct passwd *pw; char *endp; switch (key) @@ -58,6 +60,20 @@ argp_parser (int key, char *arg, struct argp_state *state) opt_stat_mode = 0444; opt_fake_self = 1; break; + + case 'a': + pw = getpwnam (arg); + if (pw) + { + opt_anon_owner = pw->pw_uid; + break; + } + + opt_anon_owner = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_anon_owner < 0) + error(1, 0, "--anonymous-owner: USER should be the a user name " + "or a numeric UID."); + break; } return 0; @@ -84,6 +100,11 @@ struct argp argp = { { "compatible", 'c', NULL, 0, "Try to be compatible with the Linux procps utilities. " "Currently equivalent to -h 100 -s 0444 -S 1." }, + { "anonymous-owner", 'a', "USER", 0, + "Make USER the owner of files related to processes without one. " + "Be aware that USER will be granted access to the environment and " + "other sensitive information about the processes in question. " + "(default: use uid 0)" }, {} }, .parser = argp_parser, @@ -138,6 +159,7 @@ int main (int argc, char **argv) opt_stat_mode = 0400; opt_fake_self = -1; opt_kernel_pid = 2; + opt_anon_owner = 0; err = argp_parse (&argp, argc, argv, 0, 0, 0); if (err) error (1, err, "Could not parse command line"); diff --git a/main.h b/main.h index 6ada229a..28d1b023 100644 --- a/main.h +++ b/main.h @@ -3,3 +3,4 @@ extern int opt_clk_tck; extern mode_t opt_stat_mode; extern pid_t opt_fake_self; extern pid_t opt_kernel_pid; +extern uid_t opt_anon_owner; diff --git a/process.c b/process.c index b2b098ff..7f5646ae 100644 --- a/process.c +++ b/process.c @@ -314,6 +314,7 @@ error_t process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) { struct proc_stat *ps; + int owner; error_t err; err = _proc_stat_create (pid, pc, &ps); @@ -334,6 +335,7 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) if (! *np) return ENOMEM; - procfs_node_chown (*np, proc_stat_owner_uid (ps)); + owner = proc_stat_owner_uid (ps); + procfs_node_chown (*np, owner >= 0 ? owner : opt_anon_owner); return 0; } -- cgit v1.2.3 From bff8f1d6895d32ded381e68cd647e437b28e22bf Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:02:48 +0000 Subject: Remove the unused procfs_file module * procfs_file.c, procfs_file.h: Remove. * Makefile: Remove procfs_file. --- procfs_file.c | 54 ------------------------------------------------------ procfs_file.h | 6 ------ 2 files changed, 60 deletions(-) delete mode 100644 procfs_file.c delete mode 100644 procfs_file.h diff --git a/procfs_file.c b/procfs_file.c deleted file mode 100644 index cb0488e9..00000000 --- a/procfs_file.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include "procfs.h" -#include "procfs_file.h" - -struct procfs_file -{ - void *contents; - size_t len; - void (*cleanup)(void *contents); -}; - -error_t -procfs_file_getcontents (void *hook, void **contents, size_t *contents_len) -{ - struct procfs_file *f = hook; - - *contents = f->contents; - *contents_len = f->len; - return 0; -} - -void -procfs_file_cleanup (void *hook) -{ - struct procfs_file *f = hook; - - if (f->cleanup) - f->cleanup (f->contents); - - free (f); -} - -struct node * -procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)) -{ - static const struct procfs_node_ops ops = { - .get_contents = procfs_file_getcontents, - .cleanup = procfs_file_cleanup, - }; - struct procfs_file *f; - - f = malloc (sizeof *f); - if (! f) - return NULL; - - f->contents = contents; - f->len = (len >= 0) ? len : strlen (f->contents); - f->cleanup = cleanup; - - return procfs_make_node (&ops, f); -} - diff --git a/procfs_file.h b/procfs_file.h deleted file mode 100644 index b615db93..00000000 --- a/procfs_file.h +++ /dev/null @@ -1,6 +0,0 @@ -/* Create a new regular file with the given CONTENTS. If LEN is negative, - CONTENTS is considered as a string and the file stops at the first - nul char. If CLEANUP is non-NULL, it is passed CONTENTS when the - node is destroyed. */ -struct node * -procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)); -- cgit v1.2.3 From 0277bc261796743148769a3f76f9cae51c61e0a4 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:24:29 +0000 Subject: Make contents a char* to avoid typecasts all over the place * procfs.h (procfs_cleanup_contents_with_free, procfs_cleanup_contents_with_vm_deallocate, procfs_get_contents, struct procfs_ops): Change CONTENTS from a void pointer to a char one. * dircat.c, netfs.c, process.c, procfs.c, procfs_dir.c, proclist.c, rootdir.c: Update. --- dircat.c | 4 ++-- netfs.c | 10 +++++----- process.c | 2 +- procfs.c | 10 +++++----- procfs.h | 10 +++++----- procfs_dir.c | 2 +- proclist.c | 2 +- rootdir.c | 32 ++++++++++++++++---------------- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/dircat.c b/dircat.c index 857ba72b..93bb2fed 100644 --- a/dircat.c +++ b/dircat.c @@ -8,7 +8,7 @@ struct dircat_node }; static error_t -dircat_get_contents (void *hook, void **contents, size_t *contents_len) +dircat_get_contents (void *hook, char **contents, size_t *contents_len) { struct dircat_node *dcn = hook; int i, sz, pos; @@ -19,7 +19,7 @@ dircat_get_contents (void *hook, void **contents, size_t *contents_len) for (i=0; dcn->dirs[i]; i++) { - void *subcon; + char *subcon; size_t sublen; err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); diff --git a/netfs.c b/netfs.c index a47861e5..6fd82a00 100644 --- a/netfs.c +++ b/netfs.c @@ -29,7 +29,7 @@ int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; responsible for the operation. NP is locked. */ error_t netfs_validate_stat (struct node *np, struct iouser *cred) { - void *contents; + char *contents; size_t contents_len; error_t err; @@ -57,7 +57,7 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *np, size_t contents_len; error_t err; - err = procfs_get_contents (np, (void **) &contents, &contents_len); + err = procfs_get_contents (np, &contents, &contents_len); if (err) return err; @@ -82,7 +82,7 @@ error_t netfs_attempt_readlink (struct iouser *user, struct node *np, size_t contents_len; error_t err; - err = procfs_get_contents (np, (void **) &contents, &contents_len); + err = procfs_get_contents (np, &contents, &contents_len); if (err) return err; @@ -140,7 +140,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, size_t contents_len; error_t err; - err = procfs_get_contents (dir, (void **) &contents, &contents_len); + err = procfs_get_contents (dir, &contents, &contents_len); if (err) return err; @@ -159,7 +159,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, putentries (contents, contents_len, nentries, NULL, datacnt); if (bufsize < *datacnt) { - void *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); + char *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); if (n == MAP_FAILED) return ENOMEM; diff --git a/process.c b/process.c index 7f5646ae..fa795522 100644 --- a/process.c +++ b/process.c @@ -204,7 +204,7 @@ struct process_file_node }; static error_t -process_file_get_contents (void *hook, void **contents, size_t *contents_len) +process_file_get_contents (void *hook, char **contents, size_t *contents_len) { struct process_file_node *file = hook; error_t err; diff --git a/procfs.c b/procfs.c index 5396ecf7..a5f52b50 100644 --- a/procfs.c +++ b/procfs.c @@ -12,7 +12,7 @@ struct netnode void *hook; /* (cached) contents of the node */ - void *contents; + char *contents; size_t contents_len; /* parent directory, if applicable */ @@ -20,13 +20,13 @@ struct netnode }; void -procfs_cleanup_contents_with_free (void *hook, void *cont, size_t len) +procfs_cleanup_contents_with_free (void *hook, char *cont, size_t len) { free (cont); } void -procfs_cleanup_contents_with_vm_deallocate (void *hook, void *cont, size_t len) +procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, size_t len) { vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); } @@ -109,7 +109,7 @@ procfs_make_ino (struct node *np, const char *filename) return (unsigned long) jrand48 (x); } -error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) +error_t procfs_get_contents (struct node *np, char **data, size_t *data_len) { if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) { @@ -121,7 +121,7 @@ error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) if (! np->nn->contents && np->nn->ops->get_contents) { - void *contents; + char *contents; size_t contents_len; error_t err; diff --git a/procfs.h b/procfs.h index 4ab3b567..8336ee85 100644 --- a/procfs.h +++ b/procfs.h @@ -17,8 +17,8 @@ struct procfs_node_ops netnode->nn_stat. For regular files and symlinks, they are what you would expect; for directories, they are an argz vector of the names of the entries. */ - error_t (*get_contents) (void *hook, void **contents, size_t *contents_len); - void (*cleanup_contents) (void *hook, void *contents, size_t contents_len); + error_t (*get_contents) (void *hook, char **contents, size_t *contents_len); + void (*cleanup_contents) (void *hook, char *contents, size_t contents_len); /* Lookup NAME in this directory, and store the result in *np. The returned node should be created by lookup() using procfs_make_node() @@ -41,8 +41,8 @@ struct procfs_node_ops }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ -void procfs_cleanup_contents_with_free (void *, void *, size_t); -void procfs_cleanup_contents_with_vm_deallocate (void *, void *, size_t); +void procfs_cleanup_contents_with_free (void *, char *, size_t); +void procfs_cleanup_contents_with_vm_deallocate (void *, char *, size_t); /* Create a new node and return it. Returns NULL if it fails to allocate enough memory. In this case, ops->cleanup will be invoked. */ @@ -69,7 +69,7 @@ void procfs_node_chtype (struct node *np, mode_t type); corresponding child nodes. */ ino64_t procfs_make_ino (struct node *np, const char *filename); -error_t procfs_get_contents (struct node *np, void **data, size_t *data_len); +error_t procfs_get_contents (struct node *np, char **data, size_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); diff --git a/procfs_dir.c b/procfs_dir.c index 431fea3e..4df46695 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -11,7 +11,7 @@ struct procfs_dir_node }; static error_t -procfs_dir_get_contents (void *hook, void **contents, size_t *contents_len) +procfs_dir_get_contents (void *hook, char **contents, size_t *contents_len) { static const char dot_dotdot[] = ".\0.."; struct procfs_dir_node *dn = hook; diff --git a/proclist.c b/proclist.c index 35422a81..b5acb26c 100644 --- a/proclist.c +++ b/proclist.c @@ -10,7 +10,7 @@ #define PID_STR_SIZE (3 * sizeof (pid_t) + 1) static error_t -proclist_get_contents (void *hook, void **contents, size_t *contents_len) +proclist_get_contents (void *hook, char **contents, size_t *contents_len) { struct ps_context *pc = hook; pidarray_t pids; diff --git a/rootdir.c b/rootdir.c index 8062f0d3..cd8949bf 100644 --- a/rootdir.c +++ b/rootdir.c @@ -44,7 +44,7 @@ get_boottime (struct ps_context *pc, struct timeval *tv) } static error_t -rootdir_gc_version (void *hook, void **contents, size_t *contents_len) +rootdir_gc_version (void *hook, char **contents, size_t *contents_len) { struct utsname uts; int r; @@ -53,7 +53,7 @@ rootdir_gc_version (void *hook, void **contents, size_t *contents_len) if (r < 0) return errno; - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, "Linux version 2.6.1 (%s %s %s %s)\n", uts.sysname, uts.release, uts.version, uts.machine); @@ -63,7 +63,7 @@ rootdir_gc_version (void *hook, void **contents, size_t *contents_len) /* Uptime -- we use the start time of init to deduce it. This is probably a bit fragile, as any clock update will make the result inaccurate. */ static error_t -rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) +rootdir_gc_uptime (void *hook, char **contents, size_t *contents_len) { struct timeval time, boottime; double up_secs; @@ -85,13 +85,13 @@ rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) proc(5) specifies that it should be equal to USER_HZ times the idle value in ticks from /proc/stat. So we assume a completely idle system both here and there to make that work. */ - *contents_len = asprintf ((char **) contents, "%.2lf %.2lf\n", up_secs, up_secs); + *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, up_secs); return *contents_len >= 0 ? 0 : ENOMEM; } static error_t -rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) +rootdir_gc_stat (void *hook, char **contents, size_t *contents_len) { struct timeval boottime, time; struct vm_statistics vmstats; @@ -113,7 +113,7 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) timersub (&time, &boottime, &time); up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, /* Does Mach keeps track of any of this? */ "cpu 0 0 0 %lu 0 0 0 0 0\n" "cpu0 0 0 0 %lu 0 0 0 0 0\n" @@ -130,7 +130,7 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) +rootdir_gc_loadavg (void *hook, char **contents, size_t *contents_len) { host_load_info_data_t hli; mach_msg_type_number_t cnt; @@ -142,7 +142,7 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) return err; assert (cnt == HOST_LOAD_INFO_COUNT); - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, "%.2f %.2f %.2f 1/0 0\n", hli.avenrun[0] / (double) LOAD_SCALE, hli.avenrun[1] / (double) LOAD_SCALE, @@ -152,7 +152,7 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_meminfo (void *hook, void **contents, size_t *contents_len) +rootdir_gc_meminfo (void *hook, char **contents, size_t *contents_len) { host_basic_info_data_t hbi; mach_msg_type_number_t cnt; @@ -169,7 +169,7 @@ rootdir_gc_meminfo (void *hook, void **contents, size_t *contents_len) return err; assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, "MemTotal: %14lu kB\n" "MemFree: %14lu kB\n" "Active: %14lu kB\n" @@ -187,7 +187,7 @@ rootdir_gc_meminfo (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) +rootdir_gc_vmstat (void *hook, char **contents, size_t *contents_len) { host_basic_info_data_t hbi; mach_msg_type_number_t cnt; @@ -204,7 +204,7 @@ rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) return err; assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, "nr_free_pages %lu\n" "nr_inactive_anon %lu\n" "nr_active_anon %lu\n" @@ -232,7 +232,7 @@ rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_cmdline (void *hook, void **contents, size_t *contents_len) +rootdir_gc_cmdline (void *hook, char **contents, size_t *contents_len) { struct ps_context *pc = hook; struct proc_stat *ps; @@ -259,7 +259,7 @@ rootdir_gc_cmdline (void *hook, void **contents, size_t *contents_len) memcpy (*contents, proc_stat_args (ps), *contents_len); argz_stringify (*contents, *contents_len, ' '); - ((char *) *contents)[*contents_len - 1] = '\n'; + (*contents)[*contents_len - 1] = '\n'; out: _proc_stat_free (ps); @@ -267,9 +267,9 @@ out: } static error_t -rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) +rootdir_gc_fakeself (void *hook, char **contents, size_t *contents_len) { - *contents_len = asprintf ((char **) contents, "%d", opt_fake_self); + *contents_len = asprintf (contents, "%d", opt_fake_self); return *contents_len >= 0 ? 0 : ENOMEM; } -- cgit v1.2.3 From 2f30d52e2753c4fc854753ee2d0efb895a317c9f Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:33:22 +0000 Subject: Detect asprintf's ENOMEM in procfs.c rather than everywhere * procfs.h: Make CONTENTS_LEN an ssize_t rather than a size_t, and document the change. * procfs.c (procfs_get_contents): Initialize CONTENTS_LEN to a negative value, and fail with ENOMEM if it's still negative after the callback returns. (everywhere): Update to ssize_t. * dircat.c, netfs.c, process.c, procfs_dir.c, proclist.c, rootdir.c: Update to ssize_t and the new GET_CONTENTS semantics. --- dircat.c | 4 ++-- netfs.c | 8 ++++---- process.c | 54 ++++++++++++++++++++---------------------------------- procfs.c | 13 ++++++++----- procfs.h | 14 ++++++++------ procfs_dir.c | 2 +- proclist.c | 2 +- rootdir.c | 30 +++++++++++++++--------------- 8 files changed, 59 insertions(+), 68 deletions(-) diff --git a/dircat.c b/dircat.c index 93bb2fed..bb66508a 100644 --- a/dircat.c +++ b/dircat.c @@ -8,7 +8,7 @@ struct dircat_node }; static error_t -dircat_get_contents (void *hook, char **contents, size_t *contents_len) +dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) { struct dircat_node *dcn = hook; int i, sz, pos; @@ -20,7 +20,7 @@ dircat_get_contents (void *hook, char **contents, size_t *contents_len) for (i=0; dcn->dirs[i]; i++) { char *subcon; - size_t sublen; + ssize_t sublen; err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); if (err) diff --git a/netfs.c b/netfs.c index 6fd82a00..e41e0623 100644 --- a/netfs.c +++ b/netfs.c @@ -30,7 +30,7 @@ int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; error_t netfs_validate_stat (struct node *np, struct iouser *cred) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; /* Only symlinks need to have their size filled, before a read is @@ -54,7 +54,7 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *np, loff_t offset, size_t *len, void *data) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; err = procfs_get_contents (np, &contents, &contents_len); @@ -79,7 +79,7 @@ error_t netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; err = procfs_get_contents (np, &contents, &contents_len); @@ -137,7 +137,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, vm_size_t bufsize, int *amt) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; err = procfs_get_contents (dir, &contents, &contents_len); diff --git a/process.c b/process.c index fa795522..624f79e7 100644 --- a/process.c +++ b/process.c @@ -12,18 +12,18 @@ /* Implementations for the process_file_desc.get_contents callback. */ -static char * -process_file_gc_cmdline (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_cmdline (struct proc_stat *ps, char **contents) { - *len = proc_stat_args_len(ps); - return proc_stat_args(ps); + *contents = proc_stat_args(ps); + return proc_stat_args_len(ps); } -static char * -process_file_gc_environ (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_environ (struct proc_stat *ps, char **contents) { - *len = proc_stat_args_len(ps); - return proc_stat_args(ps); + *contents = proc_stat_env(ps); + return proc_stat_env_len(ps); } static char state_char (struct proc_stat *ps) @@ -71,17 +71,16 @@ static long long int jiff_tv (time_value_t tv) return usecs * 100 / 1000000; } -static char * -process_file_gc_stat (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_stat (struct proc_stat *ps, char **contents) { struct procinfo *pi = proc_stat_proc_info (ps); task_basic_info_t tbi = proc_stat_task_basic_info (ps); thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); - char *contents; /* See proc(5) for more information about the contents of each field for the Linux procfs. */ - *len = asprintf (&contents, + return asprintf (contents, "%d (%s) %c " /* pid, command, state */ "%d %d %d " /* ppid, pgid, session */ "%d %d " /* controling tty stuff */ @@ -122,31 +121,23 @@ process_file_gc_stat (struct proc_stat *ps, size_t *len) 0, 0, 0, 0LL); - - return len >= 0 ? contents : NULL; } -static char * -process_file_gc_statm (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_statm (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); - char *contents; - - *len = asprintf (&contents, + return asprintf (contents, "%lu %lu 0 0 0 0 0\n", tbi->virtual_size / sysconf(_SC_PAGE_SIZE), tbi->resident_size / sysconf(_SC_PAGE_SIZE)); - - return len >= 0 ? contents : NULL; } -static char * -process_file_gc_status (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_status (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); - char *contents; - - *len = asprintf (&contents, + return asprintf (contents, "Name:\t%s\n" "State:\t%s\n" "Tgid:\t%u\n" @@ -172,8 +163,6 @@ process_file_gc_status (struct proc_stat *ps, size_t *len) tbi->resident_size / 1024, tbi->resident_size / 1024, proc_stat_num_threads (ps)); - - return len >= 0 ? contents : NULL; } @@ -186,7 +175,7 @@ struct process_file_desc /* Once we have acquired the necessary information, there can be only memory allocation errors, hence this simplified signature. */ - char *(*get_contents) (struct proc_stat *ps, size_t *len); + ssize_t (*get_contents) (struct proc_stat *ps, char **contents); /* The cmdline and environ contents don't need any cleaning since they are part of a proc_stat structure. */ @@ -204,7 +193,7 @@ struct process_file_node }; static error_t -process_file_get_contents (void *hook, char **contents, size_t *contents_len) +process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) { struct process_file_node *file = hook; error_t err; @@ -215,10 +204,7 @@ process_file_get_contents (void *hook, char **contents, size_t *contents_len) if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) return EIO; - *contents = file->desc->get_contents (file->ps, contents_len); - if (! *contents) - return ENOMEM; - + *contents_len = file->desc->get_contents (file->ps, contents); return 0; } diff --git a/procfs.c b/procfs.c index a5f52b50..6d15e4fd 100644 --- a/procfs.c +++ b/procfs.c @@ -13,20 +13,20 @@ struct netnode /* (cached) contents of the node */ char *contents; - size_t contents_len; + ssize_t contents_len; /* parent directory, if applicable */ struct node *parent; }; void -procfs_cleanup_contents_with_free (void *hook, char *cont, size_t len) +procfs_cleanup_contents_with_free (void *hook, char *cont, ssize_t len) { free (cont); } void -procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, size_t len) +procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, ssize_t len) { vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); } @@ -109,7 +109,7 @@ procfs_make_ino (struct node *np, const char *filename) return (unsigned long) jrand48 (x); } -error_t procfs_get_contents (struct node *np, char **data, size_t *data_len) +error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) { if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) { @@ -122,12 +122,15 @@ error_t procfs_get_contents (struct node *np, char **data, size_t *data_len) if (! np->nn->contents && np->nn->ops->get_contents) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; + contents_len = -1; err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); if (err) return err; + if (contents_len < 0) + return ENOMEM; np->nn->contents = contents; np->nn->contents_len = contents_len; diff --git a/procfs.h b/procfs.h index 8336ee85..e8ef18b3 100644 --- a/procfs.h +++ b/procfs.h @@ -16,9 +16,11 @@ struct procfs_node_ops symlink or directory, as determined by the file mode in netnode->nn_stat. For regular files and symlinks, they are what you would expect; for directories, they are an argz vector of the - names of the entries. */ - error_t (*get_contents) (void *hook, char **contents, size_t *contents_len); - void (*cleanup_contents) (void *hook, char *contents, size_t contents_len); + names of the entries. If upon return, *CONTENTS_LEN is negative or + unchanged, the call is considered to have failed because of a memory + allocation error. */ + error_t (*get_contents) (void *hook, char **contents, ssize_t *contents_len); + void (*cleanup_contents) (void *hook, char *contents, ssize_t contents_len); /* Lookup NAME in this directory, and store the result in *np. The returned node should be created by lookup() using procfs_make_node() @@ -41,8 +43,8 @@ struct procfs_node_ops }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ -void procfs_cleanup_contents_with_free (void *, char *, size_t); -void procfs_cleanup_contents_with_vm_deallocate (void *, char *, size_t); +void procfs_cleanup_contents_with_free (void *, char *, ssize_t); +void procfs_cleanup_contents_with_vm_deallocate (void *, char *, ssize_t); /* Create a new node and return it. Returns NULL if it fails to allocate enough memory. In this case, ops->cleanup will be invoked. */ @@ -69,7 +71,7 @@ void procfs_node_chtype (struct node *np, mode_t type); corresponding child nodes. */ ino64_t procfs_make_ino (struct node *np, const char *filename); -error_t procfs_get_contents (struct node *np, char **data, size_t *data_len); +error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); diff --git a/procfs_dir.c b/procfs_dir.c index 4df46695..8ec3f7a5 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -11,7 +11,7 @@ struct procfs_dir_node }; static error_t -procfs_dir_get_contents (void *hook, char **contents, size_t *contents_len) +procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len) { static const char dot_dotdot[] = ".\0.."; struct procfs_dir_node *dn = hook; diff --git a/proclist.c b/proclist.c index b5acb26c..38368feb 100644 --- a/proclist.c +++ b/proclist.c @@ -10,7 +10,7 @@ #define PID_STR_SIZE (3 * sizeof (pid_t) + 1) static error_t -proclist_get_contents (void *hook, char **contents, size_t *contents_len) +proclist_get_contents (void *hook, char **contents, ssize_t *contents_len) { struct ps_context *pc = hook; pidarray_t pids; diff --git a/rootdir.c b/rootdir.c index cd8949bf..364b0731 100644 --- a/rootdir.c +++ b/rootdir.c @@ -44,7 +44,7 @@ get_boottime (struct ps_context *pc, struct timeval *tv) } static error_t -rootdir_gc_version (void *hook, char **contents, size_t *contents_len) +rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { struct utsname uts; int r; @@ -57,13 +57,13 @@ rootdir_gc_version (void *hook, char **contents, size_t *contents_len) "Linux version 2.6.1 (%s %s %s %s)\n", uts.sysname, uts.release, uts.version, uts.machine); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } /* Uptime -- we use the start time of init to deduce it. This is probably a bit fragile, as any clock update will make the result inaccurate. */ static error_t -rootdir_gc_uptime (void *hook, char **contents, size_t *contents_len) +rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) { struct timeval time, boottime; double up_secs; @@ -87,11 +87,11 @@ rootdir_gc_uptime (void *hook, char **contents, size_t *contents_len) and there to make that work. */ *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, up_secs); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_stat (void *hook, char **contents, size_t *contents_len) +rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) { struct timeval boottime, time; struct vm_statistics vmstats; @@ -126,11 +126,11 @@ rootdir_gc_stat (void *hook, char **contents, size_t *contents_len) vmstats.pageins, vmstats.pageouts, boottime.tv_sec); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_loadavg (void *hook, char **contents, size_t *contents_len) +rootdir_gc_loadavg (void *hook, char **contents, ssize_t *contents_len) { host_load_info_data_t hli; mach_msg_type_number_t cnt; @@ -148,11 +148,11 @@ rootdir_gc_loadavg (void *hook, char **contents, size_t *contents_len) hli.avenrun[1] / (double) LOAD_SCALE, hli.avenrun[2] / (double) LOAD_SCALE); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_meminfo (void *hook, char **contents, size_t *contents_len) +rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) { host_basic_info_data_t hbi; mach_msg_type_number_t cnt; @@ -183,11 +183,11 @@ rootdir_gc_meminfo (void *hook, char **contents, size_t *contents_len) (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_vmstat (void *hook, char **contents, size_t *contents_len) +rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len) { host_basic_info_data_t hbi; mach_msg_type_number_t cnt; @@ -228,11 +228,11 @@ rootdir_gc_vmstat (void *hook, char **contents, size_t *contents_len) (long unsigned) vmstats.pageouts, (long unsigned) vmstats.faults); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_cmdline (void *hook, char **contents, size_t *contents_len) +rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) { struct ps_context *pc = hook; struct proc_stat *ps; @@ -267,10 +267,10 @@ out: } static error_t -rootdir_gc_fakeself (void *hook, char **contents, size_t *contents_len) +rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) { *contents_len = asprintf (contents, "%d", opt_fake_self); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } -- cgit v1.2.3 From e7563c6a79a163b727027c676e96b99b7ae2624e Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:54:10 +0000 Subject: Improve the interface for dircat_make_node * dircat.c, dircat.h (dircat_make_node): Use an explicit array size for DIRS, fail with ENOMEM is any of them is NULL, and release the reference on the non-NULL nodes on any error. * main.c (root_make_node): Use the new interface. --- dircat.c | 37 +++++++++++++++++++++++-------------- dircat.h | 17 +++++++++-------- main.c | 27 ++++++--------------------- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/dircat.c b/dircat.c index bb66508a..d043486d 100644 --- a/dircat.c +++ b/dircat.c @@ -4,7 +4,8 @@ struct dircat_node { - struct node **dirs; + int num_dirs; + struct node *dirs[0]; }; static error_t @@ -17,7 +18,7 @@ dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) pos = 0; *contents = malloc (sz = 512); - for (i=0; dcn->dirs[i]; i++) + for (i=0; i < dcn->num_dirs; i++) { char *subcon; ssize_t sublen; @@ -49,19 +50,20 @@ dircat_lookup (void *hook, const char *name, struct node **np) int i; err = ENOENT; - for (i=0; err && dcn->dirs[i]; i++) + for (i=0; err && i < dcn->num_dirs; i++) err = procfs_lookup (dcn->dirs[i], name, np); return err; } static void -dircat_release_dirs (struct node **dirs) +dircat_release_dirs (struct node *const *dirs, int num_dirs) { int i; - for (i=0; dirs[i]; i++) - netfs_nrele (dirs[i]); + for (i=0; i < num_dirs; i++) + if (dirs[i]) + netfs_nrele (dirs[i]); } static void @@ -69,12 +71,12 @@ dircat_cleanup (void *hook) { struct dircat_node *dcn = hook; - dircat_release_dirs (dcn->dirs); + dircat_release_dirs (dcn->dirs, dcn->num_dirs); free (dcn); } struct node * -dircat_make_node (struct node **dirs) +dircat_make_node (struct node *const *dirs, int num_dirs) { static struct procfs_node_ops ops = { .get_contents = dircat_get_contents, @@ -85,15 +87,22 @@ dircat_make_node (struct node **dirs) .enable_refresh_hack_and_break_readdir = 1, }; struct dircat_node *dcn; + int i; + + for (i=0; i < num_dirs; i++) + if (! dirs[i]) + goto fail; - dcn = malloc (sizeof *dcn); + dcn = malloc (sizeof *dcn + num_dirs * sizeof dcn->dirs[0]); if (! dcn) - { - dircat_release_dirs (dirs); - return NULL; - } + goto fail; - dcn->dirs = dirs; + dcn->num_dirs = num_dirs; + memcpy (dcn->dirs, dirs, num_dirs * sizeof dcn->dirs[0]); return procfs_make_node (&ops, dcn); + +fail: + dircat_release_dirs (dirs, num_dirs); + return NULL; } diff --git a/dircat.h b/dircat.h index cb228526..951d2021 100644 --- a/dircat.h +++ b/dircat.h @@ -1,9 +1,10 @@ -/* Append the contents of multiple directories. DIRS is a - NULL-terminated array of directory nodes. One reference is consumed - for each of them, even on ENOMEM, in which case NULL is returned. - DIRS has to be static data for now, or at list remain available and - unchanged for the duration of the created node's life. Strange - things will happen if they have entries with the same name or if one - of them is not a directory. */ +/* Append the contents of NUM_DIRS directories. DIRS is an array of + directory nodes. One reference is consumed for each of them. If a + memory allocation error occurs, or if one of the directories is a + NULL pointer, the references are dropped immediately and NULL is + returned. The given DIRS array is duplicated and can therefore be + allocated on the caller's stack. Strange things will happen if some + elements of DIRS have entries with the same name or if one of them is + not a directory. */ struct node * -dircat_make_node (struct node **dirs); +dircat_make_node (struct node *const *dirs, int num_dirs); diff --git a/main.c b/main.c index 06c1da04..eaab9867 100644 --- a/main.c +++ b/main.c @@ -118,35 +118,20 @@ struct argp argp = { error_t root_make_node (struct ps_context *pc, struct node **np) { - /* We never have two root nodes alive simultaneously, so it's ok to - have this as static data. */ - static struct node *root_dirs[3]; + struct node *root_dirs[] = { + proclist_make_node (pc), + rootdir_make_node (pc), + }; - root_dirs[0] = proclist_make_node (pc); - if (! root_dirs[0]) - goto nomem; - - root_dirs[1] = rootdir_make_node (pc); - if (! root_dirs[1]) - goto nomem; - - root_dirs[2] = NULL; - *np = dircat_make_node (root_dirs); + *np = dircat_make_node (root_dirs, sizeof root_dirs / sizeof root_dirs[0]); if (! *np) - goto nomem; + return ENOMEM; /* Since this one is not created through proc_lookup(), we have to affect an inode number to it. */ (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; return 0; - -nomem: - if (root_dirs[1]) - netfs_nrele (root_dirs[1]); - if (root_dirs[0]) - netfs_nrele (root_dirs[0]); - return ENOMEM; } int main (int argc, char **argv) -- cgit v1.2.3 From 6a887dbb2c998e250df612f7c28c00d3e7ba8b38 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 624f79e7..61688f7e 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 8ec3f7a5..dfe30a26 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 4eb934e0..12e99d2a 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 364b0731..dcee9a54 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 From 49ffe9e1dc9b12fa1c87c103326e0207d0cf45d8 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 16:46:04 +0000 Subject: Cleanup pass on process.c * process.c: Reorder some of the code. Improve comments. Jiffies and clock tick are the same thing, right? Replace the stat mode and cleanup hacks by more straightforward solutions. --- process.c | 122 ++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 43 deletions(-) diff --git a/process.c b/process.c index 61688f7e..1f9d5781 100644 --- a/process.c +++ b/process.c @@ -9,22 +9,20 @@ #include "procfs_dir.h" #include "process.h" #include "main.h" - -/* Implementations for the process_file_desc.get_contents callback. */ -static ssize_t -process_file_gc_cmdline (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_args(ps); - return proc_stat_args_len(ps); -} +/* This module implements the process directories and the files they + contain. A libps proc_stat structure is created for each process + node, and is used by the individual file content generators as a + source of information. Each possible file (cmdline, environ, ...) is + decribed in a process_file_desc structure, which specifies which bits + of information (ie. libps flags) it needs, and what function should + be used to generate the file's contents. -static ssize_t -process_file_gc_environ (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_env(ps); - return proc_stat_env_len(ps); -} + The content generators are defined first, followed by glue logic and + entry table. */ + + +/* Helper functions */ static char state_char (struct proc_stat *ps) { @@ -58,17 +56,26 @@ static const char *state_string (struct proc_stat *ps) return "? (unknown)"; } -static long int sc_tv (time_value_t tv) +static long long int timeval_jiffies (time_value_t tv) { - double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; - return usecs * opt_clk_tck / 1000000; + double secs = tv.seconds + tv.microseconds / 1000000.; + return secs * opt_clk_tck; } -static long long int jiff_tv (time_value_t tv) +/* Actual content generators */ + +static ssize_t +process_file_gc_cmdline (struct proc_stat *ps, char **contents) { - double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; - /* Let's say a jiffy is 1/100 of a second.. */ - return usecs * 100 / 1000000; + *contents = proc_stat_args(ps); + return proc_stat_args_len(ps); +} + +static ssize_t +process_file_gc_environ (struct proc_stat *ps, char **contents) +{ + *contents = proc_stat_env(ps); + return proc_stat_env_len(ps); } static ssize_t @@ -105,17 +112,18 @@ process_file_gc_stat (struct proc_stat *ps, char **contents) 0, 0, /* no such thing as a major:minor for ctty */ 0, /* no such thing as CLONE_* flags on Hurd */ 0L, 0L, 0L, 0L, /* TASK_EVENTS_INFO is unavailable on GNU Mach */ - sc_tv (thbi->user_time), sc_tv (thbi->system_time), + (long unsigned) timeval_jiffies (thbi->user_time), + (long unsigned) timeval_jiffies (thbi->system_time), 0L, 0L, /* cumulative time for children */ MACH_PRIORITY_TO_NICE(thbi->base_priority) + 20, MACH_PRIORITY_TO_NICE(thbi->base_priority), pi->nthreads, 0L, - jiff_tv (thbi->creation_time), /* FIXME: ... since boot */ - (long unsigned int) tbi->virtual_size, - (long unsigned int) tbi->resident_size / PAGE_SIZE, 0L, + timeval_jiffies (thbi->creation_time), /* FIXME: ... since boot */ + (long unsigned) tbi->virtual_size, + (long unsigned) tbi->resident_size / PAGE_SIZE, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, - (long unsigned int) proc_stat_thread_rpc (ps), /* close enough */ + (long unsigned) proc_stat_thread_rpc (ps), /* close enough */ 0L, 0L, 0, 0, @@ -127,6 +135,7 @@ static ssize_t process_file_gc_statm (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); + return asprintf (contents, "%lu %lu 0 0 0 0 0\n", tbi->virtual_size / sysconf(_SC_PAGE_SIZE), @@ -137,6 +146,7 @@ static ssize_t process_file_gc_status (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); + return asprintf (contents, "Name:\t%s\n" "State:\t%s\n" @@ -165,59 +175,70 @@ process_file_gc_status (struct proc_stat *ps, char **contents) proc_stat_num_threads (ps)); } + +/* Implementation of the file nodes. */ -/* Describes a file in a process directory. This is used as an "entry hook" - * for our procfs_dir entry table, passed to process_file_make_node. */ +/* Describes a file in the process directories. This structure is + filled in as an "entry hook" in our procfs_dir entry table and is + passed to the process_file_make_node function defined below. */ struct process_file_desc { /* The proc_stat information required to get the contents of this file. */ ps_flags_t needs; - /* Once we have acquired the necessary information, there can be only - memory allocation errors, hence this simplified signature. */ + /* Content generator to use for this file. Once we have acquired the + necessary information, there can be only memory allocation errors, + hence this simplified signature. */ ssize_t (*get_contents) (struct proc_stat *ps, char **contents); - /* The cmdline and environ contents don't need any cleaning since they are - part of a proc_stat structure. */ + /* The cmdline and environ contents don't need any cleaning since they + point directly into the proc_stat structure. */ int no_cleanup; /* If specified, the file mode to be set with procfs_node_chmod(). */ mode_t mode; }; -/* Information associated to an actual file node. */ struct process_file_node { const struct process_file_desc *desc; struct proc_stat *ps; }; +/* FIXME: lock the parent! */ static error_t process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) { struct process_file_node *file = hook; error_t err; + /* Fetch the required information. */ err = proc_stat_set_flags (file->ps, file->desc->needs); if (err) return EIO; if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) return EIO; + /* Call the actual content generator (see the definitions below). */ *contents_len = file->desc->get_contents (file->ps, contents); return 0; } +static void +process_file_cleanup_contents (void *hook, char *contents, ssize_t len) +{ + struct process_file_node *file = hook; + + if (! file->desc->no_cleanup) + free (contents); +} + static struct node * process_file_make_node (void *dir_hook, const void *entry_hook) { static const struct procfs_node_ops ops = { .get_contents = process_file_get_contents, - .cleanup_contents = procfs_cleanup_contents_with_free, - .cleanup = free, - }; - static const struct procfs_node_ops ops_no_cleanup = { - .get_contents = process_file_get_contents, + .cleanup_contents = process_file_cleanup_contents, .cleanup = free, }; struct process_file_node *f; @@ -230,7 +251,7 @@ process_file_make_node (void *dir_hook, const void *entry_hook) f->desc = entry_hook; f->ps = dir_hook; - np = procfs_make_node (f->desc->no_cleanup ? &ops_no_cleanup : &ops, f); + np = procfs_make_node (&ops, f); if (! np) return NULL; @@ -241,6 +262,18 @@ process_file_make_node (void *dir_hook, const void *entry_hook) return np; } +/* Stat needs its own constructor in oreder to set its mode according to + the --stat-mode command-line option. */ +static struct node * +process_stat_make_node (void *dir_hook, const void *entry_hook) +{ + struct node *np = process_file_make_node (dir_hook, entry_hook); + if (np) procfs_node_chmod (np, opt_stat_mode); + return np; +} + + +/* Implementation of the process directory per se. */ static struct procfs_dir_entry entries[] = { { @@ -261,22 +294,22 @@ static struct procfs_dir_entry entries[] = { }, }, { - /* Beware of the hack below, which requires this to be entries[2]. */ .name = "stat", .hook = & (struct process_file_desc) { .get_contents = process_file_gc_stat, .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC | PSTAT_THREAD_WAIT, - .mode = 0400, }, + .ops = { + .make_node = process_stat_make_node, + } }, { .name = "statm", .hook = & (struct process_file_desc) { .get_contents = process_file_gc_statm, .needs = PSTAT_TASK_BASIC, - .mode = 0444, }, }, { @@ -285,7 +318,6 @@ static struct procfs_dir_entry entries[] = { .get_contents = process_file_gc_status, .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS, - .mode = 0444, }, }, {} @@ -319,6 +351,10 @@ 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; + /* FIXME: have a separate proc_desc structure for each file, so this can be + accessed in a more robust and straightforward way. */ + ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; + *np = procfs_dir_make_node (&dir_ops, ps); if (! *np) return ENOMEM; -- cgit v1.2.3 From f3bac2c0691d9bd5c89de206faa809cd34d86c35 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 19:04:48 +0000 Subject: Fix the global idle time * rootdir.c: Replace INIT_PID by KERNEL_PID, for boot time and idle time purposes. (get_idletime): New function, queries the kernel's idle thread. (rootdir_gc_uptime, rootdir_gc_stat): Use the new function and provide the real idle time. --- rootdir.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/rootdir.c b/rootdir.c index dcee9a54..728008e5 100644 --- a/rootdir.c +++ b/rootdir.c @@ -16,15 +16,16 @@ using them would require locking and as a consequence it would be more complicated, not simpler. */ -#define INIT_PID 1 +#define KERNEL_PID 2 +/* We get the boot time by using that of the kernel process. */ static error_t get_boottime (struct ps_context *pc, struct timeval *tv) { struct proc_stat *ps; error_t err; - err = _proc_stat_create (INIT_PID, pc, &ps); + err = _proc_stat_create (KERNEL_PID, pc, &ps); if (err) return err; @@ -43,6 +44,63 @@ get_boottime (struct ps_context *pc, struct timeval *tv) return err; } +/* We get the idle time by querying the kernel's idle thread. */ +static error_t +get_idletime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps, *pst; + thread_basic_info_t tbi; + error_t err; + int i; + + err = _proc_stat_create (KERNEL_PID, pc, &ps); + if (err) + return err; + + pst = NULL, tbi = NULL; + + err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); + if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) + { + err = EIO; + goto out; + } + + /* Look for the idle thread */ + for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE); i++) + { + if (pst) + _proc_stat_free (pst); + + pst = NULL, tbi = NULL; + if (i >= proc_stat_num_threads (ps)) + { + err = ESRCH; + goto out; + } + + err = proc_stat_thread_create (ps, i, &pst); + if (err) + continue; + + err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC); + if (err || ! (proc_stat_flags (pst) & PSTAT_THREAD_BASIC)) + continue; + + tbi = proc_stat_thread_basic_info (pst); + } + + /* We found it! */ + tv->tv_sec = tbi->system_time.seconds; + tv->tv_usec = tbi->system_time.microseconds; + err = 0; + +out: + if (pst) _proc_stat_free (pst); + _proc_stat_free (ps); + return err; +} + static error_t rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { @@ -65,8 +123,8 @@ rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) static error_t rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) { - struct timeval time, boottime; - double up_secs; + struct timeval time, boottime, idletime; + double up_secs, idle_secs; error_t err; err = gettimeofday (&time, NULL); @@ -77,15 +135,20 @@ rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) if (err) return err; + err = get_idletime (hook, &idletime); + if (err) + return err; + timersub (&time, &boottime, &time); up_secs = time.tv_sec + time.tv_usec / 1000000.; + idle_secs = idletime.tv_sec + idletime.tv_usec / 1000000.; /* The second field is the total idle time. As far as I know we don't keep track of it. However, procps uses it to compute "USER_HZ", and proc(5) specifies that it should be equal to USER_HZ times the idle value in ticks from /proc/stat. So we assume a completely idle system both here and there to make that work. */ - *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, up_secs); + *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); return 0; } @@ -93,9 +156,9 @@ rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) static error_t rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) { - struct timeval boottime, time; + struct timeval boottime, time, idletime; struct vm_statistics vmstats; - unsigned long up_ticks; + unsigned long up_ticks, idle_ticks; error_t err; err = gettimeofday (&time, NULL); @@ -106,23 +169,26 @@ rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) if (err) return err; + err = get_idletime (hook, &idletime); + if (err) + return err; + err = vm_statistics (mach_task_self (), &vmstats); if (err) return EIO; timersub (&time, &boottime, &time); up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); + idle_ticks = opt_clk_tck * (idletime.tv_sec + idletime.tv_usec / 1000000.); *contents_len = asprintf (contents, - /* Does Mach keeps track of any of this? */ - "cpu 0 0 0 %lu 0 0 0 0 0\n" - "cpu0 0 0 0 %lu 0 0 0 0 0\n" + "cpu %lu 0 0 %lu 0 0 0 0 0\n" + "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" "intr 0\n" - /* This we know. */ "page %d %d\n" "btime %lu\n", - up_ticks, - up_ticks, + up_ticks - idle_ticks, idle_ticks, + up_ticks - idle_ticks, idle_ticks, vmstats.pageins, vmstats.pageouts, boottime.tv_sec); -- cgit v1.2.3 From 59b3864bbd09e81df1a5934a02d8af7f4eec0b28 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 20:21:52 +0000 Subject: Make sure the clock never runs backwards. * process.c, rootdir.c: When converting timeval structures into seconds or jiffies, make sure that floating point rounding errors don't make the clock the result jump backwards on second boundaries. --- process.c | 4 ++-- rootdir.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/process.c b/process.c index 1f9d5781..68e7bc1d 100644 --- a/process.c +++ b/process.c @@ -58,8 +58,8 @@ static const char *state_string (struct proc_stat *ps) static long long int timeval_jiffies (time_value_t tv) { - double secs = tv.seconds + tv.microseconds / 1000000.; - return secs * opt_clk_tck; + double secs = tv.seconds * 1000000. + tv.microseconds; + return secs * opt_clk_tck / 1000000.; } /* Actual content generators */ diff --git a/rootdir.c b/rootdir.c index 728008e5..c6ddd87c 100644 --- a/rootdir.c +++ b/rootdir.c @@ -140,8 +140,8 @@ rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) return err; timersub (&time, &boottime, &time); - up_secs = time.tv_sec + time.tv_usec / 1000000.; - idle_secs = idletime.tv_sec + idletime.tv_usec / 1000000.; + up_secs = (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; + idle_secs = (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; /* The second field is the total idle time. As far as I know we don't keep track of it. However, procps uses it to compute "USER_HZ", and @@ -178,8 +178,8 @@ rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) return EIO; timersub (&time, &boottime, &time); - up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); - idle_ticks = opt_clk_tck * (idletime.tv_sec + idletime.tv_usec / 1000000.); + up_ticks = opt_clk_tck * (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; + idle_ticks = opt_clk_tck * (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; *contents_len = asprintf (contents, "cpu %lu 0 0 %lu 0 0 0 0 0\n" -- cgit v1.2.3 From c9bfe14f2e5e09b1692ae4a120de1df2cf835d05 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 20:43:16 +0000 Subject: Refresh nodes when they're read from the start. This is necessary for top, for instance, which keeps some files open and re-reads them regularly. As an extra bonus we can drop the refresh hack. * procfs.c, procfs.h: Remove the refresh hack. (procfs_refresh): New function, invalidates the cached contents. * netfs.c (netfs_attempt_read, netfs_get_dirents): Call procfs_refresh when the read is from offset 0. * proclist.c (proclist_make_node): Remove the refresh hack. * dircat.c (dircat_make_node): Likewise. (dircat_get_contents): Use procfs_refresh to avoid keeping old data from the component nodes. --- dircat.c | 5 +++-- netfs.c | 6 ++++++ procfs.c | 19 +++++++++---------- procfs.h | 13 +++++-------- proclist.c | 1 - 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/dircat.c b/dircat.c index d043486d..d24e8345 100644 --- a/dircat.c +++ b/dircat.c @@ -23,6 +23,9 @@ dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) char *subcon; ssize_t sublen; + /* Make sure we're not getting some old stuff. */ + procfs_refresh (dcn->dirs[i]); + err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); if (err) { @@ -83,8 +86,6 @@ dircat_make_node (struct node *const *dirs, int num_dirs) .cleanup_contents = procfs_cleanup_contents_with_free, .lookup = dircat_lookup, .cleanup = dircat_cleanup, - /* necessary so that it propagates to proclist */ - .enable_refresh_hack_and_break_readdir = 1, }; struct dircat_node *dcn; int i; diff --git a/netfs.c b/netfs.c index e41e0623..02a4bf54 100644 --- a/netfs.c +++ b/netfs.c @@ -57,6 +57,9 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *np, ssize_t contents_len; error_t err; + if (offset == 0) + procfs_refresh (np); + err = procfs_get_contents (np, &contents, &contents_len); if (err) return err; @@ -139,6 +142,9 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, char *contents; ssize_t contents_len; error_t err; + + if (entry == 0) + procfs_refresh (dir); err = procfs_get_contents (dir, &contents, &contents_len); if (err) diff --git a/procfs.c b/procfs.c index 6d15e4fd..f7b28347 100644 --- a/procfs.c +++ b/procfs.c @@ -111,14 +111,6 @@ procfs_make_ino (struct node *np, const char *filename) error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) { - if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) - { - if (np->nn->ops->cleanup_contents) - np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, - np->nn->contents_len); - np->nn->contents = NULL; - } - if (! np->nn->contents && np->nn->ops->get_contents) { char *contents; @@ -141,6 +133,14 @@ error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) return 0; } +void procfs_refresh (struct node *np) +{ + if (np->nn->contents && np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); + + np->nn->contents = NULL; +} + error_t procfs_lookup (struct node *np, const char *name, struct node **npp) { error_t err = ENOENT; @@ -172,8 +172,7 @@ error_t procfs_lookup (struct node *np, const char *name, struct node **npp) void procfs_cleanup (struct node *np) { - if (np->nn->contents && np->nn->ops->cleanup_contents) - np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); + procfs_refresh (np); if (np->nn->ops->cleanup) np->nn->ops->cleanup (np->nn->hook); diff --git a/procfs.h b/procfs.h index e8ef18b3..a9665f97 100644 --- a/procfs.h +++ b/procfs.h @@ -32,14 +32,6 @@ struct procfs_node_ops /* Destroy this node. */ void (*cleanup) (void *hook); - - /* FIXME: This is needed because the root node is persistent, and we - want the list of processes to be updated. However, this means that - readdir() on the root node runs the risk of returning incoherent - results if done in multiple runs and processes are added/removed in - the meantime. The right way to fix this is probably to add a - getroot() user hook function to libnetfs. */ - int enable_refresh_hack_and_break_readdir; }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ @@ -71,6 +63,11 @@ void procfs_node_chtype (struct node *np, mode_t type); corresponding child nodes. */ ino64_t procfs_make_ino (struct node *np, const char *filename); +/* Forget the current cached contents for the node. This is done before reads + from offset 0, to ensure that the data are recent even for utilities such as + top which keep some nodes open. */ +void procfs_refresh (struct node *np); + error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); diff --git a/proclist.c b/proclist.c index 38368feb..fe5d0cff 100644 --- a/proclist.c +++ b/proclist.c @@ -69,7 +69,6 @@ proclist_make_node (struct ps_context *pc) .get_contents = proclist_get_contents, .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, - .enable_refresh_hack_and_break_readdir = 1, }; return procfs_make_node (&ops, pc); } -- cgit v1.2.3 From 2ceddf5449bb99683e096b39120a602a5de6a6ec Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 05:46:49 +0000 Subject: Fix leak in error case * process.c (process_lookup_pid): Fix leak in error case. --- process.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index 68e7bc1d..47e34708 100644 --- a/process.c +++ b/process.c @@ -345,7 +345,10 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) - return EIO; + { + _proc_stat_free (ps); + return EIO; + } /* FIXME: have a separate proc_desc structure for each file, so this can be accessed in a more robust and straightforward way. */ -- cgit v1.2.3 From f708dd1262df8033d4709dbba1da026e8b92f669 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 05:47:08 +0000 Subject: Add a PROFILE mode * Makefile: Change FOOFLAGS defaults to appropriate values when $(PROFILE) is defined. * rootdir.c: Add an "exit" file, which causes exit to be called when looked up, so that profiling data can be written to disk. --- rootdir.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rootdir.c b/rootdir.c index c6ddd87c..204bc02b 100644 --- a/rootdir.c +++ b/rootdir.c @@ -422,6 +422,15 @@ static const struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, +#ifdef PROFILE + /* In order to get a usable gmon.out file, we must apparently use exit(). */ + { + .name = "exit", + .ops = { + .make_node = exit, + }, + }, +#endif {} }; -- cgit v1.2.3 From cc1b191e53042a9938daf92ce84e869dda592868 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 12:03:16 +0000 Subject: Add swap information to the top-level stat file * rootdir.c (rootdir_gc_meminfo): Add swap information. * TODO: Update. --- rootdir.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/rootdir.c b/rootdir.c index 204bc02b..419f774e 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,7 +1,10 @@ #include #include +#include +#include #include #include +#include #include #include #include @@ -101,6 +104,22 @@ out: return err; } +static error_t +get_swapinfo (default_pager_info_t *info) +{ + mach_port_t defpager; + error_t err; + + defpager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); + if (defpager == MACH_PORT_NULL) + return errno; + + err = default_pager_info (defpager, info); + mach_port_deallocate (mach_task_self (), defpager); + + return err; +} + static error_t rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { @@ -223,6 +242,7 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) host_basic_info_data_t hbi; mach_msg_type_number_t cnt; struct vm_statistics vmstats; + default_pager_info_t swap; error_t err; err = vm_statistics (mach_task_self (), &vmstats); @@ -234,6 +254,10 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) if (err) return err; + err = get_swapinfo (&swap); + if (err) + return err; + assert (cnt == HOST_BASIC_INFO_COUNT); *contents_len = asprintf (contents, "MemTotal: %14lu kB\n" @@ -241,13 +265,17 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) "Active: %14lu kB\n" "Inactive: %14lu kB\n" "Mlocked: %14lu kB\n" + "SwapTotal:%14lu kB\n" + "SwapFree: %14lu kB\n" , /* TODO: check that these are really 1024-bytes kBs. */ (long unsigned) hbi.memory_size / 1024, (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, - (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024); + (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024, + (long unsigned) swap.dpi_total_space / 1024, + (long unsigned) swap.dpi_free_space / 1024); return 0; } -- cgit v1.2.3 From 3a19c9fb17d79c83e30bf0739246956aea80f168 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 19:04:24 +0000 Subject: Add some comments in rootdir.c * rootdir.c: Add page breaks to separate sections and add header comments for them. --- rootdir.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/rootdir.c b/rootdir.c index 419f774e..2a2ab5a5 100644 --- a/rootdir.c +++ b/rootdir.c @@ -21,6 +21,9 @@ #define KERNEL_PID 2 + +/* Helper functions */ + /* We get the boot time by using that of the kernel process. */ static error_t get_boottime (struct ps_context *pc, struct timeval *tv) @@ -120,6 +123,9 @@ get_swapinfo (default_pager_info_t *info) return err; } + +/* Content generators */ + static error_t rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { @@ -137,8 +143,6 @@ rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) return 0; } -/* Uptime -- we use the start time of init to deduce it. This is probably a bit - fragile, as any clock update will make the result inaccurate. */ static error_t rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) { @@ -373,10 +377,16 @@ rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) return 0; } + +/* Glue logic and entries table */ static struct node * rootdir_file_make_node (void *dir_hook, const void *entry_hook) { + /* The entry hook we use is actually a procfs_node_ops for the file to be + created. The hook associated to these newly created files (and passed + to the generators above as a consequence) is always the same global + ps_context, which we get from rootdir_make_node as the directory hook. */ return procfs_make_node (entry_hook, dir_hook); } -- cgit v1.2.3 From 33210c5802462b93f0e6bc16d7dccb042ec76798 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 26 Aug 2010 23:03:40 +0000 Subject: Add copyright notices * dircat.c, dircat.h, main.c, main.h, netfs.c, process.c, process.h, procfs.c, procfs.h, procfs_dir.c, procfs_dir.h, proclist.c, proclist.h, rootdir.c, rootdir.h: Add copyright notices. --- dircat.c | 19 +++++++++++++++++++ dircat.h | 19 +++++++++++++++++++ main.c | 19 +++++++++++++++++++ main.h | 19 +++++++++++++++++++ netfs.c | 19 +++++++++++++++++++ process.c | 19 +++++++++++++++++++ process.h | 19 +++++++++++++++++++ procfs.c | 19 +++++++++++++++++++ procfs.h | 19 +++++++++++++++++++ procfs_dir.c | 19 +++++++++++++++++++ procfs_dir.h | 19 +++++++++++++++++++ proclist.c | 19 +++++++++++++++++++ proclist.h | 19 +++++++++++++++++++ rootdir.c | 19 +++++++++++++++++++ rootdir.h | 19 +++++++++++++++++++ 15 files changed, 285 insertions(+) diff --git a/dircat.c b/dircat.c index d24e8345..5a60899a 100644 --- a/dircat.c +++ b/dircat.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, concatenation of two directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include "procfs.h" diff --git a/dircat.h b/dircat.h index 951d2021..4177b384 100644 --- a/dircat.h +++ b/dircat.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, concatenation of two directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* Append the contents of NUM_DIRS directories. DIRS is an array of directory nodes. One reference is consumed for each of them. If a memory allocation error occurs, or if one of the directories is a diff --git a/main.c b/main.c index eaab9867..94fc227d 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, main program. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/main.h b/main.h index 28d1b023..4e28b7eb 100644 --- a/main.h +++ b/main.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, command-line options set by main.c. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* Startup options */ extern int opt_clk_tck; extern mode_t opt_stat_mode; diff --git a/netfs.c b/netfs.c index 02a4bf54..24a6603f 100644 --- a/netfs.c +++ b/netfs.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, interface with libnetfs. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/process.c b/process.c index 47e34708..6652a4e9 100644 --- a/process.c +++ b/process.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, implementation of process directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/process.h b/process.h index 8c2ee636..b230a281 100644 --- a/process.h +++ b/process.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, implementation of process directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include /* Create a node for a directory representing the given PID, as published by diff --git a/procfs.c b/procfs.c index f7b28347..ee52de7a 100644 --- a/procfs.c +++ b/procfs.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, basic infrastructure. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/procfs.h b/procfs.h index a9665f97..64782ec4 100644 --- a/procfs.h +++ b/procfs.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, basic infrastructure. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include diff --git a/procfs_dir.c b/procfs_dir.c index dfe30a26..c250aa48 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include "procfs.h" diff --git a/procfs_dir.h b/procfs_dir.h index 12e99d2a..94c5b019 100644 --- a/procfs_dir.h +++ b/procfs_dir.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* 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 diff --git a/proclist.c b/proclist.c index fe5d0cff..58b942dc 100644 --- a/proclist.c +++ b/proclist.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, list of processes as a directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/proclist.h b/proclist.h index ce69dde3..bfe95b3d 100644 --- a/proclist.h +++ b/proclist.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, list of processes as a directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include struct node * diff --git a/rootdir.c b/rootdir.c index 2a2ab5a5..da068cf4 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/rootdir.h b/rootdir.h index 0caee258..6980da8f 100644 --- a/rootdir.h +++ b/rootdir.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include struct node * -- cgit v1.2.3 From 86935e7331b64d1ef3234a01ddca08be336162fb Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 1 Sep 2010 10:19:49 +0000 Subject: Use the user-provided kernel PID for uptime * rootdir.c (get_boottime, get_idletime): replace KERNEL_PID with the opt_kernel_pid command-line option. * main.c (argp): Document the change. --- main.c | 3 ++- rootdir.c | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index 94fc227d..3a976ccc 100644 --- a/main.c +++ b/main.c @@ -114,7 +114,8 @@ struct argp argp = { "purposes. If PID is omitted, \"self\" will point to init. " "(default: no self link)" }, { "kernel-process", 'k', "PID", 0, - "Process identifier for the kernel, used to retreive its command line " + "Process identifier for the kernel, used to retreive its command " + "line, as well as the global up and idle times. " "(default: 2)" }, { "compatible", 'c', NULL, 0, "Try to be compatible with the Linux procps utilities. " diff --git a/rootdir.c b/rootdir.c index da068cf4..15ef8bce 100644 --- a/rootdir.c +++ b/rootdir.c @@ -38,8 +38,6 @@ using them would require locking and as a consequence it would be more complicated, not simpler. */ -#define KERNEL_PID 2 - /* Helper functions */ @@ -50,7 +48,7 @@ get_boottime (struct ps_context *pc, struct timeval *tv) struct proc_stat *ps; error_t err; - err = _proc_stat_create (KERNEL_PID, pc, &ps); + err = _proc_stat_create (opt_kernel_pid, pc, &ps); if (err) return err; @@ -78,7 +76,7 @@ get_idletime (struct ps_context *pc, struct timeval *tv) error_t err; int i; - err = _proc_stat_create (KERNEL_PID, pc, &ps); + err = _proc_stat_create (opt_kernel_pid, pc, &ps); if (err) return err; -- cgit v1.2.3 From ff7be39f3df8234e9f64b9098c76493cf0977439 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 1 Sep 2010 10:49:11 +0000 Subject: Use 2 instead of 42 as the parent inode number * procfs.c (procfs_make_ino): Use 2 rather than 42 as a temporary hack, since 2 is the root's inode with ext2fs. --- procfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procfs.c b/procfs.c index ee52de7a..ae5a6769 100644 --- a/procfs.c +++ b/procfs.c @@ -114,7 +114,7 @@ procfs_make_ino (struct node *np, const char *filename) if (! strcmp (filename, ".")) return np->nn_stat.st_ino; if (! strcmp (filename, "..")) - return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 42; + return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 2; assert (sizeof np->nn_stat.st_ino > sizeof x); memcpy (x, &np->nn_stat.st_ino, sizeof x); -- cgit v1.2.3 From 494db38b7df523f566957ef5aa56b9b3d2af6764 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 26 Sep 2010 21:12:48 +0000 Subject: Move files to procfs to merge along hurd --- dircat.c | 128 ------------- dircat.h | 29 --- main.c | 190 -------------------- main.h | 25 --- netfs.c | 445 ---------------------------------------------- process.c | 387 ---------------------------------------- process.h | 27 --- procfs.c | 203 --------------------- procfs.h | 93 ---------- procfs/Makefile | 26 +++ procfs/TODO | 24 +++ procfs/dircat.c | 128 +++++++++++++ procfs/dircat.h | 29 +++ procfs/main.c | 190 ++++++++++++++++++++ procfs/main.h | 25 +++ procfs/netfs.c | 445 ++++++++++++++++++++++++++++++++++++++++++++++ procfs/process.c | 387 ++++++++++++++++++++++++++++++++++++++++ procfs/process.h | 27 +++ procfs/procfs.c | 203 +++++++++++++++++++++ procfs/procfs.h | 93 ++++++++++ procfs/procfs_dir.c | 134 ++++++++++++++ procfs/procfs_dir.h | 63 +++++++ procfs/proclist.c | 94 ++++++++++ procfs/proclist.h | 23 +++ procfs/rootdir.c | 503 ++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs/rootdir.h | 23 +++ procfs_dir.c | 134 -------------- procfs_dir.h | 63 ------- proclist.c | 94 ---------- proclist.h | 23 --- rootdir.c | 503 ---------------------------------------------------- rootdir.h | 23 --- 32 files changed, 2417 insertions(+), 2367 deletions(-) delete mode 100644 dircat.c delete mode 100644 dircat.h delete mode 100644 main.c delete mode 100644 main.h delete mode 100644 netfs.c delete mode 100644 process.c delete mode 100644 process.h delete mode 100644 procfs.c delete mode 100644 procfs.h create mode 100644 procfs/Makefile create mode 100644 procfs/TODO create mode 100644 procfs/dircat.c create mode 100644 procfs/dircat.h create mode 100644 procfs/main.c create mode 100644 procfs/main.h create mode 100644 procfs/netfs.c create mode 100644 procfs/process.c create mode 100644 procfs/process.h create mode 100644 procfs/procfs.c create mode 100644 procfs/procfs.h create mode 100644 procfs/procfs_dir.c create mode 100644 procfs/procfs_dir.h create mode 100644 procfs/proclist.c create mode 100644 procfs/proclist.h create mode 100644 procfs/rootdir.c create mode 100644 procfs/rootdir.h delete mode 100644 procfs_dir.c delete mode 100644 procfs_dir.h delete mode 100644 proclist.c delete mode 100644 proclist.h delete mode 100644 rootdir.c delete mode 100644 rootdir.h diff --git a/dircat.c b/dircat.c deleted file mode 100644 index 5a60899a..00000000 --- a/dircat.c +++ /dev/null @@ -1,128 +0,0 @@ -/* Hurd /proc filesystem, concatenation of two directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include "procfs.h" - -struct dircat_node -{ - int num_dirs; - struct node *dirs[0]; -}; - -static error_t -dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) -{ - struct dircat_node *dcn = hook; - int i, sz, pos; - error_t err; - - pos = 0; - *contents = malloc (sz = 512); - - for (i=0; i < dcn->num_dirs; i++) - { - char *subcon; - ssize_t sublen; - - /* Make sure we're not getting some old stuff. */ - procfs_refresh (dcn->dirs[i]); - - err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); - if (err) - { - free (*contents); - *contents = NULL; - return err; - } - - while (pos + sublen > sz) - *contents = realloc (*contents, sz *= 2); - - memcpy (*contents + pos, subcon, sublen); - pos += sublen; - } - - *contents_len = pos; - return 0; -} - -static error_t -dircat_lookup (void *hook, const char *name, struct node **np) -{ - struct dircat_node *dcn = hook; - error_t err; - int i; - - err = ENOENT; - for (i=0; err && i < dcn->num_dirs; i++) - err = procfs_lookup (dcn->dirs[i], name, np); - - return err; -} - -static void -dircat_release_dirs (struct node *const *dirs, int num_dirs) -{ - int i; - - for (i=0; i < num_dirs; i++) - if (dirs[i]) - netfs_nrele (dirs[i]); -} - -static void -dircat_cleanup (void *hook) -{ - struct dircat_node *dcn = hook; - - dircat_release_dirs (dcn->dirs, dcn->num_dirs); - free (dcn); -} - -struct node * -dircat_make_node (struct node *const *dirs, int num_dirs) -{ - static struct procfs_node_ops ops = { - .get_contents = dircat_get_contents, - .cleanup_contents = procfs_cleanup_contents_with_free, - .lookup = dircat_lookup, - .cleanup = dircat_cleanup, - }; - struct dircat_node *dcn; - int i; - - for (i=0; i < num_dirs; i++) - if (! dirs[i]) - goto fail; - - dcn = malloc (sizeof *dcn + num_dirs * sizeof dcn->dirs[0]); - if (! dcn) - goto fail; - - dcn->num_dirs = num_dirs; - memcpy (dcn->dirs, dirs, num_dirs * sizeof dcn->dirs[0]); - return procfs_make_node (&ops, dcn); - -fail: - dircat_release_dirs (dirs, num_dirs); - return NULL; -} - diff --git a/dircat.h b/dircat.h deleted file mode 100644 index 4177b384..00000000 --- a/dircat.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Hurd /proc filesystem, concatenation of two directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Append the contents of NUM_DIRS directories. DIRS is an array of - directory nodes. One reference is consumed for each of them. If a - memory allocation error occurs, or if one of the directories is a - NULL pointer, the references are dropped immediately and NULL is - returned. The given DIRS array is duplicated and can therefore be - allocated on the caller's stack. Strange things will happen if some - elements of DIRS have entries with the same name or if one of them is - not a directory. */ -struct node * -dircat_make_node (struct node *const *dirs, int num_dirs); diff --git a/main.c b/main.c deleted file mode 100644 index 3a976ccc..00000000 --- a/main.c +++ /dev/null @@ -1,190 +0,0 @@ -/* Hurd /proc filesystem, main program. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include -#include "procfs.h" -#include "proclist.h" -#include "rootdir.h" -#include "dircat.h" -#include "main.h" - -/* Command-line options */ -int opt_clk_tck; -mode_t opt_stat_mode; -pid_t opt_fake_self; -pid_t opt_kernel_pid; -uid_t opt_anon_owner; - -static error_t -argp_parser (int key, char *arg, struct argp_state *state) -{ - struct passwd *pw; - char *endp; - - switch (key) - { - case 'h': - opt_clk_tck = strtol (arg, &endp, 0); - if (*endp || ! *arg || opt_clk_tck <= 0) - error (1, 0, "--clk-tck: HZ should be a positive integer"); - break; - - case 's': - opt_stat_mode = strtol (arg, &endp, 8); - if (*endp || ! *arg || opt_stat_mode & ~07777) - error (1, 0, "--stat-mode: MODE should be an octal mode"); - break; - - case 'S': - if (arg) - { - opt_fake_self = strtol (arg, &endp, 0); - if (*endp || ! *arg) - error (1, 0, "--fake-self: PID must be an integer"); - } - else - opt_fake_self = 1; - break; - - case 'k': - opt_kernel_pid = strtol (arg, &endp, 0); - if (*endp || ! *arg || (signed) opt_kernel_pid < 0) - error (1, 0, "--kernel-process: PID must be a positive integer"); - break; - - case 'c': - opt_clk_tck = 100; - opt_stat_mode = 0444; - opt_fake_self = 1; - break; - - case 'a': - pw = getpwnam (arg); - if (pw) - { - opt_anon_owner = pw->pw_uid; - break; - } - - opt_anon_owner = strtol (arg, &endp, 0); - if (*endp || ! *arg || (signed) opt_anon_owner < 0) - error(1, 0, "--anonymous-owner: USER should be the a user name " - "or a numeric UID."); - break; - } - - return 0; -} - -struct argp argp = { - .options = (struct argp_option []) { - { "clk-tck", 'h', "HZ", 0, - "Unit used for the values expressed in system clock ticks " - "(default: sysconf(_SC_CLK_TCK))" }, - { "stat-mode", 's', "MODE", 0, - "The [pid]/stat file publishes information which on Hurd is only " - "available to the process owner. " - "You can use this option to override its mode to be more permissive " - "for compatibility purposes. " - "(default: 0400)" }, - { "fake-self", 'S', "PID", OPTION_ARG_OPTIONAL, - "Provide a fake \"self\" symlink to the given PID, for compatibility " - "purposes. If PID is omitted, \"self\" will point to init. " - "(default: no self link)" }, - { "kernel-process", 'k', "PID", 0, - "Process identifier for the kernel, used to retreive its command " - "line, as well as the global up and idle times. " - "(default: 2)" }, - { "compatible", 'c', NULL, 0, - "Try to be compatible with the Linux procps utilities. " - "Currently equivalent to -h 100 -s 0444 -S 1." }, - { "anonymous-owner", 'a', "USER", 0, - "Make USER the owner of files related to processes without one. " - "Be aware that USER will be granted access to the environment and " - "other sensitive information about the processes in question. " - "(default: use uid 0)" }, - {} - }, - .parser = argp_parser, - .doc = "A virtual filesystem emulating the Linux procfs.", - .children = (struct argp_child []) { - { &netfs_std_startup_argp, }, - {} - }, -}; - -error_t -root_make_node (struct ps_context *pc, struct node **np) -{ - struct node *root_dirs[] = { - proclist_make_node (pc), - rootdir_make_node (pc), - }; - - *np = dircat_make_node (root_dirs, sizeof root_dirs / sizeof root_dirs[0]); - if (! *np) - return ENOMEM; - - /* Since this one is not created through proc_lookup(), we have to affect an - inode number to it. */ - (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; - - return 0; -} - -int main (int argc, char **argv) -{ - struct ps_context *pc; - mach_port_t bootstrap; - error_t err; - - opt_clk_tck = sysconf(_SC_CLK_TCK); - opt_stat_mode = 0400; - opt_fake_self = -1; - opt_kernel_pid = 2; - opt_anon_owner = 0; - err = argp_parse (&argp, argc, argv, 0, 0, 0); - if (err) - error (1, err, "Could not parse command line"); - - err = ps_context_create (getproc (), &pc); - if (err) - error (1, err, "Could not create libps context"); - - task_get_bootstrap_port (mach_task_self (), &bootstrap); - if (bootstrap == MACH_PORT_NULL) - error (1, 0, "Must be started as a translator"); - - netfs_init (); - err = root_make_node (pc, &netfs_root_node); - if (err) - error (1, err, "Could not create the root node"); - - netfs_startup (bootstrap, 0); - netfs_server_loop (); - - assert (0 /* netfs_server_loop returned after all */); -} - diff --git a/main.h b/main.h deleted file mode 100644 index 4e28b7eb..00000000 --- a/main.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Hurd /proc filesystem, command-line options set by main.c. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Startup options */ -extern int opt_clk_tck; -extern mode_t opt_stat_mode; -extern pid_t opt_fake_self; -extern pid_t opt_kernel_pid; -extern uid_t opt_anon_owner; diff --git a/netfs.c b/netfs.c deleted file mode 100644 index 24a6603f..00000000 --- a/netfs.c +++ /dev/null @@ -1,445 +0,0 @@ -/* Hurd /proc filesystem, interface with libnetfs. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include "procfs.h" - -#define PROCFS_SERVER_NAME "procfs" -#define PROCFS_SERVER_VERSION "0.1.0" -#define PROCFS_MAXSYMLINKS 16 - - -/* Interesting libnetfs callback functions. */ - -/* The user must define this variable. Set this to the name of the - filesystem server. */ -char *netfs_server_name = PROCFS_SERVER_NAME; - -/* The user must define this variables. Set this to be the server - version number. */ -char *netfs_server_version = PROCFS_SERVER_VERSION; - -/* Maximum number of symlinks to follow before returning ELOOP. */ -int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; - -/* The user must define this function. Make sure that NP->nn_stat is - filled with the most current information. CRED identifies the user - responsible for the operation. NP is locked. */ -error_t netfs_validate_stat (struct node *np, struct iouser *cred) -{ - char *contents; - ssize_t contents_len; - error_t err; - - /* Only symlinks need to have their size filled, before a read is - attempted. */ - if (! S_ISLNK (np->nn_stat.st_mode)) - return 0; - - err = procfs_get_contents (np, &contents, &contents_len); - if (err) - return err; - - np->nn_stat.st_size = contents_len; - return 0; -} - -/* The user must define this function. Read from the locked file NP - for user CRED starting at OFFSET and continuing for up to *LEN - bytes. Put the data at DATA. Set *LEN to the amount successfully - read upon return. */ -error_t netfs_attempt_read (struct iouser *cred, struct node *np, - loff_t offset, size_t *len, void *data) -{ - char *contents; - ssize_t contents_len; - error_t err; - - if (offset == 0) - procfs_refresh (np); - - err = procfs_get_contents (np, &contents, &contents_len); - if (err) - return err; - - contents += offset; - contents_len -= offset; - - if (*len > contents_len) - *len = contents_len; - if (*len < 0) - *len = 0; - - memcpy (data, contents, *len); - return 0; -} - -/* The user must define this function. Read the contents of locked - node NP (a symlink), for USER, into BUF. */ -error_t netfs_attempt_readlink (struct iouser *user, struct node *np, - char *buf) -{ - char *contents; - ssize_t contents_len; - error_t err; - - err = procfs_get_contents (np, &contents, &contents_len); - if (err) - return err; - - assert (contents_len == np->nn_stat.st_size); - memcpy (buf, contents, contents_len); - return 0; -} - -/* Helper function for netfs_get_dirents() below. CONTENTS is an argz - vector of directory entry names, as returned by procfs_get_contents(). - Convert at most NENTRIES of them to dirent structures, put them in - DATA (if not NULL), write the number of entries processed in *AMT and - return the required/used space in DATACNT. */ -static int putentries (char *contents, size_t contents_len, int nentries, - char *data, mach_msg_type_number_t *datacnt) -{ - int i; - - *datacnt = 0; - for (i = 0; contents_len && (nentries < 0 || i < nentries); i++) - { - int namlen = strlen (contents); - int reclen = sizeof (struct dirent) + namlen; - - if (data) - { - struct dirent *d = (struct dirent *) (data + *datacnt); - d->d_fileno = 42; /* XXX */ - d->d_namlen = namlen; - d->d_reclen = reclen; - d->d_type = DT_UNKNOWN; - strcpy (d->d_name, contents); - } - - *datacnt += reclen; - contents += namlen + 1; - contents_len -= namlen + 1; - } - - return i; -} - -/* The user must define this function. Fill the array *DATA of size - BUFSIZE with up to NENTRIES dirents from DIR (which is locked) - starting with entry ENTRY for user CRED. The number of entries in - the array is stored in *AMT and the number of bytes in *DATACNT. - If the supplied buffer is not large enough to hold the data, it - should be grown. */ -error_t netfs_get_dirents (struct iouser *cred, struct node *dir, - int entry, int nentries, char **data, - mach_msg_type_number_t *datacnt, - vm_size_t bufsize, int *amt) -{ - char *contents; - ssize_t contents_len; - error_t err; - - if (entry == 0) - procfs_refresh (dir); - - err = procfs_get_contents (dir, &contents, &contents_len); - if (err) - return err; - - /* We depend on the fact that CONTENTS is terminated. */ - assert (contents_len == 0 || contents[contents_len - 1] == '\0'); - - /* Skip to the first requested entry. */ - while (contents_len && entry--) - { - int ofs = strlen (contents) + 1; - contents += ofs; - contents_len -= ofs; - } - - /* Allocate a buffer if necessary. */ - putentries (contents, contents_len, nentries, NULL, datacnt); - if (bufsize < *datacnt) - { - char *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); - if (n == MAP_FAILED) - return ENOMEM; - - *data = n; - } - - /* Do the actual conversion. */ - *amt = putentries (contents, contents_len, nentries, *data, datacnt); - - return 0; -} - -/* The user must define this function. Lookup NAME in DIR (which is - locked) for USER; set *NP to the found name upon return. If the - name was not found, then return ENOENT. On any error, clear *NP. - (*NP, if found, should be locked and a reference to it generated. - This call should unlock DIR no matter what.) */ -error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, - char *name, struct node **np) -{ - error_t err; - - err = procfs_lookup (dir, name, np); - mutex_unlock (&dir->lock); - - if (! err) - mutex_lock (&(*np)->lock); - - return err; -} - -/* The user must define this function. Node NP has no more references; - free all its associated storage. */ -void netfs_node_norefs (struct node *np) -{ - spin_unlock (&netfs_node_refcnt_lock); - - procfs_cleanup (np); - free (np); - - spin_lock (&netfs_node_refcnt_lock); -} - - -/* Libnetfs callbacks managed with libfshelp. */ - -/* The user must define this function. Locked node NP is being opened - by USER, with FLAGS. NEWNODE is nonzero if we just created this - node. Return an error if we should not permit the open to complete - because of a permission restriction. */ -error_t netfs_check_open_permissions (struct iouser *user, struct node *np, - int flags, int newnode) -{ - error_t err = 0; - if (!err && (flags & O_READ)) - err = fshelp_access (&np->nn_stat, S_IREAD, user); - if (!err && (flags & O_WRITE)) - err = fshelp_access (&np->nn_stat, S_IWRITE, user); - if (!err && (flags & O_EXEC)) - err = fshelp_access (&np->nn_stat, S_IEXEC, user); - return err; -} - -/* The user must define this function. Return the valid access - types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for - locked file NP and user CRED. */ -error_t netfs_report_access (struct iouser *cred, struct node *np, - int *types) -{ - *types = 0; - if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) - *types |= O_READ; - if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) - *types |= O_WRITE; - if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) - *types |= O_EXEC; - return 0; -} - - -/* Trivial or unsupported libnetfs callbacks. */ - -/* The user must define this function. This should attempt a chmod - call for the user specified by CRED on locked node NP, to change - the owner to UID and the group to GID. */ -error_t netfs_attempt_chown (struct iouser *cred, struct node *np, - uid_t uid, uid_t gid) -{ - return EROFS; -} - -/* The user must define this function. This should attempt a chauthor - call for the user specified by CRED on locked node NP, thereby - changing the author to AUTHOR. */ -error_t netfs_attempt_chauthor (struct iouser *cred, struct node *np, - uid_t author) -{ - return EROFS; -} - -/* The user must define this function. This should attempt a chmod - call for the user specified by CRED on locked node NODE, to change - the mode to MODE. Unlike the normal Unix and Hurd meaning of - chmod, this function is also used to attempt to change files into - other types. If such a transition is attempted which is - impossible, then return EOPNOTSUPP. */ -error_t netfs_attempt_chmod (struct iouser *cred, struct node *np, - mode_t mode) -{ - return EROFS; -} - -/* The user must define this function. Attempt to turn locked node NP - (user CRED) into a symlink with target NAME. */ -error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np, - char *name) -{ - return EROFS; -} - -/* The user must define this function. Attempt to turn NODE (user - CRED) into a device. TYPE is either S_IFBLK or S_IFCHR. NP is - locked. */ -error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np, - mode_t type, dev_t indexes) -{ - return EROFS; -} - -/* The user must define this function. This should attempt a chflags - call for the user specified by CRED on locked node NP, to change - the flags to FLAGS. */ -error_t netfs_attempt_chflags (struct iouser *cred, struct node *np, - int flags) -{ - return EROFS; -} - -/* The user must define this function. This should attempt a utimes - call for the user specified by CRED on locked node NP, to change - the atime to ATIME and the mtime to MTIME. If ATIME or MTIME is - null, then set to the current time. */ -error_t netfs_attempt_utimes (struct iouser *cred, struct node *np, - struct timespec *atime, struct timespec *mtime) -{ - return EROFS; -} - -/* The user must define this function. This should attempt to set the - size of the locked file NP (for user CRED) to SIZE bytes long. */ -error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, - loff_t size) -{ - return EROFS; -} - -/* The user must define this function. This should attempt to fetch - filesystem status information for the remote filesystem, for the - user CRED. NP is locked. */ -error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, - fsys_statfsbuf_t *st) -{ - return ENOSYS; -} - -/* The user must define this function. This should sync the locked - file NP completely to disk, for the user CRED. If WAIT is set, - return only after the sync is completely finished. */ -error_t netfs_attempt_sync (struct iouser *cred, struct node *np, - int wait) -{ - return 0; -} - -/* The user must define this function. This should sync the entire - remote filesystem. If WAIT is set, return only after the sync is - completely finished. */ -error_t netfs_attempt_syncfs (struct iouser *cred, int wait) -{ - return 0; -} - -/* The user must define this function. Delete NAME in DIR (which is - locked) for USER. */ -error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, - char *name) -{ - return EROFS; -} - -/* The user must define this function. Attempt to rename the - directory FROMDIR to TODIR. Note that neither of the specific nodes - are locked. */ -error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, - char *fromname, struct node *todir, - char *toname, int excl) -{ - return EROFS; -} - -/* The user must define this function. Attempt to create a new - directory named NAME in DIR (which is locked) for USER with mode - MODE. */ -error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, - char *name, mode_t mode) -{ - return EROFS; -} - -/* The user must define this function. Attempt to remove directory - named NAME in DIR (which is locked) for USER. */ -error_t netfs_attempt_rmdir (struct iouser *user, - struct node *dir, char *name) -{ - return EROFS; -} - - -/* The user must define this function. Create a link in DIR with name - NAME to FILE for USER. Note that neither DIR nor FILE are - locked. If EXCL is set, do not delete the target. Return EEXIST if - NAME is already found in DIR. */ -error_t netfs_attempt_link (struct iouser *user, struct node *dir, - struct node *file, char *name, int excl) -{ - return EROFS; -} - -/* The user must define this function. Attempt to create an anonymous - file related to DIR (which is locked) for USER with MODE. Set *NP - to the returned file upon success. No matter what, unlock DIR. */ -error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, - mode_t mode, struct node **np) -{ - return EROFS; -} - -/* The user must define this function. Attempt to create a file named - NAME in DIR (which is locked) for USER with MODE. Set *NP to the - new node upon return. On any error, clear *NP. *NP should be - locked on success; no matter what, unlock DIR before returning. */ -error_t netfs_attempt_create_file (struct iouser *user, struct node *dir, - char *name, mode_t mode, struct node **np) -{ - return EROFS; -} - -/* The user must define this function. Write to the locked file NP - for user CRED starting at OFSET and continuing for up to *LEN bytes - from DATA. Set *LEN to the amount successfully written upon - return. */ -error_t netfs_attempt_write (struct iouser *cred, struct node *np, - loff_t offset, size_t *len, void *data) -{ - return EROFS; -} - - diff --git a/process.c b/process.c deleted file mode 100644 index 6652a4e9..00000000 --- a/process.c +++ /dev/null @@ -1,387 +0,0 @@ -/* Hurd /proc filesystem, implementation of process directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include -#include "procfs.h" -#include "procfs_dir.h" -#include "process.h" -#include "main.h" - -/* This module implements the process directories and the files they - contain. A libps proc_stat structure is created for each process - node, and is used by the individual file content generators as a - source of information. Each possible file (cmdline, environ, ...) is - decribed in a process_file_desc structure, which specifies which bits - of information (ie. libps flags) it needs, and what function should - be used to generate the file's contents. - - The content generators are defined first, followed by glue logic and - entry table. */ - - -/* Helper functions */ - -static char state_char (struct proc_stat *ps) -{ - int i; - - for (i = 0; (1 << i) & (PSTAT_STATE_P_STATES | PSTAT_STATE_T_STATES); i++) - if (proc_stat_state (ps) & (1 << i)) - return proc_stat_state_tags[i]; - - return '?'; -} - -static const char *state_string (struct proc_stat *ps) -{ - static const char *const state_strings[] = { - "T (stopped)", - "Z (zombie)", - "R (running)", - "H (halted)", - "D (disk sleep)", - "S (sleeping)", - "I (idle)", - NULL - }; - int i; - - for (i = 0; state_strings[i]; i++) - if (proc_stat_state (ps) & (1 << i)) - return state_strings[i]; - - return "? (unknown)"; -} - -static long long int timeval_jiffies (time_value_t tv) -{ - double secs = tv.seconds * 1000000. + tv.microseconds; - return secs * opt_clk_tck / 1000000.; -} - -/* Actual content generators */ - -static ssize_t -process_file_gc_cmdline (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_args(ps); - return proc_stat_args_len(ps); -} - -static ssize_t -process_file_gc_environ (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_env(ps); - return proc_stat_env_len(ps); -} - -static ssize_t -process_file_gc_stat (struct proc_stat *ps, char **contents) -{ - struct procinfo *pi = proc_stat_proc_info (ps); - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); - - /* See proc(5) for more information about the contents of each field for the - Linux procfs. */ - return asprintf (contents, - "%d (%s) %c " /* pid, command, state */ - "%d %d %d " /* ppid, pgid, session */ - "%d %d " /* controling tty stuff */ - "%u " /* flags, as defined by */ - "%lu %lu %lu %lu " /* page fault counts */ - "%lu %lu %ld %ld " /* user/sys times, in sysconf(_SC_CLK_TCK) */ - "%d %d " /* scheduler params (priority, nice) */ - "%d %ld " /* number of threads, [obsolete] */ - "%llu " /* start time since boot (jiffies) */ - "%lu %ld %lu " /* virtual size (bytes), rss (pages), rss lim */ - "%lu %lu %lu %lu %lu " /* some vm addresses (code, stack, sp, pc) */ - "%lu %lu %lu %lu " /* pending, blocked, ignored and caught sigs */ - "%lu " /* wait channel */ - "%lu %lu " /* swap usage (not maintained in Linux) */ - "%d " /* exit signal, to be sent to the parent */ - "%d " /* last processor used */ - "%u %u " /* RT priority and policy */ - "%llu " /* aggregated block I/O delay */ - "\n", - proc_stat_pid (ps), proc_stat_args (ps), state_char (ps), - pi->ppid, pi->pgrp, pi->session, - 0, 0, /* no such thing as a major:minor for ctty */ - 0, /* no such thing as CLONE_* flags on Hurd */ - 0L, 0L, 0L, 0L, /* TASK_EVENTS_INFO is unavailable on GNU Mach */ - (long unsigned) timeval_jiffies (thbi->user_time), - (long unsigned) timeval_jiffies (thbi->system_time), - 0L, 0L, /* cumulative time for children */ - MACH_PRIORITY_TO_NICE(thbi->base_priority) + 20, - MACH_PRIORITY_TO_NICE(thbi->base_priority), - pi->nthreads, 0L, - timeval_jiffies (thbi->creation_time), /* FIXME: ... since boot */ - (long unsigned) tbi->virtual_size, - (long unsigned) tbi->resident_size / PAGE_SIZE, 0L, - 0L, 0L, 0L, 0L, 0L, - 0L, 0L, 0L, 0L, - (long unsigned) proc_stat_thread_rpc (ps), /* close enough */ - 0L, 0L, - 0, - 0, - 0, 0, - 0LL); -} - -static ssize_t -process_file_gc_statm (struct proc_stat *ps, char **contents) -{ - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - - return asprintf (contents, - "%lu %lu 0 0 0 0 0\n", - tbi->virtual_size / sysconf(_SC_PAGE_SIZE), - tbi->resident_size / sysconf(_SC_PAGE_SIZE)); -} - -static ssize_t -process_file_gc_status (struct proc_stat *ps, char **contents) -{ - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - - return asprintf (contents, - "Name:\t%s\n" - "State:\t%s\n" - "Tgid:\t%u\n" - "Pid:\t%u\n" - "PPid:\t%u\n" - "Uid:\t%u\t%u\t%u\t%u\n" - "VmSize:\t%8u kB\n" - "VmPeak:\t%8u kB\n" - "VmRSS:\t%8u kB\n" - "VmHWM:\t%8u kB\n" /* ie. resident peak */ - "Threads:\t%u\n", - proc_stat_args (ps), - state_string (ps), - proc_stat_pid (ps), /* XXX will need more work for threads */ - proc_stat_pid (ps), - proc_stat_proc_info (ps)->ppid, - proc_stat_owner_uid (ps), - proc_stat_owner_uid (ps), - proc_stat_owner_uid (ps), - proc_stat_owner_uid (ps), - tbi->virtual_size / 1024, - tbi->virtual_size / 1024, - tbi->resident_size / 1024, - tbi->resident_size / 1024, - proc_stat_num_threads (ps)); -} - - -/* Implementation of the file nodes. */ - -/* Describes a file in the process directories. This structure is - filled in as an "entry hook" in our procfs_dir entry table and is - passed to the process_file_make_node function defined below. */ -struct process_file_desc -{ - /* The proc_stat information required to get the contents of this file. */ - ps_flags_t needs; - - /* Content generator to use for this file. Once we have acquired the - necessary information, there can be only memory allocation errors, - hence this simplified signature. */ - ssize_t (*get_contents) (struct proc_stat *ps, char **contents); - - /* The cmdline and environ contents don't need any cleaning since they - point directly into the proc_stat structure. */ - int no_cleanup; - - /* If specified, the file mode to be set with procfs_node_chmod(). */ - mode_t mode; -}; - -struct process_file_node -{ - const struct process_file_desc *desc; - struct proc_stat *ps; -}; - -/* FIXME: lock the parent! */ -static error_t -process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) -{ - struct process_file_node *file = hook; - error_t err; - - /* Fetch the required information. */ - err = proc_stat_set_flags (file->ps, file->desc->needs); - if (err) - return EIO; - if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) - return EIO; - - /* Call the actual content generator (see the definitions below). */ - *contents_len = file->desc->get_contents (file->ps, contents); - return 0; -} - -static void -process_file_cleanup_contents (void *hook, char *contents, ssize_t len) -{ - struct process_file_node *file = hook; - - if (! file->desc->no_cleanup) - free (contents); -} - -static struct node * -process_file_make_node (void *dir_hook, const void *entry_hook) -{ - static const struct procfs_node_ops ops = { - .get_contents = process_file_get_contents, - .cleanup_contents = process_file_cleanup_contents, - .cleanup = free, - }; - struct process_file_node *f; - struct node *np; - - f = malloc (sizeof *f); - if (! f) - return NULL; - - f->desc = entry_hook; - f->ps = dir_hook; - - np = procfs_make_node (&ops, f); - if (! np) - return NULL; - - procfs_node_chown (np, proc_stat_owner_uid (f->ps)); - if (f->desc->mode) - procfs_node_chmod (np, f->desc->mode); - - return np; -} - -/* Stat needs its own constructor in oreder to set its mode according to - the --stat-mode command-line option. */ -static struct node * -process_stat_make_node (void *dir_hook, const void *entry_hook) -{ - struct node *np = process_file_make_node (dir_hook, entry_hook); - if (np) procfs_node_chmod (np, opt_stat_mode); - return np; -} - - -/* Implementation of the process directory per se. */ - -static struct procfs_dir_entry entries[] = { - { - .name = "cmdline", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_cmdline, - .needs = PSTAT_ARGS, - .no_cleanup = 1, - }, - }, - { - .name = "environ", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_environ, - .needs = PSTAT_ENV, - .no_cleanup = 1, - .mode = 0400, - }, - }, - { - .name = "stat", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_stat, - .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO - | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC - | PSTAT_THREAD_WAIT, - }, - .ops = { - .make_node = process_stat_make_node, - } - }, - { - .name = "statm", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_statm, - .needs = PSTAT_TASK_BASIC, - }, - }, - { - .name = "status", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_status, - .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO - | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS, - }, - }, - {} -}; - -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; - - err = _proc_stat_create (pid, pc, &ps); - if (err == ESRCH) - return ENOENT; - if (err) - return EIO; - - err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); - if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) - { - _proc_stat_free (ps); - return EIO; - } - - /* FIXME: have a separate proc_desc structure for each file, so this can be - accessed in a more robust and straightforward way. */ - ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; - - /* FIXME: have a separate proc_desc structure for each file, so this can be - accessed in a more robust and straightforward way. */ - ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; - - *np = procfs_dir_make_node (&dir_ops, ps); - if (! *np) - return ENOMEM; - - owner = proc_stat_owner_uid (ps); - procfs_node_chown (*np, owner >= 0 ? owner : opt_anon_owner); - return 0; -} diff --git a/process.h b/process.h deleted file mode 100644 index b230a281..00000000 --- a/process.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Hurd /proc filesystem, implementation of process directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include - -/* Create a node for a directory representing the given PID, as published by - the proc server refrenced by the libps context PC. On success, returns the - newly created node in *NP. */ -error_t -process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np); - diff --git a/procfs.c b/procfs.c deleted file mode 100644 index ae5a6769..00000000 --- a/procfs.c +++ /dev/null @@ -1,203 +0,0 @@ -/* Hurd /proc filesystem, basic infrastructure. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include "procfs.h" - -struct netnode -{ - const struct procfs_node_ops *ops; - void *hook; - - /* (cached) contents of the node */ - char *contents; - ssize_t contents_len; - - /* parent directory, if applicable */ - struct node *parent; -}; - -void -procfs_cleanup_contents_with_free (void *hook, char *cont, ssize_t len) -{ - free (cont); -} - -void -procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, ssize_t len) -{ - vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); -} - -struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) -{ - struct netnode *nn; - struct node *np; - - nn = malloc (sizeof *nn); - if (! nn) - goto fail; - - memset (nn, 0, sizeof *nn); - nn->ops = ops; - nn->hook = hook; - - np = netfs_make_node (nn); - if (! np) - goto fail; - - np->nn = nn; - memset (&np->nn_stat, 0, sizeof np->nn_stat); - np->nn_translated = 0; - - if (np->nn->ops->lookup) - np->nn_stat.st_mode = S_IFDIR | 0555; - else - np->nn_stat.st_mode = S_IFREG | 0444; - - return np; - -fail: - if (ops->cleanup) - ops->cleanup (hook); - - free (nn); - return NULL; -} - -void procfs_node_chown (struct node *np, uid_t owner) -{ - np->nn_stat.st_uid = owner; -} - -void procfs_node_chmod (struct node *np, mode_t mode) -{ - np->nn_stat.st_mode = (np->nn_stat.st_mode & S_IFMT) | mode; - np->nn_translated = np->nn_stat.st_mode; -} - -void procfs_node_chtype (struct node *np, mode_t type) -{ - np->nn_stat.st_mode = (np->nn_stat.st_mode & ~S_IFMT) | type; - np->nn_translated = np->nn_stat.st_mode; - if (type == S_IFLNK) - procfs_node_chmod (np, 0777); -} - -/* FIXME: possibly not the fastest hash function... */ -ino64_t -procfs_make_ino (struct node *np, const char *filename) -{ - unsigned short x[3]; - - if (! strcmp (filename, ".")) - return np->nn_stat.st_ino; - if (! strcmp (filename, "..")) - return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 2; - - assert (sizeof np->nn_stat.st_ino > sizeof x); - memcpy (x, &np->nn_stat.st_ino, sizeof x); - - while (*filename) - { - x[0] ^= *(filename++); - jrand48 (x); - } - - return (unsigned long) jrand48 (x); -} - -error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) -{ - if (! np->nn->contents && np->nn->ops->get_contents) - { - char *contents; - ssize_t contents_len; - error_t err; - - contents_len = -1; - err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); - if (err) - return err; - if (contents_len < 0) - return ENOMEM; - - np->nn->contents = contents; - np->nn->contents_len = contents_len; - } - - *data = np->nn->contents; - *data_len = np->nn->contents_len; - return 0; -} - -void procfs_refresh (struct node *np) -{ - if (np->nn->contents && np->nn->ops->cleanup_contents) - np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); - - np->nn->contents = NULL; -} - -error_t procfs_lookup (struct node *np, const char *name, struct node **npp) -{ - error_t err = ENOENT; - - if (err && ! strcmp (name, ".")) - { - netfs_nref(*npp = np); - err = 0; - } - - if (err && np->nn->parent && ! strcmp (name, "..")) - { - netfs_nref(*npp = np->nn->parent); - err = 0; - } - - if (err && np->nn->ops->lookup) - { - err = np->nn->ops->lookup (np->nn->hook, name, npp); - if (! err) - { - (*npp)->nn_stat.st_ino = procfs_make_ino (np, name); - netfs_nref ((*npp)->nn->parent = np); - } - } - - return err; -} - -void procfs_cleanup (struct node *np) -{ - procfs_refresh (np); - - if (np->nn->ops->cleanup) - np->nn->ops->cleanup (np->nn->hook); - - if (np->nn->parent) - netfs_nrele (np->nn->parent); - - free (np->nn); -} diff --git a/procfs.h b/procfs.h deleted file mode 100644 index 64782ec4..00000000 --- a/procfs.h +++ /dev/null @@ -1,93 +0,0 @@ -/* Hurd /proc filesystem, basic infrastructure. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include - - -/* Interface for the procfs side. */ - -/* Any of these callback functions can be omitted, in which case - reasonable defaults will be used. The initial file mode and type - depend on whether a lookup function is provided, but can be - overridden in update_stat(). */ -struct procfs_node_ops -{ - /* Fetch the contents of a node. A pointer to the contents should be - returned in *CONTENTS and their length in *CONTENTS_LEN. The exact - nature of these data depends on whether the node is a regular file, - symlink or directory, as determined by the file mode in - netnode->nn_stat. For regular files and symlinks, they are what - you would expect; for directories, they are an argz vector of the - names of the entries. If upon return, *CONTENTS_LEN is negative or - unchanged, the call is considered to have failed because of a memory - allocation error. */ - error_t (*get_contents) (void *hook, char **contents, ssize_t *contents_len); - void (*cleanup_contents) (void *hook, char *contents, ssize_t contents_len); - - /* Lookup NAME in this directory, and store the result in *np. The - returned node should be created by lookup() using procfs_make_node() - or a derived function. Note that the parent will be kept alive as - long as the child exists, so you can safely reference the parent's - data from the child. You may want to consider locking if there's - any mutation going on, though. */ - error_t (*lookup) (void *hook, const char *name, struct node **np); - - /* Destroy this node. */ - void (*cleanup) (void *hook); -}; - -/* These helper functions can be used as procfs_node_ops.cleanup_contents. */ -void procfs_cleanup_contents_with_free (void *, char *, ssize_t); -void procfs_cleanup_contents_with_vm_deallocate (void *, char *, ssize_t); - -/* Create a new node and return it. Returns NULL if it fails to allocate - enough memory. In this case, ops->cleanup will be invoked. */ -struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); - -/* Set the owner of the node NP. Must be called right after the node - has been created. */ -void procfs_node_chown (struct node *np, uid_t owner); - -/* Set the permission bits of the node NP. Must be called right after - the node has been created. */ -void procfs_node_chmod (struct node *np, mode_t mode); - -/* Set the type of the node NP. If type is S_IFLNK, appropriate - permission bits will be set as well. Must be called right after the - node has been created. */ -void procfs_node_chtype (struct node *np, mode_t type); - - -/* Interface for the libnetfs side. */ - -/* Get the inode number which will be given to a child of NP named FILENAME. - This allows us to retreive them for readdir() without creating the - corresponding child nodes. */ -ino64_t procfs_make_ino (struct node *np, const char *filename); - -/* Forget the current cached contents for the node. This is done before reads - from offset 0, to ensure that the data are recent even for utilities such as - top which keep some nodes open. */ -void procfs_refresh (struct node *np); - -error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); -error_t procfs_lookup (struct node *np, const char *name, struct node **npp); -void procfs_cleanup (struct node *np); - diff --git a/procfs/Makefile b/procfs/Makefile new file mode 100644 index 00000000..a397522f --- /dev/null +++ b/procfs/Makefile @@ -0,0 +1,26 @@ +TARGET = procfs +OBJS = procfs.o netfs.o procfs_dir.o \ + process.o proclist.o rootdir.o dircat.o main.o +LIBS = -lnetfs -lps + +CC = gcc +CFLAGS = -Wall -g +CPPFLAGS = +LDFLAGS = + +ifdef PROFILE +CFLAGS= -g -pg +CPPFLAGS= -DPROFILE +LDFLAGS= -static +LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lthreads -lihash -lshouldbeinlibc +endif + +CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + $(RM) $(TARGET) $(OBJS) diff --git a/procfs/TODO b/procfs/TODO new file mode 100644 index 00000000..952d67bc --- /dev/null +++ b/procfs/TODO @@ -0,0 +1,24 @@ +Known bugs to be fixed +---------------------- + +* The non-owned processes sometimes show up with INT_MAX as their owner, + instead of opt_anon_uid. This is likely to be a libps problem. + +Improvements and new features +----------------------------- + +* There is a lot of dynamic memory allocation going on and it comes with a + cost in performance. We could try to limit such allocation, as long as it + keeps the inner interface simple and preserves the read/readdir semantics + (performance is probably not critical for a proc filesystem.) + One way would be to add an (optional) "needed_length" field to + procfs_node_ops, and arrange to pass a sufficent buffer in (*contents, + *contents_len) when get_contents is called. Then the user-provided buffer + might be used directly under some circumstances. + +* Add thread directories as [pid]/task/[n]. This shouldn't be too hard if we + use "process" nodes for threads, and provide an "exists" hook for the "task" + entry itself so that it's disabled in thread nodes. It might prove necessary + to have "optional" libps flags for some content generators, though, since + some of them might be missing for threads. + diff --git a/procfs/dircat.c b/procfs/dircat.c new file mode 100644 index 00000000..5a60899a --- /dev/null +++ b/procfs/dircat.c @@ -0,0 +1,128 @@ +/* Hurd /proc filesystem, concatenation of two directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include "procfs.h" + +struct dircat_node +{ + int num_dirs; + struct node *dirs[0]; +}; + +static error_t +dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) +{ + struct dircat_node *dcn = hook; + int i, sz, pos; + error_t err; + + pos = 0; + *contents = malloc (sz = 512); + + for (i=0; i < dcn->num_dirs; i++) + { + char *subcon; + ssize_t sublen; + + /* Make sure we're not getting some old stuff. */ + procfs_refresh (dcn->dirs[i]); + + err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); + if (err) + { + free (*contents); + *contents = NULL; + return err; + } + + while (pos + sublen > sz) + *contents = realloc (*contents, sz *= 2); + + memcpy (*contents + pos, subcon, sublen); + pos += sublen; + } + + *contents_len = pos; + return 0; +} + +static error_t +dircat_lookup (void *hook, const char *name, struct node **np) +{ + struct dircat_node *dcn = hook; + error_t err; + int i; + + err = ENOENT; + for (i=0; err && i < dcn->num_dirs; i++) + err = procfs_lookup (dcn->dirs[i], name, np); + + return err; +} + +static void +dircat_release_dirs (struct node *const *dirs, int num_dirs) +{ + int i; + + for (i=0; i < num_dirs; i++) + if (dirs[i]) + netfs_nrele (dirs[i]); +} + +static void +dircat_cleanup (void *hook) +{ + struct dircat_node *dcn = hook; + + dircat_release_dirs (dcn->dirs, dcn->num_dirs); + free (dcn); +} + +struct node * +dircat_make_node (struct node *const *dirs, int num_dirs) +{ + static struct procfs_node_ops ops = { + .get_contents = dircat_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_free, + .lookup = dircat_lookup, + .cleanup = dircat_cleanup, + }; + struct dircat_node *dcn; + int i; + + for (i=0; i < num_dirs; i++) + if (! dirs[i]) + goto fail; + + dcn = malloc (sizeof *dcn + num_dirs * sizeof dcn->dirs[0]); + if (! dcn) + goto fail; + + dcn->num_dirs = num_dirs; + memcpy (dcn->dirs, dirs, num_dirs * sizeof dcn->dirs[0]); + return procfs_make_node (&ops, dcn); + +fail: + dircat_release_dirs (dirs, num_dirs); + return NULL; +} + diff --git a/procfs/dircat.h b/procfs/dircat.h new file mode 100644 index 00000000..4177b384 --- /dev/null +++ b/procfs/dircat.h @@ -0,0 +1,29 @@ +/* Hurd /proc filesystem, concatenation of two directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Append the contents of NUM_DIRS directories. DIRS is an array of + directory nodes. One reference is consumed for each of them. If a + memory allocation error occurs, or if one of the directories is a + NULL pointer, the references are dropped immediately and NULL is + returned. The given DIRS array is duplicated and can therefore be + allocated on the caller's stack. Strange things will happen if some + elements of DIRS have entries with the same name or if one of them is + not a directory. */ +struct node * +dircat_make_node (struct node *const *dirs, int num_dirs); diff --git a/procfs/main.c b/procfs/main.c new file mode 100644 index 00000000..3a976ccc --- /dev/null +++ b/procfs/main.c @@ -0,0 +1,190 @@ +/* Hurd /proc filesystem, main program. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include "procfs.h" +#include "proclist.h" +#include "rootdir.h" +#include "dircat.h" +#include "main.h" + +/* Command-line options */ +int opt_clk_tck; +mode_t opt_stat_mode; +pid_t opt_fake_self; +pid_t opt_kernel_pid; +uid_t opt_anon_owner; + +static error_t +argp_parser (int key, char *arg, struct argp_state *state) +{ + struct passwd *pw; + char *endp; + + switch (key) + { + case 'h': + opt_clk_tck = strtol (arg, &endp, 0); + if (*endp || ! *arg || opt_clk_tck <= 0) + error (1, 0, "--clk-tck: HZ should be a positive integer"); + break; + + case 's': + opt_stat_mode = strtol (arg, &endp, 8); + if (*endp || ! *arg || opt_stat_mode & ~07777) + error (1, 0, "--stat-mode: MODE should be an octal mode"); + break; + + case 'S': + if (arg) + { + opt_fake_self = strtol (arg, &endp, 0); + if (*endp || ! *arg) + error (1, 0, "--fake-self: PID must be an integer"); + } + else + opt_fake_self = 1; + break; + + case 'k': + opt_kernel_pid = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_kernel_pid < 0) + error (1, 0, "--kernel-process: PID must be a positive integer"); + break; + + case 'c': + opt_clk_tck = 100; + opt_stat_mode = 0444; + opt_fake_self = 1; + break; + + case 'a': + pw = getpwnam (arg); + if (pw) + { + opt_anon_owner = pw->pw_uid; + break; + } + + opt_anon_owner = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_anon_owner < 0) + error(1, 0, "--anonymous-owner: USER should be the a user name " + "or a numeric UID."); + break; + } + + return 0; +} + +struct argp argp = { + .options = (struct argp_option []) { + { "clk-tck", 'h', "HZ", 0, + "Unit used for the values expressed in system clock ticks " + "(default: sysconf(_SC_CLK_TCK))" }, + { "stat-mode", 's', "MODE", 0, + "The [pid]/stat file publishes information which on Hurd is only " + "available to the process owner. " + "You can use this option to override its mode to be more permissive " + "for compatibility purposes. " + "(default: 0400)" }, + { "fake-self", 'S', "PID", OPTION_ARG_OPTIONAL, + "Provide a fake \"self\" symlink to the given PID, for compatibility " + "purposes. If PID is omitted, \"self\" will point to init. " + "(default: no self link)" }, + { "kernel-process", 'k', "PID", 0, + "Process identifier for the kernel, used to retreive its command " + "line, as well as the global up and idle times. " + "(default: 2)" }, + { "compatible", 'c', NULL, 0, + "Try to be compatible with the Linux procps utilities. " + "Currently equivalent to -h 100 -s 0444 -S 1." }, + { "anonymous-owner", 'a', "USER", 0, + "Make USER the owner of files related to processes without one. " + "Be aware that USER will be granted access to the environment and " + "other sensitive information about the processes in question. " + "(default: use uid 0)" }, + {} + }, + .parser = argp_parser, + .doc = "A virtual filesystem emulating the Linux procfs.", + .children = (struct argp_child []) { + { &netfs_std_startup_argp, }, + {} + }, +}; + +error_t +root_make_node (struct ps_context *pc, struct node **np) +{ + struct node *root_dirs[] = { + proclist_make_node (pc), + rootdir_make_node (pc), + }; + + *np = dircat_make_node (root_dirs, sizeof root_dirs / sizeof root_dirs[0]); + if (! *np) + return ENOMEM; + + /* Since this one is not created through proc_lookup(), we have to affect an + inode number to it. */ + (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; + + return 0; +} + +int main (int argc, char **argv) +{ + struct ps_context *pc; + mach_port_t bootstrap; + error_t err; + + opt_clk_tck = sysconf(_SC_CLK_TCK); + opt_stat_mode = 0400; + opt_fake_self = -1; + opt_kernel_pid = 2; + opt_anon_owner = 0; + err = argp_parse (&argp, argc, argv, 0, 0, 0); + if (err) + error (1, err, "Could not parse command line"); + + err = ps_context_create (getproc (), &pc); + if (err) + error (1, err, "Could not create libps context"); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "Must be started as a translator"); + + netfs_init (); + err = root_make_node (pc, &netfs_root_node); + if (err) + error (1, err, "Could not create the root node"); + + netfs_startup (bootstrap, 0); + netfs_server_loop (); + + assert (0 /* netfs_server_loop returned after all */); +} + diff --git a/procfs/main.h b/procfs/main.h new file mode 100644 index 00000000..4e28b7eb --- /dev/null +++ b/procfs/main.h @@ -0,0 +1,25 @@ +/* Hurd /proc filesystem, command-line options set by main.c. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Startup options */ +extern int opt_clk_tck; +extern mode_t opt_stat_mode; +extern pid_t opt_fake_self; +extern pid_t opt_kernel_pid; +extern uid_t opt_anon_owner; diff --git a/procfs/netfs.c b/procfs/netfs.c new file mode 100644 index 00000000..24a6603f --- /dev/null +++ b/procfs/netfs.c @@ -0,0 +1,445 @@ +/* Hurd /proc filesystem, interface with libnetfs. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include "procfs.h" + +#define PROCFS_SERVER_NAME "procfs" +#define PROCFS_SERVER_VERSION "0.1.0" +#define PROCFS_MAXSYMLINKS 16 + + +/* Interesting libnetfs callback functions. */ + +/* The user must define this variable. Set this to the name of the + filesystem server. */ +char *netfs_server_name = PROCFS_SERVER_NAME; + +/* The user must define this variables. Set this to be the server + version number. */ +char *netfs_server_version = PROCFS_SERVER_VERSION; + +/* Maximum number of symlinks to follow before returning ELOOP. */ +int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; + +/* The user must define this function. Make sure that NP->nn_stat is + filled with the most current information. CRED identifies the user + responsible for the operation. NP is locked. */ +error_t netfs_validate_stat (struct node *np, struct iouser *cred) +{ + char *contents; + ssize_t contents_len; + error_t err; + + /* Only symlinks need to have their size filled, before a read is + attempted. */ + if (! S_ISLNK (np->nn_stat.st_mode)) + return 0; + + err = procfs_get_contents (np, &contents, &contents_len); + if (err) + return err; + + np->nn_stat.st_size = contents_len; + return 0; +} + +/* The user must define this function. Read from the locked file NP + for user CRED starting at OFFSET and continuing for up to *LEN + bytes. Put the data at DATA. Set *LEN to the amount successfully + read upon return. */ +error_t netfs_attempt_read (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + char *contents; + ssize_t contents_len; + error_t err; + + if (offset == 0) + procfs_refresh (np); + + err = procfs_get_contents (np, &contents, &contents_len); + if (err) + return err; + + contents += offset; + contents_len -= offset; + + if (*len > contents_len) + *len = contents_len; + if (*len < 0) + *len = 0; + + memcpy (data, contents, *len); + return 0; +} + +/* The user must define this function. Read the contents of locked + node NP (a symlink), for USER, into BUF. */ +error_t netfs_attempt_readlink (struct iouser *user, struct node *np, + char *buf) +{ + char *contents; + ssize_t contents_len; + error_t err; + + err = procfs_get_contents (np, &contents, &contents_len); + if (err) + return err; + + assert (contents_len == np->nn_stat.st_size); + memcpy (buf, contents, contents_len); + return 0; +} + +/* Helper function for netfs_get_dirents() below. CONTENTS is an argz + vector of directory entry names, as returned by procfs_get_contents(). + Convert at most NENTRIES of them to dirent structures, put them in + DATA (if not NULL), write the number of entries processed in *AMT and + return the required/used space in DATACNT. */ +static int putentries (char *contents, size_t contents_len, int nentries, + char *data, mach_msg_type_number_t *datacnt) +{ + int i; + + *datacnt = 0; + for (i = 0; contents_len && (nentries < 0 || i < nentries); i++) + { + int namlen = strlen (contents); + int reclen = sizeof (struct dirent) + namlen; + + if (data) + { + struct dirent *d = (struct dirent *) (data + *datacnt); + d->d_fileno = 42; /* XXX */ + d->d_namlen = namlen; + d->d_reclen = reclen; + d->d_type = DT_UNKNOWN; + strcpy (d->d_name, contents); + } + + *datacnt += reclen; + contents += namlen + 1; + contents_len -= namlen + 1; + } + + return i; +} + +/* The user must define this function. Fill the array *DATA of size + BUFSIZE with up to NENTRIES dirents from DIR (which is locked) + starting with entry ENTRY for user CRED. The number of entries in + the array is stored in *AMT and the number of bytes in *DATACNT. + If the supplied buffer is not large enough to hold the data, it + should be grown. */ +error_t netfs_get_dirents (struct iouser *cred, struct node *dir, + int entry, int nentries, char **data, + mach_msg_type_number_t *datacnt, + vm_size_t bufsize, int *amt) +{ + char *contents; + ssize_t contents_len; + error_t err; + + if (entry == 0) + procfs_refresh (dir); + + err = procfs_get_contents (dir, &contents, &contents_len); + if (err) + return err; + + /* We depend on the fact that CONTENTS is terminated. */ + assert (contents_len == 0 || contents[contents_len - 1] == '\0'); + + /* Skip to the first requested entry. */ + while (contents_len && entry--) + { + int ofs = strlen (contents) + 1; + contents += ofs; + contents_len -= ofs; + } + + /* Allocate a buffer if necessary. */ + putentries (contents, contents_len, nentries, NULL, datacnt); + if (bufsize < *datacnt) + { + char *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); + if (n == MAP_FAILED) + return ENOMEM; + + *data = n; + } + + /* Do the actual conversion. */ + *amt = putentries (contents, contents_len, nentries, *data, datacnt); + + return 0; +} + +/* The user must define this function. Lookup NAME in DIR (which is + locked) for USER; set *NP to the found name upon return. If the + name was not found, then return ENOENT. On any error, clear *NP. + (*NP, if found, should be locked and a reference to it generated. + This call should unlock DIR no matter what.) */ +error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **np) +{ + error_t err; + + err = procfs_lookup (dir, name, np); + mutex_unlock (&dir->lock); + + if (! err) + mutex_lock (&(*np)->lock); + + return err; +} + +/* The user must define this function. Node NP has no more references; + free all its associated storage. */ +void netfs_node_norefs (struct node *np) +{ + spin_unlock (&netfs_node_refcnt_lock); + + procfs_cleanup (np); + free (np); + + spin_lock (&netfs_node_refcnt_lock); +} + + +/* Libnetfs callbacks managed with libfshelp. */ + +/* The user must define this function. Locked node NP is being opened + by USER, with FLAGS. NEWNODE is nonzero if we just created this + node. Return an error if we should not permit the open to complete + because of a permission restriction. */ +error_t netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = 0; + if (!err && (flags & O_READ)) + err = fshelp_access (&np->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&np->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&np->nn_stat, S_IEXEC, user); + return err; +} + +/* The user must define this function. Return the valid access + types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for + locked file NP and user CRED. */ +error_t netfs_report_access (struct iouser *cred, struct node *np, + int *types) +{ + *types = 0; + if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + return 0; +} + + +/* Trivial or unsupported libnetfs callbacks. */ + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NP, to change + the owner to UID and the group to GID. */ +error_t netfs_attempt_chown (struct iouser *cred, struct node *np, + uid_t uid, uid_t gid) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chauthor + call for the user specified by CRED on locked node NP, thereby + changing the author to AUTHOR. */ +error_t netfs_attempt_chauthor (struct iouser *cred, struct node *np, + uid_t author) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NODE, to change + the mode to MODE. Unlike the normal Unix and Hurd meaning of + chmod, this function is also used to attempt to change files into + other types. If such a transition is attempted which is + impossible, then return EOPNOTSUPP. */ +error_t netfs_attempt_chmod (struct iouser *cred, struct node *np, + mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn locked node NP + (user CRED) into a symlink with target NAME. */ +error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn NODE (user + CRED) into a device. TYPE is either S_IFBLK or S_IFCHR. NP is + locked. */ +error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np, + mode_t type, dev_t indexes) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chflags + call for the user specified by CRED on locked node NP, to change + the flags to FLAGS. */ +error_t netfs_attempt_chflags (struct iouser *cred, struct node *np, + int flags) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a utimes + call for the user specified by CRED on locked node NP, to change + the atime to ATIME and the mtime to MTIME. If ATIME or MTIME is + null, then set to the current time. */ +error_t netfs_attempt_utimes (struct iouser *cred, struct node *np, + struct timespec *atime, struct timespec *mtime) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to set the + size of the locked file NP (for user CRED) to SIZE bytes long. */ +error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, + loff_t size) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to fetch + filesystem status information for the remote filesystem, for the + user CRED. NP is locked. */ +error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, + fsys_statfsbuf_t *st) +{ + return ENOSYS; +} + +/* The user must define this function. This should sync the locked + file NP completely to disk, for the user CRED. If WAIT is set, + return only after the sync is completely finished. */ +error_t netfs_attempt_sync (struct iouser *cred, struct node *np, + int wait) +{ + return 0; +} + +/* The user must define this function. This should sync the entire + remote filesystem. If WAIT is set, return only after the sync is + completely finished. */ +error_t netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + +/* The user must define this function. Delete NAME in DIR (which is + locked) for USER. */ +error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to rename the + directory FROMDIR to TODIR. Note that neither of the specific nodes + are locked. */ +error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, + char *fromname, struct node *todir, + char *toname, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a new + directory named NAME in DIR (which is locked) for USER with mode + MODE. */ +error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, + char *name, mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to remove directory + named NAME in DIR (which is locked) for USER. */ +error_t netfs_attempt_rmdir (struct iouser *user, + struct node *dir, char *name) +{ + return EROFS; +} + + +/* The user must define this function. Create a link in DIR with name + NAME to FILE for USER. Note that neither DIR nor FILE are + locked. If EXCL is set, do not delete the target. Return EEXIST if + NAME is already found in DIR. */ +error_t netfs_attempt_link (struct iouser *user, struct node *dir, + struct node *file, char *name, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create an anonymous + file related to DIR (which is locked) for USER with MODE. Set *NP + to the returned file upon success. No matter what, unlock DIR. */ +error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, + mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a file named + NAME in DIR (which is locked) for USER with MODE. Set *NP to the + new node upon return. On any error, clear *NP. *NP should be + locked on success; no matter what, unlock DIR before returning. */ +error_t netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Write to the locked file NP + for user CRED starting at OFSET and continuing for up to *LEN bytes + from DATA. Set *LEN to the amount successfully written upon + return. */ +error_t netfs_attempt_write (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + return EROFS; +} + + diff --git a/procfs/process.c b/procfs/process.c new file mode 100644 index 00000000..6652a4e9 --- /dev/null +++ b/procfs/process.c @@ -0,0 +1,387 @@ +/* Hurd /proc filesystem, implementation of process directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include "procfs.h" +#include "procfs_dir.h" +#include "process.h" +#include "main.h" + +/* This module implements the process directories and the files they + contain. A libps proc_stat structure is created for each process + node, and is used by the individual file content generators as a + source of information. Each possible file (cmdline, environ, ...) is + decribed in a process_file_desc structure, which specifies which bits + of information (ie. libps flags) it needs, and what function should + be used to generate the file's contents. + + The content generators are defined first, followed by glue logic and + entry table. */ + + +/* Helper functions */ + +static char state_char (struct proc_stat *ps) +{ + int i; + + for (i = 0; (1 << i) & (PSTAT_STATE_P_STATES | PSTAT_STATE_T_STATES); i++) + if (proc_stat_state (ps) & (1 << i)) + return proc_stat_state_tags[i]; + + return '?'; +} + +static const char *state_string (struct proc_stat *ps) +{ + static const char *const state_strings[] = { + "T (stopped)", + "Z (zombie)", + "R (running)", + "H (halted)", + "D (disk sleep)", + "S (sleeping)", + "I (idle)", + NULL + }; + int i; + + for (i = 0; state_strings[i]; i++) + if (proc_stat_state (ps) & (1 << i)) + return state_strings[i]; + + return "? (unknown)"; +} + +static long long int timeval_jiffies (time_value_t tv) +{ + double secs = tv.seconds * 1000000. + tv.microseconds; + return secs * opt_clk_tck / 1000000.; +} + +/* Actual content generators */ + +static ssize_t +process_file_gc_cmdline (struct proc_stat *ps, char **contents) +{ + *contents = proc_stat_args(ps); + return proc_stat_args_len(ps); +} + +static ssize_t +process_file_gc_environ (struct proc_stat *ps, char **contents) +{ + *contents = proc_stat_env(ps); + return proc_stat_env_len(ps); +} + +static ssize_t +process_file_gc_stat (struct proc_stat *ps, char **contents) +{ + struct procinfo *pi = proc_stat_proc_info (ps); + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); + + /* See proc(5) for more information about the contents of each field for the + Linux procfs. */ + return asprintf (contents, + "%d (%s) %c " /* pid, command, state */ + "%d %d %d " /* ppid, pgid, session */ + "%d %d " /* controling tty stuff */ + "%u " /* flags, as defined by */ + "%lu %lu %lu %lu " /* page fault counts */ + "%lu %lu %ld %ld " /* user/sys times, in sysconf(_SC_CLK_TCK) */ + "%d %d " /* scheduler params (priority, nice) */ + "%d %ld " /* number of threads, [obsolete] */ + "%llu " /* start time since boot (jiffies) */ + "%lu %ld %lu " /* virtual size (bytes), rss (pages), rss lim */ + "%lu %lu %lu %lu %lu " /* some vm addresses (code, stack, sp, pc) */ + "%lu %lu %lu %lu " /* pending, blocked, ignored and caught sigs */ + "%lu " /* wait channel */ + "%lu %lu " /* swap usage (not maintained in Linux) */ + "%d " /* exit signal, to be sent to the parent */ + "%d " /* last processor used */ + "%u %u " /* RT priority and policy */ + "%llu " /* aggregated block I/O delay */ + "\n", + proc_stat_pid (ps), proc_stat_args (ps), state_char (ps), + pi->ppid, pi->pgrp, pi->session, + 0, 0, /* no such thing as a major:minor for ctty */ + 0, /* no such thing as CLONE_* flags on Hurd */ + 0L, 0L, 0L, 0L, /* TASK_EVENTS_INFO is unavailable on GNU Mach */ + (long unsigned) timeval_jiffies (thbi->user_time), + (long unsigned) timeval_jiffies (thbi->system_time), + 0L, 0L, /* cumulative time for children */ + MACH_PRIORITY_TO_NICE(thbi->base_priority) + 20, + MACH_PRIORITY_TO_NICE(thbi->base_priority), + pi->nthreads, 0L, + timeval_jiffies (thbi->creation_time), /* FIXME: ... since boot */ + (long unsigned) tbi->virtual_size, + (long unsigned) tbi->resident_size / PAGE_SIZE, 0L, + 0L, 0L, 0L, 0L, 0L, + 0L, 0L, 0L, 0L, + (long unsigned) proc_stat_thread_rpc (ps), /* close enough */ + 0L, 0L, + 0, + 0, + 0, 0, + 0LL); +} + +static ssize_t +process_file_gc_statm (struct proc_stat *ps, char **contents) +{ + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + + return asprintf (contents, + "%lu %lu 0 0 0 0 0\n", + tbi->virtual_size / sysconf(_SC_PAGE_SIZE), + tbi->resident_size / sysconf(_SC_PAGE_SIZE)); +} + +static ssize_t +process_file_gc_status (struct proc_stat *ps, char **contents) +{ + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + + return asprintf (contents, + "Name:\t%s\n" + "State:\t%s\n" + "Tgid:\t%u\n" + "Pid:\t%u\n" + "PPid:\t%u\n" + "Uid:\t%u\t%u\t%u\t%u\n" + "VmSize:\t%8u kB\n" + "VmPeak:\t%8u kB\n" + "VmRSS:\t%8u kB\n" + "VmHWM:\t%8u kB\n" /* ie. resident peak */ + "Threads:\t%u\n", + proc_stat_args (ps), + state_string (ps), + proc_stat_pid (ps), /* XXX will need more work for threads */ + proc_stat_pid (ps), + proc_stat_proc_info (ps)->ppid, + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + tbi->virtual_size / 1024, + tbi->virtual_size / 1024, + tbi->resident_size / 1024, + tbi->resident_size / 1024, + proc_stat_num_threads (ps)); +} + + +/* Implementation of the file nodes. */ + +/* Describes a file in the process directories. This structure is + filled in as an "entry hook" in our procfs_dir entry table and is + passed to the process_file_make_node function defined below. */ +struct process_file_desc +{ + /* The proc_stat information required to get the contents of this file. */ + ps_flags_t needs; + + /* Content generator to use for this file. Once we have acquired the + necessary information, there can be only memory allocation errors, + hence this simplified signature. */ + ssize_t (*get_contents) (struct proc_stat *ps, char **contents); + + /* The cmdline and environ contents don't need any cleaning since they + point directly into the proc_stat structure. */ + int no_cleanup; + + /* If specified, the file mode to be set with procfs_node_chmod(). */ + mode_t mode; +}; + +struct process_file_node +{ + const struct process_file_desc *desc; + struct proc_stat *ps; +}; + +/* FIXME: lock the parent! */ +static error_t +process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) +{ + struct process_file_node *file = hook; + error_t err; + + /* Fetch the required information. */ + err = proc_stat_set_flags (file->ps, file->desc->needs); + if (err) + return EIO; + if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) + return EIO; + + /* Call the actual content generator (see the definitions below). */ + *contents_len = file->desc->get_contents (file->ps, contents); + return 0; +} + +static void +process_file_cleanup_contents (void *hook, char *contents, ssize_t len) +{ + struct process_file_node *file = hook; + + if (! file->desc->no_cleanup) + free (contents); +} + +static struct node * +process_file_make_node (void *dir_hook, const void *entry_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = process_file_get_contents, + .cleanup_contents = process_file_cleanup_contents, + .cleanup = free, + }; + struct process_file_node *f; + struct node *np; + + f = malloc (sizeof *f); + if (! f) + return NULL; + + f->desc = entry_hook; + f->ps = dir_hook; + + np = procfs_make_node (&ops, f); + if (! np) + return NULL; + + procfs_node_chown (np, proc_stat_owner_uid (f->ps)); + if (f->desc->mode) + procfs_node_chmod (np, f->desc->mode); + + return np; +} + +/* Stat needs its own constructor in oreder to set its mode according to + the --stat-mode command-line option. */ +static struct node * +process_stat_make_node (void *dir_hook, const void *entry_hook) +{ + struct node *np = process_file_make_node (dir_hook, entry_hook); + if (np) procfs_node_chmod (np, opt_stat_mode); + return np; +} + + +/* Implementation of the process directory per se. */ + +static struct procfs_dir_entry entries[] = { + { + .name = "cmdline", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_cmdline, + .needs = PSTAT_ARGS, + .no_cleanup = 1, + }, + }, + { + .name = "environ", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_environ, + .needs = PSTAT_ENV, + .no_cleanup = 1, + .mode = 0400, + }, + }, + { + .name = "stat", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_stat, + .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO + | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC + | PSTAT_THREAD_WAIT, + }, + .ops = { + .make_node = process_stat_make_node, + } + }, + { + .name = "statm", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_statm, + .needs = PSTAT_TASK_BASIC, + }, + }, + { + .name = "status", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_status, + .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO + | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS, + }, + }, + {} +}; + +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; + + err = _proc_stat_create (pid, pc, &ps); + if (err == ESRCH) + return ENOENT; + if (err) + return EIO; + + err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); + if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) + { + _proc_stat_free (ps); + return EIO; + } + + /* FIXME: have a separate proc_desc structure for each file, so this can be + accessed in a more robust and straightforward way. */ + ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; + + /* FIXME: have a separate proc_desc structure for each file, so this can be + accessed in a more robust and straightforward way. */ + ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; + + *np = procfs_dir_make_node (&dir_ops, ps); + if (! *np) + return ENOMEM; + + owner = proc_stat_owner_uid (ps); + procfs_node_chown (*np, owner >= 0 ? owner : opt_anon_owner); + return 0; +} diff --git a/procfs/process.h b/procfs/process.h new file mode 100644 index 00000000..b230a281 --- /dev/null +++ b/procfs/process.h @@ -0,0 +1,27 @@ +/* Hurd /proc filesystem, implementation of process directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +/* Create a node for a directory representing the given PID, as published by + the proc server refrenced by the libps context PC. On success, returns the + newly created node in *NP. */ +error_t +process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np); + diff --git a/procfs/procfs.c b/procfs/procfs.c new file mode 100644 index 00000000..ae5a6769 --- /dev/null +++ b/procfs/procfs.c @@ -0,0 +1,203 @@ +/* Hurd /proc filesystem, basic infrastructure. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include "procfs.h" + +struct netnode +{ + const struct procfs_node_ops *ops; + void *hook; + + /* (cached) contents of the node */ + char *contents; + ssize_t contents_len; + + /* parent directory, if applicable */ + struct node *parent; +}; + +void +procfs_cleanup_contents_with_free (void *hook, char *cont, ssize_t len) +{ + free (cont); +} + +void +procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, ssize_t len) +{ + vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); +} + +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) +{ + struct netnode *nn; + struct node *np; + + nn = malloc (sizeof *nn); + if (! nn) + goto fail; + + memset (nn, 0, sizeof *nn); + nn->ops = ops; + nn->hook = hook; + + np = netfs_make_node (nn); + if (! np) + goto fail; + + np->nn = nn; + memset (&np->nn_stat, 0, sizeof np->nn_stat); + np->nn_translated = 0; + + if (np->nn->ops->lookup) + np->nn_stat.st_mode = S_IFDIR | 0555; + else + np->nn_stat.st_mode = S_IFREG | 0444; + + return np; + +fail: + if (ops->cleanup) + ops->cleanup (hook); + + free (nn); + return NULL; +} + +void procfs_node_chown (struct node *np, uid_t owner) +{ + np->nn_stat.st_uid = owner; +} + +void procfs_node_chmod (struct node *np, mode_t mode) +{ + np->nn_stat.st_mode = (np->nn_stat.st_mode & S_IFMT) | mode; + np->nn_translated = np->nn_stat.st_mode; +} + +void procfs_node_chtype (struct node *np, mode_t type) +{ + np->nn_stat.st_mode = (np->nn_stat.st_mode & ~S_IFMT) | type; + np->nn_translated = np->nn_stat.st_mode; + if (type == S_IFLNK) + procfs_node_chmod (np, 0777); +} + +/* FIXME: possibly not the fastest hash function... */ +ino64_t +procfs_make_ino (struct node *np, const char *filename) +{ + unsigned short x[3]; + + if (! strcmp (filename, ".")) + return np->nn_stat.st_ino; + if (! strcmp (filename, "..")) + return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 2; + + assert (sizeof np->nn_stat.st_ino > sizeof x); + memcpy (x, &np->nn_stat.st_ino, sizeof x); + + while (*filename) + { + x[0] ^= *(filename++); + jrand48 (x); + } + + return (unsigned long) jrand48 (x); +} + +error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) +{ + if (! np->nn->contents && np->nn->ops->get_contents) + { + char *contents; + ssize_t contents_len; + error_t err; + + contents_len = -1; + err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); + if (err) + return err; + if (contents_len < 0) + return ENOMEM; + + np->nn->contents = contents; + np->nn->contents_len = contents_len; + } + + *data = np->nn->contents; + *data_len = np->nn->contents_len; + return 0; +} + +void procfs_refresh (struct node *np) +{ + if (np->nn->contents && np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); + + np->nn->contents = NULL; +} + +error_t procfs_lookup (struct node *np, const char *name, struct node **npp) +{ + error_t err = ENOENT; + + if (err && ! strcmp (name, ".")) + { + netfs_nref(*npp = np); + err = 0; + } + + if (err && np->nn->parent && ! strcmp (name, "..")) + { + netfs_nref(*npp = np->nn->parent); + err = 0; + } + + if (err && np->nn->ops->lookup) + { + err = np->nn->ops->lookup (np->nn->hook, name, npp); + if (! err) + { + (*npp)->nn_stat.st_ino = procfs_make_ino (np, name); + netfs_nref ((*npp)->nn->parent = np); + } + } + + return err; +} + +void procfs_cleanup (struct node *np) +{ + procfs_refresh (np); + + if (np->nn->ops->cleanup) + np->nn->ops->cleanup (np->nn->hook); + + if (np->nn->parent) + netfs_nrele (np->nn->parent); + + free (np->nn); +} diff --git a/procfs/procfs.h b/procfs/procfs.h new file mode 100644 index 00000000..64782ec4 --- /dev/null +++ b/procfs/procfs.h @@ -0,0 +1,93 @@ +/* Hurd /proc filesystem, basic infrastructure. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include + + +/* Interface for the procfs side. */ + +/* Any of these callback functions can be omitted, in which case + reasonable defaults will be used. The initial file mode and type + depend on whether a lookup function is provided, but can be + overridden in update_stat(). */ +struct procfs_node_ops +{ + /* Fetch the contents of a node. A pointer to the contents should be + returned in *CONTENTS and their length in *CONTENTS_LEN. The exact + nature of these data depends on whether the node is a regular file, + symlink or directory, as determined by the file mode in + netnode->nn_stat. For regular files and symlinks, they are what + you would expect; for directories, they are an argz vector of the + names of the entries. If upon return, *CONTENTS_LEN is negative or + unchanged, the call is considered to have failed because of a memory + allocation error. */ + error_t (*get_contents) (void *hook, char **contents, ssize_t *contents_len); + void (*cleanup_contents) (void *hook, char *contents, ssize_t contents_len); + + /* Lookup NAME in this directory, and store the result in *np. The + returned node should be created by lookup() using procfs_make_node() + or a derived function. Note that the parent will be kept alive as + long as the child exists, so you can safely reference the parent's + data from the child. You may want to consider locking if there's + any mutation going on, though. */ + error_t (*lookup) (void *hook, const char *name, struct node **np); + + /* Destroy this node. */ + void (*cleanup) (void *hook); +}; + +/* These helper functions can be used as procfs_node_ops.cleanup_contents. */ +void procfs_cleanup_contents_with_free (void *, char *, ssize_t); +void procfs_cleanup_contents_with_vm_deallocate (void *, char *, ssize_t); + +/* Create a new node and return it. Returns NULL if it fails to allocate + enough memory. In this case, ops->cleanup will be invoked. */ +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); + +/* Set the owner of the node NP. Must be called right after the node + has been created. */ +void procfs_node_chown (struct node *np, uid_t owner); + +/* Set the permission bits of the node NP. Must be called right after + the node has been created. */ +void procfs_node_chmod (struct node *np, mode_t mode); + +/* Set the type of the node NP. If type is S_IFLNK, appropriate + permission bits will be set as well. Must be called right after the + node has been created. */ +void procfs_node_chtype (struct node *np, mode_t type); + + +/* Interface for the libnetfs side. */ + +/* Get the inode number which will be given to a child of NP named FILENAME. + This allows us to retreive them for readdir() without creating the + corresponding child nodes. */ +ino64_t procfs_make_ino (struct node *np, const char *filename); + +/* Forget the current cached contents for the node. This is done before reads + from offset 0, to ensure that the data are recent even for utilities such as + top which keep some nodes open. */ +void procfs_refresh (struct node *np); + +error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); +error_t procfs_lookup (struct node *np, const char *name, struct node **npp); +void procfs_cleanup (struct node *np); + diff --git a/procfs/procfs_dir.c b/procfs/procfs_dir.c new file mode 100644 index 00000000..c250aa48 --- /dev/null +++ b/procfs/procfs_dir.c @@ -0,0 +1,134 @@ +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include "procfs.h" +#include "procfs_dir.h" + +struct procfs_dir_node +{ + const struct procfs_dir_ops *ops; + 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 *dir = hook; + const struct procfs_dir_entry *ent; + int pos; + + /* 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 (pos); + if (! *contents) + return ENOMEM; + + memcpy (*contents, dot_dotdot, sizeof dot_dotdot); + pos = sizeof dot_dotdot; + for (ent = dir->ops->entries; ent->name; ent++) + { + 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 *dir = hook; + const struct procfs_dir_entry *ent; + + for (ent = dir->ops->entries; ent->name && strcmp (name, ent->name); ent++); + if (! ent->name) + return ENOENT; + + 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; + + return 0; +} + +static void +procfs_dir_cleanup (void *hook) +{ + struct procfs_dir_node *dir = hook; + + if (dir->ops->cleanup) + dir->ops->cleanup (dir->hook); + + free (dir); +} + +struct node * +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, + .lookup = procfs_dir_lookup, + .cleanup_contents = procfs_cleanup_contents_with_free, + .cleanup = procfs_dir_cleanup, + }; + struct procfs_dir_node *dir; + + dir = malloc (sizeof *dir); + if (! dir) + { + if (dir_ops->cleanup) + dir_ops->cleanup (dir_hook); + + return NULL; + } + + dir->ops = dir_ops; + dir->hook = dir_hook; + + return procfs_make_node (&ops, dir); +} + diff --git a/procfs/procfs_dir.h b/procfs/procfs_dir.h new file mode 100644 index 00000000..94c5b019 --- /dev/null +++ b/procfs/procfs_dir.h @@ -0,0 +1,63 @@ +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* 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. */ + +/* 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; + 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; +}; + +/* 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_ops *ops, void *dir_hook); + diff --git a/procfs/proclist.c b/procfs/proclist.c new file mode 100644 index 00000000..58b942dc --- /dev/null +++ b/procfs/proclist.c @@ -0,0 +1,94 @@ +/* Hurd /proc filesystem, list of processes as a directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include "procfs.h" +#include "process.h" + +#define PID_STR_SIZE (3 * sizeof (pid_t) + 1) + +static error_t +proclist_get_contents (void *hook, char **contents, ssize_t *contents_len) +{ + struct ps_context *pc = hook; + pidarray_t pids; + mach_msg_type_number_t num_pids; + error_t err; + int i; + + num_pids = 0; + err = proc_getallpids (pc->server, &pids, &num_pids); + if (err) + return EIO; + + *contents = malloc (num_pids * PID_STR_SIZE); + if (*contents) + { + *contents_len = 0; + for (i=0; i < num_pids; i++) + { + int n = sprintf (*contents + *contents_len, "%d", pids[i]); + assert (n >= 0); + *contents_len += (n + 1); + } + } + else + err = ENOMEM; + + vm_deallocate (mach_task_self (), (vm_address_t) pids, num_pids * sizeof pids[0]); + return err; +} + +static error_t +proclist_lookup (void *hook, const char *name, struct node **np) +{ + struct ps_context *pc = hook; + char *endp; + pid_t pid; + + /* Self-lookups should not end up here. */ + assert (name[0]); + + /* No leading zeros allowed */ + if (name[0] == '0' && name[1]) + return ENOENT; + + pid = strtol (name, &endp, 10); + if (*endp) + return ENOENT; + + return process_lookup_pid (pc, pid, np); +} + +struct node * +proclist_make_node (struct ps_context *pc) +{ + static const struct procfs_node_ops ops = { + .get_contents = proclist_get_contents, + .lookup = proclist_lookup, + .cleanup_contents = procfs_cleanup_contents_with_free, + }; + return procfs_make_node (&ops, pc); +} + diff --git a/procfs/proclist.h b/procfs/proclist.h new file mode 100644 index 00000000..bfe95b3d --- /dev/null +++ b/procfs/proclist.h @@ -0,0 +1,23 @@ +/* Hurd /proc filesystem, list of processes as a directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +struct node * +proclist_make_node (struct ps_context *pc); diff --git a/procfs/rootdir.c b/procfs/rootdir.c new file mode 100644 index 00000000..15ef8bce --- /dev/null +++ b/procfs/rootdir.c @@ -0,0 +1,503 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "procfs.h" +#include "procfs_dir.h" +#include "main.h" + +/* This implements a directory node with the static files in /proc. + NB: the libps functions for host information return static storage; + using them would require locking and as a consequence it would be + more complicated, not simpler. */ + + +/* Helper functions */ + +/* We get the boot time by using that of the kernel process. */ +static error_t +get_boottime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps; + error_t err; + + err = _proc_stat_create (opt_kernel_pid, pc, &ps); + if (err) + return err; + + err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); + if (err || !(proc_stat_flags (ps) & PSTAT_TASK_BASIC)) + err = EIO; + + if (! err) + { + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + tv->tv_sec = tbi->creation_time.seconds; + tv->tv_usec = tbi->creation_time.microseconds; + } + + _proc_stat_free (ps); + return err; +} + +/* We get the idle time by querying the kernel's idle thread. */ +static error_t +get_idletime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps, *pst; + thread_basic_info_t tbi; + error_t err; + int i; + + err = _proc_stat_create (opt_kernel_pid, pc, &ps); + if (err) + return err; + + pst = NULL, tbi = NULL; + + err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); + if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) + { + err = EIO; + goto out; + } + + /* Look for the idle thread */ + for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE); i++) + { + if (pst) + _proc_stat_free (pst); + + pst = NULL, tbi = NULL; + if (i >= proc_stat_num_threads (ps)) + { + err = ESRCH; + goto out; + } + + err = proc_stat_thread_create (ps, i, &pst); + if (err) + continue; + + err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC); + if (err || ! (proc_stat_flags (pst) & PSTAT_THREAD_BASIC)) + continue; + + tbi = proc_stat_thread_basic_info (pst); + } + + /* We found it! */ + tv->tv_sec = tbi->system_time.seconds; + tv->tv_usec = tbi->system_time.microseconds; + err = 0; + +out: + if (pst) _proc_stat_free (pst); + _proc_stat_free (ps); + return err; +} + +static error_t +get_swapinfo (default_pager_info_t *info) +{ + mach_port_t defpager; + error_t err; + + defpager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); + if (defpager == MACH_PORT_NULL) + return errno; + + err = default_pager_info (defpager, info); + mach_port_deallocate (mach_task_self (), defpager); + + return err; +} + + +/* Content generators */ + +static error_t +rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) +{ + struct utsname uts; + int r; + + r = uname (&uts); + if (r < 0) + return errno; + + *contents_len = asprintf (contents, + "Linux version 2.6.1 (%s %s %s %s)\n", + uts.sysname, uts.release, uts.version, uts.machine); + + return 0; +} + +static error_t +rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) +{ + struct timeval time, boottime, idletime; + double up_secs, idle_secs; + error_t err; + + err = gettimeofday (&time, NULL); + if (err < 0) + return errno; + + err = get_boottime (hook, &boottime); + if (err) + return err; + + err = get_idletime (hook, &idletime); + if (err) + return err; + + timersub (&time, &boottime, &time); + up_secs = (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; + idle_secs = (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; + + /* The second field is the total idle time. As far as I know we don't + keep track of it. However, procps uses it to compute "USER_HZ", and + proc(5) specifies that it should be equal to USER_HZ times the idle value + in ticks from /proc/stat. So we assume a completely idle system both here + and there to make that work. */ + *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); + + return 0; +} + +static error_t +rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) +{ + struct timeval boottime, time, idletime; + struct vm_statistics vmstats; + unsigned long up_ticks, idle_ticks; + error_t err; + + err = gettimeofday (&time, NULL); + if (err < 0) + return errno; + + err = get_boottime (hook, &boottime); + if (err) + return err; + + err = get_idletime (hook, &idletime); + if (err) + return err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + timersub (&time, &boottime, &time); + up_ticks = opt_clk_tck * (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; + idle_ticks = opt_clk_tck * (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; + + *contents_len = asprintf (contents, + "cpu %lu 0 0 %lu 0 0 0 0 0\n" + "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" + "intr 0\n" + "page %d %d\n" + "btime %lu\n", + up_ticks - idle_ticks, idle_ticks, + up_ticks - idle_ticks, idle_ticks, + vmstats.pageins, vmstats.pageouts, + boottime.tv_sec); + + return 0; +} + +static error_t +rootdir_gc_loadavg (void *hook, char **contents, ssize_t *contents_len) +{ + host_load_info_data_t hli; + mach_msg_type_number_t cnt; + error_t err; + + cnt = HOST_LOAD_INFO_COUNT; + err = host_info (mach_host_self (), HOST_LOAD_INFO, (host_info_t) &hli, &cnt); + if (err) + return err; + + assert (cnt == HOST_LOAD_INFO_COUNT); + *contents_len = asprintf (contents, + "%.2f %.2f %.2f 1/0 0\n", + hli.avenrun[0] / (double) LOAD_SCALE, + hli.avenrun[1] / (double) LOAD_SCALE, + hli.avenrun[2] / (double) LOAD_SCALE); + + return 0; +} + +static error_t +rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) +{ + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + struct vm_statistics vmstats; + default_pager_info_t swap; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + err = get_swapinfo (&swap); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf (contents, + "MemTotal: %14lu kB\n" + "MemFree: %14lu kB\n" + "Active: %14lu kB\n" + "Inactive: %14lu kB\n" + "Mlocked: %14lu kB\n" + "SwapTotal:%14lu kB\n" + "SwapFree: %14lu kB\n" + , + /* TODO: check that these are really 1024-bytes kBs. */ + (long unsigned) hbi.memory_size / 1024, + (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024, + (long unsigned) swap.dpi_total_space / 1024, + (long unsigned) swap.dpi_free_space / 1024); + + return 0; +} + +static error_t +rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len) +{ + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + struct vm_statistics vmstats; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf (contents, + "nr_free_pages %lu\n" + "nr_inactive_anon %lu\n" + "nr_active_anon %lu\n" + "nr_inactive_file %lu\n" + "nr_active_file %lu\n" + "nr_unevictable %lu\n" + "nr_mlock %lu\n" + "pgpgin %lu\n" + "pgpgout %lu\n" + "pgfault %lu\n", + (long unsigned) vmstats.free_count, + /* FIXME: how can we distinguish the anon/file pages? Maybe we can + ask the default pager how many it manages? */ + (long unsigned) vmstats.inactive_count, + (long unsigned) vmstats.active_count, + (long unsigned) 0, + (long unsigned) 0, + (long unsigned) vmstats.wire_count, + (long unsigned) vmstats.wire_count, + (long unsigned) vmstats.pageins, + (long unsigned) vmstats.pageouts, + (long unsigned) vmstats.faults); + + return 0; +} + +static error_t +rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) +{ + struct ps_context *pc = hook; + struct proc_stat *ps; + error_t err; + + err = _proc_stat_create (opt_kernel_pid, pc, &ps); + if (err) + return EIO; + + err = proc_stat_set_flags (ps, PSTAT_ARGS); + if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS)) + { + err = EIO; + goto out; + } + + *contents_len = proc_stat_args_len (ps); + *contents = malloc (*contents_len); + if (! *contents) + { + err = ENOMEM; + goto out; + } + + memcpy (*contents, proc_stat_args (ps), *contents_len); + argz_stringify (*contents, *contents_len, ' '); + (*contents)[*contents_len - 1] = '\n'; + +out: + _proc_stat_free (ps); + 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) +{ + *contents_len = asprintf (contents, "%d", opt_fake_self); + return 0; +} + + +/* Glue logic and entries table */ + +static struct node * +rootdir_file_make_node (void *dir_hook, const void *entry_hook) +{ + /* The entry hook we use is actually a procfs_node_ops for the file to be + created. The hook associated to these newly created files (and passed + to the generators above as a consequence) is always the same global + ps_context, which we get from rootdir_make_node as the directory hook. */ + return procfs_make_node (entry_hook, dir_hook); +} + +static struct node * +rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) +{ + struct node *np = procfs_make_node (entry_hook, dir_hook); + if (np) + procfs_node_chtype (np, S_IFLNK); + return np; +} + +static const struct procfs_dir_entry rootdir_entries[] = { + { + .name = "self", + .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", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_version, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "uptime", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_uptime, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "stat", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_stat, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "loadavg", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_loadavg, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "meminfo", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_meminfo, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "vmstat", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_vmstat, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "cmdline", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_cmdline, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, +#ifdef PROFILE + /* In order to get a usable gmon.out file, we must apparently use exit(). */ + { + .name = "exit", + .ops = { + .make_node = exit, + }, + }, +#endif + {} +}; + +struct node +*rootdir_make_node (struct ps_context *pc) +{ + 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); +} + diff --git a/procfs/rootdir.h b/procfs/rootdir.h new file mode 100644 index 00000000..6980da8f --- /dev/null +++ b/procfs/rootdir.h @@ -0,0 +1,23 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +struct node * +rootdir_make_node (struct ps_context *pc); diff --git a/procfs_dir.c b/procfs_dir.c deleted file mode 100644 index c250aa48..00000000 --- a/procfs_dir.c +++ /dev/null @@ -1,134 +0,0 @@ -/* Hurd /proc filesystem, infrastructure for directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include "procfs.h" -#include "procfs_dir.h" - -struct procfs_dir_node -{ - const struct procfs_dir_ops *ops; - 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 *dir = hook; - const struct procfs_dir_entry *ent; - int pos; - - /* 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 (pos); - if (! *contents) - return ENOMEM; - - memcpy (*contents, dot_dotdot, sizeof dot_dotdot); - pos = sizeof dot_dotdot; - for (ent = dir->ops->entries; ent->name; ent++) - { - 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 *dir = hook; - const struct procfs_dir_entry *ent; - - for (ent = dir->ops->entries; ent->name && strcmp (name, ent->name); ent++); - if (! ent->name) - return ENOENT; - - 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; - - return 0; -} - -static void -procfs_dir_cleanup (void *hook) -{ - struct procfs_dir_node *dir = hook; - - if (dir->ops->cleanup) - dir->ops->cleanup (dir->hook); - - free (dir); -} - -struct node * -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, - .lookup = procfs_dir_lookup, - .cleanup_contents = procfs_cleanup_contents_with_free, - .cleanup = procfs_dir_cleanup, - }; - struct procfs_dir_node *dir; - - dir = malloc (sizeof *dir); - if (! dir) - { - if (dir_ops->cleanup) - dir_ops->cleanup (dir_hook); - - return NULL; - } - - dir->ops = dir_ops; - dir->hook = dir_hook; - - return procfs_make_node (&ops, dir); -} - diff --git a/procfs_dir.h b/procfs_dir.h deleted file mode 100644 index 94c5b019..00000000 --- a/procfs_dir.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Hurd /proc filesystem, infrastructure for directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* 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. */ - -/* 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; - 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; -}; - -/* 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_ops *ops, void *dir_hook); - diff --git a/proclist.c b/proclist.c deleted file mode 100644 index 58b942dc..00000000 --- a/proclist.c +++ /dev/null @@ -1,94 +0,0 @@ -/* Hurd /proc filesystem, list of processes as a directory. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include "procfs.h" -#include "process.h" - -#define PID_STR_SIZE (3 * sizeof (pid_t) + 1) - -static error_t -proclist_get_contents (void *hook, char **contents, ssize_t *contents_len) -{ - struct ps_context *pc = hook; - pidarray_t pids; - mach_msg_type_number_t num_pids; - error_t err; - int i; - - num_pids = 0; - err = proc_getallpids (pc->server, &pids, &num_pids); - if (err) - return EIO; - - *contents = malloc (num_pids * PID_STR_SIZE); - if (*contents) - { - *contents_len = 0; - for (i=0; i < num_pids; i++) - { - int n = sprintf (*contents + *contents_len, "%d", pids[i]); - assert (n >= 0); - *contents_len += (n + 1); - } - } - else - err = ENOMEM; - - vm_deallocate (mach_task_self (), (vm_address_t) pids, num_pids * sizeof pids[0]); - return err; -} - -static error_t -proclist_lookup (void *hook, const char *name, struct node **np) -{ - struct ps_context *pc = hook; - char *endp; - pid_t pid; - - /* Self-lookups should not end up here. */ - assert (name[0]); - - /* No leading zeros allowed */ - if (name[0] == '0' && name[1]) - return ENOENT; - - pid = strtol (name, &endp, 10); - if (*endp) - return ENOENT; - - return process_lookup_pid (pc, pid, np); -} - -struct node * -proclist_make_node (struct ps_context *pc) -{ - static const struct procfs_node_ops ops = { - .get_contents = proclist_get_contents, - .lookup = proclist_lookup, - .cleanup_contents = procfs_cleanup_contents_with_free, - }; - return procfs_make_node (&ops, pc); -} - diff --git a/proclist.h b/proclist.h deleted file mode 100644 index bfe95b3d..00000000 --- a/proclist.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Hurd /proc filesystem, list of processes as a directory. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include - -struct node * -proclist_make_node (struct ps_context *pc); diff --git a/rootdir.c b/rootdir.c deleted file mode 100644 index 15ef8bce..00000000 --- a/rootdir.c +++ /dev/null @@ -1,503 +0,0 @@ -/* Hurd /proc filesystem, permanent files of the root directory. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "procfs.h" -#include "procfs_dir.h" -#include "main.h" - -/* This implements a directory node with the static files in /proc. - NB: the libps functions for host information return static storage; - using them would require locking and as a consequence it would be - more complicated, not simpler. */ - - -/* Helper functions */ - -/* We get the boot time by using that of the kernel process. */ -static error_t -get_boottime (struct ps_context *pc, struct timeval *tv) -{ - struct proc_stat *ps; - error_t err; - - err = _proc_stat_create (opt_kernel_pid, pc, &ps); - if (err) - return err; - - err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); - if (err || !(proc_stat_flags (ps) & PSTAT_TASK_BASIC)) - err = EIO; - - if (! err) - { - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - tv->tv_sec = tbi->creation_time.seconds; - tv->tv_usec = tbi->creation_time.microseconds; - } - - _proc_stat_free (ps); - return err; -} - -/* We get the idle time by querying the kernel's idle thread. */ -static error_t -get_idletime (struct ps_context *pc, struct timeval *tv) -{ - struct proc_stat *ps, *pst; - thread_basic_info_t tbi; - error_t err; - int i; - - err = _proc_stat_create (opt_kernel_pid, pc, &ps); - if (err) - return err; - - pst = NULL, tbi = NULL; - - err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); - if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) - { - err = EIO; - goto out; - } - - /* Look for the idle thread */ - for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE); i++) - { - if (pst) - _proc_stat_free (pst); - - pst = NULL, tbi = NULL; - if (i >= proc_stat_num_threads (ps)) - { - err = ESRCH; - goto out; - } - - err = proc_stat_thread_create (ps, i, &pst); - if (err) - continue; - - err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC); - if (err || ! (proc_stat_flags (pst) & PSTAT_THREAD_BASIC)) - continue; - - tbi = proc_stat_thread_basic_info (pst); - } - - /* We found it! */ - tv->tv_sec = tbi->system_time.seconds; - tv->tv_usec = tbi->system_time.microseconds; - err = 0; - -out: - if (pst) _proc_stat_free (pst); - _proc_stat_free (ps); - return err; -} - -static error_t -get_swapinfo (default_pager_info_t *info) -{ - mach_port_t defpager; - error_t err; - - defpager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); - if (defpager == MACH_PORT_NULL) - return errno; - - err = default_pager_info (defpager, info); - mach_port_deallocate (mach_task_self (), defpager); - - return err; -} - - -/* Content generators */ - -static error_t -rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) -{ - struct utsname uts; - int r; - - r = uname (&uts); - if (r < 0) - return errno; - - *contents_len = asprintf (contents, - "Linux version 2.6.1 (%s %s %s %s)\n", - uts.sysname, uts.release, uts.version, uts.machine); - - return 0; -} - -static error_t -rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) -{ - struct timeval time, boottime, idletime; - double up_secs, idle_secs; - error_t err; - - err = gettimeofday (&time, NULL); - if (err < 0) - return errno; - - err = get_boottime (hook, &boottime); - if (err) - return err; - - err = get_idletime (hook, &idletime); - if (err) - return err; - - timersub (&time, &boottime, &time); - up_secs = (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; - idle_secs = (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; - - /* The second field is the total idle time. As far as I know we don't - keep track of it. However, procps uses it to compute "USER_HZ", and - proc(5) specifies that it should be equal to USER_HZ times the idle value - in ticks from /proc/stat. So we assume a completely idle system both here - and there to make that work. */ - *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); - - return 0; -} - -static error_t -rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) -{ - struct timeval boottime, time, idletime; - struct vm_statistics vmstats; - unsigned long up_ticks, idle_ticks; - error_t err; - - err = gettimeofday (&time, NULL); - if (err < 0) - return errno; - - err = get_boottime (hook, &boottime); - if (err) - return err; - - err = get_idletime (hook, &idletime); - if (err) - return err; - - err = vm_statistics (mach_task_self (), &vmstats); - if (err) - return EIO; - - timersub (&time, &boottime, &time); - up_ticks = opt_clk_tck * (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; - idle_ticks = opt_clk_tck * (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; - - *contents_len = asprintf (contents, - "cpu %lu 0 0 %lu 0 0 0 0 0\n" - "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" - "intr 0\n" - "page %d %d\n" - "btime %lu\n", - up_ticks - idle_ticks, idle_ticks, - up_ticks - idle_ticks, idle_ticks, - vmstats.pageins, vmstats.pageouts, - boottime.tv_sec); - - return 0; -} - -static error_t -rootdir_gc_loadavg (void *hook, char **contents, ssize_t *contents_len) -{ - host_load_info_data_t hli; - mach_msg_type_number_t cnt; - error_t err; - - cnt = HOST_LOAD_INFO_COUNT; - err = host_info (mach_host_self (), HOST_LOAD_INFO, (host_info_t) &hli, &cnt); - if (err) - return err; - - assert (cnt == HOST_LOAD_INFO_COUNT); - *contents_len = asprintf (contents, - "%.2f %.2f %.2f 1/0 0\n", - hli.avenrun[0] / (double) LOAD_SCALE, - hli.avenrun[1] / (double) LOAD_SCALE, - hli.avenrun[2] / (double) LOAD_SCALE); - - return 0; -} - -static error_t -rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) -{ - host_basic_info_data_t hbi; - mach_msg_type_number_t cnt; - struct vm_statistics vmstats; - default_pager_info_t swap; - error_t err; - - err = vm_statistics (mach_task_self (), &vmstats); - if (err) - return EIO; - - cnt = HOST_BASIC_INFO_COUNT; - err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); - if (err) - return err; - - err = get_swapinfo (&swap); - if (err) - return err; - - assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf (contents, - "MemTotal: %14lu kB\n" - "MemFree: %14lu kB\n" - "Active: %14lu kB\n" - "Inactive: %14lu kB\n" - "Mlocked: %14lu kB\n" - "SwapTotal:%14lu kB\n" - "SwapFree: %14lu kB\n" - , - /* TODO: check that these are really 1024-bytes kBs. */ - (long unsigned) hbi.memory_size / 1024, - (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, - (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, - (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, - (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024, - (long unsigned) swap.dpi_total_space / 1024, - (long unsigned) swap.dpi_free_space / 1024); - - return 0; -} - -static error_t -rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len) -{ - host_basic_info_data_t hbi; - mach_msg_type_number_t cnt; - struct vm_statistics vmstats; - error_t err; - - err = vm_statistics (mach_task_self (), &vmstats); - if (err) - return EIO; - - cnt = HOST_BASIC_INFO_COUNT; - err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); - if (err) - return err; - - assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf (contents, - "nr_free_pages %lu\n" - "nr_inactive_anon %lu\n" - "nr_active_anon %lu\n" - "nr_inactive_file %lu\n" - "nr_active_file %lu\n" - "nr_unevictable %lu\n" - "nr_mlock %lu\n" - "pgpgin %lu\n" - "pgpgout %lu\n" - "pgfault %lu\n", - (long unsigned) vmstats.free_count, - /* FIXME: how can we distinguish the anon/file pages? Maybe we can - ask the default pager how many it manages? */ - (long unsigned) vmstats.inactive_count, - (long unsigned) vmstats.active_count, - (long unsigned) 0, - (long unsigned) 0, - (long unsigned) vmstats.wire_count, - (long unsigned) vmstats.wire_count, - (long unsigned) vmstats.pageins, - (long unsigned) vmstats.pageouts, - (long unsigned) vmstats.faults); - - return 0; -} - -static error_t -rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) -{ - struct ps_context *pc = hook; - struct proc_stat *ps; - error_t err; - - err = _proc_stat_create (opt_kernel_pid, pc, &ps); - if (err) - return EIO; - - err = proc_stat_set_flags (ps, PSTAT_ARGS); - if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS)) - { - err = EIO; - goto out; - } - - *contents_len = proc_stat_args_len (ps); - *contents = malloc (*contents_len); - if (! *contents) - { - err = ENOMEM; - goto out; - } - - memcpy (*contents, proc_stat_args (ps), *contents_len); - argz_stringify (*contents, *contents_len, ' '); - (*contents)[*contents_len - 1] = '\n'; - -out: - _proc_stat_free (ps); - 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) -{ - *contents_len = asprintf (contents, "%d", opt_fake_self); - return 0; -} - - -/* Glue logic and entries table */ - -static struct node * -rootdir_file_make_node (void *dir_hook, const void *entry_hook) -{ - /* The entry hook we use is actually a procfs_node_ops for the file to be - created. The hook associated to these newly created files (and passed - to the generators above as a consequence) is always the same global - ps_context, which we get from rootdir_make_node as the directory hook. */ - return procfs_make_node (entry_hook, dir_hook); -} - -static struct node * -rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) -{ - struct node *np = procfs_make_node (entry_hook, dir_hook); - if (np) - procfs_node_chtype (np, S_IFLNK); - return np; -} - -static const struct procfs_dir_entry rootdir_entries[] = { - { - .name = "self", - .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", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_version, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "uptime", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_uptime, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "stat", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_stat, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "loadavg", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_loadavg, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "meminfo", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_meminfo, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "vmstat", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_vmstat, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "cmdline", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_cmdline, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, -#ifdef PROFILE - /* In order to get a usable gmon.out file, we must apparently use exit(). */ - { - .name = "exit", - .ops = { - .make_node = exit, - }, - }, -#endif - {} -}; - -struct node -*rootdir_make_node (struct ps_context *pc) -{ - 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); -} - diff --git a/rootdir.h b/rootdir.h deleted file mode 100644 index 6980da8f..00000000 --- a/rootdir.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Hurd /proc filesystem, permanent files of the root directory. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include - -struct node * -rootdir_make_node (struct ps_context *pc); -- cgit v1.2.3