diff options
Diffstat (limited to 'procfs')
-rw-r--r-- | procfs/Makefile | 26 | ||||
-rw-r--r-- | procfs/TODO | 24 | ||||
-rw-r--r-- | procfs/dircat.c | 128 | ||||
-rw-r--r-- | procfs/dircat.h | 29 | ||||
-rw-r--r-- | procfs/main.c | 190 | ||||
-rw-r--r-- | procfs/main.h | 25 | ||||
-rw-r--r-- | procfs/netfs.c | 445 | ||||
-rw-r--r-- | procfs/process.c | 387 | ||||
-rw-r--r-- | procfs/process.h | 27 | ||||
-rw-r--r-- | procfs/procfs.c | 203 | ||||
-rw-r--r-- | procfs/procfs.h | 93 | ||||
-rw-r--r-- | procfs/procfs_dir.c | 134 | ||||
-rw-r--r-- | procfs/procfs_dir.h | 63 | ||||
-rw-r--r-- | procfs/proclist.c | 94 | ||||
-rw-r--r-- | procfs/proclist.h | 23 | ||||
-rw-r--r-- | procfs/rootdir.c | 503 | ||||
-rw-r--r-- | procfs/rootdir.h | 23 |
17 files changed, 2417 insertions, 0 deletions
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 <stdlib.h> +#include <string.h> +#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 <mach.h> +#include <hurd.h> +#include <unistd.h> +#include <error.h> +#include <argp.h> +#include <hurd/netfs.h> +#include <ps.h> +#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 <hurd/netfs.h> +#include <hurd/fshelp.h> +#include <sys/mman.h> +#include <mach/vm_param.h> +#include <dirent.h> +#include <fcntl.h> +#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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <hurd/process.h> +#include <hurd/resource.h> +#include <mach/vm_param.h> +#include <ps.h> +#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 <linux/sched.h> */ + "%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 <ps.h> + +/* 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 <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <mach.h> +#include <hurd/netfs.h> +#include <hurd/fshelp.h> +#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 <hurd/hurd_types.h> +#include <hurd/netfs.h> + + +/* 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 <stdlib.h> +#include <string.h> +#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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <mach.h> +#include <hurd/process.h> +#include <ps.h> +#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 <ps.h> + +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 <mach/vm_param.h> +#include <mach/vm_statistics.h> +#include <mach/default_pager.h> +#include <hurd/paths.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/utsname.h> +#include <sys/stat.h> +#include <argz.h> +#include <ps.h> +#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 <ps.h> + +struct node * +rootdir_make_node (struct ps_context *pc); |