From 494db38b7df523f566957ef5aa56b9b3d2af6764 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 26 Sep 2010 21:12:48 +0000 Subject: Move files to procfs to merge along hurd --- dircat.c | 128 ------------- dircat.h | 29 --- main.c | 190 -------------------- main.h | 25 --- netfs.c | 445 ---------------------------------------------- process.c | 387 ---------------------------------------- process.h | 27 --- procfs.c | 203 --------------------- procfs.h | 93 ---------- procfs/Makefile | 26 +++ procfs/TODO | 24 +++ procfs/dircat.c | 128 +++++++++++++ procfs/dircat.h | 29 +++ procfs/main.c | 190 ++++++++++++++++++++ procfs/main.h | 25 +++ procfs/netfs.c | 445 ++++++++++++++++++++++++++++++++++++++++++++++ procfs/process.c | 387 ++++++++++++++++++++++++++++++++++++++++ procfs/process.h | 27 +++ procfs/procfs.c | 203 +++++++++++++++++++++ procfs/procfs.h | 93 ++++++++++ procfs/procfs_dir.c | 134 ++++++++++++++ procfs/procfs_dir.h | 63 +++++++ procfs/proclist.c | 94 ++++++++++ procfs/proclist.h | 23 +++ procfs/rootdir.c | 503 ++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs/rootdir.h | 23 +++ procfs_dir.c | 134 -------------- procfs_dir.h | 63 ------- proclist.c | 94 ---------- proclist.h | 23 --- rootdir.c | 503 ---------------------------------------------------- rootdir.h | 23 --- 32 files changed, 2417 insertions(+), 2367 deletions(-) delete mode 100644 dircat.c delete mode 100644 dircat.h delete mode 100644 main.c delete mode 100644 main.h delete mode 100644 netfs.c delete mode 100644 process.c delete mode 100644 process.h delete mode 100644 procfs.c delete mode 100644 procfs.h create mode 100644 procfs/Makefile create mode 100644 procfs/TODO create mode 100644 procfs/dircat.c create mode 100644 procfs/dircat.h create mode 100644 procfs/main.c create mode 100644 procfs/main.h create mode 100644 procfs/netfs.c create mode 100644 procfs/process.c create mode 100644 procfs/process.h create mode 100644 procfs/procfs.c create mode 100644 procfs/procfs.h create mode 100644 procfs/procfs_dir.c create mode 100644 procfs/procfs_dir.h create mode 100644 procfs/proclist.c create mode 100644 procfs/proclist.h create mode 100644 procfs/rootdir.c create mode 100644 procfs/rootdir.h delete mode 100644 procfs_dir.c delete mode 100644 procfs_dir.h delete mode 100644 proclist.c delete mode 100644 proclist.h delete mode 100644 rootdir.c delete mode 100644 rootdir.h diff --git a/dircat.c b/dircat.c deleted file mode 100644 index 5a60899a..00000000 --- a/dircat.c +++ /dev/null @@ -1,128 +0,0 @@ -/* Hurd /proc filesystem, concatenation of two directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include "procfs.h" - -struct dircat_node -{ - int num_dirs; - struct node *dirs[0]; -}; - -static error_t -dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) -{ - struct dircat_node *dcn = hook; - int i, sz, pos; - error_t err; - - pos = 0; - *contents = malloc (sz = 512); - - for (i=0; i < dcn->num_dirs; i++) - { - char *subcon; - ssize_t sublen; - - /* Make sure we're not getting some old stuff. */ - procfs_refresh (dcn->dirs[i]); - - err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); - if (err) - { - free (*contents); - *contents = NULL; - return err; - } - - while (pos + sublen > sz) - *contents = realloc (*contents, sz *= 2); - - memcpy (*contents + pos, subcon, sublen); - pos += sublen; - } - - *contents_len = pos; - return 0; -} - -static error_t -dircat_lookup (void *hook, const char *name, struct node **np) -{ - struct dircat_node *dcn = hook; - error_t err; - int i; - - err = ENOENT; - for (i=0; err && i < dcn->num_dirs; i++) - err = procfs_lookup (dcn->dirs[i], name, np); - - return err; -} - -static void -dircat_release_dirs (struct node *const *dirs, int num_dirs) -{ - int i; - - for (i=0; i < num_dirs; i++) - if (dirs[i]) - netfs_nrele (dirs[i]); -} - -static void -dircat_cleanup (void *hook) -{ - struct dircat_node *dcn = hook; - - dircat_release_dirs (dcn->dirs, dcn->num_dirs); - free (dcn); -} - -struct node * -dircat_make_node (struct node *const *dirs, int num_dirs) -{ - static struct procfs_node_ops ops = { - .get_contents = dircat_get_contents, - .cleanup_contents = procfs_cleanup_contents_with_free, - .lookup = dircat_lookup, - .cleanup = dircat_cleanup, - }; - struct dircat_node *dcn; - int i; - - for (i=0; i < num_dirs; i++) - if (! dirs[i]) - goto fail; - - dcn = malloc (sizeof *dcn + num_dirs * sizeof dcn->dirs[0]); - if (! dcn) - goto fail; - - dcn->num_dirs = num_dirs; - memcpy (dcn->dirs, dirs, num_dirs * sizeof dcn->dirs[0]); - return procfs_make_node (&ops, dcn); - -fail: - dircat_release_dirs (dirs, num_dirs); - return NULL; -} - diff --git a/dircat.h b/dircat.h deleted file mode 100644 index 4177b384..00000000 --- a/dircat.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Hurd /proc filesystem, concatenation of two directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Append the contents of NUM_DIRS directories. DIRS is an array of - directory nodes. One reference is consumed for each of them. If a - memory allocation error occurs, or if one of the directories is a - NULL pointer, the references are dropped immediately and NULL is - returned. The given DIRS array is duplicated and can therefore be - allocated on the caller's stack. Strange things will happen if some - elements of DIRS have entries with the same name or if one of them is - not a directory. */ -struct node * -dircat_make_node (struct node *const *dirs, int num_dirs); diff --git a/main.c b/main.c deleted file mode 100644 index 3a976ccc..00000000 --- a/main.c +++ /dev/null @@ -1,190 +0,0 @@ -/* Hurd /proc filesystem, main program. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include -#include "procfs.h" -#include "proclist.h" -#include "rootdir.h" -#include "dircat.h" -#include "main.h" - -/* Command-line options */ -int opt_clk_tck; -mode_t opt_stat_mode; -pid_t opt_fake_self; -pid_t opt_kernel_pid; -uid_t opt_anon_owner; - -static error_t -argp_parser (int key, char *arg, struct argp_state *state) -{ - struct passwd *pw; - char *endp; - - switch (key) - { - case 'h': - opt_clk_tck = strtol (arg, &endp, 0); - if (*endp || ! *arg || opt_clk_tck <= 0) - error (1, 0, "--clk-tck: HZ should be a positive integer"); - break; - - case 's': - opt_stat_mode = strtol (arg, &endp, 8); - if (*endp || ! *arg || opt_stat_mode & ~07777) - error (1, 0, "--stat-mode: MODE should be an octal mode"); - break; - - case 'S': - if (arg) - { - opt_fake_self = strtol (arg, &endp, 0); - if (*endp || ! *arg) - error (1, 0, "--fake-self: PID must be an integer"); - } - else - opt_fake_self = 1; - break; - - case 'k': - opt_kernel_pid = strtol (arg, &endp, 0); - if (*endp || ! *arg || (signed) opt_kernel_pid < 0) - error (1, 0, "--kernel-process: PID must be a positive integer"); - break; - - case 'c': - opt_clk_tck = 100; - opt_stat_mode = 0444; - opt_fake_self = 1; - break; - - case 'a': - pw = getpwnam (arg); - if (pw) - { - opt_anon_owner = pw->pw_uid; - break; - } - - opt_anon_owner = strtol (arg, &endp, 0); - if (*endp || ! *arg || (signed) opt_anon_owner < 0) - error(1, 0, "--anonymous-owner: USER should be the a user name " - "or a numeric UID."); - break; - } - - return 0; -} - -struct argp argp = { - .options = (struct argp_option []) { - { "clk-tck", 'h', "HZ", 0, - "Unit used for the values expressed in system clock ticks " - "(default: sysconf(_SC_CLK_TCK))" }, - { "stat-mode", 's', "MODE", 0, - "The [pid]/stat file publishes information which on Hurd is only " - "available to the process owner. " - "You can use this option to override its mode to be more permissive " - "for compatibility purposes. " - "(default: 0400)" }, - { "fake-self", 'S', "PID", OPTION_ARG_OPTIONAL, - "Provide a fake \"self\" symlink to the given PID, for compatibility " - "purposes. If PID is omitted, \"self\" will point to init. " - "(default: no self link)" }, - { "kernel-process", 'k', "PID", 0, - "Process identifier for the kernel, used to retreive its command " - "line, as well as the global up and idle times. " - "(default: 2)" }, - { "compatible", 'c', NULL, 0, - "Try to be compatible with the Linux procps utilities. " - "Currently equivalent to -h 100 -s 0444 -S 1." }, - { "anonymous-owner", 'a', "USER", 0, - "Make USER the owner of files related to processes without one. " - "Be aware that USER will be granted access to the environment and " - "other sensitive information about the processes in question. " - "(default: use uid 0)" }, - {} - }, - .parser = argp_parser, - .doc = "A virtual filesystem emulating the Linux procfs.", - .children = (struct argp_child []) { - { &netfs_std_startup_argp, }, - {} - }, -}; - -error_t -root_make_node (struct ps_context *pc, struct node **np) -{ - struct node *root_dirs[] = { - proclist_make_node (pc), - rootdir_make_node (pc), - }; - - *np = dircat_make_node (root_dirs, sizeof root_dirs / sizeof root_dirs[0]); - if (! *np) - return ENOMEM; - - /* Since this one is not created through proc_lookup(), we have to affect an - inode number to it. */ - (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; - - return 0; -} - -int main (int argc, char **argv) -{ - struct ps_context *pc; - mach_port_t bootstrap; - error_t err; - - opt_clk_tck = sysconf(_SC_CLK_TCK); - opt_stat_mode = 0400; - opt_fake_self = -1; - opt_kernel_pid = 2; - opt_anon_owner = 0; - err = argp_parse (&argp, argc, argv, 0, 0, 0); - if (err) - error (1, err, "Could not parse command line"); - - err = ps_context_create (getproc (), &pc); - if (err) - error (1, err, "Could not create libps context"); - - task_get_bootstrap_port (mach_task_self (), &bootstrap); - if (bootstrap == MACH_PORT_NULL) - error (1, 0, "Must be started as a translator"); - - netfs_init (); - err = root_make_node (pc, &netfs_root_node); - if (err) - error (1, err, "Could not create the root node"); - - netfs_startup (bootstrap, 0); - netfs_server_loop (); - - assert (0 /* netfs_server_loop returned after all */); -} - diff --git a/main.h b/main.h deleted file mode 100644 index 4e28b7eb..00000000 --- a/main.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Hurd /proc filesystem, command-line options set by main.c. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Startup options */ -extern int opt_clk_tck; -extern mode_t opt_stat_mode; -extern pid_t opt_fake_self; -extern pid_t opt_kernel_pid; -extern uid_t opt_anon_owner; diff --git a/netfs.c b/netfs.c deleted file mode 100644 index 24a6603f..00000000 --- a/netfs.c +++ /dev/null @@ -1,445 +0,0 @@ -/* Hurd /proc filesystem, interface with libnetfs. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include "procfs.h" - -#define PROCFS_SERVER_NAME "procfs" -#define PROCFS_SERVER_VERSION "0.1.0" -#define PROCFS_MAXSYMLINKS 16 - - -/* Interesting libnetfs callback functions. */ - -/* The user must define this variable. Set this to the name of the - filesystem server. */ -char *netfs_server_name = PROCFS_SERVER_NAME; - -/* The user must define this variables. Set this to be the server - version number. */ -char *netfs_server_version = PROCFS_SERVER_VERSION; - -/* Maximum number of symlinks to follow before returning ELOOP. */ -int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; - -/* The user must define this function. Make sure that NP->nn_stat is - filled with the most current information. CRED identifies the user - responsible for the operation. NP is locked. */ -error_t netfs_validate_stat (struct node *np, struct iouser *cred) -{ - char *contents; - ssize_t contents_len; - error_t err; - - /* Only symlinks need to have their size filled, before a read is - attempted. */ - if (! S_ISLNK (np->nn_stat.st_mode)) - return 0; - - err = procfs_get_contents (np, &contents, &contents_len); - if (err) - return err; - - np->nn_stat.st_size = contents_len; - return 0; -} - -/* The user must define this function. Read from the locked file NP - for user CRED starting at OFFSET and continuing for up to *LEN - bytes. Put the data at DATA. Set *LEN to the amount successfully - read upon return. */ -error_t netfs_attempt_read (struct iouser *cred, struct node *np, - loff_t offset, size_t *len, void *data) -{ - char *contents; - ssize_t contents_len; - error_t err; - - if (offset == 0) - procfs_refresh (np); - - err = procfs_get_contents (np, &contents, &contents_len); - if (err) - return err; - - contents += offset; - contents_len -= offset; - - if (*len > contents_len) - *len = contents_len; - if (*len < 0) - *len = 0; - - memcpy (data, contents, *len); - return 0; -} - -/* The user must define this function. Read the contents of locked - node NP (a symlink), for USER, into BUF. */ -error_t netfs_attempt_readlink (struct iouser *user, struct node *np, - char *buf) -{ - char *contents; - ssize_t contents_len; - error_t err; - - err = procfs_get_contents (np, &contents, &contents_len); - if (err) - return err; - - assert (contents_len == np->nn_stat.st_size); - memcpy (buf, contents, contents_len); - return 0; -} - -/* Helper function for netfs_get_dirents() below. CONTENTS is an argz - vector of directory entry names, as returned by procfs_get_contents(). - Convert at most NENTRIES of them to dirent structures, put them in - DATA (if not NULL), write the number of entries processed in *AMT and - return the required/used space in DATACNT. */ -static int putentries (char *contents, size_t contents_len, int nentries, - char *data, mach_msg_type_number_t *datacnt) -{ - int i; - - *datacnt = 0; - for (i = 0; contents_len && (nentries < 0 || i < nentries); i++) - { - int namlen = strlen (contents); - int reclen = sizeof (struct dirent) + namlen; - - if (data) - { - struct dirent *d = (struct dirent *) (data + *datacnt); - d->d_fileno = 42; /* XXX */ - d->d_namlen = namlen; - d->d_reclen = reclen; - d->d_type = DT_UNKNOWN; - strcpy (d->d_name, contents); - } - - *datacnt += reclen; - contents += namlen + 1; - contents_len -= namlen + 1; - } - - return i; -} - -/* The user must define this function. Fill the array *DATA of size - BUFSIZE with up to NENTRIES dirents from DIR (which is locked) - starting with entry ENTRY for user CRED. The number of entries in - the array is stored in *AMT and the number of bytes in *DATACNT. - If the supplied buffer is not large enough to hold the data, it - should be grown. */ -error_t netfs_get_dirents (struct iouser *cred, struct node *dir, - int entry, int nentries, char **data, - mach_msg_type_number_t *datacnt, - vm_size_t bufsize, int *amt) -{ - char *contents; - ssize_t contents_len; - error_t err; - - if (entry == 0) - procfs_refresh (dir); - - err = procfs_get_contents (dir, &contents, &contents_len); - if (err) - return err; - - /* We depend on the fact that CONTENTS is terminated. */ - assert (contents_len == 0 || contents[contents_len - 1] == '\0'); - - /* Skip to the first requested entry. */ - while (contents_len && entry--) - { - int ofs = strlen (contents) + 1; - contents += ofs; - contents_len -= ofs; - } - - /* Allocate a buffer if necessary. */ - putentries (contents, contents_len, nentries, NULL, datacnt); - if (bufsize < *datacnt) - { - char *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); - if (n == MAP_FAILED) - return ENOMEM; - - *data = n; - } - - /* Do the actual conversion. */ - *amt = putentries (contents, contents_len, nentries, *data, datacnt); - - return 0; -} - -/* The user must define this function. Lookup NAME in DIR (which is - locked) for USER; set *NP to the found name upon return. If the - name was not found, then return ENOENT. On any error, clear *NP. - (*NP, if found, should be locked and a reference to it generated. - This call should unlock DIR no matter what.) */ -error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, - char *name, struct node **np) -{ - error_t err; - - err = procfs_lookup (dir, name, np); - mutex_unlock (&dir->lock); - - if (! err) - mutex_lock (&(*np)->lock); - - return err; -} - -/* The user must define this function. Node NP has no more references; - free all its associated storage. */ -void netfs_node_norefs (struct node *np) -{ - spin_unlock (&netfs_node_refcnt_lock); - - procfs_cleanup (np); - free (np); - - spin_lock (&netfs_node_refcnt_lock); -} - - -/* Libnetfs callbacks managed with libfshelp. */ - -/* The user must define this function. Locked node NP is being opened - by USER, with FLAGS. NEWNODE is nonzero if we just created this - node. Return an error if we should not permit the open to complete - because of a permission restriction. */ -error_t netfs_check_open_permissions (struct iouser *user, struct node *np, - int flags, int newnode) -{ - error_t err = 0; - if (!err && (flags & O_READ)) - err = fshelp_access (&np->nn_stat, S_IREAD, user); - if (!err && (flags & O_WRITE)) - err = fshelp_access (&np->nn_stat, S_IWRITE, user); - if (!err && (flags & O_EXEC)) - err = fshelp_access (&np->nn_stat, S_IEXEC, user); - return err; -} - -/* The user must define this function. Return the valid access - types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for - locked file NP and user CRED. */ -error_t netfs_report_access (struct iouser *cred, struct node *np, - int *types) -{ - *types = 0; - if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) - *types |= O_READ; - if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) - *types |= O_WRITE; - if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) - *types |= O_EXEC; - return 0; -} - - -/* Trivial or unsupported libnetfs callbacks. */ - -/* The user must define this function. This should attempt a chmod - call for the user specified by CRED on locked node NP, to change - the owner to UID and the group to GID. */ -error_t netfs_attempt_chown (struct iouser *cred, struct node *np, - uid_t uid, uid_t gid) -{ - return EROFS; -} - -/* The user must define this function. This should attempt a chauthor - call for the user specified by CRED on locked node NP, thereby - changing the author to AUTHOR. */ -error_t netfs_attempt_chauthor (struct iouser *cred, struct node *np, - uid_t author) -{ - return EROFS; -} - -/* The user must define this function. This should attempt a chmod - call for the user specified by CRED on locked node NODE, to change - the mode to MODE. Unlike the normal Unix and Hurd meaning of - chmod, this function is also used to attempt to change files into - other types. If such a transition is attempted which is - impossible, then return EOPNOTSUPP. */ -error_t netfs_attempt_chmod (struct iouser *cred, struct node *np, - mode_t mode) -{ - return EROFS; -} - -/* The user must define this function. Attempt to turn locked node NP - (user CRED) into a symlink with target NAME. */ -error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np, - char *name) -{ - return EROFS; -} - -/* The user must define this function. Attempt to turn NODE (user - CRED) into a device. TYPE is either S_IFBLK or S_IFCHR. NP is - locked. */ -error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np, - mode_t type, dev_t indexes) -{ - return EROFS; -} - -/* The user must define this function. This should attempt a chflags - call for the user specified by CRED on locked node NP, to change - the flags to FLAGS. */ -error_t netfs_attempt_chflags (struct iouser *cred, struct node *np, - int flags) -{ - return EROFS; -} - -/* The user must define this function. This should attempt a utimes - call for the user specified by CRED on locked node NP, to change - the atime to ATIME and the mtime to MTIME. If ATIME or MTIME is - null, then set to the current time. */ -error_t netfs_attempt_utimes (struct iouser *cred, struct node *np, - struct timespec *atime, struct timespec *mtime) -{ - return EROFS; -} - -/* The user must define this function. This should attempt to set the - size of the locked file NP (for user CRED) to SIZE bytes long. */ -error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, - loff_t size) -{ - return EROFS; -} - -/* The user must define this function. This should attempt to fetch - filesystem status information for the remote filesystem, for the - user CRED. NP is locked. */ -error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, - fsys_statfsbuf_t *st) -{ - return ENOSYS; -} - -/* The user must define this function. This should sync the locked - file NP completely to disk, for the user CRED. If WAIT is set, - return only after the sync is completely finished. */ -error_t netfs_attempt_sync (struct iouser *cred, struct node *np, - int wait) -{ - return 0; -} - -/* The user must define this function. This should sync the entire - remote filesystem. If WAIT is set, return only after the sync is - completely finished. */ -error_t netfs_attempt_syncfs (struct iouser *cred, int wait) -{ - return 0; -} - -/* The user must define this function. Delete NAME in DIR (which is - locked) for USER. */ -error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, - char *name) -{ - return EROFS; -} - -/* The user must define this function. Attempt to rename the - directory FROMDIR to TODIR. Note that neither of the specific nodes - are locked. */ -error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, - char *fromname, struct node *todir, - char *toname, int excl) -{ - return EROFS; -} - -/* The user must define this function. Attempt to create a new - directory named NAME in DIR (which is locked) for USER with mode - MODE. */ -error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, - char *name, mode_t mode) -{ - return EROFS; -} - -/* The user must define this function. Attempt to remove directory - named NAME in DIR (which is locked) for USER. */ -error_t netfs_attempt_rmdir (struct iouser *user, - struct node *dir, char *name) -{ - return EROFS; -} - - -/* The user must define this function. Create a link in DIR with name - NAME to FILE for USER. Note that neither DIR nor FILE are - locked. If EXCL is set, do not delete the target. Return EEXIST if - NAME is already found in DIR. */ -error_t netfs_attempt_link (struct iouser *user, struct node *dir, - struct node *file, char *name, int excl) -{ - return EROFS; -} - -/* The user must define this function. Attempt to create an anonymous - file related to DIR (which is locked) for USER with MODE. Set *NP - to the returned file upon success. No matter what, unlock DIR. */ -error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, - mode_t mode, struct node **np) -{ - return EROFS; -} - -/* The user must define this function. Attempt to create a file named - NAME in DIR (which is locked) for USER with MODE. Set *NP to the - new node upon return. On any error, clear *NP. *NP should be - locked on success; no matter what, unlock DIR before returning. */ -error_t netfs_attempt_create_file (struct iouser *user, struct node *dir, - char *name, mode_t mode, struct node **np) -{ - return EROFS; -} - -/* The user must define this function. Write to the locked file NP - for user CRED starting at OFSET and continuing for up to *LEN bytes - from DATA. Set *LEN to the amount successfully written upon - return. */ -error_t netfs_attempt_write (struct iouser *cred, struct node *np, - loff_t offset, size_t *len, void *data) -{ - return EROFS; -} - - diff --git a/process.c b/process.c deleted file mode 100644 index 6652a4e9..00000000 --- a/process.c +++ /dev/null @@ -1,387 +0,0 @@ -/* Hurd /proc filesystem, implementation of process directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include -#include "procfs.h" -#include "procfs_dir.h" -#include "process.h" -#include "main.h" - -/* This module implements the process directories and the files they - contain. A libps proc_stat structure is created for each process - node, and is used by the individual file content generators as a - source of information. Each possible file (cmdline, environ, ...) is - decribed in a process_file_desc structure, which specifies which bits - of information (ie. libps flags) it needs, and what function should - be used to generate the file's contents. - - The content generators are defined first, followed by glue logic and - entry table. */ - - -/* Helper functions */ - -static char state_char (struct proc_stat *ps) -{ - int i; - - for (i = 0; (1 << i) & (PSTAT_STATE_P_STATES | PSTAT_STATE_T_STATES); i++) - if (proc_stat_state (ps) & (1 << i)) - return proc_stat_state_tags[i]; - - return '?'; -} - -static const char *state_string (struct proc_stat *ps) -{ - static const char *const state_strings[] = { - "T (stopped)", - "Z (zombie)", - "R (running)", - "H (halted)", - "D (disk sleep)", - "S (sleeping)", - "I (idle)", - NULL - }; - int i; - - for (i = 0; state_strings[i]; i++) - if (proc_stat_state (ps) & (1 << i)) - return state_strings[i]; - - return "? (unknown)"; -} - -static long long int timeval_jiffies (time_value_t tv) -{ - double secs = tv.seconds * 1000000. + tv.microseconds; - return secs * opt_clk_tck / 1000000.; -} - -/* Actual content generators */ - -static ssize_t -process_file_gc_cmdline (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_args(ps); - return proc_stat_args_len(ps); -} - -static ssize_t -process_file_gc_environ (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_env(ps); - return proc_stat_env_len(ps); -} - -static ssize_t -process_file_gc_stat (struct proc_stat *ps, char **contents) -{ - struct procinfo *pi = proc_stat_proc_info (ps); - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); - - /* See proc(5) for more information about the contents of each field for the - Linux procfs. */ - return asprintf (contents, - "%d (%s) %c " /* pid, command, state */ - "%d %d %d " /* ppid, pgid, session */ - "%d %d " /* controling tty stuff */ - "%u " /* flags, as defined by */ - "%lu %lu %lu %lu " /* page fault counts */ - "%lu %lu %ld %ld " /* user/sys times, in sysconf(_SC_CLK_TCK) */ - "%d %d " /* scheduler params (priority, nice) */ - "%d %ld " /* number of threads, [obsolete] */ - "%llu " /* start time since boot (jiffies) */ - "%lu %ld %lu " /* virtual size (bytes), rss (pages), rss lim */ - "%lu %lu %lu %lu %lu " /* some vm addresses (code, stack, sp, pc) */ - "%lu %lu %lu %lu " /* pending, blocked, ignored and caught sigs */ - "%lu " /* wait channel */ - "%lu %lu " /* swap usage (not maintained in Linux) */ - "%d " /* exit signal, to be sent to the parent */ - "%d " /* last processor used */ - "%u %u " /* RT priority and policy */ - "%llu " /* aggregated block I/O delay */ - "\n", - proc_stat_pid (ps), proc_stat_args (ps), state_char (ps), - pi->ppid, pi->pgrp, pi->session, - 0, 0, /* no such thing as a major:minor for ctty */ - 0, /* no such thing as CLONE_* flags on Hurd */ - 0L, 0L, 0L, 0L, /* TASK_EVENTS_INFO is unavailable on GNU Mach */ - (long unsigned) timeval_jiffies (thbi->user_time), - (long unsigned) timeval_jiffies (thbi->system_time), - 0L, 0L, /* cumulative time for children */ - MACH_PRIORITY_TO_NICE(thbi->base_priority) + 20, - MACH_PRIORITY_TO_NICE(thbi->base_priority), - pi->nthreads, 0L, - timeval_jiffies (thbi->creation_time), /* FIXME: ... since boot */ - (long unsigned) tbi->virtual_size, - (long unsigned) tbi->resident_size / PAGE_SIZE, 0L, - 0L, 0L, 0L, 0L, 0L, - 0L, 0L, 0L, 0L, - (long unsigned) proc_stat_thread_rpc (ps), /* close enough */ - 0L, 0L, - 0, - 0, - 0, 0, - 0LL); -} - -static ssize_t -process_file_gc_statm (struct proc_stat *ps, char **contents) -{ - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - - return asprintf (contents, - "%lu %lu 0 0 0 0 0\n", - tbi->virtual_size / sysconf(_SC_PAGE_SIZE), - tbi->resident_size / sysconf(_SC_PAGE_SIZE)); -} - -static ssize_t -process_file_gc_status (struct proc_stat *ps, char **contents) -{ - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - - return asprintf (contents, - "Name:\t%s\n" - "State:\t%s\n" - "Tgid:\t%u\n" - "Pid:\t%u\n" - "PPid:\t%u\n" - "Uid:\t%u\t%u\t%u\t%u\n" - "VmSize:\t%8u kB\n" - "VmPeak:\t%8u kB\n" - "VmRSS:\t%8u kB\n" - "VmHWM:\t%8u kB\n" /* ie. resident peak */ - "Threads:\t%u\n", - proc_stat_args (ps), - state_string (ps), - proc_stat_pid (ps), /* XXX will need more work for threads */ - proc_stat_pid (ps), - proc_stat_proc_info (ps)->ppid, - proc_stat_owner_uid (ps), - proc_stat_owner_uid (ps), - proc_stat_owner_uid (ps), - proc_stat_owner_uid (ps), - tbi->virtual_size / 1024, - tbi->virtual_size / 1024, - tbi->resident_size / 1024, - tbi->resident_size / 1024, - proc_stat_num_threads (ps)); -} - - -/* Implementation of the file nodes. */ - -/* Describes a file in the process directories. This structure is - filled in as an "entry hook" in our procfs_dir entry table and is - passed to the process_file_make_node function defined below. */ -struct process_file_desc -{ - /* The proc_stat information required to get the contents of this file. */ - ps_flags_t needs; - - /* Content generator to use for this file. Once we have acquired the - necessary information, there can be only memory allocation errors, - hence this simplified signature. */ - ssize_t (*get_contents) (struct proc_stat *ps, char **contents); - - /* The cmdline and environ contents don't need any cleaning since they - point directly into the proc_stat structure. */ - int no_cleanup; - - /* If specified, the file mode to be set with procfs_node_chmod(). */ - mode_t mode; -}; - -struct process_file_node -{ - const struct process_file_desc *desc; - struct proc_stat *ps; -}; - -/* FIXME: lock the parent! */ -static error_t -process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) -{ - struct process_file_node *file = hook; - error_t err; - - /* Fetch the required information. */ - err = proc_stat_set_flags (file->ps, file->desc->needs); - if (err) - return EIO; - if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) - return EIO; - - /* Call the actual content generator (see the definitions below). */ - *contents_len = file->desc->get_contents (file->ps, contents); - return 0; -} - -static void -process_file_cleanup_contents (void *hook, char *contents, ssize_t len) -{ - struct process_file_node *file = hook; - - if (! file->desc->no_cleanup) - free (contents); -} - -static struct node * -process_file_make_node (void *dir_hook, const void *entry_hook) -{ - static const struct procfs_node_ops ops = { - .get_contents = process_file_get_contents, - .cleanup_contents = process_file_cleanup_contents, - .cleanup = free, - }; - struct process_file_node *f; - struct node *np; - - f = malloc (sizeof *f); - if (! f) - return NULL; - - f->desc = entry_hook; - f->ps = dir_hook; - - np = procfs_make_node (&ops, f); - if (! np) - return NULL; - - procfs_node_chown (np, proc_stat_owner_uid (f->ps)); - if (f->desc->mode) - procfs_node_chmod (np, f->desc->mode); - - return np; -} - -/* Stat needs its own constructor in oreder to set its mode according to - the --stat-mode command-line option. */ -static struct node * -process_stat_make_node (void *dir_hook, const void *entry_hook) -{ - struct node *np = process_file_make_node (dir_hook, entry_hook); - if (np) procfs_node_chmod (np, opt_stat_mode); - return np; -} - - -/* Implementation of the process directory per se. */ - -static struct procfs_dir_entry entries[] = { - { - .name = "cmdline", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_cmdline, - .needs = PSTAT_ARGS, - .no_cleanup = 1, - }, - }, - { - .name = "environ", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_environ, - .needs = PSTAT_ENV, - .no_cleanup = 1, - .mode = 0400, - }, - }, - { - .name = "stat", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_stat, - .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO - | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC - | PSTAT_THREAD_WAIT, - }, - .ops = { - .make_node = process_stat_make_node, - } - }, - { - .name = "statm", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_statm, - .needs = PSTAT_TASK_BASIC, - }, - }, - { - .name = "status", - .hook = & (struct process_file_desc) { - .get_contents = process_file_gc_status, - .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO - | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS, - }, - }, - {} -}; - -error_t -process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) -{ - static const struct procfs_dir_ops dir_ops = { - .entries = entries, - .cleanup = (void (*)(void *)) _proc_stat_free, - .entry_ops = { - .make_node = process_file_make_node, - }, - }; - struct proc_stat *ps; - int owner; - error_t err; - - err = _proc_stat_create (pid, pc, &ps); - if (err == ESRCH) - return ENOENT; - if (err) - return EIO; - - err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); - if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) - { - _proc_stat_free (ps); - return EIO; - } - - /* FIXME: have a separate proc_desc structure for each file, so this can be - accessed in a more robust and straightforward way. */ - ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; - - /* FIXME: have a separate proc_desc structure for each file, so this can be - accessed in a more robust and straightforward way. */ - ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; - - *np = procfs_dir_make_node (&dir_ops, ps); - if (! *np) - return ENOMEM; - - owner = proc_stat_owner_uid (ps); - procfs_node_chown (*np, owner >= 0 ? owner : opt_anon_owner); - return 0; -} diff --git a/process.h b/process.h deleted file mode 100644 index b230a281..00000000 --- a/process.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Hurd /proc filesystem, implementation of process directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include - -/* Create a node for a directory representing the given PID, as published by - the proc server refrenced by the libps context PC. On success, returns the - newly created node in *NP. */ -error_t -process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np); - diff --git a/procfs.c b/procfs.c deleted file mode 100644 index ae5a6769..00000000 --- a/procfs.c +++ /dev/null @@ -1,203 +0,0 @@ -/* Hurd /proc filesystem, basic infrastructure. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include "procfs.h" - -struct netnode -{ - const struct procfs_node_ops *ops; - void *hook; - - /* (cached) contents of the node */ - char *contents; - ssize_t contents_len; - - /* parent directory, if applicable */ - struct node *parent; -}; - -void -procfs_cleanup_contents_with_free (void *hook, char *cont, ssize_t len) -{ - free (cont); -} - -void -procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, ssize_t len) -{ - vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); -} - -struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) -{ - struct netnode *nn; - struct node *np; - - nn = malloc (sizeof *nn); - if (! nn) - goto fail; - - memset (nn, 0, sizeof *nn); - nn->ops = ops; - nn->hook = hook; - - np = netfs_make_node (nn); - if (! np) - goto fail; - - np->nn = nn; - memset (&np->nn_stat, 0, sizeof np->nn_stat); - np->nn_translated = 0; - - if (np->nn->ops->lookup) - np->nn_stat.st_mode = S_IFDIR | 0555; - else - np->nn_stat.st_mode = S_IFREG | 0444; - - return np; - -fail: - if (ops->cleanup) - ops->cleanup (hook); - - free (nn); - return NULL; -} - -void procfs_node_chown (struct node *np, uid_t owner) -{ - np->nn_stat.st_uid = owner; -} - -void procfs_node_chmod (struct node *np, mode_t mode) -{ - np->nn_stat.st_mode = (np->nn_stat.st_mode & S_IFMT) | mode; - np->nn_translated = np->nn_stat.st_mode; -} - -void procfs_node_chtype (struct node *np, mode_t type) -{ - np->nn_stat.st_mode = (np->nn_stat.st_mode & ~S_IFMT) | type; - np->nn_translated = np->nn_stat.st_mode; - if (type == S_IFLNK) - procfs_node_chmod (np, 0777); -} - -/* FIXME: possibly not the fastest hash function... */ -ino64_t -procfs_make_ino (struct node *np, const char *filename) -{ - unsigned short x[3]; - - if (! strcmp (filename, ".")) - return np->nn_stat.st_ino; - if (! strcmp (filename, "..")) - return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 2; - - assert (sizeof np->nn_stat.st_ino > sizeof x); - memcpy (x, &np->nn_stat.st_ino, sizeof x); - - while (*filename) - { - x[0] ^= *(filename++); - jrand48 (x); - } - - return (unsigned long) jrand48 (x); -} - -error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) -{ - if (! np->nn->contents && np->nn->ops->get_contents) - { - char *contents; - ssize_t contents_len; - error_t err; - - contents_len = -1; - err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); - if (err) - return err; - if (contents_len < 0) - return ENOMEM; - - np->nn->contents = contents; - np->nn->contents_len = contents_len; - } - - *data = np->nn->contents; - *data_len = np->nn->contents_len; - return 0; -} - -void procfs_refresh (struct node *np) -{ - if (np->nn->contents && np->nn->ops->cleanup_contents) - np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); - - np->nn->contents = NULL; -} - -error_t procfs_lookup (struct node *np, const char *name, struct node **npp) -{ - error_t err = ENOENT; - - if (err && ! strcmp (name, ".")) - { - netfs_nref(*npp = np); - err = 0; - } - - if (err && np->nn->parent && ! strcmp (name, "..")) - { - netfs_nref(*npp = np->nn->parent); - err = 0; - } - - if (err && np->nn->ops->lookup) - { - err = np->nn->ops->lookup (np->nn->hook, name, npp); - if (! err) - { - (*npp)->nn_stat.st_ino = procfs_make_ino (np, name); - netfs_nref ((*npp)->nn->parent = np); - } - } - - return err; -} - -void procfs_cleanup (struct node *np) -{ - procfs_refresh (np); - - if (np->nn->ops->cleanup) - np->nn->ops->cleanup (np->nn->hook); - - if (np->nn->parent) - netfs_nrele (np->nn->parent); - - free (np->nn); -} diff --git a/procfs.h b/procfs.h deleted file mode 100644 index 64782ec4..00000000 --- a/procfs.h +++ /dev/null @@ -1,93 +0,0 @@ -/* Hurd /proc filesystem, basic infrastructure. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include - - -/* Interface for the procfs side. */ - -/* Any of these callback functions can be omitted, in which case - reasonable defaults will be used. The initial file mode and type - depend on whether a lookup function is provided, but can be - overridden in update_stat(). */ -struct procfs_node_ops -{ - /* Fetch the contents of a node. A pointer to the contents should be - returned in *CONTENTS and their length in *CONTENTS_LEN. The exact - nature of these data depends on whether the node is a regular file, - symlink or directory, as determined by the file mode in - netnode->nn_stat. For regular files and symlinks, they are what - you would expect; for directories, they are an argz vector of the - names of the entries. If upon return, *CONTENTS_LEN is negative or - unchanged, the call is considered to have failed because of a memory - allocation error. */ - error_t (*get_contents) (void *hook, char **contents, ssize_t *contents_len); - void (*cleanup_contents) (void *hook, char *contents, ssize_t contents_len); - - /* Lookup NAME in this directory, and store the result in *np. The - returned node should be created by lookup() using procfs_make_node() - or a derived function. Note that the parent will be kept alive as - long as the child exists, so you can safely reference the parent's - data from the child. You may want to consider locking if there's - any mutation going on, though. */ - error_t (*lookup) (void *hook, const char *name, struct node **np); - - /* Destroy this node. */ - void (*cleanup) (void *hook); -}; - -/* These helper functions can be used as procfs_node_ops.cleanup_contents. */ -void procfs_cleanup_contents_with_free (void *, char *, ssize_t); -void procfs_cleanup_contents_with_vm_deallocate (void *, char *, ssize_t); - -/* Create a new node and return it. Returns NULL if it fails to allocate - enough memory. In this case, ops->cleanup will be invoked. */ -struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); - -/* Set the owner of the node NP. Must be called right after the node - has been created. */ -void procfs_node_chown (struct node *np, uid_t owner); - -/* Set the permission bits of the node NP. Must be called right after - the node has been created. */ -void procfs_node_chmod (struct node *np, mode_t mode); - -/* Set the type of the node NP. If type is S_IFLNK, appropriate - permission bits will be set as well. Must be called right after the - node has been created. */ -void procfs_node_chtype (struct node *np, mode_t type); - - -/* Interface for the libnetfs side. */ - -/* Get the inode number which will be given to a child of NP named FILENAME. - This allows us to retreive them for readdir() without creating the - corresponding child nodes. */ -ino64_t procfs_make_ino (struct node *np, const char *filename); - -/* Forget the current cached contents for the node. This is done before reads - from offset 0, to ensure that the data are recent even for utilities such as - top which keep some nodes open. */ -void procfs_refresh (struct node *np); - -error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); -error_t procfs_lookup (struct node *np, const char *name, struct node **npp); -void procfs_cleanup (struct node *np); - diff --git a/procfs/Makefile b/procfs/Makefile new file mode 100644 index 00000000..a397522f --- /dev/null +++ b/procfs/Makefile @@ -0,0 +1,26 @@ +TARGET = procfs +OBJS = procfs.o netfs.o procfs_dir.o \ + process.o proclist.o rootdir.o dircat.o main.o +LIBS = -lnetfs -lps + +CC = gcc +CFLAGS = -Wall -g +CPPFLAGS = +LDFLAGS = + +ifdef PROFILE +CFLAGS= -g -pg +CPPFLAGS= -DPROFILE +LDFLAGS= -static +LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lthreads -lihash -lshouldbeinlibc +endif + +CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + $(RM) $(TARGET) $(OBJS) diff --git a/procfs/TODO b/procfs/TODO new file mode 100644 index 00000000..952d67bc --- /dev/null +++ b/procfs/TODO @@ -0,0 +1,24 @@ +Known bugs to be fixed +---------------------- + +* The non-owned processes sometimes show up with INT_MAX as their owner, + instead of opt_anon_uid. This is likely to be a libps problem. + +Improvements and new features +----------------------------- + +* There is a lot of dynamic memory allocation going on and it comes with a + cost in performance. We could try to limit such allocation, as long as it + keeps the inner interface simple and preserves the read/readdir semantics + (performance is probably not critical for a proc filesystem.) + One way would be to add an (optional) "needed_length" field to + procfs_node_ops, and arrange to pass a sufficent buffer in (*contents, + *contents_len) when get_contents is called. Then the user-provided buffer + might be used directly under some circumstances. + +* Add thread directories as [pid]/task/[n]. This shouldn't be too hard if we + use "process" nodes for threads, and provide an "exists" hook for the "task" + entry itself so that it's disabled in thread nodes. It might prove necessary + to have "optional" libps flags for some content generators, though, since + some of them might be missing for threads. + diff --git a/procfs/dircat.c b/procfs/dircat.c new file mode 100644 index 00000000..5a60899a --- /dev/null +++ b/procfs/dircat.c @@ -0,0 +1,128 @@ +/* Hurd /proc filesystem, concatenation of two directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include "procfs.h" + +struct dircat_node +{ + int num_dirs; + struct node *dirs[0]; +}; + +static error_t +dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) +{ + struct dircat_node *dcn = hook; + int i, sz, pos; + error_t err; + + pos = 0; + *contents = malloc (sz = 512); + + for (i=0; i < dcn->num_dirs; i++) + { + char *subcon; + ssize_t sublen; + + /* Make sure we're not getting some old stuff. */ + procfs_refresh (dcn->dirs[i]); + + err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); + if (err) + { + free (*contents); + *contents = NULL; + return err; + } + + while (pos + sublen > sz) + *contents = realloc (*contents, sz *= 2); + + memcpy (*contents + pos, subcon, sublen); + pos += sublen; + } + + *contents_len = pos; + return 0; +} + +static error_t +dircat_lookup (void *hook, const char *name, struct node **np) +{ + struct dircat_node *dcn = hook; + error_t err; + int i; + + err = ENOENT; + for (i=0; err && i < dcn->num_dirs; i++) + err = procfs_lookup (dcn->dirs[i], name, np); + + return err; +} + +static void +dircat_release_dirs (struct node *const *dirs, int num_dirs) +{ + int i; + + for (i=0; i < num_dirs; i++) + if (dirs[i]) + netfs_nrele (dirs[i]); +} + +static void +dircat_cleanup (void *hook) +{ + struct dircat_node *dcn = hook; + + dircat_release_dirs (dcn->dirs, dcn->num_dirs); + free (dcn); +} + +struct node * +dircat_make_node (struct node *const *dirs, int num_dirs) +{ + static struct procfs_node_ops ops = { + .get_contents = dircat_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_free, + .lookup = dircat_lookup, + .cleanup = dircat_cleanup, + }; + struct dircat_node *dcn; + int i; + + for (i=0; i < num_dirs; i++) + if (! dirs[i]) + goto fail; + + dcn = malloc (sizeof *dcn + num_dirs * sizeof dcn->dirs[0]); + if (! dcn) + goto fail; + + dcn->num_dirs = num_dirs; + memcpy (dcn->dirs, dirs, num_dirs * sizeof dcn->dirs[0]); + return procfs_make_node (&ops, dcn); + +fail: + dircat_release_dirs (dirs, num_dirs); + return NULL; +} + diff --git a/procfs/dircat.h b/procfs/dircat.h new file mode 100644 index 00000000..4177b384 --- /dev/null +++ b/procfs/dircat.h @@ -0,0 +1,29 @@ +/* Hurd /proc filesystem, concatenation of two directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Append the contents of NUM_DIRS directories. DIRS is an array of + directory nodes. One reference is consumed for each of them. If a + memory allocation error occurs, or if one of the directories is a + NULL pointer, the references are dropped immediately and NULL is + returned. The given DIRS array is duplicated and can therefore be + allocated on the caller's stack. Strange things will happen if some + elements of DIRS have entries with the same name or if one of them is + not a directory. */ +struct node * +dircat_make_node (struct node *const *dirs, int num_dirs); diff --git a/procfs/main.c b/procfs/main.c new file mode 100644 index 00000000..3a976ccc --- /dev/null +++ b/procfs/main.c @@ -0,0 +1,190 @@ +/* Hurd /proc filesystem, main program. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include "procfs.h" +#include "proclist.h" +#include "rootdir.h" +#include "dircat.h" +#include "main.h" + +/* Command-line options */ +int opt_clk_tck; +mode_t opt_stat_mode; +pid_t opt_fake_self; +pid_t opt_kernel_pid; +uid_t opt_anon_owner; + +static error_t +argp_parser (int key, char *arg, struct argp_state *state) +{ + struct passwd *pw; + char *endp; + + switch (key) + { + case 'h': + opt_clk_tck = strtol (arg, &endp, 0); + if (*endp || ! *arg || opt_clk_tck <= 0) + error (1, 0, "--clk-tck: HZ should be a positive integer"); + break; + + case 's': + opt_stat_mode = strtol (arg, &endp, 8); + if (*endp || ! *arg || opt_stat_mode & ~07777) + error (1, 0, "--stat-mode: MODE should be an octal mode"); + break; + + case 'S': + if (arg) + { + opt_fake_self = strtol (arg, &endp, 0); + if (*endp || ! *arg) + error (1, 0, "--fake-self: PID must be an integer"); + } + else + opt_fake_self = 1; + break; + + case 'k': + opt_kernel_pid = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_kernel_pid < 0) + error (1, 0, "--kernel-process: PID must be a positive integer"); + break; + + case 'c': + opt_clk_tck = 100; + opt_stat_mode = 0444; + opt_fake_self = 1; + break; + + case 'a': + pw = getpwnam (arg); + if (pw) + { + opt_anon_owner = pw->pw_uid; + break; + } + + opt_anon_owner = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_anon_owner < 0) + error(1, 0, "--anonymous-owner: USER should be the a user name " + "or a numeric UID."); + break; + } + + return 0; +} + +struct argp argp = { + .options = (struct argp_option []) { + { "clk-tck", 'h', "HZ", 0, + "Unit used for the values expressed in system clock ticks " + "(default: sysconf(_SC_CLK_TCK))" }, + { "stat-mode", 's', "MODE", 0, + "The [pid]/stat file publishes information which on Hurd is only " + "available to the process owner. " + "You can use this option to override its mode to be more permissive " + "for compatibility purposes. " + "(default: 0400)" }, + { "fake-self", 'S', "PID", OPTION_ARG_OPTIONAL, + "Provide a fake \"self\" symlink to the given PID, for compatibility " + "purposes. If PID is omitted, \"self\" will point to init. " + "(default: no self link)" }, + { "kernel-process", 'k', "PID", 0, + "Process identifier for the kernel, used to retreive its command " + "line, as well as the global up and idle times. " + "(default: 2)" }, + { "compatible", 'c', NULL, 0, + "Try to be compatible with the Linux procps utilities. " + "Currently equivalent to -h 100 -s 0444 -S 1." }, + { "anonymous-owner", 'a', "USER", 0, + "Make USER the owner of files related to processes without one. " + "Be aware that USER will be granted access to the environment and " + "other sensitive information about the processes in question. " + "(default: use uid 0)" }, + {} + }, + .parser = argp_parser, + .doc = "A virtual filesystem emulating the Linux procfs.", + .children = (struct argp_child []) { + { &netfs_std_startup_argp, }, + {} + }, +}; + +error_t +root_make_node (struct ps_context *pc, struct node **np) +{ + struct node *root_dirs[] = { + proclist_make_node (pc), + rootdir_make_node (pc), + }; + + *np = dircat_make_node (root_dirs, sizeof root_dirs / sizeof root_dirs[0]); + if (! *np) + return ENOMEM; + + /* Since this one is not created through proc_lookup(), we have to affect an + inode number to it. */ + (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; + + return 0; +} + +int main (int argc, char **argv) +{ + struct ps_context *pc; + mach_port_t bootstrap; + error_t err; + + opt_clk_tck = sysconf(_SC_CLK_TCK); + opt_stat_mode = 0400; + opt_fake_self = -1; + opt_kernel_pid = 2; + opt_anon_owner = 0; + err = argp_parse (&argp, argc, argv, 0, 0, 0); + if (err) + error (1, err, "Could not parse command line"); + + err = ps_context_create (getproc (), &pc); + if (err) + error (1, err, "Could not create libps context"); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "Must be started as a translator"); + + netfs_init (); + err = root_make_node (pc, &netfs_root_node); + if (err) + error (1, err, "Could not create the root node"); + + netfs_startup (bootstrap, 0); + netfs_server_loop (); + + assert (0 /* netfs_server_loop returned after all */); +} + diff --git a/procfs/main.h b/procfs/main.h new file mode 100644 index 00000000..4e28b7eb --- /dev/null +++ b/procfs/main.h @@ -0,0 +1,25 @@ +/* Hurd /proc filesystem, command-line options set by main.c. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Startup options */ +extern int opt_clk_tck; +extern mode_t opt_stat_mode; +extern pid_t opt_fake_self; +extern pid_t opt_kernel_pid; +extern uid_t opt_anon_owner; diff --git a/procfs/netfs.c b/procfs/netfs.c new file mode 100644 index 00000000..24a6603f --- /dev/null +++ b/procfs/netfs.c @@ -0,0 +1,445 @@ +/* Hurd /proc filesystem, interface with libnetfs. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include "procfs.h" + +#define PROCFS_SERVER_NAME "procfs" +#define PROCFS_SERVER_VERSION "0.1.0" +#define PROCFS_MAXSYMLINKS 16 + + +/* Interesting libnetfs callback functions. */ + +/* The user must define this variable. Set this to the name of the + filesystem server. */ +char *netfs_server_name = PROCFS_SERVER_NAME; + +/* The user must define this variables. Set this to be the server + version number. */ +char *netfs_server_version = PROCFS_SERVER_VERSION; + +/* Maximum number of symlinks to follow before returning ELOOP. */ +int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; + +/* The user must define this function. Make sure that NP->nn_stat is + filled with the most current information. CRED identifies the user + responsible for the operation. NP is locked. */ +error_t netfs_validate_stat (struct node *np, struct iouser *cred) +{ + char *contents; + ssize_t contents_len; + error_t err; + + /* Only symlinks need to have their size filled, before a read is + attempted. */ + if (! S_ISLNK (np->nn_stat.st_mode)) + return 0; + + err = procfs_get_contents (np, &contents, &contents_len); + if (err) + return err; + + np->nn_stat.st_size = contents_len; + return 0; +} + +/* The user must define this function. Read from the locked file NP + for user CRED starting at OFFSET and continuing for up to *LEN + bytes. Put the data at DATA. Set *LEN to the amount successfully + read upon return. */ +error_t netfs_attempt_read (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + char *contents; + ssize_t contents_len; + error_t err; + + if (offset == 0) + procfs_refresh (np); + + err = procfs_get_contents (np, &contents, &contents_len); + if (err) + return err; + + contents += offset; + contents_len -= offset; + + if (*len > contents_len) + *len = contents_len; + if (*len < 0) + *len = 0; + + memcpy (data, contents, *len); + return 0; +} + +/* The user must define this function. Read the contents of locked + node NP (a symlink), for USER, into BUF. */ +error_t netfs_attempt_readlink (struct iouser *user, struct node *np, + char *buf) +{ + char *contents; + ssize_t contents_len; + error_t err; + + err = procfs_get_contents (np, &contents, &contents_len); + if (err) + return err; + + assert (contents_len == np->nn_stat.st_size); + memcpy (buf, contents, contents_len); + return 0; +} + +/* Helper function for netfs_get_dirents() below. CONTENTS is an argz + vector of directory entry names, as returned by procfs_get_contents(). + Convert at most NENTRIES of them to dirent structures, put them in + DATA (if not NULL), write the number of entries processed in *AMT and + return the required/used space in DATACNT. */ +static int putentries (char *contents, size_t contents_len, int nentries, + char *data, mach_msg_type_number_t *datacnt) +{ + int i; + + *datacnt = 0; + for (i = 0; contents_len && (nentries < 0 || i < nentries); i++) + { + int namlen = strlen (contents); + int reclen = sizeof (struct dirent) + namlen; + + if (data) + { + struct dirent *d = (struct dirent *) (data + *datacnt); + d->d_fileno = 42; /* XXX */ + d->d_namlen = namlen; + d->d_reclen = reclen; + d->d_type = DT_UNKNOWN; + strcpy (d->d_name, contents); + } + + *datacnt += reclen; + contents += namlen + 1; + contents_len -= namlen + 1; + } + + return i; +} + +/* The user must define this function. Fill the array *DATA of size + BUFSIZE with up to NENTRIES dirents from DIR (which is locked) + starting with entry ENTRY for user CRED. The number of entries in + the array is stored in *AMT and the number of bytes in *DATACNT. + If the supplied buffer is not large enough to hold the data, it + should be grown. */ +error_t netfs_get_dirents (struct iouser *cred, struct node *dir, + int entry, int nentries, char **data, + mach_msg_type_number_t *datacnt, + vm_size_t bufsize, int *amt) +{ + char *contents; + ssize_t contents_len; + error_t err; + + if (entry == 0) + procfs_refresh (dir); + + err = procfs_get_contents (dir, &contents, &contents_len); + if (err) + return err; + + /* We depend on the fact that CONTENTS is terminated. */ + assert (contents_len == 0 || contents[contents_len - 1] == '\0'); + + /* Skip to the first requested entry. */ + while (contents_len && entry--) + { + int ofs = strlen (contents) + 1; + contents += ofs; + contents_len -= ofs; + } + + /* Allocate a buffer if necessary. */ + putentries (contents, contents_len, nentries, NULL, datacnt); + if (bufsize < *datacnt) + { + char *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); + if (n == MAP_FAILED) + return ENOMEM; + + *data = n; + } + + /* Do the actual conversion. */ + *amt = putentries (contents, contents_len, nentries, *data, datacnt); + + return 0; +} + +/* The user must define this function. Lookup NAME in DIR (which is + locked) for USER; set *NP to the found name upon return. If the + name was not found, then return ENOENT. On any error, clear *NP. + (*NP, if found, should be locked and a reference to it generated. + This call should unlock DIR no matter what.) */ +error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **np) +{ + error_t err; + + err = procfs_lookup (dir, name, np); + mutex_unlock (&dir->lock); + + if (! err) + mutex_lock (&(*np)->lock); + + return err; +} + +/* The user must define this function. Node NP has no more references; + free all its associated storage. */ +void netfs_node_norefs (struct node *np) +{ + spin_unlock (&netfs_node_refcnt_lock); + + procfs_cleanup (np); + free (np); + + spin_lock (&netfs_node_refcnt_lock); +} + + +/* Libnetfs callbacks managed with libfshelp. */ + +/* The user must define this function. Locked node NP is being opened + by USER, with FLAGS. NEWNODE is nonzero if we just created this + node. Return an error if we should not permit the open to complete + because of a permission restriction. */ +error_t netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = 0; + if (!err && (flags & O_READ)) + err = fshelp_access (&np->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&np->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&np->nn_stat, S_IEXEC, user); + return err; +} + +/* The user must define this function. Return the valid access + types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for + locked file NP and user CRED. */ +error_t netfs_report_access (struct iouser *cred, struct node *np, + int *types) +{ + *types = 0; + if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + return 0; +} + + +/* Trivial or unsupported libnetfs callbacks. */ + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NP, to change + the owner to UID and the group to GID. */ +error_t netfs_attempt_chown (struct iouser *cred, struct node *np, + uid_t uid, uid_t gid) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chauthor + call for the user specified by CRED on locked node NP, thereby + changing the author to AUTHOR. */ +error_t netfs_attempt_chauthor (struct iouser *cred, struct node *np, + uid_t author) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NODE, to change + the mode to MODE. Unlike the normal Unix and Hurd meaning of + chmod, this function is also used to attempt to change files into + other types. If such a transition is attempted which is + impossible, then return EOPNOTSUPP. */ +error_t netfs_attempt_chmod (struct iouser *cred, struct node *np, + mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn locked node NP + (user CRED) into a symlink with target NAME. */ +error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn NODE (user + CRED) into a device. TYPE is either S_IFBLK or S_IFCHR. NP is + locked. */ +error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np, + mode_t type, dev_t indexes) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chflags + call for the user specified by CRED on locked node NP, to change + the flags to FLAGS. */ +error_t netfs_attempt_chflags (struct iouser *cred, struct node *np, + int flags) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a utimes + call for the user specified by CRED on locked node NP, to change + the atime to ATIME and the mtime to MTIME. If ATIME or MTIME is + null, then set to the current time. */ +error_t netfs_attempt_utimes (struct iouser *cred, struct node *np, + struct timespec *atime, struct timespec *mtime) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to set the + size of the locked file NP (for user CRED) to SIZE bytes long. */ +error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, + loff_t size) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to fetch + filesystem status information for the remote filesystem, for the + user CRED. NP is locked. */ +error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, + fsys_statfsbuf_t *st) +{ + return ENOSYS; +} + +/* The user must define this function. This should sync the locked + file NP completely to disk, for the user CRED. If WAIT is set, + return only after the sync is completely finished. */ +error_t netfs_attempt_sync (struct iouser *cred, struct node *np, + int wait) +{ + return 0; +} + +/* The user must define this function. This should sync the entire + remote filesystem. If WAIT is set, return only after the sync is + completely finished. */ +error_t netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + +/* The user must define this function. Delete NAME in DIR (which is + locked) for USER. */ +error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to rename the + directory FROMDIR to TODIR. Note that neither of the specific nodes + are locked. */ +error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, + char *fromname, struct node *todir, + char *toname, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a new + directory named NAME in DIR (which is locked) for USER with mode + MODE. */ +error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, + char *name, mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to remove directory + named NAME in DIR (which is locked) for USER. */ +error_t netfs_attempt_rmdir (struct iouser *user, + struct node *dir, char *name) +{ + return EROFS; +} + + +/* The user must define this function. Create a link in DIR with name + NAME to FILE for USER. Note that neither DIR nor FILE are + locked. If EXCL is set, do not delete the target. Return EEXIST if + NAME is already found in DIR. */ +error_t netfs_attempt_link (struct iouser *user, struct node *dir, + struct node *file, char *name, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create an anonymous + file related to DIR (which is locked) for USER with MODE. Set *NP + to the returned file upon success. No matter what, unlock DIR. */ +error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, + mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a file named + NAME in DIR (which is locked) for USER with MODE. Set *NP to the + new node upon return. On any error, clear *NP. *NP should be + locked on success; no matter what, unlock DIR before returning. */ +error_t netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Write to the locked file NP + for user CRED starting at OFSET and continuing for up to *LEN bytes + from DATA. Set *LEN to the amount successfully written upon + return. */ +error_t netfs_attempt_write (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + return EROFS; +} + + diff --git a/procfs/process.c b/procfs/process.c new file mode 100644 index 00000000..6652a4e9 --- /dev/null +++ b/procfs/process.c @@ -0,0 +1,387 @@ +/* Hurd /proc filesystem, implementation of process directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include "procfs.h" +#include "procfs_dir.h" +#include "process.h" +#include "main.h" + +/* This module implements the process directories and the files they + contain. A libps proc_stat structure is created for each process + node, and is used by the individual file content generators as a + source of information. Each possible file (cmdline, environ, ...) is + decribed in a process_file_desc structure, which specifies which bits + of information (ie. libps flags) it needs, and what function should + be used to generate the file's contents. + + The content generators are defined first, followed by glue logic and + entry table. */ + + +/* Helper functions */ + +static char state_char (struct proc_stat *ps) +{ + int i; + + for (i = 0; (1 << i) & (PSTAT_STATE_P_STATES | PSTAT_STATE_T_STATES); i++) + if (proc_stat_state (ps) & (1 << i)) + return proc_stat_state_tags[i]; + + return '?'; +} + +static const char *state_string (struct proc_stat *ps) +{ + static const char *const state_strings[] = { + "T (stopped)", + "Z (zombie)", + "R (running)", + "H (halted)", + "D (disk sleep)", + "S (sleeping)", + "I (idle)", + NULL + }; + int i; + + for (i = 0; state_strings[i]; i++) + if (proc_stat_state (ps) & (1 << i)) + return state_strings[i]; + + return "? (unknown)"; +} + +static long long int timeval_jiffies (time_value_t tv) +{ + double secs = tv.seconds * 1000000. + tv.microseconds; + return secs * opt_clk_tck / 1000000.; +} + +/* Actual content generators */ + +static ssize_t +process_file_gc_cmdline (struct proc_stat *ps, char **contents) +{ + *contents = proc_stat_args(ps); + return proc_stat_args_len(ps); +} + +static ssize_t +process_file_gc_environ (struct proc_stat *ps, char **contents) +{ + *contents = proc_stat_env(ps); + return proc_stat_env_len(ps); +} + +static ssize_t +process_file_gc_stat (struct proc_stat *ps, char **contents) +{ + struct procinfo *pi = proc_stat_proc_info (ps); + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); + + /* See proc(5) for more information about the contents of each field for the + Linux procfs. */ + return asprintf (contents, + "%d (%s) %c " /* pid, command, state */ + "%d %d %d " /* ppid, pgid, session */ + "%d %d " /* controling tty stuff */ + "%u " /* flags, as defined by */ + "%lu %lu %lu %lu " /* page fault counts */ + "%lu %lu %ld %ld " /* user/sys times, in sysconf(_SC_CLK_TCK) */ + "%d %d " /* scheduler params (priority, nice) */ + "%d %ld " /* number of threads, [obsolete] */ + "%llu " /* start time since boot (jiffies) */ + "%lu %ld %lu " /* virtual size (bytes), rss (pages), rss lim */ + "%lu %lu %lu %lu %lu " /* some vm addresses (code, stack, sp, pc) */ + "%lu %lu %lu %lu " /* pending, blocked, ignored and caught sigs */ + "%lu " /* wait channel */ + "%lu %lu " /* swap usage (not maintained in Linux) */ + "%d " /* exit signal, to be sent to the parent */ + "%d " /* last processor used */ + "%u %u " /* RT priority and policy */ + "%llu " /* aggregated block I/O delay */ + "\n", + proc_stat_pid (ps), proc_stat_args (ps), state_char (ps), + pi->ppid, pi->pgrp, pi->session, + 0, 0, /* no such thing as a major:minor for ctty */ + 0, /* no such thing as CLONE_* flags on Hurd */ + 0L, 0L, 0L, 0L, /* TASK_EVENTS_INFO is unavailable on GNU Mach */ + (long unsigned) timeval_jiffies (thbi->user_time), + (long unsigned) timeval_jiffies (thbi->system_time), + 0L, 0L, /* cumulative time for children */ + MACH_PRIORITY_TO_NICE(thbi->base_priority) + 20, + MACH_PRIORITY_TO_NICE(thbi->base_priority), + pi->nthreads, 0L, + timeval_jiffies (thbi->creation_time), /* FIXME: ... since boot */ + (long unsigned) tbi->virtual_size, + (long unsigned) tbi->resident_size / PAGE_SIZE, 0L, + 0L, 0L, 0L, 0L, 0L, + 0L, 0L, 0L, 0L, + (long unsigned) proc_stat_thread_rpc (ps), /* close enough */ + 0L, 0L, + 0, + 0, + 0, 0, + 0LL); +} + +static ssize_t +process_file_gc_statm (struct proc_stat *ps, char **contents) +{ + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + + return asprintf (contents, + "%lu %lu 0 0 0 0 0\n", + tbi->virtual_size / sysconf(_SC_PAGE_SIZE), + tbi->resident_size / sysconf(_SC_PAGE_SIZE)); +} + +static ssize_t +process_file_gc_status (struct proc_stat *ps, char **contents) +{ + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + + return asprintf (contents, + "Name:\t%s\n" + "State:\t%s\n" + "Tgid:\t%u\n" + "Pid:\t%u\n" + "PPid:\t%u\n" + "Uid:\t%u\t%u\t%u\t%u\n" + "VmSize:\t%8u kB\n" + "VmPeak:\t%8u kB\n" + "VmRSS:\t%8u kB\n" + "VmHWM:\t%8u kB\n" /* ie. resident peak */ + "Threads:\t%u\n", + proc_stat_args (ps), + state_string (ps), + proc_stat_pid (ps), /* XXX will need more work for threads */ + proc_stat_pid (ps), + proc_stat_proc_info (ps)->ppid, + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + tbi->virtual_size / 1024, + tbi->virtual_size / 1024, + tbi->resident_size / 1024, + tbi->resident_size / 1024, + proc_stat_num_threads (ps)); +} + + +/* Implementation of the file nodes. */ + +/* Describes a file in the process directories. This structure is + filled in as an "entry hook" in our procfs_dir entry table and is + passed to the process_file_make_node function defined below. */ +struct process_file_desc +{ + /* The proc_stat information required to get the contents of this file. */ + ps_flags_t needs; + + /* Content generator to use for this file. Once we have acquired the + necessary information, there can be only memory allocation errors, + hence this simplified signature. */ + ssize_t (*get_contents) (struct proc_stat *ps, char **contents); + + /* The cmdline and environ contents don't need any cleaning since they + point directly into the proc_stat structure. */ + int no_cleanup; + + /* If specified, the file mode to be set with procfs_node_chmod(). */ + mode_t mode; +}; + +struct process_file_node +{ + const struct process_file_desc *desc; + struct proc_stat *ps; +}; + +/* FIXME: lock the parent! */ +static error_t +process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) +{ + struct process_file_node *file = hook; + error_t err; + + /* Fetch the required information. */ + err = proc_stat_set_flags (file->ps, file->desc->needs); + if (err) + return EIO; + if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) + return EIO; + + /* Call the actual content generator (see the definitions below). */ + *contents_len = file->desc->get_contents (file->ps, contents); + return 0; +} + +static void +process_file_cleanup_contents (void *hook, char *contents, ssize_t len) +{ + struct process_file_node *file = hook; + + if (! file->desc->no_cleanup) + free (contents); +} + +static struct node * +process_file_make_node (void *dir_hook, const void *entry_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = process_file_get_contents, + .cleanup_contents = process_file_cleanup_contents, + .cleanup = free, + }; + struct process_file_node *f; + struct node *np; + + f = malloc (sizeof *f); + if (! f) + return NULL; + + f->desc = entry_hook; + f->ps = dir_hook; + + np = procfs_make_node (&ops, f); + if (! np) + return NULL; + + procfs_node_chown (np, proc_stat_owner_uid (f->ps)); + if (f->desc->mode) + procfs_node_chmod (np, f->desc->mode); + + return np; +} + +/* Stat needs its own constructor in oreder to set its mode according to + the --stat-mode command-line option. */ +static struct node * +process_stat_make_node (void *dir_hook, const void *entry_hook) +{ + struct node *np = process_file_make_node (dir_hook, entry_hook); + if (np) procfs_node_chmod (np, opt_stat_mode); + return np; +} + + +/* Implementation of the process directory per se. */ + +static struct procfs_dir_entry entries[] = { + { + .name = "cmdline", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_cmdline, + .needs = PSTAT_ARGS, + .no_cleanup = 1, + }, + }, + { + .name = "environ", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_environ, + .needs = PSTAT_ENV, + .no_cleanup = 1, + .mode = 0400, + }, + }, + { + .name = "stat", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_stat, + .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO + | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC + | PSTAT_THREAD_WAIT, + }, + .ops = { + .make_node = process_stat_make_node, + } + }, + { + .name = "statm", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_statm, + .needs = PSTAT_TASK_BASIC, + }, + }, + { + .name = "status", + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_status, + .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO + | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS, + }, + }, + {} +}; + +error_t +process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) +{ + static const struct procfs_dir_ops dir_ops = { + .entries = entries, + .cleanup = (void (*)(void *)) _proc_stat_free, + .entry_ops = { + .make_node = process_file_make_node, + }, + }; + struct proc_stat *ps; + int owner; + error_t err; + + err = _proc_stat_create (pid, pc, &ps); + if (err == ESRCH) + return ENOENT; + if (err) + return EIO; + + err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); + if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) + { + _proc_stat_free (ps); + return EIO; + } + + /* FIXME: have a separate proc_desc structure for each file, so this can be + accessed in a more robust and straightforward way. */ + ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; + + /* FIXME: have a separate proc_desc structure for each file, so this can be + accessed in a more robust and straightforward way. */ + ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; + + *np = procfs_dir_make_node (&dir_ops, ps); + if (! *np) + return ENOMEM; + + owner = proc_stat_owner_uid (ps); + procfs_node_chown (*np, owner >= 0 ? owner : opt_anon_owner); + return 0; +} diff --git a/procfs/process.h b/procfs/process.h new file mode 100644 index 00000000..b230a281 --- /dev/null +++ b/procfs/process.h @@ -0,0 +1,27 @@ +/* Hurd /proc filesystem, implementation of process directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +/* Create a node for a directory representing the given PID, as published by + the proc server refrenced by the libps context PC. On success, returns the + newly created node in *NP. */ +error_t +process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np); + diff --git a/procfs/procfs.c b/procfs/procfs.c new file mode 100644 index 00000000..ae5a6769 --- /dev/null +++ b/procfs/procfs.c @@ -0,0 +1,203 @@ +/* Hurd /proc filesystem, basic infrastructure. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include "procfs.h" + +struct netnode +{ + const struct procfs_node_ops *ops; + void *hook; + + /* (cached) contents of the node */ + char *contents; + ssize_t contents_len; + + /* parent directory, if applicable */ + struct node *parent; +}; + +void +procfs_cleanup_contents_with_free (void *hook, char *cont, ssize_t len) +{ + free (cont); +} + +void +procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, ssize_t len) +{ + vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); +} + +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) +{ + struct netnode *nn; + struct node *np; + + nn = malloc (sizeof *nn); + if (! nn) + goto fail; + + memset (nn, 0, sizeof *nn); + nn->ops = ops; + nn->hook = hook; + + np = netfs_make_node (nn); + if (! np) + goto fail; + + np->nn = nn; + memset (&np->nn_stat, 0, sizeof np->nn_stat); + np->nn_translated = 0; + + if (np->nn->ops->lookup) + np->nn_stat.st_mode = S_IFDIR | 0555; + else + np->nn_stat.st_mode = S_IFREG | 0444; + + return np; + +fail: + if (ops->cleanup) + ops->cleanup (hook); + + free (nn); + return NULL; +} + +void procfs_node_chown (struct node *np, uid_t owner) +{ + np->nn_stat.st_uid = owner; +} + +void procfs_node_chmod (struct node *np, mode_t mode) +{ + np->nn_stat.st_mode = (np->nn_stat.st_mode & S_IFMT) | mode; + np->nn_translated = np->nn_stat.st_mode; +} + +void procfs_node_chtype (struct node *np, mode_t type) +{ + np->nn_stat.st_mode = (np->nn_stat.st_mode & ~S_IFMT) | type; + np->nn_translated = np->nn_stat.st_mode; + if (type == S_IFLNK) + procfs_node_chmod (np, 0777); +} + +/* FIXME: possibly not the fastest hash function... */ +ino64_t +procfs_make_ino (struct node *np, const char *filename) +{ + unsigned short x[3]; + + if (! strcmp (filename, ".")) + return np->nn_stat.st_ino; + if (! strcmp (filename, "..")) + return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 2; + + assert (sizeof np->nn_stat.st_ino > sizeof x); + memcpy (x, &np->nn_stat.st_ino, sizeof x); + + while (*filename) + { + x[0] ^= *(filename++); + jrand48 (x); + } + + return (unsigned long) jrand48 (x); +} + +error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) +{ + if (! np->nn->contents && np->nn->ops->get_contents) + { + char *contents; + ssize_t contents_len; + error_t err; + + contents_len = -1; + err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); + if (err) + return err; + if (contents_len < 0) + return ENOMEM; + + np->nn->contents = contents; + np->nn->contents_len = contents_len; + } + + *data = np->nn->contents; + *data_len = np->nn->contents_len; + return 0; +} + +void procfs_refresh (struct node *np) +{ + if (np->nn->contents && np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); + + np->nn->contents = NULL; +} + +error_t procfs_lookup (struct node *np, const char *name, struct node **npp) +{ + error_t err = ENOENT; + + if (err && ! strcmp (name, ".")) + { + netfs_nref(*npp = np); + err = 0; + } + + if (err && np->nn->parent && ! strcmp (name, "..")) + { + netfs_nref(*npp = np->nn->parent); + err = 0; + } + + if (err && np->nn->ops->lookup) + { + err = np->nn->ops->lookup (np->nn->hook, name, npp); + if (! err) + { + (*npp)->nn_stat.st_ino = procfs_make_ino (np, name); + netfs_nref ((*npp)->nn->parent = np); + } + } + + return err; +} + +void procfs_cleanup (struct node *np) +{ + procfs_refresh (np); + + if (np->nn->ops->cleanup) + np->nn->ops->cleanup (np->nn->hook); + + if (np->nn->parent) + netfs_nrele (np->nn->parent); + + free (np->nn); +} diff --git a/procfs/procfs.h b/procfs/procfs.h new file mode 100644 index 00000000..64782ec4 --- /dev/null +++ b/procfs/procfs.h @@ -0,0 +1,93 @@ +/* Hurd /proc filesystem, basic infrastructure. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include + + +/* Interface for the procfs side. */ + +/* Any of these callback functions can be omitted, in which case + reasonable defaults will be used. The initial file mode and type + depend on whether a lookup function is provided, but can be + overridden in update_stat(). */ +struct procfs_node_ops +{ + /* Fetch the contents of a node. A pointer to the contents should be + returned in *CONTENTS and their length in *CONTENTS_LEN. The exact + nature of these data depends on whether the node is a regular file, + symlink or directory, as determined by the file mode in + netnode->nn_stat. For regular files and symlinks, they are what + you would expect; for directories, they are an argz vector of the + names of the entries. If upon return, *CONTENTS_LEN is negative or + unchanged, the call is considered to have failed because of a memory + allocation error. */ + error_t (*get_contents) (void *hook, char **contents, ssize_t *contents_len); + void (*cleanup_contents) (void *hook, char *contents, ssize_t contents_len); + + /* Lookup NAME in this directory, and store the result in *np. The + returned node should be created by lookup() using procfs_make_node() + or a derived function. Note that the parent will be kept alive as + long as the child exists, so you can safely reference the parent's + data from the child. You may want to consider locking if there's + any mutation going on, though. */ + error_t (*lookup) (void *hook, const char *name, struct node **np); + + /* Destroy this node. */ + void (*cleanup) (void *hook); +}; + +/* These helper functions can be used as procfs_node_ops.cleanup_contents. */ +void procfs_cleanup_contents_with_free (void *, char *, ssize_t); +void procfs_cleanup_contents_with_vm_deallocate (void *, char *, ssize_t); + +/* Create a new node and return it. Returns NULL if it fails to allocate + enough memory. In this case, ops->cleanup will be invoked. */ +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); + +/* Set the owner of the node NP. Must be called right after the node + has been created. */ +void procfs_node_chown (struct node *np, uid_t owner); + +/* Set the permission bits of the node NP. Must be called right after + the node has been created. */ +void procfs_node_chmod (struct node *np, mode_t mode); + +/* Set the type of the node NP. If type is S_IFLNK, appropriate + permission bits will be set as well. Must be called right after the + node has been created. */ +void procfs_node_chtype (struct node *np, mode_t type); + + +/* Interface for the libnetfs side. */ + +/* Get the inode number which will be given to a child of NP named FILENAME. + This allows us to retreive them for readdir() without creating the + corresponding child nodes. */ +ino64_t procfs_make_ino (struct node *np, const char *filename); + +/* Forget the current cached contents for the node. This is done before reads + from offset 0, to ensure that the data are recent even for utilities such as + top which keep some nodes open. */ +void procfs_refresh (struct node *np); + +error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); +error_t procfs_lookup (struct node *np, const char *name, struct node **npp); +void procfs_cleanup (struct node *np); + diff --git a/procfs/procfs_dir.c b/procfs/procfs_dir.c new file mode 100644 index 00000000..c250aa48 --- /dev/null +++ b/procfs/procfs_dir.c @@ -0,0 +1,134 @@ +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include "procfs.h" +#include "procfs_dir.h" + +struct procfs_dir_node +{ + const struct procfs_dir_ops *ops; + void *hook; +}; + +static int +entry_exists (struct procfs_dir_node *dir, const struct procfs_dir_entry *ent) +{ + if (ent->ops.exists) + return ent->ops.exists (dir->hook, ent->hook); + if (dir->ops->entry_ops.exists) + return dir->ops->entry_ops.exists (dir->hook, ent->hook); + + return 1; +} + +static error_t +procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len) +{ + static const char dot_dotdot[] = ".\0.."; + struct procfs_dir_node *dir = hook; + const struct procfs_dir_entry *ent; + int pos; + + /* Evaluate how much space is needed. Note that we include the hidden + entries, just in case their status changes between now and then. */ + pos = sizeof dot_dotdot; + for (ent = dir->ops->entries; ent->name; ent++) + pos += strlen (ent->name) + 1; + + *contents = malloc (pos); + if (! *contents) + return ENOMEM; + + memcpy (*contents, dot_dotdot, sizeof dot_dotdot); + pos = sizeof dot_dotdot; + for (ent = dir->ops->entries; ent->name; ent++) + { + if (! entry_exists (dir, ent)) + continue; + + strcpy (*contents + pos, ent->name); + pos += strlen (ent->name) + 1; + } + + *contents_len = pos; + return 0; +} + +static error_t +procfs_dir_lookup (void *hook, const char *name, struct node **np) +{ + struct procfs_dir_node *dir = hook; + const struct procfs_dir_entry *ent; + + for (ent = dir->ops->entries; ent->name && strcmp (name, ent->name); ent++); + if (! ent->name) + return ENOENT; + + if (ent->ops.make_node) + *np = ent->ops.make_node (dir->hook, ent->hook); + else if (dir->ops->entry_ops.make_node) + *np = dir->ops->entry_ops.make_node (dir->hook, ent->hook); + else + return EGRATUITOUS; + + if (! *np) + return ENOMEM; + + return 0; +} + +static void +procfs_dir_cleanup (void *hook) +{ + struct procfs_dir_node *dir = hook; + + if (dir->ops->cleanup) + dir->ops->cleanup (dir->hook); + + free (dir); +} + +struct node * +procfs_dir_make_node (const struct procfs_dir_ops *dir_ops, void *dir_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = procfs_dir_get_contents, + .lookup = procfs_dir_lookup, + .cleanup_contents = procfs_cleanup_contents_with_free, + .cleanup = procfs_dir_cleanup, + }; + struct procfs_dir_node *dir; + + dir = malloc (sizeof *dir); + if (! dir) + { + if (dir_ops->cleanup) + dir_ops->cleanup (dir_hook); + + return NULL; + } + + dir->ops = dir_ops; + dir->hook = dir_hook; + + return procfs_make_node (&ops, dir); +} + diff --git a/procfs/procfs_dir.h b/procfs/procfs_dir.h new file mode 100644 index 00000000..94c5b019 --- /dev/null +++ b/procfs/procfs_dir.h @@ -0,0 +1,63 @@ +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This module provides an abstraction layer for implementing simple + directories with (mostly) static contents. The user defines the + contents of the directory by providing a table of entries and various + optional callback functions. */ + +/* These operations define how a given entry will behave. Either can be + omitted, both from the entry-specific operations and from the + directory-wide defaults. */ +struct procfs_dir_entry_ops +{ + /* Called when this entry is looked up to create a corresponding node. */ + struct node *(*make_node)(void *dir_hook, const void *entry_hook); + /* If this is provided and returns 0, this entry will be hidden. */ + int (*exists)(void *dir_hook, const void *entry_hook); +}; + +/* Describes an individual directory entry, associating a NAME with + * arbitrary HOOK data and node-specific OPS. */ +struct procfs_dir_entry +{ + const char *name; + const void *hook; + struct procfs_dir_entry_ops ops; +}; + +/* Describes a complete directory. ENTRIES is a table terminated by a + null NAME field. ENTRY_OPS provides default operations for the + entries which don't specify them. The optional CLEANUP function + should release all the resources associated with the directory hook. */ +struct procfs_dir_ops +{ + const struct procfs_dir_entry *entries; + void (*cleanup)(void *dir_hook); + struct procfs_dir_entry_ops entry_ops; +}; + +/* Create and return a new node for the directory described in OPS. + The DIR_HOOK is passed the MAKE_NODE callback function of looked up + entries, as well as to the CLEANUP callback when the node is + destroyed. If not enough memory can be allocated, OPS->CLEANUP is + invoked immediately and NULL is returned. */ +struct node * +procfs_dir_make_node (const struct procfs_dir_ops *ops, void *dir_hook); + diff --git a/procfs/proclist.c b/procfs/proclist.c new file mode 100644 index 00000000..58b942dc --- /dev/null +++ b/procfs/proclist.c @@ -0,0 +1,94 @@ +/* Hurd /proc filesystem, list of processes as a directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include "procfs.h" +#include "process.h" + +#define PID_STR_SIZE (3 * sizeof (pid_t) + 1) + +static error_t +proclist_get_contents (void *hook, char **contents, ssize_t *contents_len) +{ + struct ps_context *pc = hook; + pidarray_t pids; + mach_msg_type_number_t num_pids; + error_t err; + int i; + + num_pids = 0; + err = proc_getallpids (pc->server, &pids, &num_pids); + if (err) + return EIO; + + *contents = malloc (num_pids * PID_STR_SIZE); + if (*contents) + { + *contents_len = 0; + for (i=0; i < num_pids; i++) + { + int n = sprintf (*contents + *contents_len, "%d", pids[i]); + assert (n >= 0); + *contents_len += (n + 1); + } + } + else + err = ENOMEM; + + vm_deallocate (mach_task_self (), (vm_address_t) pids, num_pids * sizeof pids[0]); + return err; +} + +static error_t +proclist_lookup (void *hook, const char *name, struct node **np) +{ + struct ps_context *pc = hook; + char *endp; + pid_t pid; + + /* Self-lookups should not end up here. */ + assert (name[0]); + + /* No leading zeros allowed */ + if (name[0] == '0' && name[1]) + return ENOENT; + + pid = strtol (name, &endp, 10); + if (*endp) + return ENOENT; + + return process_lookup_pid (pc, pid, np); +} + +struct node * +proclist_make_node (struct ps_context *pc) +{ + static const struct procfs_node_ops ops = { + .get_contents = proclist_get_contents, + .lookup = proclist_lookup, + .cleanup_contents = procfs_cleanup_contents_with_free, + }; + return procfs_make_node (&ops, pc); +} + diff --git a/procfs/proclist.h b/procfs/proclist.h new file mode 100644 index 00000000..bfe95b3d --- /dev/null +++ b/procfs/proclist.h @@ -0,0 +1,23 @@ +/* Hurd /proc filesystem, list of processes as a directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +struct node * +proclist_make_node (struct ps_context *pc); diff --git a/procfs/rootdir.c b/procfs/rootdir.c new file mode 100644 index 00000000..15ef8bce --- /dev/null +++ b/procfs/rootdir.c @@ -0,0 +1,503 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "procfs.h" +#include "procfs_dir.h" +#include "main.h" + +/* This implements a directory node with the static files in /proc. + NB: the libps functions for host information return static storage; + using them would require locking and as a consequence it would be + more complicated, not simpler. */ + + +/* Helper functions */ + +/* We get the boot time by using that of the kernel process. */ +static error_t +get_boottime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps; + error_t err; + + err = _proc_stat_create (opt_kernel_pid, pc, &ps); + if (err) + return err; + + err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); + if (err || !(proc_stat_flags (ps) & PSTAT_TASK_BASIC)) + err = EIO; + + if (! err) + { + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + tv->tv_sec = tbi->creation_time.seconds; + tv->tv_usec = tbi->creation_time.microseconds; + } + + _proc_stat_free (ps); + return err; +} + +/* We get the idle time by querying the kernel's idle thread. */ +static error_t +get_idletime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps, *pst; + thread_basic_info_t tbi; + error_t err; + int i; + + err = _proc_stat_create (opt_kernel_pid, pc, &ps); + if (err) + return err; + + pst = NULL, tbi = NULL; + + err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); + if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) + { + err = EIO; + goto out; + } + + /* Look for the idle thread */ + for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE); i++) + { + if (pst) + _proc_stat_free (pst); + + pst = NULL, tbi = NULL; + if (i >= proc_stat_num_threads (ps)) + { + err = ESRCH; + goto out; + } + + err = proc_stat_thread_create (ps, i, &pst); + if (err) + continue; + + err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC); + if (err || ! (proc_stat_flags (pst) & PSTAT_THREAD_BASIC)) + continue; + + tbi = proc_stat_thread_basic_info (pst); + } + + /* We found it! */ + tv->tv_sec = tbi->system_time.seconds; + tv->tv_usec = tbi->system_time.microseconds; + err = 0; + +out: + if (pst) _proc_stat_free (pst); + _proc_stat_free (ps); + return err; +} + +static error_t +get_swapinfo (default_pager_info_t *info) +{ + mach_port_t defpager; + error_t err; + + defpager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); + if (defpager == MACH_PORT_NULL) + return errno; + + err = default_pager_info (defpager, info); + mach_port_deallocate (mach_task_self (), defpager); + + return err; +} + + +/* Content generators */ + +static error_t +rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) +{ + struct utsname uts; + int r; + + r = uname (&uts); + if (r < 0) + return errno; + + *contents_len = asprintf (contents, + "Linux version 2.6.1 (%s %s %s %s)\n", + uts.sysname, uts.release, uts.version, uts.machine); + + return 0; +} + +static error_t +rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) +{ + struct timeval time, boottime, idletime; + double up_secs, idle_secs; + error_t err; + + err = gettimeofday (&time, NULL); + if (err < 0) + return errno; + + err = get_boottime (hook, &boottime); + if (err) + return err; + + err = get_idletime (hook, &idletime); + if (err) + return err; + + timersub (&time, &boottime, &time); + up_secs = (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; + idle_secs = (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; + + /* The second field is the total idle time. As far as I know we don't + keep track of it. However, procps uses it to compute "USER_HZ", and + proc(5) specifies that it should be equal to USER_HZ times the idle value + in ticks from /proc/stat. So we assume a completely idle system both here + and there to make that work. */ + *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); + + return 0; +} + +static error_t +rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) +{ + struct timeval boottime, time, idletime; + struct vm_statistics vmstats; + unsigned long up_ticks, idle_ticks; + error_t err; + + err = gettimeofday (&time, NULL); + if (err < 0) + return errno; + + err = get_boottime (hook, &boottime); + if (err) + return err; + + err = get_idletime (hook, &idletime); + if (err) + return err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + timersub (&time, &boottime, &time); + up_ticks = opt_clk_tck * (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; + idle_ticks = opt_clk_tck * (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; + + *contents_len = asprintf (contents, + "cpu %lu 0 0 %lu 0 0 0 0 0\n" + "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" + "intr 0\n" + "page %d %d\n" + "btime %lu\n", + up_ticks - idle_ticks, idle_ticks, + up_ticks - idle_ticks, idle_ticks, + vmstats.pageins, vmstats.pageouts, + boottime.tv_sec); + + return 0; +} + +static error_t +rootdir_gc_loadavg (void *hook, char **contents, ssize_t *contents_len) +{ + host_load_info_data_t hli; + mach_msg_type_number_t cnt; + error_t err; + + cnt = HOST_LOAD_INFO_COUNT; + err = host_info (mach_host_self (), HOST_LOAD_INFO, (host_info_t) &hli, &cnt); + if (err) + return err; + + assert (cnt == HOST_LOAD_INFO_COUNT); + *contents_len = asprintf (contents, + "%.2f %.2f %.2f 1/0 0\n", + hli.avenrun[0] / (double) LOAD_SCALE, + hli.avenrun[1] / (double) LOAD_SCALE, + hli.avenrun[2] / (double) LOAD_SCALE); + + return 0; +} + +static error_t +rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) +{ + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + struct vm_statistics vmstats; + default_pager_info_t swap; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + err = get_swapinfo (&swap); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf (contents, + "MemTotal: %14lu kB\n" + "MemFree: %14lu kB\n" + "Active: %14lu kB\n" + "Inactive: %14lu kB\n" + "Mlocked: %14lu kB\n" + "SwapTotal:%14lu kB\n" + "SwapFree: %14lu kB\n" + , + /* TODO: check that these are really 1024-bytes kBs. */ + (long unsigned) hbi.memory_size / 1024, + (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024, + (long unsigned) swap.dpi_total_space / 1024, + (long unsigned) swap.dpi_free_space / 1024); + + return 0; +} + +static error_t +rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len) +{ + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + struct vm_statistics vmstats; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf (contents, + "nr_free_pages %lu\n" + "nr_inactive_anon %lu\n" + "nr_active_anon %lu\n" + "nr_inactive_file %lu\n" + "nr_active_file %lu\n" + "nr_unevictable %lu\n" + "nr_mlock %lu\n" + "pgpgin %lu\n" + "pgpgout %lu\n" + "pgfault %lu\n", + (long unsigned) vmstats.free_count, + /* FIXME: how can we distinguish the anon/file pages? Maybe we can + ask the default pager how many it manages? */ + (long unsigned) vmstats.inactive_count, + (long unsigned) vmstats.active_count, + (long unsigned) 0, + (long unsigned) 0, + (long unsigned) vmstats.wire_count, + (long unsigned) vmstats.wire_count, + (long unsigned) vmstats.pageins, + (long unsigned) vmstats.pageouts, + (long unsigned) vmstats.faults); + + return 0; +} + +static error_t +rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) +{ + struct ps_context *pc = hook; + struct proc_stat *ps; + error_t err; + + err = _proc_stat_create (opt_kernel_pid, pc, &ps); + if (err) + return EIO; + + err = proc_stat_set_flags (ps, PSTAT_ARGS); + if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS)) + { + err = EIO; + goto out; + } + + *contents_len = proc_stat_args_len (ps); + *contents = malloc (*contents_len); + if (! *contents) + { + err = ENOMEM; + goto out; + } + + memcpy (*contents, proc_stat_args (ps), *contents_len); + argz_stringify (*contents, *contents_len, ' '); + (*contents)[*contents_len - 1] = '\n'; + +out: + _proc_stat_free (ps); + return err; +} + +static int +rootdir_fakeself_exists () +{ + return opt_fake_self >= 0; +} + +static error_t +rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) +{ + *contents_len = asprintf (contents, "%d", opt_fake_self); + return 0; +} + + +/* Glue logic and entries table */ + +static struct node * +rootdir_file_make_node (void *dir_hook, const void *entry_hook) +{ + /* The entry hook we use is actually a procfs_node_ops for the file to be + created. The hook associated to these newly created files (and passed + to the generators above as a consequence) is always the same global + ps_context, which we get from rootdir_make_node as the directory hook. */ + return procfs_make_node (entry_hook, dir_hook); +} + +static struct node * +rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) +{ + struct node *np = procfs_make_node (entry_hook, dir_hook); + if (np) + procfs_node_chtype (np, S_IFLNK); + return np; +} + +static const struct procfs_dir_entry rootdir_entries[] = { + { + .name = "self", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_fakeself, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + .ops = { + .make_node = rootdir_symlink_make_node, + .exists = rootdir_fakeself_exists, + } + }, + { + .name = "version", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_version, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "uptime", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_uptime, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "stat", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_stat, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "loadavg", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_loadavg, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "meminfo", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_meminfo, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "vmstat", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_vmstat, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "cmdline", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_cmdline, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, +#ifdef PROFILE + /* In order to get a usable gmon.out file, we must apparently use exit(). */ + { + .name = "exit", + .ops = { + .make_node = exit, + }, + }, +#endif + {} +}; + +struct node +*rootdir_make_node (struct ps_context *pc) +{ + static const struct procfs_dir_ops ops = { + .entries = rootdir_entries, + .entry_ops = { + .make_node = rootdir_file_make_node, + }, + }; + return procfs_dir_make_node (&ops, pc); +} + diff --git a/procfs/rootdir.h b/procfs/rootdir.h new file mode 100644 index 00000000..6980da8f --- /dev/null +++ b/procfs/rootdir.h @@ -0,0 +1,23 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +struct node * +rootdir_make_node (struct ps_context *pc); diff --git a/procfs_dir.c b/procfs_dir.c deleted file mode 100644 index c250aa48..00000000 --- a/procfs_dir.c +++ /dev/null @@ -1,134 +0,0 @@ -/* Hurd /proc filesystem, infrastructure for directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include "procfs.h" -#include "procfs_dir.h" - -struct procfs_dir_node -{ - const struct procfs_dir_ops *ops; - void *hook; -}; - -static int -entry_exists (struct procfs_dir_node *dir, const struct procfs_dir_entry *ent) -{ - if (ent->ops.exists) - return ent->ops.exists (dir->hook, ent->hook); - if (dir->ops->entry_ops.exists) - return dir->ops->entry_ops.exists (dir->hook, ent->hook); - - return 1; -} - -static error_t -procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len) -{ - static const char dot_dotdot[] = ".\0.."; - struct procfs_dir_node *dir = hook; - const struct procfs_dir_entry *ent; - int pos; - - /* Evaluate how much space is needed. Note that we include the hidden - entries, just in case their status changes between now and then. */ - pos = sizeof dot_dotdot; - for (ent = dir->ops->entries; ent->name; ent++) - pos += strlen (ent->name) + 1; - - *contents = malloc (pos); - if (! *contents) - return ENOMEM; - - memcpy (*contents, dot_dotdot, sizeof dot_dotdot); - pos = sizeof dot_dotdot; - for (ent = dir->ops->entries; ent->name; ent++) - { - if (! entry_exists (dir, ent)) - continue; - - strcpy (*contents + pos, ent->name); - pos += strlen (ent->name) + 1; - } - - *contents_len = pos; - return 0; -} - -static error_t -procfs_dir_lookup (void *hook, const char *name, struct node **np) -{ - struct procfs_dir_node *dir = hook; - const struct procfs_dir_entry *ent; - - for (ent = dir->ops->entries; ent->name && strcmp (name, ent->name); ent++); - if (! ent->name) - return ENOENT; - - if (ent->ops.make_node) - *np = ent->ops.make_node (dir->hook, ent->hook); - else if (dir->ops->entry_ops.make_node) - *np = dir->ops->entry_ops.make_node (dir->hook, ent->hook); - else - return EGRATUITOUS; - - if (! *np) - return ENOMEM; - - return 0; -} - -static void -procfs_dir_cleanup (void *hook) -{ - struct procfs_dir_node *dir = hook; - - if (dir->ops->cleanup) - dir->ops->cleanup (dir->hook); - - free (dir); -} - -struct node * -procfs_dir_make_node (const struct procfs_dir_ops *dir_ops, void *dir_hook) -{ - static const struct procfs_node_ops ops = { - .get_contents = procfs_dir_get_contents, - .lookup = procfs_dir_lookup, - .cleanup_contents = procfs_cleanup_contents_with_free, - .cleanup = procfs_dir_cleanup, - }; - struct procfs_dir_node *dir; - - dir = malloc (sizeof *dir); - if (! dir) - { - if (dir_ops->cleanup) - dir_ops->cleanup (dir_hook); - - return NULL; - } - - dir->ops = dir_ops; - dir->hook = dir_hook; - - return procfs_make_node (&ops, dir); -} - diff --git a/procfs_dir.h b/procfs_dir.h deleted file mode 100644 index 94c5b019..00000000 --- a/procfs_dir.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Hurd /proc filesystem, infrastructure for directories. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* This module provides an abstraction layer for implementing simple - directories with (mostly) static contents. The user defines the - contents of the directory by providing a table of entries and various - optional callback functions. */ - -/* These operations define how a given entry will behave. Either can be - omitted, both from the entry-specific operations and from the - directory-wide defaults. */ -struct procfs_dir_entry_ops -{ - /* Called when this entry is looked up to create a corresponding node. */ - struct node *(*make_node)(void *dir_hook, const void *entry_hook); - /* If this is provided and returns 0, this entry will be hidden. */ - int (*exists)(void *dir_hook, const void *entry_hook); -}; - -/* Describes an individual directory entry, associating a NAME with - * arbitrary HOOK data and node-specific OPS. */ -struct procfs_dir_entry -{ - const char *name; - const void *hook; - struct procfs_dir_entry_ops ops; -}; - -/* Describes a complete directory. ENTRIES is a table terminated by a - null NAME field. ENTRY_OPS provides default operations for the - entries which don't specify them. The optional CLEANUP function - should release all the resources associated with the directory hook. */ -struct procfs_dir_ops -{ - const struct procfs_dir_entry *entries; - void (*cleanup)(void *dir_hook); - struct procfs_dir_entry_ops entry_ops; -}; - -/* Create and return a new node for the directory described in OPS. - The DIR_HOOK is passed the MAKE_NODE callback function of looked up - entries, as well as to the CLEANUP callback when the node is - destroyed. If not enough memory can be allocated, OPS->CLEANUP is - invoked immediately and NULL is returned. */ -struct node * -procfs_dir_make_node (const struct procfs_dir_ops *ops, void *dir_hook); - diff --git a/proclist.c b/proclist.c deleted file mode 100644 index 58b942dc..00000000 --- a/proclist.c +++ /dev/null @@ -1,94 +0,0 @@ -/* Hurd /proc filesystem, list of processes as a directory. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include "procfs.h" -#include "process.h" - -#define PID_STR_SIZE (3 * sizeof (pid_t) + 1) - -static error_t -proclist_get_contents (void *hook, char **contents, ssize_t *contents_len) -{ - struct ps_context *pc = hook; - pidarray_t pids; - mach_msg_type_number_t num_pids; - error_t err; - int i; - - num_pids = 0; - err = proc_getallpids (pc->server, &pids, &num_pids); - if (err) - return EIO; - - *contents = malloc (num_pids * PID_STR_SIZE); - if (*contents) - { - *contents_len = 0; - for (i=0; i < num_pids; i++) - { - int n = sprintf (*contents + *contents_len, "%d", pids[i]); - assert (n >= 0); - *contents_len += (n + 1); - } - } - else - err = ENOMEM; - - vm_deallocate (mach_task_self (), (vm_address_t) pids, num_pids * sizeof pids[0]); - return err; -} - -static error_t -proclist_lookup (void *hook, const char *name, struct node **np) -{ - struct ps_context *pc = hook; - char *endp; - pid_t pid; - - /* Self-lookups should not end up here. */ - assert (name[0]); - - /* No leading zeros allowed */ - if (name[0] == '0' && name[1]) - return ENOENT; - - pid = strtol (name, &endp, 10); - if (*endp) - return ENOENT; - - return process_lookup_pid (pc, pid, np); -} - -struct node * -proclist_make_node (struct ps_context *pc) -{ - static const struct procfs_node_ops ops = { - .get_contents = proclist_get_contents, - .lookup = proclist_lookup, - .cleanup_contents = procfs_cleanup_contents_with_free, - }; - return procfs_make_node (&ops, pc); -} - diff --git a/proclist.h b/proclist.h deleted file mode 100644 index bfe95b3d..00000000 --- a/proclist.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Hurd /proc filesystem, list of processes as a directory. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include - -struct node * -proclist_make_node (struct ps_context *pc); diff --git a/rootdir.c b/rootdir.c deleted file mode 100644 index 15ef8bce..00000000 --- a/rootdir.c +++ /dev/null @@ -1,503 +0,0 @@ -/* Hurd /proc filesystem, permanent files of the root directory. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "procfs.h" -#include "procfs_dir.h" -#include "main.h" - -/* This implements a directory node with the static files in /proc. - NB: the libps functions for host information return static storage; - using them would require locking and as a consequence it would be - more complicated, not simpler. */ - - -/* Helper functions */ - -/* We get the boot time by using that of the kernel process. */ -static error_t -get_boottime (struct ps_context *pc, struct timeval *tv) -{ - struct proc_stat *ps; - error_t err; - - err = _proc_stat_create (opt_kernel_pid, pc, &ps); - if (err) - return err; - - err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); - if (err || !(proc_stat_flags (ps) & PSTAT_TASK_BASIC)) - err = EIO; - - if (! err) - { - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - tv->tv_sec = tbi->creation_time.seconds; - tv->tv_usec = tbi->creation_time.microseconds; - } - - _proc_stat_free (ps); - return err; -} - -/* We get the idle time by querying the kernel's idle thread. */ -static error_t -get_idletime (struct ps_context *pc, struct timeval *tv) -{ - struct proc_stat *ps, *pst; - thread_basic_info_t tbi; - error_t err; - int i; - - err = _proc_stat_create (opt_kernel_pid, pc, &ps); - if (err) - return err; - - pst = NULL, tbi = NULL; - - err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); - if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) - { - err = EIO; - goto out; - } - - /* Look for the idle thread */ - for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE); i++) - { - if (pst) - _proc_stat_free (pst); - - pst = NULL, tbi = NULL; - if (i >= proc_stat_num_threads (ps)) - { - err = ESRCH; - goto out; - } - - err = proc_stat_thread_create (ps, i, &pst); - if (err) - continue; - - err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC); - if (err || ! (proc_stat_flags (pst) & PSTAT_THREAD_BASIC)) - continue; - - tbi = proc_stat_thread_basic_info (pst); - } - - /* We found it! */ - tv->tv_sec = tbi->system_time.seconds; - tv->tv_usec = tbi->system_time.microseconds; - err = 0; - -out: - if (pst) _proc_stat_free (pst); - _proc_stat_free (ps); - return err; -} - -static error_t -get_swapinfo (default_pager_info_t *info) -{ - mach_port_t defpager; - error_t err; - - defpager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); - if (defpager == MACH_PORT_NULL) - return errno; - - err = default_pager_info (defpager, info); - mach_port_deallocate (mach_task_self (), defpager); - - return err; -} - - -/* Content generators */ - -static error_t -rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) -{ - struct utsname uts; - int r; - - r = uname (&uts); - if (r < 0) - return errno; - - *contents_len = asprintf (contents, - "Linux version 2.6.1 (%s %s %s %s)\n", - uts.sysname, uts.release, uts.version, uts.machine); - - return 0; -} - -static error_t -rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) -{ - struct timeval time, boottime, idletime; - double up_secs, idle_secs; - error_t err; - - err = gettimeofday (&time, NULL); - if (err < 0) - return errno; - - err = get_boottime (hook, &boottime); - if (err) - return err; - - err = get_idletime (hook, &idletime); - if (err) - return err; - - timersub (&time, &boottime, &time); - up_secs = (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; - idle_secs = (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; - - /* The second field is the total idle time. As far as I know we don't - keep track of it. However, procps uses it to compute "USER_HZ", and - proc(5) specifies that it should be equal to USER_HZ times the idle value - in ticks from /proc/stat. So we assume a completely idle system both here - and there to make that work. */ - *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); - - return 0; -} - -static error_t -rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) -{ - struct timeval boottime, time, idletime; - struct vm_statistics vmstats; - unsigned long up_ticks, idle_ticks; - error_t err; - - err = gettimeofday (&time, NULL); - if (err < 0) - return errno; - - err = get_boottime (hook, &boottime); - if (err) - return err; - - err = get_idletime (hook, &idletime); - if (err) - return err; - - err = vm_statistics (mach_task_self (), &vmstats); - if (err) - return EIO; - - timersub (&time, &boottime, &time); - up_ticks = opt_clk_tck * (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; - idle_ticks = opt_clk_tck * (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; - - *contents_len = asprintf (contents, - "cpu %lu 0 0 %lu 0 0 0 0 0\n" - "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" - "intr 0\n" - "page %d %d\n" - "btime %lu\n", - up_ticks - idle_ticks, idle_ticks, - up_ticks - idle_ticks, idle_ticks, - vmstats.pageins, vmstats.pageouts, - boottime.tv_sec); - - return 0; -} - -static error_t -rootdir_gc_loadavg (void *hook, char **contents, ssize_t *contents_len) -{ - host_load_info_data_t hli; - mach_msg_type_number_t cnt; - error_t err; - - cnt = HOST_LOAD_INFO_COUNT; - err = host_info (mach_host_self (), HOST_LOAD_INFO, (host_info_t) &hli, &cnt); - if (err) - return err; - - assert (cnt == HOST_LOAD_INFO_COUNT); - *contents_len = asprintf (contents, - "%.2f %.2f %.2f 1/0 0\n", - hli.avenrun[0] / (double) LOAD_SCALE, - hli.avenrun[1] / (double) LOAD_SCALE, - hli.avenrun[2] / (double) LOAD_SCALE); - - return 0; -} - -static error_t -rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) -{ - host_basic_info_data_t hbi; - mach_msg_type_number_t cnt; - struct vm_statistics vmstats; - default_pager_info_t swap; - error_t err; - - err = vm_statistics (mach_task_self (), &vmstats); - if (err) - return EIO; - - cnt = HOST_BASIC_INFO_COUNT; - err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); - if (err) - return err; - - err = get_swapinfo (&swap); - if (err) - return err; - - assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf (contents, - "MemTotal: %14lu kB\n" - "MemFree: %14lu kB\n" - "Active: %14lu kB\n" - "Inactive: %14lu kB\n" - "Mlocked: %14lu kB\n" - "SwapTotal:%14lu kB\n" - "SwapFree: %14lu kB\n" - , - /* TODO: check that these are really 1024-bytes kBs. */ - (long unsigned) hbi.memory_size / 1024, - (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, - (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, - (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, - (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024, - (long unsigned) swap.dpi_total_space / 1024, - (long unsigned) swap.dpi_free_space / 1024); - - return 0; -} - -static error_t -rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len) -{ - host_basic_info_data_t hbi; - mach_msg_type_number_t cnt; - struct vm_statistics vmstats; - error_t err; - - err = vm_statistics (mach_task_self (), &vmstats); - if (err) - return EIO; - - cnt = HOST_BASIC_INFO_COUNT; - err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); - if (err) - return err; - - assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf (contents, - "nr_free_pages %lu\n" - "nr_inactive_anon %lu\n" - "nr_active_anon %lu\n" - "nr_inactive_file %lu\n" - "nr_active_file %lu\n" - "nr_unevictable %lu\n" - "nr_mlock %lu\n" - "pgpgin %lu\n" - "pgpgout %lu\n" - "pgfault %lu\n", - (long unsigned) vmstats.free_count, - /* FIXME: how can we distinguish the anon/file pages? Maybe we can - ask the default pager how many it manages? */ - (long unsigned) vmstats.inactive_count, - (long unsigned) vmstats.active_count, - (long unsigned) 0, - (long unsigned) 0, - (long unsigned) vmstats.wire_count, - (long unsigned) vmstats.wire_count, - (long unsigned) vmstats.pageins, - (long unsigned) vmstats.pageouts, - (long unsigned) vmstats.faults); - - return 0; -} - -static error_t -rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) -{ - struct ps_context *pc = hook; - struct proc_stat *ps; - error_t err; - - err = _proc_stat_create (opt_kernel_pid, pc, &ps); - if (err) - return EIO; - - err = proc_stat_set_flags (ps, PSTAT_ARGS); - if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS)) - { - err = EIO; - goto out; - } - - *contents_len = proc_stat_args_len (ps); - *contents = malloc (*contents_len); - if (! *contents) - { - err = ENOMEM; - goto out; - } - - memcpy (*contents, proc_stat_args (ps), *contents_len); - argz_stringify (*contents, *contents_len, ' '); - (*contents)[*contents_len - 1] = '\n'; - -out: - _proc_stat_free (ps); - return err; -} - -static int -rootdir_fakeself_exists () -{ - return opt_fake_self >= 0; -} - -static error_t -rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) -{ - *contents_len = asprintf (contents, "%d", opt_fake_self); - return 0; -} - - -/* Glue logic and entries table */ - -static struct node * -rootdir_file_make_node (void *dir_hook, const void *entry_hook) -{ - /* The entry hook we use is actually a procfs_node_ops for the file to be - created. The hook associated to these newly created files (and passed - to the generators above as a consequence) is always the same global - ps_context, which we get from rootdir_make_node as the directory hook. */ - return procfs_make_node (entry_hook, dir_hook); -} - -static struct node * -rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) -{ - struct node *np = procfs_make_node (entry_hook, dir_hook); - if (np) - procfs_node_chtype (np, S_IFLNK); - return np; -} - -static const struct procfs_dir_entry rootdir_entries[] = { - { - .name = "self", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_fakeself, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - .ops = { - .make_node = rootdir_symlink_make_node, - .exists = rootdir_fakeself_exists, - } - }, - { - .name = "version", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_version, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "uptime", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_uptime, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "stat", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_stat, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "loadavg", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_loadavg, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "meminfo", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_meminfo, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "vmstat", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_vmstat, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, - { - .name = "cmdline", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_cmdline, - .cleanup_contents = procfs_cleanup_contents_with_free, - }, - }, -#ifdef PROFILE - /* In order to get a usable gmon.out file, we must apparently use exit(). */ - { - .name = "exit", - .ops = { - .make_node = exit, - }, - }, -#endif - {} -}; - -struct node -*rootdir_make_node (struct ps_context *pc) -{ - static const struct procfs_dir_ops ops = { - .entries = rootdir_entries, - .entry_ops = { - .make_node = rootdir_file_make_node, - }, - }; - return procfs_dir_make_node (&ops, pc); -} - diff --git a/rootdir.h b/rootdir.h deleted file mode 100644 index 6980da8f..00000000 --- a/rootdir.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Hurd /proc filesystem, permanent files of the root directory. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include - -struct node * -rootdir_make_node (struct ps_context *pc); -- cgit v1.2.3