summaryrefslogtreecommitdiff
path: root/procfs
diff options
context:
space:
mode:
Diffstat (limited to 'procfs')
-rw-r--r--procfs/Makefile26
-rw-r--r--procfs/TODO24
-rw-r--r--procfs/dircat.c128
-rw-r--r--procfs/dircat.h29
-rw-r--r--procfs/main.c190
-rw-r--r--procfs/main.h25
-rw-r--r--procfs/netfs.c445
-rw-r--r--procfs/process.c387
-rw-r--r--procfs/process.h27
-rw-r--r--procfs/procfs.c203
-rw-r--r--procfs/procfs.h93
-rw-r--r--procfs/procfs_dir.c134
-rw-r--r--procfs/procfs_dir.h63
-rw-r--r--procfs/proclist.c94
-rw-r--r--procfs/proclist.h23
-rw-r--r--procfs/rootdir.c503
-rw-r--r--procfs/rootdir.h23
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);