summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile48
-rw-r--r--TODO24
-rw-r--r--bootstrap.c95
-rw-r--r--dircat.c128
-rw-r--r--dircat.h29
-rw-r--r--main.c190
-rw-r--r--main.h25
-rw-r--r--netfs.c652
-rw-r--r--node.c195
-rw-r--r--process.c393
-rw-r--r--process.h27
-rw-r--r--procfs.c282
-rw-r--r--procfs.h263
-rw-r--r--procfs_dir.c691
-rw-r--r--procfs_dir.h63
-rw-r--r--procfs_nonpid_files.c527
-rw-r--r--procfs_pid.h88
-rw-r--r--procfs_pid_files.c583
-rw-r--r--proclist.c94
-rw-r--r--proclist.h23
-rw-r--r--rootdir.c506
-rw-r--r--rootdir.h23
22 files changed, 2177 insertions, 2772 deletions
diff --git a/Makefile b/Makefile
index 500a237..a397522 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,26 @@
-# Makefile - for procfs
-#
-# Copyright (C) 2008 Free Software Foundation, Inc.
-#
-# This program 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.
-#
-# This program 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.
+TARGET = procfs
+OBJS = procfs.o netfs.o procfs_dir.o \
+ process.o proclist.o rootdir.o dircat.o main.o
+LIBS = -lnetfs -lps
-dir := procfs
-makemode := server
+CC = gcc
+CFLAGS = -Wall -g
+CPPFLAGS =
+LDFLAGS =
-target = procfs
+ifdef PROFILE
+CFLAGS= -g -pg
+CPPFLAGS= -DPROFILE
+LDFLAGS= -static
+LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lthreads -lihash -lshouldbeinlibc
+endif
-SRCS = procfs.c bootstrap.c netfs.c procfs_dir.c node.c procfs_pid_files.c procfs_nonpid_files.c
-LCLHDRS = procfs.h procfs_pid.h
+CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
-OBJS = $(SRCS:.c=.o)
-HURDLIBS = netfs fshelp iohelp threads ports ihash ps shouldbeinlibc
-
-include ../Makeconf
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..952d67b
--- /dev/null
+++ b/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/bootstrap.c b/bootstrap.c
deleted file mode 100644
index 73d31f0..0000000
--- a/bootstrap.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
-
- bootstrap.c -- This file is functions for starting up
- and initializers for the procfs translator
- defined in procfs.h
-
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
- procfs 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.
-
- procfs 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-*/
-
-#include <stddef.h>
-#include <hurd/ihash.h>
-#include <hurd/netfs.h>
-
-#include "procfs.h"
-
-struct ps_context *ps_context;
-
-/* This function is used to initialize the whole translator, can be
- effect called as bootstrapping the translator. */
-error_t procfs_init ()
-{
- error_t err;
-
- err = ps_context_create (getproc (), &ps_context);
-
- return err;
-}
-
-/* Create a new procfs filesystem. */
-error_t procfs_create (char *procfs_root, int fsid,
- struct procfs **fs)
-{
- error_t err;
- /* This is the enclosing directory for this filesystem's
- root node */
- struct procfs_dir *topmost_root_dir;
-
- /* And also a topmost-root node, just used for locking
- TOPMOST_ROOT_DIR. */
- struct node *topmost_root;
-
- /* The new node for the filesystem's root. */
- struct procfs *new = malloc (sizeof (struct procfs));
-
- if (! new)
- return ENOMEM;
-
- new->fsid = fsid;
- new->next_inode = 2;
-
- hurd_ihash_init (&new->inode_mappings,
- offsetof (struct procfs_dir_entry, inode_locp));
- spin_lock_init (&new->inode_mappings_lock);
-
- topmost_root = netfs_make_node (0);
- if (! topmost_root)
- err = ENOMEM;
- else
- {
- err = procfs_dir_create (new, topmost_root, procfs_root,
- &topmost_root_dir);
- if (! err)
- {
- /* ADDITIONAL BOOTSTRAPPING OF THE ROOT NODE */
- err = procfs_dir_null_lookup (topmost_root_dir, &new->root);
- }
- }
-
- if (err)
- {
- hurd_ihash_destroy (&new->inode_mappings);
- free (new);
- }
- else
- *fs = new;
-
- return err;
-}
-
diff --git a/dircat.c b/dircat.c
new file mode 100644
index 0000000..5a60899
--- /dev/null
+++ b/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/dircat.h b/dircat.h
new file mode 100644
index 0000000..4177b38
--- /dev/null
+++ b/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/main.c b/main.c
new file mode 100644
index 0000000..3a976cc
--- /dev/null
+++ b/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/main.h b/main.h
new file mode 100644
index 0000000..4e28b7e
--- /dev/null
+++ b/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/netfs.c b/netfs.c
index 4f6fd5c..24a6603 100644
--- a/netfs.c
+++ b/netfs.c
@@ -1,467 +1,445 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
+/* Hurd /proc filesystem, interface with libnetfs.
+ Copyright (C) 2010 Free Software Foundation, Inc.
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
- procfs is free software; you can redistribute it and/or
+ 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.
- procfs is distributed in the hope that it will be useful, but
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-*/
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-#include <stddef.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
+#include <hurd/netfs.h>
+#include <hurd/fshelp.h>
+#include <sys/mman.h>
+#include <mach/vm_param.h>
#include <dirent.h>
-#include <string.h>
+#include <fcntl.h>
+#include "procfs.h"
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <hurd/netfs.h>
+#define PROCFS_SERVER_NAME "procfs"
+#define PROCFS_SERVER_VERSION "0.1.0"
+#define PROCFS_MAXSYMLINKS 16
-#include "procfs.h"
+
+/* Interesting libnetfs callback functions. */
-/* Trivial definitions. */
+/* The user must define this variable. Set this to the name of the
+ filesystem server. */
+char *netfs_server_name = PROCFS_SERVER_NAME;
-/* Make sure that NP->nn_stat is filled with current information. CRED
- identifies the user responsible for the operation. */
-error_t
-netfs_validate_stat (struct node *node, struct iouser *cred)
-{
- return procfs_refresh_node (node);
-}
+/* The user must define this variables. Set this to be the server
+ version number. */
+char *netfs_server_version = PROCFS_SERVER_VERSION;
-/* This should sync the file NODE completely to disk, for the user CRED. If
- WAIT is set, return only after sync is completely finished. */
-error_t
-netfs_attempt_sync (struct iouser *cred, struct node *node, int wait)
+/* 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;
}
-/* Attempt to create a new directory named NAME in DIR for USER with mode
- MODE. */
-error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir,
- char *name, mode_t mode)
+/* 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)
{
- return EROFS;
-}
+ char *contents;
+ ssize_t contents_len;
+ error_t err;
-/* Attempt to remove directory named NAME in DIR for USER. */
-error_t netfs_attempt_rmdir (struct iouser *user,
- struct node *dir, char *name)
-{
- return EROFS;
-}
+ if (offset == 0)
+ procfs_refresh (np);
-/* Attempt to set the passive translator record for FILE to ARGZ (of length
- ARGZLEN) for user CRED. */
-error_t netfs_set_translator (struct iouser *cred, struct node *node,
- char *argz, size_t argzlen)
-{
- return EROFS;
-}
+ err = procfs_get_contents (np, &contents, &contents_len);
+ if (err)
+ return err;
-/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE
- to the new node upon return. On any error, clear *NODE. *NODE 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 **node)
-{
- *node = NULL;
- mutex_unlock (&dir->lock);
- return EROFS;
+ contents += offset;
+ contents_len -= offset;
+
+ if (*len > contents_len)
+ *len = contents_len;
+ if (*len < 0)
+ *len = 0;
+
+ memcpy (data, contents, *len);
+ return 0;
}
-/* Node NODE 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 *node,
- int flags, int newnode)
+/* 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)
{
- error_t err = procfs_refresh_node (node);
- if (!err && (flags & O_READ))
- err = fshelp_access (&node->nn_stat, S_IREAD, user);
- if (!err && (flags & O_WRITE))
- err = fshelp_access (&node->nn_stat, S_IWRITE, user);
- if (!err && (flags & O_EXEC))
- err = fshelp_access (&node->nn_stat, S_IEXEC, user);
- return err;
+ 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;
}
-/* This should attempt a utimes call for the user specified by CRED on node
- NODE, to change the atime to ATIME and the mtime to MTIME. */
-error_t
-netfs_attempt_utimes (struct iouser *cred, struct node *node,
- struct timespec *atime, struct timespec *mtime)
+/* 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)
{
- error_t err = procfs_refresh_node (node);
- int flags = TOUCH_CTIME;
-
- if (! err)
- err = fshelp_isowner (&node->nn_stat, cred);
+ int i;
- if (! err)
+ *datacnt = 0;
+ for (i = 0; contents_len && (nentries < 0 || i < nentries); i++)
{
- if (atime)
- node->nn_stat.st_atim = *atime;
- else
- flags |= TOUCH_ATIME;
+ int namlen = strlen (contents);
+ int reclen = sizeof (struct dirent) + namlen;
- if (mtime)
- node->nn_stat.st_mtim = *mtime;
- else
- flags |= TOUCH_MTIME;
-
- fshelp_touch (&node->nn_stat, flags, procfs_maptime);
+ 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 err;
+ return i;
}
-/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
- in *TYPES for file NODE and user CRED. */
-error_t
-netfs_report_access (struct iouser *cred, struct node *node, int *types)
+/* 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)
{
- error_t err = procfs_refresh_node (node);
+ char *contents;
+ ssize_t contents_len;
+ error_t err;
- if (! err)
- {
- *types = 0;
- if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
- *types |= O_READ;
- if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
- *types |= O_WRITE;
- if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
- *types |= O_EXEC;
- }
+ if (entry == 0)
+ procfs_refresh (dir);
+
+ err = procfs_get_contents (dir, &contents, &contents_len);
+ if (err)
+ return err;
- return err;
-}
+ /* We depend on the fact that CONTENTS is terminated. */
+ assert (contents_len == 0 || contents[contents_len - 1] == '\0');
-/* The granularity with which we allocate space to return our result. */
-#define DIRENTS_CHUNK_SIZE (8*1024)
+ /* Skip to the first requested entry. */
+ while (contents_len && entry--)
+ {
+ int ofs = strlen (contents) + 1;
+ contents += ofs;
+ contents_len -= ofs;
+ }
-/* Returned directory entries are aligned to blocks this many bytes long.
- Must be a power of two. */
-#define DIRENT_ALIGN 4
-#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+ /* 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;
-/* Length is structure before the name + the name + '\0', all
- padded to a four-byte alignment. */
-#define DIRENT_LEN(name_len) \
- ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
- & ~(DIRENT_ALIGN - 1))
+ *data = n;
+ }
+ /* Do the actual conversion. */
+ *amt = putentries (contents, contents_len, nentries, *data, datacnt);
+ return 0;
+}
-/* Fetch a directory */
-error_t
-netfs_get_dirents (struct iouser *cred, struct node *dir,
- int first_entry, int max_entries, char **data,
- mach_msg_type_number_t *data_len,
- vm_size_t max_data_len, int *data_entries)
+/* 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 = procfs_refresh_node (dir);
- struct procfs_dir_entry *dir_entry;
+ error_t err;
+
+ err = procfs_lookup (dir, name, np);
+ mutex_unlock (&dir->lock);
if (! err)
- {
- if (dir->nn->dir)
- {
- if (! procfs_dir_refresh (dir->nn->dir, dir == dir->nn->fs->root))
- {
- for (dir_entry = dir->nn->dir->ordered; first_entry > 0 &&
- dir_entry; first_entry--,
- dir_entry = dir_entry->ordered_next);
- if (! dir_entry )
- max_entries = 0;
-
- if (max_entries != 0)
- {
- size_t size = 0;
- char *p;
- int count = 0;
-
-
- if (max_data_len == 0)
- size = DIRENTS_CHUNK_SIZE;
- else if (max_data_len > DIRENTS_CHUNK_SIZE)
- size = DIRENTS_CHUNK_SIZE;
- else
- size = max_data_len;
-
- *data = mmap (0, size, PROT_READ|PROT_WRITE,
- MAP_ANON, 0, 0);
-
- err = ((void *) *data == (void *) -1) ? errno : 0;
-
- if (! err)
- {
- p = *data;
-
- /* This gets all the actual entries present. */
-
- while ((max_entries == -1 || count < max_entries) && dir_entry)
- {
- struct dirent hdr;
- size_t name_len = strlen (dir_entry->name);
- size_t sz = DIRENT_LEN (name_len);
- int entry_type = IFTODT (dir_entry->stat.st_mode);
-
- if ((p - *data) + sz > size)
- {
- if (max_data_len > 0)
- break;
- else /* The Buffer Size must be increased. */
- {
- vm_address_t extension = (vm_address_t)(*data + size);
- err = vm_allocate (mach_task_self (), &extension,
- DIRENTS_CHUNK_SIZE, 0);
-
- if (err)
- break;
-
- size += DIRENTS_CHUNK_SIZE;
- }
- }
-
- hdr.d_namlen = name_len;
- hdr.d_fileno = dir_entry->stat.st_ino;
- hdr.d_reclen = sz;
- hdr.d_type = entry_type;
-
- memcpy (p, &hdr, DIRENT_NAME_OFFS);
- strcpy (p + DIRENT_NAME_OFFS, dir_entry->name);
-
- p += sz;
-
- count++;
- dir_entry = dir_entry->ordered_next;
- }
-
- if (err)
- munmap (*data, size);
- else
- {
- vm_address_t alloc_end = (vm_address_t)(*data + size);
- vm_address_t real_end = round_page (p);
- if (alloc_end > real_end)
- munmap ((caddr_t) real_end, alloc_end - real_end);
- *data_len = p - *data;
- *data_entries = count;
- }
- }
- }
- else
- {
- *data_len = 0;
- *data_entries = 0;
- }
- }
- }
- else
- return ENOTDIR;
- }
-
- procfs_dir_entries_remove (dir->nn->dir);
+ mutex_lock (&(*np)->lock);
+
return err;
-}
+}
-/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If
- the name was not found, then return ENOENT. On any error, clear *NODE.
- (*NODE, if found, should be locked, this call should unlock DIR no matter
- what.) */
-error_t netfs_attempt_lookup (struct iouser *user, struct node *dir,
- char *name, struct node **node)
+/* The user must define this function. Node NP has no more references;
+ free all its associated storage. */
+void netfs_node_norefs (struct node *np)
{
- error_t err = procfs_refresh_node (dir);
+ spin_unlock (&netfs_node_refcnt_lock);
- if (! err)
- err = procfs_dir_lookup (dir->nn->dir, name, node);
+ procfs_cleanup (np);
+ free (np);
- return err;
+ spin_lock (&netfs_node_refcnt_lock);
}
-/* Delete NAME in DIR for USER. */
-error_t netfs_attempt_unlink (struct iouser *user, struct node *dir,
- char *name)
+
+/* 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)
{
- return EROFS;
+ 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;
}
-/* Note that in this one call, 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)
+/* 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)
{
- return EROFS;
+ *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;
}
-/* This should attempt a chmod call for the user specified by CRED on node
- NODE, to change the owner to UID and the group to GID. */
-error_t netfs_attempt_chown (struct iouser *cred, struct node *node,
+
+/* 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;
}
-/* This should attempt a chauthor call for the user specified by CRED on node
- NODE, to change the author to AUTHOR. */
-error_t netfs_attempt_chauthor (struct iouser *cred, struct node *node,
+/* 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;
}
-/* This should attempt a chmod call for the user specified by CRED on 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 *node,
+/* 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;
}
-/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
-error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *node,
+/* 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;
}
-/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
- S_IFCHR. */
-error_t netfs_attempt_mkdev (struct iouser *cred, struct node *node,
+/* 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;
}
-
-/* This should attempt a chflags call for the user specified by CRED on node
- NODE, to change the flags to FLAGS. */
-error_t netfs_attempt_chflags (struct iouser *cred, struct node *node,
+/* 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;
}
-/* This should attempt to set the size of the file NODE (for user CRED) to
- SIZE bytes long. */
-error_t netfs_attempt_set_size (struct iouser *cred, struct node *node,
- off_t size)
+/* 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;
}
-/* This should attempt to fetch filesystem status information for the remote
- filesystem, for the user CRED. */
-error_t
-netfs_attempt_statfs (struct iouser *cred, struct node *node,
- struct statfs *st)
+/* 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)
{
- bzero (st, sizeof *st);
- st->f_type = PROCFILESYSTEM;
- st->f_fsid = getpid ();
return 0;
}
-/* This should sync the entire remote filesystem. If WAIT is set, return
- only after sync is completely finished. */
+/* 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;
}
-/* 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, but
- 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)
+/* 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;
}
-/* Attempt to create an anonymous file related to DIR for USER with MODE.
- Set *NODE 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 **node)
+/* 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)
{
- *node = NULL;
- mutex_unlock (&dir->lock);
return EROFS;
}
-/* Read the contents of NODE (a symlink), for USER, into BUF. */
-error_t netfs_attempt_readlink (struct iouser *user, struct node *node, char *buf)
+/* 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)
{
- error_t err = procfs_refresh_node (node);
- if (! err)
- {
- struct procfs_dir_entry *dir_entry = node->nn->dir_entry;
- if (dir_entry)
- bcopy (dir_entry->symlink_target, buf, node->nn_stat.st_size);
- else
- err = EINVAL;
- }
- return err;
+ return EROFS;
}
-/* Read from the file NODE 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 *node,
- off_t offset, size_t *len, void *data)
+/* 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)
{
- error_t err;
- err = procfs_refresh_node (node);
+ return EROFS;
+}
- if (! err)
- {
- if (*len > 0)
- procfs_read_files_contents (node, offset,
- len, data);
- if (*len > 0)
- if (offset >= *len)
- *len = 0;
- }
- return err;
+/* 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;
}
-/* Write to the file NODE for user CRED starting at OFFSET and continuing for up
- to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon
- return. */
-error_t netfs_attempt_write (struct iouser *cred, struct node *node,
- off_t offset, size_t *len, void *data)
+/* 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. Node NP is all done; free
- all its associated storage. */
-void netfs_node_norefs (struct node *np)
+/* 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)
{
- mutex_lock (&np->lock);
- *np->prevp = np->next;
- np->next->prevp = np->prevp;
- procfs_remove_node (np);
+ return EROFS;
}
+
diff --git a/node.c b/node.c
deleted file mode 100644
index f11fa7b..0000000
--- a/node.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
-
- node.c -- This file contains function defintions to handle
- node creation and destruction.
-
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
- procfs 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.
-
- procfs 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <hurd/ihash.h>
-#include <hurd/fshelp.h>
-#include <hurd/iohelp.h>
-
-#include <hurd/netfs.h>
-
-#include "procfs.h"
-
-/* Return a new node in NODE, with a name NAME, and return the
- new node with a single reference in NODE. */
-error_t procfs_create_node (struct procfs_dir_entry *dir_entry,
- const char *fs_path, struct node **node)
-{
- struct node *new;
- struct netnode *nn = malloc (sizeof (struct netnode));
- error_t err;
-
- if (! nn)
- return ENOMEM;
- if (! fs_path)
- fs_path = strdup ("");
- nn->fs = dir_entry->dir->fs;
- nn->dir_entry = dir_entry;
- nn->dir = NULL;
- nn->fs_path = strdup (fs_path);
-
- new = netfs_make_node (nn);
- if (! new)
- {
- free (nn);
- return ENOMEM;
- }
-
- fshelp_touch (&new->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
- procfs_maptime);
-
- spin_lock (&nn->fs->inode_mappings_lock);
- err = hurd_ihash_add (&nn->fs->inode_mappings, dir_entry->stat.st_ino, dir_entry);
- spin_unlock (&nn->fs->inode_mappings_lock);
-
- if (err)
- {
- free (nn);
- free (new);
- return err;
- }
-
- dir_entry->node = new;
- *node = new;
-
- return 0;
-}
-
-/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
- True is returned if successful, or false if there was a memory allocation
- error. TIMESTAMP is used to record the time of this update. */
-static void
-update_entry (struct procfs_dir_entry *dir_entry, const struct stat *st,
- const char *symlink_target, time_t timestamp)
-{
- ino_t ino;
- struct procfs *fs = dir_entry->dir->fs;
-
- if (dir_entry->stat.st_ino)
- ino = dir_entry->stat.st_ino;
- else
- ino = fs->next_inode++;
-
- dir_entry->name_timestamp = timestamp;
-
- if (st)
- /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */
- {
- dir_entry->stat = *st;
- dir_entry->stat_timestamp = timestamp;
-
- if (!dir_entry->symlink_target || !symlink_target
- || strcmp (dir_entry->symlink_target, symlink_target) != 0)
- {
- if (dir_entry->symlink_target)
- free (dir_entry->symlink_target);
- dir_entry->symlink_target = symlink_target ? strdup (symlink_target) : 0;
- }
- }
-
- /* The st_ino field is always valid. */
- dir_entry->stat.st_ino = ino;
- dir_entry->stat.st_fsid = fs->fsid;
- dir_entry->stat.st_fstype = PROCFILESYSTEM;
-}
-
-/* Refresh stat information for NODE */
-error_t procfs_refresh_node (struct node *node)
-{
- struct netnode *nn = node->nn;
- struct procfs_dir_entry *dir_entry = nn->dir_entry;
-
- if (! dir_entry)
- /* This is a deleted node, don't attempt to do anything. */
- return 0;
- else
- {
- error_t err = 0;
-
- struct timeval tv;
- maptime_read (procfs_maptime, &tv);
-
- time_t timestamp = tv.tv_sec;
-
- struct procfs_dir *dir = dir_entry->dir;
-
- mutex_lock (&dir->node->lock);
-
- if (! dir_entry->self_p)
- /* This is a deleted entry, just awaiting disposal; do so. */
- {
-#if 0
- nn->dir_entry = 0;
- free_entry (dir_entry);
- return 0;
-#endif
- }
-
- else if (dir_entry->noent)
- err = ENOENT;
- else
- {
- if (*(dir_entry->name))
- {
- err = procfs_dir_refresh (dir_entry->dir,
- dir_entry->dir->node == dir_entry->dir->fs->root);
- if (!err && dir_entry->noent)
- err = ENOENT;
-
- if (err == ENOENT)
- {
- dir_entry->noent = 1; /* A negative entry. */
- dir_entry->name_timestamp = timestamp;
- }
- }
- else
- {
- /* Refresh the root node with the old stat
- information. */
- update_entry (dir_entry, &netfs_root_node->nn_stat, NULL, timestamp);
- }
- }
-
- node->nn_stat = dir_entry->stat;
- node->nn_translated = S_ISLNK (dir_entry->stat.st_mode) ? S_IFLNK : 0;
- if (!nn->dir && S_ISDIR (dir_entry->stat.st_mode))
- procfs_dir_create (nn->fs, node, nn->fs_path, &nn->dir);
-
- mutex_unlock (&dir->node->lock);
-
- return err;
- }
-}
-
-/* Remove NODE from its entry */
-error_t procfs_remove_node (struct node *node)
-{
-
- /* STUB */
-
- return 0;
-}
diff --git a/process.c b/process.c
new file mode 100644
index 0000000..17a38ea
--- /dev/null
+++ b/process.c
@@ -0,0 +1,393 @@
+/* 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.;
+}
+
+static const char *args_filename (const char *name)
+{
+ char *sp = strrchr (name, '/');
+ return sp != NULL && *(sp + 1) != '\0' ? sp + 1 : name;
+}
+
+/* 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), args_filename (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",
+ args_filename (proc_stat_args (ps)),
+ state_string (ps),
+ proc_stat_pid (ps), /* XXX will need more work for threads */
+ proc_stat_pid (ps),
+ proc_stat_proc_info (ps)->ppid,
+ proc_stat_owner_uid (ps),
+ proc_stat_owner_uid (ps),
+ proc_stat_owner_uid (ps),
+ proc_stat_owner_uid (ps),
+ tbi->virtual_size / 1024,
+ tbi->virtual_size / 1024,
+ tbi->resident_size / 1024,
+ tbi->resident_size / 1024,
+ proc_stat_num_threads (ps));
+}
+
+
+/* Implementation of the file nodes. */
+
+/* Describes a file in the process directories. This structure is
+ filled in as an "entry hook" in our procfs_dir entry table and is
+ passed to the process_file_make_node function defined below. */
+struct process_file_desc
+{
+ /* The proc_stat information required to get the contents of this file. */
+ ps_flags_t needs;
+
+ /* Content generator to use for this file. Once we have acquired the
+ necessary information, there can be only memory allocation errors,
+ hence this simplified signature. */
+ ssize_t (*get_contents) (struct proc_stat *ps, char **contents);
+
+ /* The cmdline and environ contents don't need any cleaning since they
+ point directly into the proc_stat structure. */
+ int no_cleanup;
+
+ /* If specified, the file mode to be set with procfs_node_chmod(). */
+ mode_t mode;
+};
+
+struct process_file_node
+{
+ const struct process_file_desc *desc;
+ struct proc_stat *ps;
+};
+
+/* FIXME: lock the parent! */
+static error_t
+process_file_get_contents (void *hook, char **contents, ssize_t *contents_len)
+{
+ struct process_file_node *file = hook;
+ error_t err;
+
+ /* Fetch the required information. */
+ err = proc_stat_set_flags (file->ps, file->desc->needs);
+ if (err)
+ return EIO;
+ if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs)
+ return EIO;
+
+ /* Call the actual content generator (see the definitions below). */
+ *contents_len = file->desc->get_contents (file->ps, contents);
+ return 0;
+}
+
+static void
+process_file_cleanup_contents (void *hook, char *contents, ssize_t len)
+{
+ struct process_file_node *file = hook;
+
+ if (! file->desc->no_cleanup)
+ free (contents);
+}
+
+static struct node *
+process_file_make_node (void *dir_hook, const void *entry_hook)
+{
+ static const struct procfs_node_ops ops = {
+ .get_contents = process_file_get_contents,
+ .cleanup_contents = process_file_cleanup_contents,
+ .cleanup = free,
+ };
+ struct process_file_node *f;
+ struct node *np;
+
+ f = malloc (sizeof *f);
+ if (! f)
+ return NULL;
+
+ f->desc = entry_hook;
+ f->ps = dir_hook;
+
+ np = procfs_make_node (&ops, f);
+ if (! np)
+ return NULL;
+
+ procfs_node_chown (np, proc_stat_owner_uid (f->ps));
+ if (f->desc->mode)
+ procfs_node_chmod (np, f->desc->mode);
+
+ return np;
+}
+
+/* Stat needs its own constructor in oreder to set its mode according to
+ the --stat-mode command-line option. */
+static struct node *
+process_stat_make_node (void *dir_hook, const void *entry_hook)
+{
+ struct node *np = process_file_make_node (dir_hook, entry_hook);
+ if (np) procfs_node_chmod (np, opt_stat_mode);
+ return np;
+}
+
+
+/* Implementation of the process directory per se. */
+
+static struct procfs_dir_entry entries[] = {
+ {
+ .name = "cmdline",
+ .hook = & (struct process_file_desc) {
+ .get_contents = process_file_gc_cmdline,
+ .needs = PSTAT_ARGS,
+ .no_cleanup = 1,
+ },
+ },
+ {
+ .name = "environ",
+ .hook = & (struct process_file_desc) {
+ .get_contents = process_file_gc_environ,
+ .needs = PSTAT_ENV,
+ .no_cleanup = 1,
+ .mode = 0400,
+ },
+ },
+ {
+ .name = "stat",
+ .hook = & (struct process_file_desc) {
+ .get_contents = process_file_gc_stat,
+ .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO
+ | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC
+ | PSTAT_THREAD_WAIT,
+ },
+ .ops = {
+ .make_node = process_stat_make_node,
+ }
+ },
+ {
+ .name = "statm",
+ .hook = & (struct process_file_desc) {
+ .get_contents = process_file_gc_statm,
+ .needs = PSTAT_TASK_BASIC,
+ },
+ },
+ {
+ .name = "status",
+ .hook = & (struct process_file_desc) {
+ .get_contents = process_file_gc_status,
+ .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO
+ | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS,
+ },
+ },
+ {}
+};
+
+error_t
+process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np)
+{
+ static const struct procfs_dir_ops dir_ops = {
+ .entries = entries,
+ .cleanup = (void (*)(void *)) _proc_stat_free,
+ .entry_ops = {
+ .make_node = process_file_make_node,
+ },
+ };
+ struct proc_stat *ps;
+ int owner;
+ error_t err;
+
+ err = _proc_stat_create (pid, pc, &ps);
+ if (err == ESRCH)
+ return ENOENT;
+ if (err)
+ return EIO;
+
+ err = proc_stat_set_flags (ps, PSTAT_OWNER_UID);
+ if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID))
+ {
+ _proc_stat_free (ps);
+ return EIO;
+ }
+
+ /* FIXME: have a separate proc_desc structure for each file, so this can be
+ accessed in a more robust and straightforward way. */
+ ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode;
+
+ /* FIXME: have a separate proc_desc structure for each file, so this can be
+ accessed in a more robust and straightforward way. */
+ ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode;
+
+ *np = procfs_dir_make_node (&dir_ops, ps);
+ if (! *np)
+ return ENOMEM;
+
+ owner = proc_stat_owner_uid (ps);
+ procfs_node_chown (*np, owner >= 0 ? owner : opt_anon_owner);
+ return 0;
+}
diff --git a/process.h b/process.h
new file mode 100644
index 0000000..b230a28
--- /dev/null
+++ b/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.c b/procfs.c
index 1fd0d61..ae5a676 100644
--- a/procfs.c
+++ b/procfs.c
@@ -1,149 +1,203 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
-
- procfs.c -- This file is the main file of the translator.
- This has important definitions and initializes
- the translator
-
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
- procfs is free software; you can redistribute it and/or
+/* 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.
- procfs is distributed in the hope that it will be useful, but
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-*/
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-#include <stdio.h>
-#include <argp.h>
-#include <string.h>
#include <stdlib.h>
-
-#include <unistd.h>
-#include <error.h>
-#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <mach.h>
#include <hurd/netfs.h>
-
+#include <hurd/fshelp.h>
#include "procfs.h"
-/* Defines this Tanslator Name */
-char *netfs_server_name = PROCFS_SERVER_NAME;
-char *netfs_server_version = PROCFS_SERVER_VERSION;
-int netfs_maxsymlinks = 12;
+struct netnode
+{
+ const struct procfs_node_ops *ops;
+ void *hook;
-static const struct argp_child argp_children[] =
- {
- {&netfs_std_startup_argp, 0, NULL, 0},
- {0}
- };
+ /* (cached) contents of the node */
+ char *contents;
+ ssize_t contents_len;
+ /* parent directory, if applicable */
+ struct node *parent;
+};
-const char *argp_program_version = "/proc pseudo-filesystem (" PROCFS_SERVER_NAME
- ") " PROCFS_SERVER_VERSION "\n"
-"Copyright (C) 2008 Free Software Foundation\n"
-"This is free software; see the source for copying conditions. There is NO\n"
-"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-"\n";
+void
+procfs_cleanup_contents_with_free (void *hook, char *cont, ssize_t len)
+{
+ free (cont);
+}
-static char *args_doc = "PROCFSROOT";
-static char *doc = "proc pseudo-filesystem for Hurd implemented as a translator. "
-"This is still under very humble and initial stages of development.\n"
-"Any Contribution or help is welcome. The code may not even compile";
+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;
+}
-/* The Filesystem */
-struct procfs *procfs;
+void procfs_node_chown (struct node *np, uid_t owner)
+{
+ np->nn_stat.st_uid = owner;
+}
-/* The FILESYSTEM component of PROCFS_FS. */
-char *procfs_root = "";
+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;
+}
-volatile struct mapped_time_value *procfs_maptime;
+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];
-/* Startup options. */
-static const struct argp_option procfs_options[] =
- {
- { 0 }
- };
+ 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;
-
-/* argp parser function for parsing single procfs command line options */
-static error_t
-parse_procfs_opt (int key, char *arg, struct argp_state *state)
+ 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)
{
- switch (key)
+ if (! np->nn->contents && np->nn->ops->get_contents)
{
- case ARGP_KEY_ARG:
- if (state->arg_num > 1)
- argp_usage (state);
- break;
-
- case ARGP_KEY_NO_ARGS:
- argp_usage(state);
- break;
-
- default:
- return ARGP_ERR_UNKNOWN;
+ 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;
}
-/* Program entry point. */
-int
-main (int argc, char **argv)
+void procfs_refresh (struct node *np)
{
- error_t err;
- mach_port_t bootstrap, underlying_node;
- struct stat underlying_stat;
-
- struct argp argp =
+ 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, ".."))
{
- procfs_options, parse_procfs_opt,
- args_doc, doc, argp_children,
- NULL, NULL
- };
-
-
- /* Parse the command line arguments */
-// argp_parse (&argp, argc, argv, 0, 0, 0);
-
- task_get_bootstrap_port (mach_task_self (), &bootstrap);
-
- netfs_init ();
-
- if (maptime_map (0, 0, &procfs_maptime))
+ netfs_nref(*npp = np->nn->parent);
+ err = 0;
+ }
+
+ if (err && np->nn->ops->lookup)
{
- perror (PROCFS_SERVER_NAME ": Cannot map time");
- return 1;
+ 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);
+ }
}
-
- procfs_init ();
-
- err = procfs_create (procfs_root, getpid (), &procfs);
- if (err)
- error (4, err, "%s", procfs_root);
-
- /* Create our root node */
- netfs_root_node = procfs->root;
-
- /* Start netfs activities */
- underlying_node = netfs_startup (bootstrap, 0);
- if (io_stat (underlying_node, &underlying_stat))
- error (1, err, "cannot stat underling node");
-
- /* Initialize stat information of the root node. */
- netfs_root_node->nn_stat = underlying_stat;
- netfs_root_node->nn_stat.st_mode =
- S_IFDIR | (underlying_stat.st_mode & ~S_IFMT & ~S_ITRANS);
-
- for (;;)
- netfs_server_loop ();
- return 1;
+
+ return err;
+}
+
+void procfs_cleanup (struct node *np)
+{
+ procfs_refresh (np);
+
+ if (np->nn->ops->cleanup)
+ np->nn->ops->cleanup (np->nn->hook);
+
+ if (np->nn->parent)
+ netfs_nrele (np->nn->parent);
+
+ free (np->nn);
}
diff --git a/procfs.h b/procfs.h
index fa2fb7f..64782ec 100644
--- a/procfs.h
+++ b/procfs.h
@@ -1,220 +1,93 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
-
- procfs.h -- This file is the main header file of this
- translator. This has important header
- definitions for constants and functions
- used in the translator.
-
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
- procfs is free software; you can redistribute it and/or
+/* 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.
- procfs is distributed in the hope that it will be useful, but
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-
- A portion of the code in this file is based on ftpfs code
- present in the hurd repositories copyrighted to FSF. The
- Copyright notice from that file is given below.
-
- Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
- Written by Miles Bader <miles@gnu.org>
- This file is part of the GNU Hurd.
-*/
-
-#ifndef __PROCFS_H__
-#define __PROCFS_H__
-
-#define PROCFS_SERVER_NAME "procfs"
-#define PROCFS_SERVER_VERSION "0.0.1"
-
-/* /proc Filesystem type. */
-#define PROCFILESYSTEM "procfs"
-
-#define NUMBER_OF_FILES_PER_PID 1
-#define JIFFY_ADJUST 100
-#define PAGES_TO_BYTES(pages) ((pages) * sysconf(_SC_PAGESIZE))
-#define BYTES_TO_PAGES(bytes) ((bytes) / sysconf(_SC_PAGESIZE))
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <cthreads.h>
-#include <maptime.h>
-#include <hurd/ihash.h>
-#include <ps.h>
-
-typedef unsigned long long jiffy_t;
-
-/* A single entry in a directory. */
-struct procfs_dir_entry
-{
- char *name; /* Name of this entry */
- size_t hv; /* Hash value of NAME */
-
- /* The active node referred to by this name (may be 0).
- NETFS_NODE_REFCNT_LOCK should be held while frobbing this. */
- struct node *node;
-
- struct stat stat;
- char *symlink_target;
- time_t stat_timestamp;
-
- /* The directory to which this entry belongs. */
- struct procfs_dir *dir;
-
- /* Link to next entry in hash bucket, and address of previous entry's (or
- hash table's) pointer to this entry. If the SELF_P field is 0, then
- this is a deleted entry, awaiting final disposal. */
- struct procfs_dir_entry *next, **self_p;
-
- /* Next entry in 'directory order', or 0 if none known. */
- struct procfs_dir_entry *ordered_next, **ordered_self_p;
-
- /* When the presence/absence of this file was last checked. */
- time_t name_timestamp;
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- hurd_ihash_locp_t inode_locp; /* Used for removing this entry */
+#include <hurd/hurd_types.h>
+#include <hurd/netfs.h>
- int noent : 1; /* A negative lookup result. */
- int valid : 1; /* Marker for GC'ing. */
-};
-
-/* A directory. */
-struct procfs_dir
-{
- /* Number of entries in HTABLE. */
- size_t num_entries;
-
- /* The number of entries that have nodes attached. We keep an additional
- reference to our node if there are any, to prevent it from going away. */
- size_t num_live_entries;
-
- /* Hash table of entries. */
- struct procfs_dir_entry **htable;
- size_t htable_len; /* # of elements in HTABLE (not bytes). */
-
- /* List of dir entries in 'directory order', in a linked list using the
- ORDERED_NEXT and ORDERED_SELF_P fields in each entry. Not all entries
- in HTABLE need be in this list. */
- struct procfs_dir_entry *ordered;
-
- /* The filesystem node that this is the directory for. */
- struct node *node;
-
- /* The filesystem this directory is in. */
- struct procfs *fs;
-
- /* The path to this directory in the filesystem. */
- const char *fs_path;
+
+/* Interface for the procfs side. */
- time_t stat_timestamp;
- time_t name_timestamp;
-
-};
-
-
-/* libnetfs node structure */
-struct netnode
-{
- /* Name of this node */
- char *name;
-
- /* The path in the filesystem that corresponds
- this node. */
- char *fs_path;
-
- /* The directory entry for this node. */
- struct procfs_dir_entry *dir_entry;
-
- /* The proc filesystem */
- struct procfs *fs;
-
- /* inode number, assigned to this netnode structure. */
- unsigned int inode_num;
-
- /* If this is a directory, the contents, or 0 if not fetched. */
- struct procfs_dir *dir;
-
- /* pointer to node structure, assigned to this node. */
- struct node *node;
-
- /* links to the previous and next nodes in the list */
- struct netnode *nextnode, *prevnode;
-
- /* link to parent netnode of this file or directory */
- struct netnode *parent;
-
- /* link to the first child netnode of this directory */
- struct netnode *child_first;
-};
-
-/* The actual procfs filesystem structure */
-struct procfs
+/* 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
{
- /* Root of the filesystem. */
- struct node *root;
-
- /* Inode numbers are assigned sequentially in order of creation. */
- ino_t next_inode;
- int fsid;
-
- /* A hash table mapping inode numbers to directory entries. */
- struct hurd_ihash inode_mappings;
- spin_lock_t inode_mappings_lock;
+ /* 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);
};
-extern struct procfs *procfs;
-
-extern volatile struct mapped_time_value *procfs_maptime;
-
-extern struct ps_context *ps_context;
-
-/* Create a new procfs filesystem. */
-error_t procfs_create (char *procfs_root, int fsid,
- struct procfs **fs);
+/* 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);
-/* Initialize the procfs filesystem for use. */
-error_t procfs_init ();
+/* 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);
-/* Refresh stat information for NODE */
-error_t procfs_refresh_node (struct node *node);
+/* 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);
-/* Return a new node in NODE, with a name NAME,
- and return the new node with a single
- reference in NODE. */
-error_t procfs_create_node (struct procfs_dir_entry *dir_entry,
- const char *fs_path,
- struct node **node);
+/* 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);
-/* Remove NODE from its entry */
-error_t procfs_remove_node (struct node *node);
+/* 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);
-/* Return in DIR a new procfs directory, in the filesystem FS,
- with node NODE and path PATH. */
-error_t procfs_dir_create (struct procfs *fs, struct node *node,
- const char *path, struct procfs_dir **dir);
+
+/* Interface for the libnetfs side. */
-/* Remove the specified DIR and free all its allocated
- storage. */
-void procfs_dir_remove (struct procfs_dir *dir);
+/* 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);
-/* Refresh DIR. */
-error_t procfs_dir_refresh (struct procfs_dir *dir, int isroot);
+/* 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);
-/* Lookup NAME in DIR, returning its entry, or an error.
- *NODE will contain the result node, locked, and with
- an additional reference, or 0 if an error occurs. */
-error_t procfs_dir_lookup (struct procfs_dir *dir, const char *name,
- struct node **node);
+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);
-#endif /* __PROCFS_H__ */
diff --git a/procfs_dir.c b/procfs_dir.c
index bd1e49d..c250aa4 100644
--- a/procfs_dir.c
+++ b/procfs_dir.c
@@ -1,667 +1,134 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
+/* Hurd /proc filesystem, infrastructure for directories.
+ Copyright (C) 2010 Free Software Foundation, Inc.
- procfs_dir.c -- This file contains definitions to perform
- directory operations such as creating,
- removing and refreshing directories.
-
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
+ This file is part of the GNU Hurd.
- procfs is free software; you can redistribute it and/or
+ 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.
- procfs is distributed in the hope that it will be useful, but
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-
- A portion of the code in this file is based on ftpfs code
- present in the hurd repositories copyrighted to FSF. The
- Copyright notice from that file is given below.
-
- Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
- Written by Miles Bader <miles@gnu.org>
- This file is part of the GNU Hurd.
-*/
-
-
-#include <stdio.h>
-#include <unistd.h>
-#include <hurd/netfs.h>
-#include <hurd/ihash.h>
-#include <sys/stat.h>
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+#include <stdlib.h>
+#include <string.h>
#include "procfs.h"
+#include "procfs_dir.h"
-/* Initial HASHTABLE length for the new directories to be created. */
-#define INIT_HTABLE_LEN 5
-
-struct procfs_dir_entry **cur_entry;
-
-/* Return in DIR a new procfs directory, in the filesystem FS,
- with node NODE and path PATH. */
-error_t procfs_dir_create (struct procfs *fs, struct node *node,
- const char *path, struct procfs_dir **dir)
+struct procfs_dir_node
{
- struct procfs_dir *new = malloc (sizeof (struct procfs_dir));
- if (!new)
- return ENOMEM;
- struct procfs_dir_entry **htable = calloc (INIT_HTABLE_LEN,
- sizeof (struct procfs_dir_entry *));
- if (!htable)
- return ENOMEM;
-
- /* Hold a reference to the new dir's node. */
- spin_lock (&netfs_node_refcnt_lock);
- node->references++;
- spin_unlock (&netfs_node_refcnt_lock);
+ const struct procfs_dir_ops *ops;
+ void *hook;
+};
- new->num_entries = 0;
- new->num_live_entries = 0;
- new->htable_len = INIT_HTABLE_LEN;
- new->htable = htable;
- new->ordered = NULL;
- new->fs_path = path;
- new->fs = fs;
- new->node = node;
- new->stat_timestamp = 0;
- new->name_timestamp = 0;
-
- *dir = new;
-
- if (fs->root != 0)
- node->nn->dir = new;
-
- return 0;
-}
-
-/* Put the directory entry DIR_ENTRY into the hash table HTABLE. */
-static void
-insert (struct procfs_dir_entry *dir_entry,
- struct procfs_dir_entry **htable, size_t htable_len)
+static int
+entry_exists (struct procfs_dir_node *dir, const struct procfs_dir_entry *ent)
{
- struct procfs_dir_entry **new_htable = &htable[dir_entry->hv % htable_len];
- if (*new_htable)
- (*new_htable)->self_p = &dir_entry->next;
- dir_entry->next = *new_htable;
- dir_entry->self_p = new_htable;
- *new_htable = dir_entry;
-}
+ 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);
-/* Calculate NAME's hash value. */
-static size_t
-hash (const char *name)
-{
- size_t hash_value = 0;
- while (*name)
- hash_value = ((hash_value << 5) + *name++) & 0xFFFFFF;
- return hash_value;
+ return 1;
}
-/* Extend the existing hashtable for DIR to accomodate values for new length
- NEW_LEN. We retain all the previous entries. */
static error_t
-rehash (struct procfs_dir *dir, size_t new_len)
+procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len)
{
- int count;
- size_t old_len = dir->htable_len;
- struct procfs_dir_entry **old_htable = dir->htable;
- struct procfs_dir_entry **new_htable = (struct procfs_dir_entry **)
- malloc (new_len * sizeof (struct procfs_dir_entry *));
+ static const char dot_dotdot[] = ".\0..";
+ struct procfs_dir_node *dir = hook;
+ const struct procfs_dir_entry *ent;
+ int pos;
- if (! new_htable)
- return ENOMEM;
-
- bzero (new_htable, new_len * sizeof (struct procfs_dir_entry *));
-
- for (count = 0; count < old_len; count++)
- while (old_htable[count])
- {
- struct procfs_dir_entry *dir_entry = old_htable[count];
-
- /* Remove DIR_ENTRY from the old table */
- old_htable[count] = dir_entry->next;
-
- insert (dir_entry, new_htable, new_len);
- }
-
- free (old_htable);
-
- dir->htable = new_htable;
- dir->htable_len = new_len;
-
- return 0;
-}
-
-/* Lookup NAME in DIR and return its entry. If there is no such entry, and
- DNEW, the decision variable, is true, then a new entry is allocated and
- returned, otherwise 0 is returned (if DNEW is true then 0 can be returned
- if a memory allocation error occurs). */
-struct procfs_dir_entry *
-lookup_entry (struct procfs_dir *dir, const char *name, int dnew)
-{
- size_t hv = hash (name);
- struct procfs_dir_entry *dir_entry = dir->htable[hv % dir->htable_len];
-
- while (dir_entry && strcmp (name, dir_entry->name) != 0)
- dir_entry = dir_entry->next;
-
- if (!dir_entry && dnew)
- {
- if (dir->num_entries > dir->htable_len)
- /* Grow the hash table. */
- if (rehash (dir, (dir->htable_len + 1) * 2 - 1) != 0)
- return 0;
-
- dir_entry =
- (struct procfs_dir_entry *) malloc (sizeof (struct procfs_dir_entry));
-
- if (dir_entry)
- {
- dir_entry->hv = hv;
- dir_entry->name = strdup (name);
- dir_entry->node = 0;
- dir_entry->dir = dir;
- dir_entry->stat_timestamp = 0;
- bzero (&dir_entry->stat, sizeof dir_entry->stat);
- dir_entry->symlink_target = 0;
- dir_entry->noent = 0;
- dir_entry->valid = 0;
- dir_entry->name_timestamp = 0;
- dir_entry->ordered_next = 0;
- dir_entry->ordered_self_p = 0;
- dir_entry->next = 0;
- dir_entry->self_p = 0;
- insert (dir_entry, dir->htable, dir->htable_len);
- dir->num_entries++;
- }
- }
+ /* 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;
- return dir_entry;
-}
-
-
-/* Lookup NAME in DIR, returning its entry, or an error.
- *NODE will contain the result node, locked, and with
- an additional reference, or 0 if an error occurs. */
-error_t procfs_dir_lookup (struct procfs_dir *dir, const char *name,
- struct node **node)
-{
- struct procfs_dir_entry *dir_entry = 0;
- error_t err = 0;
- char *fs_path = dir->fs_path;
-
- struct timeval tv;
- maptime_read (procfs_maptime, &tv);
-
- time_t timestamp = tv.tv_sec;
-
- if (*name == '\0' || strcmp (name, ".") == 0)
- /* Current directory -- just add an additional reference to DIR's node
- and return it. */
- {
- netfs_nref (dir->node);
- *node = dir->node;
- return 0;
- }
- else if (strcmp (name, "..") == 0)
- /* Parent directory. */
- {
- if (dir->node->nn->dir_entry)
- {
- *node = dir->node->nn->dir_entry->dir->node;
- mutex_lock (&(*node)->lock);
- netfs_nref (*node);
- }
- else
- {
- err = ENOENT; /* No .. */
- *node = 0;
- }
-
- mutex_unlock (&dir->node->lock);
-
- return err;
- }
-
- err = procfs_dir_refresh (dir, dir->node == dir->fs->root);
- if (!err && !dir_entry)
- dir_entry = lookup_entry (dir, name, 0);
-
- if (! err)
- {
- if (dir_entry && !dir_entry->noent)
- /* We've got a dir entry, get a node for it. */
- {
- /* If there's already a node, add a ref so that it doesn't go
- away. */
- spin_lock (&netfs_node_refcnt_lock);
- if (dir_entry->node)
- dir_entry->node->references++;
- spin_unlock (&netfs_node_refcnt_lock);
-
- if (! dir_entry->node)
- /* No node; make one and install it into E. */
- {
- if (! fs_path)
- err = EROFS;
-
- if (! err)
- {
- err = procfs_create_node (dir_entry, fs_path, &dir_entry->node);
-
- if (!err && dir->num_live_entries++ == 0)
- /* Keep a reference to dir's node corresponding to
- children. */
- {
- spin_lock (&netfs_node_refcnt_lock);
- dir->node->references++;
- spin_unlock (&netfs_node_refcnt_lock);
- }
- }
- }
-
- if (! err)
- {
- *node = dir_entry->node;
- /* We have to unlock DIR's node before locking the child node
- because the locking order is always child-parent. We know
- the child node won't go away because we already hold the
- additional reference to it. */
- mutex_unlock (&dir->node->lock);
- mutex_lock (&dir_entry->node->lock);
- }
- }
- else
- err = ENOENT;
- }
-
- if (err)
- {
- *node = 0;
- mutex_unlock (&dir->node->lock);
- }
-
-#if 0
- if (fs_path)
- free (fs_path);
-#endif
-
- return err;
-}
-
-/* Lookup the null name in DIR, and return a node for it in NODE. Unlike
- procfs_dir_lookup, this won't attempt to validate the existance of the
- entry (to avoid opening a new connection if possible) -- that will happen
- the first time the entry is refreshed. Also unlink ftpfs_dir_lookup, this
- function doesn't expect DIR to be locked, and won't return *NODE locked.
- This function is only used for bootstrapping the root node. */
-error_t
-procfs_dir_null_lookup (struct procfs_dir *dir, struct node **node)
-{
- struct procfs_dir_entry *dir_entry;
- error_t err = 0;
-
- dir_entry = lookup_entry (dir, "", 1);
- if (! dir_entry)
+ *contents = malloc (pos);
+ if (! *contents)
return ENOMEM;
- if (! dir_entry->noent)
- /* We've got a dir entry, get a node for it. */
- {
- /* If there's already a node, add a ref so that it doesn't go away. */
- spin_lock (&netfs_node_refcnt_lock);
- if (dir_entry->node)
- dir_entry->node->references++;
- spin_unlock (&netfs_node_refcnt_lock);
-
- if (! dir_entry->node)
- /* No node; make one and install it into DIR_ENTRY. */
- {
- err = procfs_create_node (dir_entry, dir->fs_path, &dir_entry->node);
-
- if (!err && dir->num_live_entries++ == 0)
- /* Keep a reference to dir's node corresponding to children. */
- {
- spin_lock (&netfs_node_refcnt_lock);
- dir->node->references++;
- spin_unlock (&netfs_node_refcnt_lock);
- }
- }
-
- if (! err)
- *node = dir_entry->node;
- }
- else
- err = ENOENT;
-
- return err;
-}
-
-/* Free the directory entry DIR_ENTRY and all resources it consumes. */
-void
-free_entry (struct procfs_dir_entry *dir_entry)
-{
-
- assert (! dir_entry->self_p); /* We should only free deleted nodes. */
- free (dir_entry->name);
- if (dir_entry->symlink_target)
- free (dir_entry->symlink_target);
- free (dir_entry->node->nn->dir);
- free (dir_entry->node->nn);
- free (dir_entry->node);
- free (dir_entry);
-}
-
-/* Remove DIR_ENTRY from its position in the ordered_next chain. */
-static void
-ordered_unlink (struct procfs_dir_entry *dir_entry)
-{
- if (dir_entry->ordered_self_p)
- *dir_entry->ordered_self_p = dir_entry->ordered_next;
- if (dir_entry->ordered_next)
- dir_entry->ordered_next->self_p = dir_entry->ordered_self_p;
-}
-
-/* Delete DIR_ENTRY from its directory, freeing any resources it holds. */
-static void
-delete (struct procfs_dir_entry *dir_entry, struct procfs_dir *dir)
-{
- dir->num_entries--;
-
- /* Take out of the hash chain. */
- if (dir_entry->self_p)
- *dir_entry->self_p = dir_entry->next;
- if (dir_entry->next)
- dir_entry->next->self_p = dir_entry->self_p;
-
- /* Take out of the directory ordered list. */
- ordered_unlink (dir_entry);
-
- /* If there's a node attached, we'll delete the entry whenever it goes
- away, otherwise, just delete it now. */
- if (! dir_entry->node)
- free_entry (dir_entry);
-}
-
-/* Make all the directory entries invalid */
-static void
-make_dir_invalid (struct procfs_dir *dir)
-{
- int count;
- size_t len = dir->htable_len;
- struct procfs_dir_entry **htable = dir->htable;
- struct procfs_dir_entry *dir_entry;
-
- for (count = 0; count < len; count++)
- {
- dir_entry = htable[count];
- while (dir_entry)
- {
- dir_entry->valid = 0;
- dir_entry = dir_entry->next;
- }
- }
-}
-
-/* Delete any entries in DIR which don't have their valid bit set. */
-static void
-sweep (struct procfs_dir *dir)
-{
- size_t len = dir->htable_len, i;
- struct procfs_dir_entry **htable = dir->htable, *dir_entry;
-
- for (i = 0; i < len; i++)
- {
- dir_entry = htable[i];
- while (dir_entry)
- {
- if (!dir_entry->valid && !dir_entry->noent && dir->num_entries)
- delete (dir_entry, dir);
- dir_entry = dir_entry->next;
- }
- if (htable[i])
- {
- free (htable[i]);
- htable[i] = 0;
- }
-
- }
-
-}
-
-/* Remove the specified DIR and free all its allocated
- storage. */
-void procfs_dir_entries_remove (struct procfs_dir *dir)
-{
- /* Free all entries. */
- make_dir_invalid (dir);
- sweep (dir);
-}
-
-/* Checks if the DIR name is in list of
- Active pids. */
-int is_in_pid_list (struct procfs_dir *dir)
-{
- int dir_name;
- int count;
- pid_t *pids = NULL;
- int pidslen = 0;
- error_t err;
-
- if (dir->node->nn)
+ memcpy (*contents, dot_dotdot, sizeof dot_dotdot);
+ pos = sizeof dot_dotdot;
+ for (ent = dir->ops->entries; ent->name; ent++)
{
- dir_name = atoi (dir->node->nn->dir_entry->name);
- err = proc_getallpids (getproc (), &pids, &pidslen);
+ if (! entry_exists (dir, ent))
+ continue;
- for (count = 0; count < pidslen; ++count)
- if (pids[count] == dir_name)
- return 1;
+ strcpy (*contents + pos, ent->name);
+ pos += strlen (ent->name) + 1;
}
+ *contents_len = pos;
return 0;
-
}
-/* Checks if DIR is a directory that
- represents a pid. */
-int check_parent (struct procfs_dir *dir)
+static error_t
+procfs_dir_lookup (void *hook, const char *name, struct node **np)
{
- if (dir == dir->fs->root)
- return 0;
- else
- if (is_in_pid_list (dir))
- return 1;
- else
- return 0;
+ 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;
-/* Refresh DIR. */
-error_t procfs_dir_refresh (struct procfs_dir *dir, int isroot)
-{
- error_t err;
- int is_parent_pid;
- struct node *node;
-
- struct timeval tv;
- maptime_read (procfs_maptime, &tv);
-
- time_t timestamp = tv.tv_sec;
- cur_entry = &dir->ordered;
- if (isroot)
- err = procfs_fill_root_dir(dir, timestamp);
+ 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
- {
- err = update_dir_entries (dir, timestamp);
- is_parent_pid = check_parent (dir);
- if (is_parent_pid)
- err = procfs_create_files (dir, &node, timestamp);
- }
+ return EGRATUITOUS;
- return err;
-}
-
-/* Update the directory entry for NAME to reflect STAT and SYMLINK_TARGET.
- This also creates a valid linked list of entries imposing ordering on
- them. */
-struct procfs_dir_entry*
-update_entries_list (struct procfs_dir *dir, const char *name,
- const struct stat *stat, time_t timestamp,
- const char *symlink_target)
-{
- ino_t ino;
- struct procfs_dir_entry *dir_entry = lookup_entry (dir, name, 1);
- struct procfs *fs = dir->fs;
-
- if (! dir_entry)
+ if (! *np)
return ENOMEM;
- if (dir_entry->stat.st_ino)
- ino = dir_entry->stat.st_ino;
- else
- ino = fs->next_inode++;
-
- dir_entry->name_timestamp = timestamp;
-
- if (stat)
- /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */
- {
- dir_entry->stat = *stat;
- dir_entry->stat_timestamp = timestamp;
-
- if (!dir_entry->symlink_target || !symlink_target
- || strcmp (dir_entry->symlink_target, symlink_target) != 0)
- {
- if (dir_entry->symlink_target)
- free (dir_entry->symlink_target);
- dir_entry->symlink_target = symlink_target ? strdup (symlink_target) : 0;
- }
- }
-
- /* The st_ino field is always valid. */
- dir_entry->stat.st_ino = ino;
- dir_entry->stat.st_fsid = fs->fsid;
- dir_entry->stat.st_fstype = PROCFILESYSTEM;
-
- dir_entry->valid = 1;
-
- if (! dir_entry->ordered_self_p)
- /* Position DIR_ENTRY in the ordered chain following the previously seen entry. */
- {
- /* The PREV_ENTRY_NEXT_P field holds a pointer to the NEXT-field of the
- previous entry, or a pointer to the ORDERED field in the directory. */
- dir_entry->ordered_self_p = cur_entry;
-
- if (*dir_entry->ordered_self_p)
- /* Update the self_p pointer of the previous successor. */
- (*dir_entry->ordered_self_p)->ordered_self_p = &dir_entry->ordered_next;
-
- /* DIR_ENTRY comes before the previous successor. */
- dir_entry->ordered_next = *dir_entry->ordered_self_p;
+ return 0;
+}
- *dir_entry->ordered_self_p = dir_entry; /* Put DIR_ENTRY there. */
- }
+static void
+procfs_dir_cleanup (void *hook)
+{
+ struct procfs_dir_node *dir = hook;
- /* Put the next entry after this one. */
- cur_entry = &dir_entry->ordered_next;
+ if (dir->ops->cleanup)
+ dir->ops->cleanup (dir->hook);
- return dir_entry;
+ free (dir);
}
-/* Fills DIR, the root directory with all the pids of
- processes running in the system as directories. */
-error_t
-procfs_fill_root_dir(struct procfs_dir *dir, time_t timestamp)
+struct node *
+procfs_dir_make_node (const struct procfs_dir_ops *dir_ops, void *dir_hook)
{
- error_t err;
- char *data;
- pid_t *pids;
- int pidslen;
- struct stat stat;
- stat.st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP |
- S_IROTH | S_IXOTH;
- stat.st_nlink = 1;
- stat.st_size = 0;
+ 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;
- int count;
- char *dir_name_pid;
- struct node *node;
- struct procfs_dir *new_dir;
- struct procfs_dir_entry *dir_entry;
- struct proc_stat *ps;
-
- pids = NULL;
- pidslen = 0;
- err = proc_getallpids (getproc (), &pids, &pidslen);
-
- if (!err)
+ dir = malloc (sizeof *dir);
+ if (! dir)
{
- for (count = 0; count < pidslen; count++)
- {
- if (asprintf (&dir_name_pid, "%d", pids[count]) == -1)
- return errno;
-
-#if 0
- node = (struct node *) malloc (sizeof (struct node));
- new_dir = (struct procfs_dir *) malloc (sizeof (struct procfs_dir ));
-
- if (! node || ! new_dir )
- return ENOMEM;
-#endif
- err = _proc_stat_create (pids[count], ps_context, &ps);
- if (! err)
- {
- err = set_field_value (ps, PSTAT_PROC_INFO);
- if (! err)
- {
- stat.st_uid = proc_stat_proc_info (ps)->owner;
- stat.st_gid = proc_stat_proc_info (ps)->pgrp;
-
- dir_entry = update_entries_list (dir, dir_name_pid,
- &stat, timestamp, NULL);
- err = procfs_create_node (dir_entry, dir_name_pid, &node);
+ if (dir_ops->cleanup)
+ dir_ops->cleanup (dir_hook);
- procfs_dir_create (dir->fs, node,
- dir_name_pid, &new_dir);
- free(dir_name_pid);
- _proc_stat_free (ps);
- }
- }
- }
+ return NULL;
}
- if ((err = procfs_create_uptime (dir, &node, timestamp)) != 0)
- return err;
+ dir->ops = dir_ops;
+ dir->hook = dir_hook;
- if ((err = procfs_create_stat (dir, &node, timestamp)) != 0)
- return err;
-
- if ((err = procfs_create_version (dir, &node, timestamp)) != 0)
- return err;
-
- if ((err = procfs_create_meminfo (dir, &node, timestamp)) != 0)
- return err;
-
- if ((err = procfs_create_loadavg (dir, &node, timestamp)) != 0)
- return err;
-
- if ((err = procfs_create_mounts (dir, &node, timestamp)) != 0)
- return err;
-
- return 0;
+ return procfs_make_node (&ops, dir);
}
-error_t update_dir_entries (struct procfs_dir *dir)
-{
- /* STUB */
- return 0;
-}
diff --git a/procfs_dir.h b/procfs_dir.h
new file mode 100644
index 0000000..94c5b01
--- /dev/null
+++ b/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_nonpid_files.c b/procfs_nonpid_files.c
deleted file mode 100644
index f130066..0000000
--- a/procfs_nonpid_files.c
+++ /dev/null
@@ -1,527 +0,0 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
-
- procfs_nonpid_files.c -- This file contains function definitions
- to create and update the non-Per PID
- files and their contents.
-
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
-
- procfs 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.
-
- procfs 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-
- A portion of the code in this file is based on vmstat.c code
- present in the hurd repositories copyrighted to FSF. The
- Copyright notice from that file is given below.
-
- Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
- Written by Miles Bader <miles@gnu.org>
- This file is part of the GNU Hurd.
-*/
-
-#include <stdio.h>
-#include <unistd.h>
-#include <hurd/netfs.h>
-#include <hurd/ihash.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/sysinfo.h>
-#include <mach/vm_statistics.h>
-#include <mach/default_pager.h>
-#include <hurd.h>
-#include <hurd/paths.h>
-#include <mach.h>
-#include <ps.h>
-#include <time.h>
-
-#include "procfs.h"
-
-typedef long long val_t;
-#define BADVAL ((val_t) - 1LL)
-
-/* default pager port (must be privileged to fetch this). */
-mach_port_t def_pager;
-struct default_pager_info def_pager_info;
-
-error_t procfs_create_uptime (struct procfs_dir *dir,
- struct node **node,
- time_t timestamp)
-{
- int err;
- char *file_name, *file_path;
- struct procfs_dir_entry *dir_entry;
-
- if (asprintf (&file_name, "%s", "uptime") == -1)
- return errno;
- if (asprintf (&file_path, "%s", "uptime") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
- return err;
-}
-
-error_t procfs_create_version(struct procfs_dir *dir,
- struct node **node,
- time_t timestamp)
-{
- int err;
- char *file_name, *file_path;
- struct procfs_dir_entry *dir_entry;
-
- if (asprintf (&file_name, "%s", "version") == -1)
- return errno;
- if (asprintf (&file_path, "%s", "version") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
- return 0;
-}
-
-error_t procfs_create_stat (struct procfs_dir *dir,
- struct node **node,
- time_t timestamp)
-{
- int err;
- char *file_name, *file_path;
- struct procfs_dir_entry *dir_entry;
-
- if (asprintf (&file_name, "%s", "stat") == -1)
- return errno;
- if (asprintf (&file_path, "%s", "stat") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
- return err;
-}
-
-error_t procfs_create_meminfo (struct procfs_dir *dir,
- struct node **node,
- time_t timestamp)
-{
- int err;
- char *file_name, *file_path;
- struct procfs_dir_entry *dir_entry;
-
- if (asprintf (&file_name, "%s", "meminfo") == -1)
- return errno;
- if (asprintf (&file_path, "%s", "meminfo") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
- return err;
-}
-
-error_t procfs_create_loadavg (struct procfs_dir *dir,
- struct node **node,
- time_t timestamp)
-{
- int err;
- char *file_name, *file_path;
- struct procfs_dir_entry *dir_entry;
-
- if (asprintf (&file_name, "%s", "loadavg") == -1)
- return errno;
- if (asprintf (&file_path, "%s", "loadavg") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
- return err;
-}
-
-error_t procfs_create_mounts (struct procfs_dir *dir,
- struct node **node,
- time_t timestamp)
-{
- struct procfs_dir_entry *dir_entry;
- int err;
-
- dir_entry = update_pid_entries (dir, "mounts", timestamp, "/etc/mtab");
- err = procfs_create_node (dir_entry, "mounts", node);
-
- return err;
-}
-
-error_t get_uptime (struct timeval *uptime)
-{
- struct timeval boot_time, now;
- error_t err;
- struct proc_stat *ps;
-
- err = _proc_stat_create (1, ps_context, &ps);
-
- if (err)
- return err;
-
- err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC);
- if (!err && !(ps->flags & PSTAT_TASK_BASIC))
- err = EGRATUITOUS;
-
- if (! err)
- {
- time_value_t *const tv = &proc_stat_task_basic_info (ps)->creation_time;
- boot_time.tv_sec = tv->seconds;
- boot_time.tv_usec = tv->microseconds;
- if (gettimeofday (&now, 0) < 0)
- error (0, errno, "gettimeofday");
- timersub (&now, &boot_time, uptime);
- }
-
- _proc_stat_free (ps);
- return err;
-}
-
-error_t get_total_times (struct timeval *total_user_time,
- struct timeval *total_system_time)
-{
- error_t err;
- pid_t *pids;
- int pidslen = 0, count;
- struct proc_stat *ps;
- struct task_thread_times_info live_threads_times;
-
- struct timeval total_user_time_tmp;
- struct timeval total_system_time_tmp;
- struct timeval tmpval;
-
- timerclear (&total_user_time_tmp);
- timerclear (&total_system_time_tmp);
-
- pids = NULL;
- err = proc_getallpids (getproc (), &pids, &pidslen);
-
- if (!err)
- for (count = 0; count < pidslen; count++)
- {
- err = _proc_stat_create (pids[count], ps_context, &ps);
- if (err)
- return err;
-
- err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC);
- if (!err && !(ps->flags & PSTAT_TASK_BASIC))
- err = EGRATUITOUS;
-
- if (! err)
- {
- tmpval.tv_sec = proc_stat_task_basic_info (ps)->user_time.seconds;
- tmpval.tv_usec = proc_stat_task_basic_info (ps)->user_time.seconds;
- timeradd (&total_user_time_tmp, &tmpval, &total_user_time_tmp);
-
- tmpval.tv_sec = proc_stat_task_basic_info (ps)->system_time.seconds;
- tmpval.tv_usec = proc_stat_task_basic_info (ps)->system_time.seconds;
- timeradd (&total_system_time_tmp, &tmpval, &total_system_time_tmp);
-
- error_t err = set_field_value (ps, PSTAT_TASK);
- if (! err)
- {
- err = get_task_thread_times (ps->task, &live_threads_times);
- if (! err)
- {
- tmpval.tv_sec = live_threads_times.user_time.seconds;
- tmpval.tv_usec = live_threads_times.user_time.microseconds;
- timeradd (&total_user_time_tmp, &tmpval, &total_user_time_tmp);
-
- tmpval.tv_sec = live_threads_times.system_time.seconds;
- tmpval.tv_usec = live_threads_times.system_time.microseconds;
- timeradd (&total_system_time_tmp, &tmpval, &total_system_time_tmp);
- }
- }
- }
- _proc_stat_free (ps);
- }
-
- total_user_time->tv_sec = total_user_time_tmp.tv_sec;
- total_user_time->tv_usec = total_user_time_tmp.tv_usec;
-
- total_system_time->tv_sec = total_system_time_tmp.tv_sec;
- total_system_time->tv_usec = total_system_time_tmp.tv_usec;
-
- return err;
-}
-
-error_t procfs_read_nonpid_stat (struct dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- char *stat_data;
- error_t err;
- jiffy_t total_user_time_jiffy, total_system_time_jiffy;
- jiffy_t idle_time_jiffy;
- struct timeval uptime, total_user_time, total_system_time;
- struct timeval idle_time;
-
- err = get_uptime (&uptime);
-
- if (! err)
- {
- err = get_total_times (&total_user_time, &total_system_time);
-
- if (! err)
- {
- timersub (&uptime, &total_system_time,
- &idle_time);
-
- total_user_time_jiffy = 100 * ((double) total_user_time.tv_sec +
- (double) total_user_time.tv_usec / (1000 * 1000));
- total_system_time_jiffy = 100 * ((double) total_system_time.tv_sec +
- (double) total_system_time.tv_usec / (1000 * 1000));
- idle_time_jiffy = 100 * ((double) idle_time.tv_sec +
- (double) idle_time.tv_usec / (1000 * 1000));
-
- if (asprintf (&stat_data, "cpu %llu %llu %llu %llu %llu %llu %d %d %d\n"
- "cpu0 %llu %llu %llu %llu %llu %llu %d %d %d\n"
- "intr %llu %llu %llu %llu %llu %llu %d %d %d\n",
- total_user_time_jiffy, (long long unsigned) 0,
- total_system_time_jiffy, idle_time_jiffy,
- (long long unsigned) 0, (long long unsigned) 0,
- 0, 0, 0,
- total_user_time_jiffy, (long long unsigned) 0,
- total_system_time_jiffy, idle_time_jiffy,
- (long long unsigned) 0, (long long unsigned) 0,
- 0, 0, 0,
- (long long unsigned) 0,
- (long long unsigned) 0, (long long unsigned) 0, (long long unsigned) 0,
- (long long unsigned) 0,
- (long long unsigned) 0, (long long unsigned) 0,
- (long long unsigned) 0, (long long unsigned) 0) == -1)
- return errno;
- }
- }
-
- memcpy (data, stat_data, strlen(stat_data));
- *len = strlen (data);
-
- free (stat_data);
- return err;
-}
-
-/* Makes sure the default pager port and associated
- info exists, and returns 0 if not (after printing
- an error). */
-static int
-ensure_def_pager_info ()
-{
- error_t err;
-
- if (def_pager == MACH_PORT_NULL)
- {
- mach_port_t host;
-
- err = get_privileged_ports (&host, 0);
- if (err == EPERM)
- {
- /* We are not root, so try opening the /servers file. */
- def_pager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0);
- if (def_pager == MACH_PORT_NULL)
- {
- error (0, errno, _SERVERS_DEFPAGER);
- return 0;
- }
- }
- if (def_pager == MACH_PORT_NULL)
- {
- if (err)
- {
- error (0, err, "get_privileged_ports");
- return 0;
- }
-
- err = vm_set_default_memory_manager (host, &def_pager);
- mach_port_deallocate (mach_task_self (), host);
-
- if (err)
- {
- error (0, err, "vm_set_default_memory_manager");
- return 0;
- }
- }
- }
-
- if (!MACH_PORT_VALID (def_pager))
- {
- if (def_pager == MACH_PORT_NULL)
- {
- error (0, 0,
- "No default pager running, so no swap information available");
- def_pager = MACH_PORT_DEAD; /* so we don't try again */
- }
- return 0;
- }
-
- err = default_pager_info (def_pager, &def_pager_info);
- if (err)
- error (0, err, "default_pager_info");
- return (err == 0);
-}
-
-#define SWAP_FIELD(getter, expr) \
- static val_t getter () \
- { return ensure_def_pager_info () ? (val_t) (expr) : BADVAL; }
-
-SWAP_FIELD (get_swap_size, def_pager_info.dpi_total_space)
-SWAP_FIELD (get_swap_free, def_pager_info.dpi_free_space)
-SWAP_FIELD (get_swap_page_size, def_pager_info.dpi_page_size)
-SWAP_FIELD (get_swap_active, (def_pager_info.dpi_total_space
- - def_pager_info.dpi_free_space))
-
-error_t procfs_read_nonpid_meminfo (struct dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- char *meminfo_data;
- error_t err;
- struct vm_statistics vmstats;
-
- err = vm_statistics (mach_task_self (), &vmstats);
-
- unsigned long mem_size = ((vmstats.free_count +
- vmstats.active_count + vmstats.inactive_count +
- vmstats.wire_count) * vmstats.pagesize) / 1024;
-
- if (! err)
- if (asprintf (&meminfo_data, "MemTotal:\t%lu kB\n"
- "MemFree:\t%lu kB\n"
- "Buffers:\t%ld kB\n"
- "Cached:\t\t%ld kB\n"
- "SwapCached:\t%ld kB\n"
- "Active:\t\t%lu kB\n"
- "Inactive:\t%lu kB\n"
- "HighTotal:\t%lu kB\n"
- "HighFree:\t%lu kB\n"
- "LowTotal:\t%lu kB\n"
- "LowFree:\t%lu kB\n"
- "SwapTotal:\t%llu kB\n"
- "SwapFree:\t%llu kB\n",
- mem_size, (PAGES_TO_BYTES(vmstats.free_count)) / 1024 , 0, 0, 0,
- (PAGES_TO_BYTES(vmstats.active_count)) / 1024,
- (PAGES_TO_BYTES(vmstats.inactive_count)) / 1024, 0, 0, 0, 0,
- get_swap_size () / 1024, get_swap_free () / 1024) == -1)
- return errno;
-
- memcpy (data, meminfo_data, strlen(meminfo_data));
- *len = strlen (data);
-
- free (meminfo_data);
- return err;
-}
-
-error_t procfs_read_nonpid_loadavg (struct dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- char *loadavg_data;
- error_t err;
- processor_set_info_t info;
- natural_t *count;
- struct host_load_info *load;
- mach_port_t host;
-
- err = ps_host_load_info (&load);
- if (err)
- error (0, err, "ps_host_load_info");
-
- if (! err)
- if (asprintf (&loadavg_data, "%.2f %.2f %.2f %d/%d %d\n",
- (double)load->avenrun[0] / (double)LOAD_SCALE,
- (double)load->avenrun[1] / (double)LOAD_SCALE,
- (double)load->avenrun[2] / (double)LOAD_SCALE, 0, 0, 0) == -1)
- return errno;
-
- memcpy (data, loadavg_data, strlen(loadavg_data));
- *len = strlen (data);
-
- free (loadavg_data);
- return err;
-}
-
-error_t procfs_read_nonpid_uptime (struct dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- char *uptime_data;
- error_t err;
- double uptime_secs, idle_time_secs;
-
- struct timeval uptime_val;
- struct timeval uptime, total_user_time, total_system_time;
- struct timeval idle_time;
-
-
- err = get_uptime (&uptime);
- if (! err)
- {
- err = get_total_times (&total_user_time,
- &total_system_time);
- if (! err)
- {
- timersub (&uptime, &total_system_time,
- &idle_time);
-
- uptime_secs = (double) uptime.tv_sec +
- (double) uptime.tv_usec / (1000 * 1000);
-
- idle_time_secs = (double) idle_time.tv_sec +
- (double) idle_time.tv_usec / (1000 * 1000);
-
- if (asprintf (&uptime_data, "%.2f %.2f\n",
- uptime_secs, idle_time_secs) == -1)
- return errno;
- }
- }
-
-
- memcpy (data, uptime_data, strlen(uptime_data));
- *len = strlen (data);
-
- free (uptime_data);
- return err;
-}
-
-error_t procfs_read_nonpid_version (struct dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- char *version_data;
- error_t err = 0;
-
- if (asprintf (&version_data, "Linux version 2.6.18\n", NULL) == -1)
- return errno;
-
- memcpy (data, version_data, strlen(version_data));
- *len = strlen (data);
-
- free (version_data);
- return err;
-}
diff --git a/procfs_pid.h b/procfs_pid.h
deleted file mode 100644
index 566c83e..0000000
--- a/procfs_pid.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
-
- procfs_pid.h -- This is the header file of which contains defintions
- for structure of directory with PID as the name and
- structure of each file in this directory.
-
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
- procfs 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.
-
- procfs 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-*/
-
-#ifndef __PROCFS_PID_H__
-#define __PROCFS_PID_H__
-
-#include "procfs.h"
-
-struct procfs_pid_files
-{
- struct procfs_cwd *procfs_cwd;
- struct procfs_environ *procfs_environ;
- struct procfs_cpu *procfs_cpu;
- struct procfs_root *procfs_root;
- struct procfs_exe *procfs_exe;
- struct procfs_stat *_procfs_stat;
- struct procfs_statm *procfs_statm;
-};
-
-struct procfs_stat
-{
- pid_t pid;
- char *comm;
- char *state;
- pid_t ppid;
- pid_t pgid;
- pid_t sid;
- int tty_nr;
- pid_t tty_pgrp;
- unsigned flags;
- long unsigned minflt;
- long unsigned cminflt;
- long unsigned majflt;
- long unsigned cmajflt;
- jiffy_t utime;
- jiffy_t stime;
- jiffy_t cutime;
- jiffy_t cstime;
- long priority;
- long nice;
- long num_threads;
- long itrealvalue;
- long long unsigned starttime;
- long unsigned vsize;
- long rss;
- long unsigned rlim;
- long unsigned startcode;
- long unsigned endcode;
- long unsigned startstack;
- long unsigned kstkesp;
- long unsigned kstkeip;
- long unsigned signal;
- long unsigned blocked;
- long unsigned sigignore;
- long unsigned sigcatch;
- long unsigned wchan;
- long unsigned nswap;
- long unsigned cnswap;
- int exit_signal;
- int processor;
- unsigned rt_priority;
- unsigned policy;
- long long unsigned delayacct_blkio_ticks;
-};
-
-#endif
diff --git a/procfs_pid_files.c b/procfs_pid_files.c
deleted file mode 100644
index 26a0af3..0000000
--- a/procfs_pid_files.c
+++ /dev/null
@@ -1,583 +0,0 @@
-/* procfs -- a translator for providing GNU/Linux compatible
- proc pseudo-filesystem
-
- procfs_pid_files.c -- This file contains definitions to perform
- file operations such as creating, writing to,
- reading from and removing files that holds
- information for each process with PID
-
- Copyright (C) 2008, FSF.
- Written as a Summer of Code Project
-
-
- procfs 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.
-
- procfs 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
-
- A portion of the code in this file is based on ftpfs code
- present in the hurd repositories copyrighted to FSF. The
- Copyright notice from that file is given below.
-
-*/
-
-#include <hurd/netfs.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdio.h>
-#include <mach/task_info.h>
-#include <sys/resource.h>
-
-#include "procfs_pid.h"
-
-/* Update the files named NAME within the directory named
- PID also with SYMLINK TARGET if necessary. */
-struct procfs_dir_entry*
-update_pid_entries (struct procfs_dir *dir, const char *name,
- time_t timestamp,
- const char *symlink_target)
-{
- struct stat stat;
-
- memset (&stat, 0, sizeof stat);
- if (symlink_target)
- {
- stat.st_size = strlen (symlink_target);
- stat.st_mode = S_IFLNK | 0777;
- }
- else
- {
- stat.st_size = 0;
- stat.st_mode = S_IFREG | 0444;
- }
-
- return update_entries_list (dir, name, &stat, timestamp, symlink_target);
-}
-
-/* Creates files to store process information for DIR
- whose names are pids and returns these files in *NODE. */
-error_t
-procfs_create_files (struct procfs_dir *dir,
- struct node **node,
- time_t timestamp)
-{
- int err;
- char *file_name, *file_path;
- struct procfs_dir_entry *dir_entry;
-
- if (asprintf (&file_name, "%s", "stat") == -1)
- return errno;
- if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "stat") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
- if (asprintf (&file_name, "%s", "status") == -1)
- return errno;
- if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "status") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
- if (asprintf (&file_name, "%s", "cmdline") == -1)
- return errno;
- if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "cmdline") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
- if (asprintf (&file_name, "%s", "statm") == -1)
- return errno;
- if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "statm") == -1)
- return errno;
-
- dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
- err = procfs_create_node (dir_entry, file_path, node);
-
- free (file_name);
- free (file_path);
-
-#if 0
- nodes_list = &node_stat;
- nodes_list++;
- node = nodes_list;
-#endif
-
- return err;
-}
-
-/* Check if the PSTAT_FLAG is set in the corresponding PS
- structure, if not set it and check again and return error
- status accordingly. */
-error_t set_field_value (struct proc_stat *ps, int pstat_flag)
-{
- error_t err;
-
- if (! (ps->flags & pstat_flag))
- {
- err = proc_stat_set_flags (ps, pstat_flag);
- if (err)
- return err;
-
- /* This second check is done since ps.h specifies to
- do so since the previous call would not have set
- the required value. */
- if (! (ps->flags & pstat_flag))
- return EGRATUITOUS;
- }
-
- return 0;
-}
-
-/* Adjusts TIME_VAL structure having Seconds and
- Microseconds into the value in jiffies. The
- value of jiffy is a hack to adjust to what
- procps uses. */
-jiffy_t adjust_jiffy_time (time_value_t time_val)
-{
- jiffy_t jiffy_time = time_val.seconds * JIFFY_ADJUST;
- jiffy_time += (time_val.microseconds * JIFFY_ADJUST)
- / (1000 * 1000);
-
- return jiffy_time;
-}
-
-/* Extract the user and system time for the live threads of
- the process. This information is directly retrieved from
- MACH since neither libps not proc makes this available. */
-error_t get_task_thread_times (task_t task,
- struct task_thread_times_info *live_threads_times)
-{
- error_t err;
- size_t tkcount = TASK_THREAD_TIMES_INFO_COUNT;
-
- err = task_info (task, TASK_THREAD_TIMES_INFO,
- (task_info_t) live_threads_times, &tkcount);
- if (err == MACH_SEND_INVALID_DEST)
- err = ESRCH;
-
- return err;
-}
-
-/* Obtains the User Time in UTIME and System Time in STIME from
- MACH directly since this is neither made available by libps
- nor by proc server. */
-error_t get_live_threads_time (struct proc_stat *ps,
- jiffy_t *utime, jiffy_t *stime)
-{
- struct task_thread_times_info live_threads_times;
- error_t err = set_field_value (ps, PSTAT_TASK);
-
- if (! err)
- {
- err = get_task_thread_times (ps->task, &live_threads_times);
- if (! err)
- {
- *utime = adjust_jiffy_time (
- live_threads_times.user_time);
- *stime = adjust_jiffy_time (
- live_threads_times.system_time);
- }
- }
-
- return err;
-}
-
-/* Get the data for stat file into the structure
- PROCFS_STAT. */
-error_t get_stat_data (pid_t pid,
- struct procfs_stat **procfs_stat)
-{
- error_t err;
- struct procfs_stat *new = (struct procfs_stat *)
- malloc (sizeof (struct procfs_stat));
-
- struct proc_stat *ps;
- jiffy_t utime, stime;
-
- err = _proc_stat_create (pid, ps_context, &ps);
-
- new->pid = pid;
-
- if (! err)
- {
- err = set_field_value (ps, PSTAT_ARGS);
- if (! err)
- asprintf (&new->comm, "%s", ps->args);
- }
-
- err = set_field_value (ps, PSTAT_STATE);
- if (! err)
- {
- if (ps->state & PSTAT_STATE_P_STOP)
- new->state = strdup ("T");
- if (ps->state & PSTAT_STATE_P_ZOMBIE)
- new->state = strdup ("Z");
- if (ps->state & PSTAT_STATE_P_FG)
- new->state = strdup ("+");
- if (ps->state & PSTAT_STATE_P_SESSLDR)
- new->state = strdup ("s");
- if (ps->state & PSTAT_STATE_P_LOGINLDR)
- new->state = strdup ("l");
- if (ps->state & PSTAT_STATE_P_FORKED)
- new->state = strdup ("f");
- if (ps->state & PSTAT_STATE_P_NOMSG)
- new->state = strdup ("m");
- if (ps->state & PSTAT_STATE_P_NOPARENT)
- new->state = strdup ("p");
- if (ps->state & PSTAT_STATE_P_ORPHAN)
- new->state = strdup ("o");
- if (ps->state & PSTAT_STATE_P_TRACE)
- new->state = strdup ("x");
- if (ps->state & PSTAT_STATE_P_WAIT)
- new->state = strdup ("w");
- if (ps->state & PSTAT_STATE_P_GETMSG)
- new->state = strdup ("g");
- }
-
- err = set_field_value (ps, PSTAT_PROC_INFO);
- if (! err)
- {
- new->ppid = ps->proc_info->ppid;
- new->pgid = ps->proc_info->pgrp;
- new->sid = ps->proc_info->session;
- new->tty_pgrp = ps->proc_info->pgrp;
- }
- else
- {
- new->ppid = 0;
- new->pgid = 0;
- new->sid = 0;
- new->tty_pgrp = 0;
- }
-
- err = set_field_value (ps, PSTAT_STATE);
- if (! err)
- new->flags = ps->state;
- else
- new->flags = 0;
-
- err = set_field_value (ps, PSTAT_TASK_EVENTS);
- if (! err)
- {
- new->minflt = ps->task_events_info->faults;
- new->majflt = ps->task_events_info->pageins;
- }
- else
- {
- new->minflt = 0;
- new->majflt = 0;
- }
-
- /* This seems to be a bit inconsistent with setting of other
- fields in this code. There are two reasons for this.
- 1. The actual information required is not made available
- by libps which should be directly obtained from MACH.
- 2. The same code which is required to get the information
- have to be reused in procfs_nonpid_files.c */
- err = get_live_threads_time (ps, &utime, &stime);
- if (! err)
- {
- new->utime = utime;
- new->stime = stime;
- }
- else
- {
- new->utime = 0;
- new->stime = 0;
- }
-
- err = set_field_value (ps, PSTAT_TASK_BASIC);
- if (! err)
- {
- new->cutime = adjust_jiffy_time (
- ps->task_basic_info->user_time);
- new->cstime = adjust_jiffy_time (
- ps->task_basic_info->system_time);
-
- new->priority = ps->task_basic_info->base_priority;
- new->starttime = adjust_jiffy_time (
- ps->task_basic_info->creation_time);
-
- new->vsize = ps->task_basic_info->virtual_size;
- new->rss = ps->task_basic_info->resident_size;
- }
- else
- {
- new->cutime = 0;
- new->cstime = 0;
- new->priority = 0;
- new->starttime = 0;
- new->vsize = 0;
- new->rss = 0;
- }
-
- new->nice = getpriority (0, pid);
-
- err = set_field_value (ps, PSTAT_NUM_THREADS);
- if (! err)
- new->num_threads = ps->num_threads;
- else
- new->num_threads = 0;
-
- /* Not Supported in Linux 2.6 or later. */
- new->tty_nr = 0;
- new->itrealvalue = 0;
- new->nswap = 0;
- new->cnswap = 0;
-
- /* Temporarily set to 0 until correct
- values are found .*/
- new->cminflt = 0;
- new->cmajflt = 0;
- new->rlim = 0;
- new->startcode = 0;
- new->endcode = 0;
- new->startstack = 0;
- new->kstkesp = 0;
- new->kstkeip = 0;
- new->signal = 0;
- new->blocked = 0;
- new->sigignore = 0;
- new->sigcatch = 0;
- new->wchan = 0;
- new->exit_signal = 0;
- new->processor = 0;
- new->rt_priority = 0;
- new->policy = 0;
- new->delayacct_blkio_ticks = 0;
-
- *procfs_stat = new;
- _proc_stat_free (ps);
-
- return err;
-}
-
-/* Reads required process information from stat file
- within the directory represented by pid. Return
- the data in DATA and actual length to be written
- in LEN. */
-error_t
-procfs_read_stat_file (struct procfs_dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- error_t err;
- char *stat_data;
- struct procfs_stat *procfs_stat;
- pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name);
-
- err = get_stat_data (pid, &procfs_stat);
-
- if (asprintf (&stat_data, "%d (%s) %s %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %llu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu \n",
- procfs_stat->pid, procfs_stat->comm,
- procfs_stat->state, procfs_stat->ppid,
- procfs_stat->pgid, procfs_stat->sid,
- procfs_stat->tty_nr, procfs_stat->tty_pgrp,
- procfs_stat->flags, procfs_stat->minflt,
- procfs_stat->cminflt, procfs_stat->majflt,
- procfs_stat->cmajflt, procfs_stat->utime,
- procfs_stat->stime, procfs_stat->cutime,
- procfs_stat->cstime, procfs_stat->priority,
- procfs_stat->nice, procfs_stat->num_threads,
- procfs_stat->itrealvalue, procfs_stat->starttime,
- procfs_stat->vsize, BYTES_TO_PAGES(procfs_stat->rss),
- procfs_stat->rlim, procfs_stat->startcode,
- procfs_stat->endcode, procfs_stat->startstack,
- procfs_stat->kstkesp, procfs_stat->kstkeip,
- procfs_stat->signal, procfs_stat->blocked,
- procfs_stat->sigignore, procfs_stat->sigcatch,
- procfs_stat->wchan, procfs_stat->nswap,
- procfs_stat->cnswap, procfs_stat->exit_signal,
- procfs_stat->processor, procfs_stat->rt_priority,
- procfs_stat->policy,
- procfs_stat->delayacct_blkio_ticks) == -1)
- return errno;
-
-
- memcpy (data, stat_data, strlen(stat_data));
- *len = strlen (data);
-
- free (stat_data);
- free (procfs_stat);
-
- return err;
-}
-
-/* Reads required process's command line information
- from cmline file within the directory represented
- by pid. Return the data in DATA and actual length
- to be written in LEN. */
-error_t
-procfs_read_cmdline_file (struct procfs_dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- char *cmdline_data;
- error_t err;
- struct proc_stat *ps;
- pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name);
- err = _proc_stat_create (pid, ps_context, &ps);
-
- err = set_field_value (ps, PSTAT_ARGS);
-
- if (! err)
- if (asprintf (&cmdline_data, "%s \n", ps->args) == -1)
- return errno;
-
- memcpy (data, cmdline_data, strlen(cmdline_data));
- *len = strlen (data);
-
- _proc_stat_free (ps);
- free (cmdline_data);
- return err;
-}
-
-/* Reads required process's information that is represented by
- stat and statm in a human readable format from status file
- within the directory represented by pid. Return the data
- in DATA and actual length to be written in LEN. */
-error_t
-procfs_read_status_file (struct procfs_dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- char *status_data;
- error_t err;
- struct proc_stat *ps;
- struct procfs_stat *procfs_stat;
-
- pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name);
- err = _proc_stat_create (pid, ps_context, &ps);
-
- err = get_stat_data (pid, &procfs_stat);
-
- if (! err)
- if (asprintf (&status_data, "Name:\t%s\nState:\t%s\nTgid:\t%d\nPid:\t%d\n", procfs_stat->comm, procfs_stat->state, procfs_stat->pid, procfs_stat->pid) == -1)
- return errno;
-
- memcpy (data, status_data, strlen(status_data));
- *len = strlen (data);
-
- _proc_stat_free (ps);
-
- free (status_data);
- free (procfs_stat);
-
- return err;
-}
-
-/* Reads required process information from statm file
- within the directory represented by pid. Return
- the data in DATA and actual length to be written
- in LEN. */
-error_t
-procfs_read_statm_file (struct procfs_dir_entry *dir_entry,
- off_t offset, size_t *len, void *data)
-{
- char *statm_data;
- error_t err;
- struct proc_stat *ps;
- struct procfs_stat *procfs_stat;
-
- pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name);
- err = _proc_stat_create (pid, ps_context, &ps);
-
- err = get_stat_data (pid, &procfs_stat);
-
- if (! err)
- if (asprintf (&statm_data, "%lu %ld %d %d %d %d %d\n",
- BYTES_TO_PAGES(procfs_stat->vsize),
- BYTES_TO_PAGES(procfs_stat->rss),
- 0, 0, 0, 0, 0) == -1)
- return errno;
-
- memcpy (data, statm_data, strlen(statm_data));
- *len = strlen (data);
-
- _proc_stat_free (ps);
-
- free (statm_data);
- free (procfs_stat);
-
- return err;
-}
-
-/* Reads required process information from each of files
- within directory represented by pid, for files specified
- by NODE. Return the data in DATA and actual length of
- data in LEN. */
-error_t
-procfs_read_files_contents (struct node *node,
- off_t offset, size_t *len, void *data)
-{
- error_t err;
-
- if (! strcmp (node->nn->dir_entry->name, "stat"))
- if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
- err = procfs_read_nonpid_stat (node->nn->dir_entry,
- offset, len, data);
- else
- err = procfs_read_stat_file (node->nn->dir_entry,
- offset, len, data);
-
- if (! strcmp (node->nn->dir_entry->name, "cmdline"))
- err = procfs_read_cmdline_file (node->nn->dir_entry,
- offset, len, data);
-
- if (! strcmp (node->nn->dir_entry->name, "status"))
- err = procfs_read_status_file (node->nn->dir_entry,
- offset, len, data);
-
- if (! strcmp (node->nn->dir_entry->name, "statm"))
- err = procfs_read_statm_file (node->nn->dir_entry,
- offset, len, data);
-
- if (! strcmp (node->nn->dir_entry->name, "meminfo"))
- if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
- err = procfs_read_nonpid_meminfo (node->nn->dir_entry,
- offset, len, data);
- else
- err = ENOENT;
-
- if (! strcmp (node->nn->dir_entry->name, "loadavg"))
- if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
- err = procfs_read_nonpid_loadavg (node->nn->dir_entry,
- offset, len, data);
- else
- err = ENOENT;
-
- if (! strcmp (node->nn->dir_entry->name, "uptime"))
- if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
- err = procfs_read_nonpid_uptime (node->nn->dir_entry,
- offset, len, data);
- else
- err = ENOENT;
-
- if (! strcmp (node->nn->dir_entry->name, "version"))
- if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
- err = procfs_read_nonpid_version (node->nn->dir_entry,
- offset, len, data);
- else
- err = ENOENT;
-
- return err;
-}
diff --git a/proclist.c b/proclist.c
new file mode 100644
index 0000000..58b942d
--- /dev/null
+++ b/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/proclist.h b/proclist.h
new file mode 100644
index 0000000..bfe95b3
--- /dev/null
+++ b/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/rootdir.c b/rootdir.c
new file mode 100644
index 0000000..1fa71b0
--- /dev/null
+++ b/rootdir.c
@@ -0,0 +1,506 @@
+/* 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"
+ "Buffers: %14lu kB\n"
+ "Cached: %14lu kB\n"
+ "Active: %14lu kB\n"
+ "Inactive: %14lu kB\n"
+ "Mlocked: %14lu kB\n"
+ "SwapTotal:%14lu kB\n"
+ "SwapFree: %14lu kB\n"
+ ,
+ (long unsigned) hbi.memory_size / 1024,
+ (long unsigned) vmstats.free_count * PAGE_SIZE / 1024,
+ 0,
+ 0,
+ (long unsigned) vmstats.active_count * PAGE_SIZE / 1024,
+ (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024,
+ (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024,
+ (long unsigned) swap.dpi_total_space / 1024,
+ (long unsigned) swap.dpi_free_space / 1024);
+
+ return 0;
+}
+
+static error_t
+rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len)
+{
+ host_basic_info_data_t hbi;
+ mach_msg_type_number_t cnt;
+ struct vm_statistics vmstats;
+ error_t err;
+
+ err = vm_statistics (mach_task_self (), &vmstats);
+ if (err)
+ return EIO;
+
+ cnt = HOST_BASIC_INFO_COUNT;
+ err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt);
+ if (err)
+ return err;
+
+ assert (cnt == HOST_BASIC_INFO_COUNT);
+ *contents_len = asprintf (contents,
+ "nr_free_pages %lu\n"
+ "nr_inactive_anon %lu\n"
+ "nr_active_anon %lu\n"
+ "nr_inactive_file %lu\n"
+ "nr_active_file %lu\n"
+ "nr_unevictable %lu\n"
+ "nr_mlock %lu\n"
+ "pgpgin %lu\n"
+ "pgpgout %lu\n"
+ "pgfault %lu\n",
+ (long unsigned) vmstats.free_count,
+ /* FIXME: how can we distinguish the anon/file pages? Maybe we can
+ ask the default pager how many it manages? */
+ (long unsigned) vmstats.inactive_count,
+ (long unsigned) vmstats.active_count,
+ (long unsigned) 0,
+ (long unsigned) 0,
+ (long unsigned) vmstats.wire_count,
+ (long unsigned) vmstats.wire_count,
+ (long unsigned) vmstats.pageins,
+ (long unsigned) vmstats.pageouts,
+ (long unsigned) vmstats.faults);
+
+ return 0;
+}
+
+static error_t
+rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len)
+{
+ struct ps_context *pc = hook;
+ struct proc_stat *ps;
+ error_t err;
+
+ err = _proc_stat_create (opt_kernel_pid, pc, &ps);
+ if (err)
+ return EIO;
+
+ err = proc_stat_set_flags (ps, PSTAT_ARGS);
+ if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS))
+ {
+ err = EIO;
+ goto out;
+ }
+
+ *contents_len = proc_stat_args_len (ps);
+ *contents = malloc (*contents_len);
+ if (! *contents)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+
+ memcpy (*contents, proc_stat_args (ps), *contents_len);
+ argz_stringify (*contents, *contents_len, ' ');
+ (*contents)[*contents_len - 1] = '\n';
+
+out:
+ _proc_stat_free (ps);
+ return err;
+}
+
+static int
+rootdir_fakeself_exists ()
+{
+ return opt_fake_self >= 0;
+}
+
+static error_t
+rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len)
+{
+ *contents_len = asprintf (contents, "%d", opt_fake_self);
+ return 0;
+}
+
+
+/* Glue logic and entries table */
+
+static struct node *
+rootdir_file_make_node (void *dir_hook, const void *entry_hook)
+{
+ /* The entry hook we use is actually a procfs_node_ops for the file to be
+ created. The hook associated to these newly created files (and passed
+ to the generators above as a consequence) is always the same global
+ ps_context, which we get from rootdir_make_node as the directory hook. */
+ return procfs_make_node (entry_hook, dir_hook);
+}
+
+static struct node *
+rootdir_symlink_make_node (void *dir_hook, const void *entry_hook)
+{
+ struct node *np = procfs_make_node (entry_hook, dir_hook);
+ if (np)
+ procfs_node_chtype (np, S_IFLNK);
+ return np;
+}
+
+static const struct procfs_dir_entry rootdir_entries[] = {
+ {
+ .name = "self",
+ .hook = & (struct procfs_node_ops) {
+ .get_contents = rootdir_gc_fakeself,
+ .cleanup_contents = procfs_cleanup_contents_with_free,
+ },
+ .ops = {
+ .make_node = rootdir_symlink_make_node,
+ .exists = rootdir_fakeself_exists,
+ }
+ },
+ {
+ .name = "version",
+ .hook = & (struct procfs_node_ops) {
+ .get_contents = rootdir_gc_version,
+ .cleanup_contents = procfs_cleanup_contents_with_free,
+ },
+ },
+ {
+ .name = "uptime",
+ .hook = & (struct procfs_node_ops) {
+ .get_contents = rootdir_gc_uptime,
+ .cleanup_contents = procfs_cleanup_contents_with_free,
+ },
+ },
+ {
+ .name = "stat",
+ .hook = & (struct procfs_node_ops) {
+ .get_contents = rootdir_gc_stat,
+ .cleanup_contents = procfs_cleanup_contents_with_free,
+ },
+ },
+ {
+ .name = "loadavg",
+ .hook = & (struct procfs_node_ops) {
+ .get_contents = rootdir_gc_loadavg,
+ .cleanup_contents = procfs_cleanup_contents_with_free,
+ },
+ },
+ {
+ .name = "meminfo",
+ .hook = & (struct procfs_node_ops) {
+ .get_contents = rootdir_gc_meminfo,
+ .cleanup_contents = procfs_cleanup_contents_with_free,
+ },
+ },
+ {
+ .name = "vmstat",
+ .hook = & (struct procfs_node_ops) {
+ .get_contents = rootdir_gc_vmstat,
+ .cleanup_contents = procfs_cleanup_contents_with_free,
+ },
+ },
+ {
+ .name = "cmdline",
+ .hook = & (struct procfs_node_ops) {
+ .get_contents = rootdir_gc_cmdline,
+ .cleanup_contents = procfs_cleanup_contents_with_free,
+ },
+ },
+#ifdef PROFILE
+ /* In order to get a usable gmon.out file, we must apparently use exit(). */
+ {
+ .name = "exit",
+ .ops = {
+ .make_node = exit,
+ },
+ },
+#endif
+ {}
+};
+
+struct node
+*rootdir_make_node (struct ps_context *pc)
+{
+ static const struct procfs_dir_ops ops = {
+ .entries = rootdir_entries,
+ .entry_ops = {
+ .make_node = rootdir_file_make_node,
+ },
+ };
+ return procfs_dir_make_node (&ops, pc);
+}
+
diff --git a/rootdir.h b/rootdir.h
new file mode 100644
index 0000000..6980da8
--- /dev/null
+++ b/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);