From c15f3bee8d46f05afe1d3f582778d74c7e706485 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Thu, 14 Aug 2008 15:24:00 +0200 Subject: 2008-08-14 Madhusudan.C.S * ChangeLog: New file added to procfs * AUTHORS: New file added to procfs * COPYING: New file added to procfs * README: New file added to procfs * Makefile: New file added to procfs * bootstrap.c: New file added to procfs * netfs.c: New file added to procfs * node.c: New file added to procfs * procfs.c: New file added to procfs * procfs.h: New file added to procfs * procfs_dir.c: New file added to procfs * procfs_nonpid_files.c: New file added to procfs * procfs_pid.h: New file added to procfs * procfs_pid_files.c: New file added to procfs --- AUTHORS | 10 + COPYING | 340 +++++++++++++++++++++++++++++ ChangeLog | 20 ++ Makefile | 40 ++++ README | 0 bootstrap.c | 95 +++++++++ netfs.c | 466 ++++++++++++++++++++++++++++++++++++++++ node.c | 195 +++++++++++++++++ procfs.c | 149 +++++++++++++ procfs.h | 218 +++++++++++++++++++ procfs_dir.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++ procfs_nonpid_files.c | 462 ++++++++++++++++++++++++++++++++++++++++ procfs_pid.h | 86 ++++++++ procfs_pid_files.c | 570 +++++++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 3226 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Makefile create mode 100644 README create mode 100644 bootstrap.c create mode 100644 netfs.c create mode 100644 node.c create mode 100644 procfs.c create mode 100644 procfs.h create mode 100644 procfs_dir.c create mode 100644 procfs_nonpid_files.c create mode 100644 procfs_pid.h create mode 100644 procfs_pid_files.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..622b6488 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,10 @@ +Written By +~~~~~~~~~~ + +Madhusudan.C.S + + +Mentored By +~~~~~~~~~~~ + +Olaf Buddenhagen diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..60549be5 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..36f6bf5c --- /dev/null +++ b/ChangeLog @@ -0,0 +1,20 @@ +2008-08-14 Madhusudan.C.S + + * ChangeLog: New file added to procfs + * AUTHORS: New file added to procfs + * COPYING: New file added to procfs + * README: New file added to procfs + * Makefile: New file added to procfs + * bootstrap.c: New file added to procfs + * netfs.c: New file added to procfs + * node.c: New file added to procfs + * procfs.c: New file added to procfs + * procfs.h: New file added to procfs + * procfs_dir.c: New file added to procfs + * procfs_nonpid_files.c: New file added to procfs + * procfs_pid.h: New file added to procfs + * procfs_pid_files.c: New file added to procfs + +2008-05-13 Madhusudan.C.S + + * /sources/hurd/procfs: New directory added to the repository diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..dae406d3 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +# Makefile - for procfs +# +# Copyright (C) 1997, 2000 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +dir := procfs +makemode := server + +target = procfs + +CC = gcc +CFLAGS = -Wall -g -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 +INCLUDES = -I. + +SRCS = procfs.c bootstrap.c netfs.c procfs_dir.c node.c procfs_pid_files.c procfs_nonpid_files.c +LCLHDRS = procfs.h procfs_pid.h + +OBJS = $(SRCS:.c=.o) +HURDLIBS = -lnetfs -lfshelp -liohelp -lthreads -lports -lihash -lps -lshouldbeinlibc + +all: $(target) + +$(target): $(OBJS) + $(CC) $(CFLAGS) -o $(target) $(OBJS) $(HURDLIBS) + +%.o: %.c $(LCLHDRS) + $(CC) $(CFLAGS) $(INCLUDES) -o $@ -c $< diff --git a/README b/README new file mode 100644 index 00000000..e69de29b diff --git a/bootstrap.c b/bootstrap.c new file mode 100644 index 00000000..73d31f00 --- /dev/null +++ b/bootstrap.c @@ -0,0 +1,95 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + bootstrap.c -- This file is functions for starting up + and initializers for the procfs translator + defined in procfs.h + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. +*/ + +#include +#include +#include + +#include "procfs.h" + +struct ps_context *ps_context; + +/* This function is used to initialize the whole translator, can be + effect called as bootstrapping the translator. */ +error_t procfs_init () +{ + error_t err; + + err = ps_context_create (getproc (), &ps_context); + + return err; +} + +/* Create a new procfs filesystem. */ +error_t procfs_create (char *procfs_root, int fsid, + struct procfs **fs) +{ + error_t err; + /* This is the enclosing directory for this filesystem's + root node */ + struct procfs_dir *topmost_root_dir; + + /* And also a topmost-root node, just used for locking + TOPMOST_ROOT_DIR. */ + struct node *topmost_root; + + /* The new node for the filesystem's root. */ + struct procfs *new = malloc (sizeof (struct procfs)); + + if (! new) + return ENOMEM; + + new->fsid = fsid; + new->next_inode = 2; + + hurd_ihash_init (&new->inode_mappings, + offsetof (struct procfs_dir_entry, inode_locp)); + spin_lock_init (&new->inode_mappings_lock); + + topmost_root = netfs_make_node (0); + if (! topmost_root) + err = ENOMEM; + else + { + err = procfs_dir_create (new, topmost_root, procfs_root, + &topmost_root_dir); + if (! err) + { + /* ADDITIONAL BOOTSTRAPPING OF THE ROOT NODE */ + err = procfs_dir_null_lookup (topmost_root_dir, &new->root); + } + } + + if (err) + { + hurd_ihash_destroy (&new->inode_mappings); + free (new); + } + else + *fs = new; + + return err; +} + diff --git a/netfs.c b/netfs.c new file mode 100644 index 00000000..31ffe1b5 --- /dev/null +++ b/netfs.c @@ -0,0 +1,466 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "procfs.h" + +/* Trivial definitions. */ + +/* Make sure that NP->nn_stat is filled with current information. CRED + identifies the user responsible for the operation. */ +error_t +netfs_validate_stat (struct node *node, struct iouser *cred) +{ + return procfs_refresh_node (node); +} + +/* This should sync the file NODE completely to disk, for the user CRED. If + WAIT is set, return only after sync is completely finished. */ +error_t +netfs_attempt_sync (struct iouser *cred, struct node *node, int wait) +{ + return 0; +} + +/* Attempt to create a new directory named NAME in DIR for USER with mode + MODE. */ +error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, + char *name, mode_t mode) +{ + return EROFS; +} + +/* Attempt to remove directory named NAME in DIR for USER. */ +error_t netfs_attempt_rmdir (struct iouser *user, + struct node *dir, char *name) +{ + return EROFS; +} + +/* Attempt to set the passive translator record for FILE to ARGZ (of length + ARGZLEN) for user CRED. */ +error_t netfs_set_translator (struct iouser *cred, struct node *node, + char *argz, size_t argzlen) +{ + return EROFS; +} + +/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE + to the new node upon return. On any error, clear *NODE. *NODE should be + locked on success; no matter what, unlock DIR before returning. */ +error_t +netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **node) +{ + *node = NULL; + mutex_unlock (&dir->lock); + return EROFS; +} + +/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we + just created this node. Return an error if we should not permit the open + to complete because of a permission restriction. */ +error_t +netfs_check_open_permissions (struct iouser *user, struct node *node, + int flags, int newnode) +{ + error_t err = procfs_refresh_node (node); + if (!err && (flags & O_READ)) + err = fshelp_access (&node->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&node->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&node->nn_stat, S_IEXEC, user); + return err; +} + +/* This should attempt a utimes call for the user specified by CRED on node + NODE, to change the atime to ATIME and the mtime to MTIME. */ +error_t +netfs_attempt_utimes (struct iouser *cred, struct node *node, + struct timespec *atime, struct timespec *mtime) +{ + error_t err = procfs_refresh_node (node); + int flags = TOUCH_CTIME; + + if (! err) + err = fshelp_isowner (&node->nn_stat, cred); + + if (! err) + { + if (atime) + node->nn_stat.st_atim = *atime; + else + flags |= TOUCH_ATIME; + + if (mtime) + node->nn_stat.st_mtim = *mtime; + else + flags |= TOUCH_MTIME; + + fshelp_touch (&node->nn_stat, flags, procfs_maptime); + } + + return err; +} + +/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC) + in *TYPES for file NODE and user CRED. */ +error_t +netfs_report_access (struct iouser *cred, struct node *node, int *types) +{ + error_t err = procfs_refresh_node (node); + + if (! err) + { + *types = 0; + if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + } + + return err; +} + +/* The granularity with which we allocate space to return our result. */ +#define DIRENTS_CHUNK_SIZE (8*1024) + +/* Returned directory entries are aligned to blocks this many bytes long. + Must be a power of two. */ +#define DIRENT_ALIGN 4 +#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name) + +/* Length is structure before the name + the name + '\0', all + padded to a four-byte alignment. */ +#define DIRENT_LEN(name_len) \ + ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \ + & ~(DIRENT_ALIGN - 1)) + + + +/* Fetch a directory */ +error_t +netfs_get_dirents (struct iouser *cred, struct node *dir, + int first_entry, int max_entries, char **data, + mach_msg_type_number_t *data_len, + vm_size_t max_data_len, int *data_entries) +{ + error_t err = procfs_refresh_node (dir); + struct procfs_dir_entry *dir_entry; + + if (! err) + { + if (dir->nn->dir) + { + if (! procfs_dir_refresh (dir->nn->dir, dir == dir->nn->fs->root)) + { + for (dir_entry = dir->nn->dir->ordered; first_entry > 0 && + dir_entry; first_entry--, + dir_entry = dir_entry->ordered_next); + if (! dir_entry ) + max_entries = 0; + + if (max_entries != 0) + { + size_t size = 0; + char *p; + int count = 0; + + + if (max_data_len == 0) + size = DIRENTS_CHUNK_SIZE; + else if (max_data_len > DIRENTS_CHUNK_SIZE) + size = DIRENTS_CHUNK_SIZE; + else + size = max_data_len; + + *data = mmap (0, size, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + + err = ((void *) *data == (void *) -1) ? errno : 0; + + if (! err) + { + p = *data; + + /* This gets all the actual entries present. */ + + while ((max_entries == -1 || count < max_entries) && dir_entry) + { + struct dirent hdr; + size_t name_len = strlen (dir_entry->name); + size_t sz = DIRENT_LEN (name_len); + int entry_type = IFTODT (dir_entry->stat.st_mode); + + if ((p - *data) + sz > size) + { + if (max_data_len > 0) + break; + else /* The Buffer Size must be increased. */ + { + vm_address_t extension = (vm_address_t)(*data + size); + err = vm_allocate (mach_task_self (), &extension, + DIRENTS_CHUNK_SIZE, 0); + + if (err) + break; + + size += DIRENTS_CHUNK_SIZE; + } + } + + hdr.d_namlen = name_len; + hdr.d_fileno = dir_entry->stat.st_ino; + hdr.d_reclen = sz; + hdr.d_type = entry_type; + + memcpy (p, &hdr, DIRENT_NAME_OFFS); + strcpy (p + DIRENT_NAME_OFFS, dir_entry->name); + + p += sz; + + count++; + dir_entry = dir_entry->ordered_next; + } + + if (err) + munmap (*data, size); + else + { + vm_address_t alloc_end = (vm_address_t)(*data + size); + vm_address_t real_end = round_page (p); + if (alloc_end > real_end) + munmap ((caddr_t) real_end, alloc_end - real_end); + *data_len = p - *data; + *data_entries = count; + } + } + } + else + { + *data_len = 0; + *data_entries = 0; + } + } + } + else + return ENOTDIR; + } + + return err; +} + +/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If + the name was not found, then return ENOENT. On any error, clear *NODE. + (*NODE, if found, should be locked, this call should unlock DIR no matter + what.) */ +error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **node) +{ + error_t err = procfs_refresh_node (dir); + + if (! err) + err = procfs_dir_lookup (dir->nn->dir, name, node); + + return err; +} + +/* Delete NAME in DIR for USER. */ +error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, + char *name) +{ + return EROFS; +} + +/* Note that in this one call, neither of the specific nodes are locked. */ +error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, + char *fromname, struct node *todir, + char *toname, int excl) +{ + return EROFS; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the owner to UID and the group to GID. */ +error_t netfs_attempt_chown (struct iouser *cred, struct node *node, + uid_t uid, uid_t gid) +{ + return EROFS; +} + +/* This should attempt a chauthor call for the user specified by CRED on node + NODE, to change the author to AUTHOR. */ +error_t netfs_attempt_chauthor (struct iouser *cred, struct node *node, + uid_t author) +{ + return EROFS; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning + of chmod, this function is also used to attempt to change files into other + types. If such a transition is attempted which is impossible, then return + EOPNOTSUPP. */ +error_t netfs_attempt_chmod (struct iouser *cred, struct node *node, + mode_t mode) +{ + return EROFS; +} + +/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */ +error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *node, + char *name) +{ + return EROFS; +} + +/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or + S_IFCHR. */ +error_t netfs_attempt_mkdev (struct iouser *cred, struct node *node, + mode_t type, dev_t indexes) +{ + return EROFS; +} + + +/* This should attempt a chflags call for the user specified by CRED on node + NODE, to change the flags to FLAGS. */ +error_t netfs_attempt_chflags (struct iouser *cred, struct node *node, + int flags) +{ + return EROFS; +} + +/* This should attempt to set the size of the file NODE (for user CRED) to + SIZE bytes long. */ +error_t netfs_attempt_set_size (struct iouser *cred, struct node *node, + off_t size) +{ + return EROFS; +} + +/* This should attempt to fetch filesystem status information for the remote + filesystem, for the user CRED. */ +error_t +netfs_attempt_statfs (struct iouser *cred, struct node *node, + struct statfs *st) +{ + bzero (st, sizeof *st); + st->f_type = PROCFILESYSTEM; + st->f_fsid = getpid (); + return 0; +} + +/* This should sync the entire remote filesystem. If WAIT is set, return + only after sync is completely finished. */ +error_t netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + +/* Create a link in DIR with name NAME to FILE for USER. Note that neither + DIR nor FILE are locked. If EXCL is set, do not delete the target, but + return EEXIST if NAME is already found in DIR. */ +error_t netfs_attempt_link (struct iouser *user, struct node *dir, + struct node *file, char *name, int excl) +{ + return EROFS; +} + +/* Attempt to create an anonymous file related to DIR for USER with MODE. + Set *NODE to the returned file upon success. No matter what, unlock DIR. */ +error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, + mode_t mode, struct node **node) +{ + *node = NULL; + mutex_unlock (&dir->lock); + return EROFS; +} + +/* Read the contents of NODE (a symlink), for USER, into BUF. */ +error_t netfs_attempt_readlink (struct iouser *user, struct node *node, char *buf) +{ + error_t err = procfs_refresh_node (node); + if (! err) + { + struct procfs_dir_entry *dir_entry = node->nn->dir_entry; + if (dir_entry) + bcopy (dir_entry->symlink_target, buf, node->nn_stat.st_size); + else + err = EINVAL; + } + return err; +} + +/* Read from the file NODE for user CRED starting at OFFSET and continuing for + up to *LEN bytes. Put the data at DATA. Set *LEN to the amount + successfully read upon return. */ +error_t netfs_attempt_read (struct iouser *cred, struct node *node, + off_t offset, size_t *len, void *data) +{ + error_t err; + err = procfs_refresh_node (node); + + if (! err) + { + if (*len > 0) + procfs_write_files_contents (node, offset, + len, data); + if (*len > 0) + if (offset >= *len) + *len = 0; + } + + return err; +} + +/* Write to the file NODE for user CRED starting at OFFSET and continuing for up + to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon + return. */ +error_t netfs_attempt_write (struct iouser *cred, struct node *node, + off_t offset, size_t *len, void *data) +{ + return EROFS; +} + +/* The user must define this function. Node NP is all done; free + all its associated storage. */ +void netfs_node_norefs (struct node *np) +{ + mutex_lock (&np->lock); + *np->prevp = np->next; + np->next->prevp = np->prevp; + procfs_remove_node (np); +} + diff --git a/node.c b/node.c new file mode 100644 index 00000000..f11fa7b0 --- /dev/null +++ b/node.c @@ -0,0 +1,195 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + node.c -- This file contains function defintions to handle + node creation and destruction. + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "procfs.h" + +/* Return a new node in NODE, with a name NAME, and return the + new node with a single reference in NODE. */ +error_t procfs_create_node (struct procfs_dir_entry *dir_entry, + const char *fs_path, struct node **node) +{ + struct node *new; + struct netnode *nn = malloc (sizeof (struct netnode)); + error_t err; + + if (! nn) + return ENOMEM; + if (! fs_path) + fs_path = strdup (""); + nn->fs = dir_entry->dir->fs; + nn->dir_entry = dir_entry; + nn->dir = NULL; + nn->fs_path = strdup (fs_path); + + new = netfs_make_node (nn); + if (! new) + { + free (nn); + return ENOMEM; + } + + fshelp_touch (&new->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, + procfs_maptime); + + spin_lock (&nn->fs->inode_mappings_lock); + err = hurd_ihash_add (&nn->fs->inode_mappings, dir_entry->stat.st_ino, dir_entry); + spin_unlock (&nn->fs->inode_mappings_lock); + + if (err) + { + free (nn); + free (new); + return err; + } + + dir_entry->node = new; + *node = new; + + return 0; +} + +/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET. + True is returned if successful, or false if there was a memory allocation + error. TIMESTAMP is used to record the time of this update. */ +static void +update_entry (struct procfs_dir_entry *dir_entry, const struct stat *st, + const char *symlink_target, time_t timestamp) +{ + ino_t ino; + struct procfs *fs = dir_entry->dir->fs; + + if (dir_entry->stat.st_ino) + ino = dir_entry->stat.st_ino; + else + ino = fs->next_inode++; + + dir_entry->name_timestamp = timestamp; + + if (st) + /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */ + { + dir_entry->stat = *st; + dir_entry->stat_timestamp = timestamp; + + if (!dir_entry->symlink_target || !symlink_target + || strcmp (dir_entry->symlink_target, symlink_target) != 0) + { + if (dir_entry->symlink_target) + free (dir_entry->symlink_target); + dir_entry->symlink_target = symlink_target ? strdup (symlink_target) : 0; + } + } + + /* The st_ino field is always valid. */ + dir_entry->stat.st_ino = ino; + dir_entry->stat.st_fsid = fs->fsid; + dir_entry->stat.st_fstype = PROCFILESYSTEM; +} + +/* Refresh stat information for NODE */ +error_t procfs_refresh_node (struct node *node) +{ + struct netnode *nn = node->nn; + struct procfs_dir_entry *dir_entry = nn->dir_entry; + + if (! dir_entry) + /* This is a deleted node, don't attempt to do anything. */ + return 0; + else + { + error_t err = 0; + + struct timeval tv; + maptime_read (procfs_maptime, &tv); + + time_t timestamp = tv.tv_sec; + + struct procfs_dir *dir = dir_entry->dir; + + mutex_lock (&dir->node->lock); + + if (! dir_entry->self_p) + /* This is a deleted entry, just awaiting disposal; do so. */ + { +#if 0 + nn->dir_entry = 0; + free_entry (dir_entry); + return 0; +#endif + } + + else if (dir_entry->noent) + err = ENOENT; + else + { + if (*(dir_entry->name)) + { + err = procfs_dir_refresh (dir_entry->dir, + dir_entry->dir->node == dir_entry->dir->fs->root); + if (!err && dir_entry->noent) + err = ENOENT; + + if (err == ENOENT) + { + dir_entry->noent = 1; /* A negative entry. */ + dir_entry->name_timestamp = timestamp; + } + } + else + { + /* Refresh the root node with the old stat + information. */ + update_entry (dir_entry, &netfs_root_node->nn_stat, NULL, timestamp); + } + } + + node->nn_stat = dir_entry->stat; + node->nn_translated = S_ISLNK (dir_entry->stat.st_mode) ? S_IFLNK : 0; + if (!nn->dir && S_ISDIR (dir_entry->stat.st_mode)) + procfs_dir_create (nn->fs, node, nn->fs_path, &nn->dir); + + mutex_unlock (&dir->node->lock); + + return err; + } +} + +/* Remove NODE from its entry */ +error_t procfs_remove_node (struct node *node) +{ + + /* STUB */ + + return 0; +} diff --git a/procfs.c b/procfs.c new file mode 100644 index 00000000..1fd0d619 --- /dev/null +++ b/procfs.c @@ -0,0 +1,149 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + procfs.c -- This file is the main file of the translator. + This has important definitions and initializes + the translator + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "procfs.h" + +/* Defines this Tanslator Name */ +char *netfs_server_name = PROCFS_SERVER_NAME; +char *netfs_server_version = PROCFS_SERVER_VERSION; +int netfs_maxsymlinks = 12; + +static const struct argp_child argp_children[] = + { + {&netfs_std_startup_argp, 0, NULL, 0}, + {0} + }; + + +const char *argp_program_version = "/proc pseudo-filesystem (" PROCFS_SERVER_NAME + ") " PROCFS_SERVER_VERSION "\n" +"Copyright (C) 2008 Free Software Foundation\n" +"This is free software; see the source for copying conditions. There is NO\n" +"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." +"\n"; + +static char *args_doc = "PROCFSROOT"; +static char *doc = "proc pseudo-filesystem for Hurd implemented as a translator. " +"This is still under very humble and initial stages of development.\n" +"Any Contribution or help is welcome. The code may not even compile"; + + +/* The Filesystem */ +struct procfs *procfs; + +/* The FILESYSTEM component of PROCFS_FS. */ +char *procfs_root = ""; + +volatile struct mapped_time_value *procfs_maptime; + +/* Startup options. */ +static const struct argp_option procfs_options[] = + { + { 0 } + }; + + +/* argp parser function for parsing single procfs command line options */ +static error_t +parse_procfs_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case ARGP_KEY_ARG: + if (state->arg_num > 1) + argp_usage (state); + break; + + case ARGP_KEY_NO_ARGS: + argp_usage(state); + break; + + default: + return ARGP_ERR_UNKNOWN; + } +} + +/* Program entry point. */ +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap, underlying_node; + struct stat underlying_stat; + + struct argp argp = + { + procfs_options, parse_procfs_opt, + args_doc, doc, argp_children, + NULL, NULL + }; + + + /* Parse the command line arguments */ +// argp_parse (&argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + + netfs_init (); + + if (maptime_map (0, 0, &procfs_maptime)) + { + perror (PROCFS_SERVER_NAME ": Cannot map time"); + return 1; + } + + procfs_init (); + + err = procfs_create (procfs_root, getpid (), &procfs); + if (err) + error (4, err, "%s", procfs_root); + + /* Create our root node */ + netfs_root_node = procfs->root; + + /* Start netfs activities */ + underlying_node = netfs_startup (bootstrap, 0); + if (io_stat (underlying_node, &underlying_stat)) + error (1, err, "cannot stat underling node"); + + /* Initialize stat information of the root node. */ + netfs_root_node->nn_stat = underlying_stat; + netfs_root_node->nn_stat.st_mode = + S_IFDIR | (underlying_stat.st_mode & ~S_IFMT & ~S_ITRANS); + + for (;;) + netfs_server_loop (); + return 1; +} diff --git a/procfs.h b/procfs.h new file mode 100644 index 00000000..95d4ee35 --- /dev/null +++ b/procfs.h @@ -0,0 +1,218 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + procfs.h -- This file is the main header file of this + translator. This has important header + definitions for constants and functions + used in the translator. + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + + A portion of the code in this file is based on ftpfs code + present in the hurd repositories copyrighted to FSF. The + Copyright notice from that file is given below. + + Copyright (C) 1997,98,2002 Free Software Foundation, Inc. + Written by Miles Bader + This file is part of the GNU Hurd. +*/ + +#ifndef __PROCFS_H__ +#define __PROCFS_H__ + +#define PROCFS_SERVER_NAME "procfs" +#define PROCFS_SERVER_VERSION "0.0.1" + +/* /proc Filesystem type. */ +#define PROCFILESYSTEM "procfs" + +#define NUMBER_OF_FILES_PER_PID 1 +#define JIFFY_ADJUST 100 +#define PAGES_TO_BYTES(pages) ((pages) * sysconf(_SC_PAGESIZE)) +#define BYTES_TO_PAGES(bytes) ((bytes) / sysconf(_SC_PAGESIZE)) + +#include +#include +#include +#include +#include +#include + +/* A single entry in a directory. */ +struct procfs_dir_entry +{ + char *name; /* Name of this entry */ + size_t hv; /* Hash value of NAME */ + + /* The active node referred to by this name (may be 0). + NETFS_NODE_REFCNT_LOCK should be held while frobbing this. */ + struct node *node; + + struct stat stat; + char *symlink_target; + time_t stat_timestamp; + + /* The directory to which this entry belongs. */ + struct procfs_dir *dir; + + /* Link to next entry in hash bucket, and address of previous entry's (or + hash table's) pointer to this entry. If the SELF_P field is 0, then + this is a deleted entry, awaiting final disposal. */ + struct procfs_dir_entry *next, **self_p; + + /* Next entry in 'directory order', or 0 if none known. */ + struct procfs_dir_entry *ordered_next, **ordered_self_p; + + /* When the presence/absence of this file was last checked. */ + time_t name_timestamp; + + hurd_ihash_locp_t inode_locp; /* Used for removing this entry */ + + int noent : 1; /* A negative lookup result. */ + int valid : 1; /* Marker for GC'ing. */ +}; + +/* A directory. */ +struct procfs_dir +{ + /* Number of entries in HTABLE. */ + size_t num_entries; + + /* The number of entries that have nodes attached. We keep an additional + reference to our node if there are any, to prevent it from going away. */ + size_t num_live_entries; + + /* Hash table of entries. */ + struct procfs_dir_entry **htable; + size_t htable_len; /* # of elements in HTABLE (not bytes). */ + + /* List of dir entries in 'directory order', in a linked list using the + ORDERED_NEXT and ORDERED_SELF_P fields in each entry. Not all entries + in HTABLE need be in this list. */ + struct procfs_dir_entry *ordered; + + /* The filesystem node that this is the directory for. */ + struct node *node; + + /* The filesystem this directory is in. */ + struct procfs *fs; + + /* The path to this directory in the filesystem. */ + const char *fs_path; + + time_t stat_timestamp; + time_t name_timestamp; + +}; + + +/* libnetfs node structure */ +struct netnode +{ + /* Name of this node */ + char *name; + + /* The path in the filesystem that corresponds + this node. */ + char *fs_path; + + /* The directory entry for this node. */ + struct procfs_dir_entry *dir_entry; + + /* The proc filesystem */ + struct procfs *fs; + + /* inode number, assigned to this netnode structure. */ + unsigned int inode_num; + + /* If this is a directory, the contents, or 0 if not fetched. */ + struct procfs_dir *dir; + + /* pointer to node structure, assigned to this node. */ + struct node *node; + + /* links to the previous and next nodes in the list */ + struct netnode *nextnode, *prevnode; + + /* link to parent netnode of this file or directory */ + struct netnode *parent; + + /* link to the first child netnode of this directory */ + struct netnode *child_first; +}; + +/* The actual procfs filesystem structure */ +struct procfs +{ + /* Root of the filesystem. */ + struct node *root; + + /* Inode numbers are assigned sequentially in order of creation. */ + ino_t next_inode; + int fsid; + + /* A hash table mapping inode numbers to directory entries. */ + struct hurd_ihash inode_mappings; + spin_lock_t inode_mappings_lock; +}; + +extern struct procfs *procfs; + +extern volatile struct mapped_time_value *procfs_maptime; + +extern struct ps_context *ps_context; + +/* Create a new procfs filesystem. */ +error_t procfs_create (char *procfs_root, int fsid, + struct procfs **fs); + +/* Initialize the procfs filesystem for use. */ +error_t procfs_init (); + +/* Refresh stat information for NODE */ +error_t procfs_refresh_node (struct node *node); + +/* Return a new node in NODE, with a name NAME, + and return the new node with a single + reference in NODE. */ +error_t procfs_create_node (struct procfs_dir_entry *dir_entry, + const char *fs_path, + struct node **node); + +/* Remove NODE from its entry */ +error_t procfs_remove_node (struct node *node); + +/* Return in DIR a new procfs directory, in the filesystem FS, + with node NODE and path PATH. */ +error_t procfs_dir_create (struct procfs *fs, struct node *node, + const char *path, struct procfs_dir **dir); + +/* Remove the specified DIR and free all its allocated + storage. */ +void procfs_dir_remove (struct procfs_dir *dir); + +/* Refresh DIR. */ +error_t procfs_dir_refresh (struct procfs_dir *dir, int isroot); + +/* Lookup NAME in DIR, returning its entry, or an error. + *NODE will contain the result node, locked, and with + an additional reference, or 0 if an error occurs. */ +error_t procfs_dir_lookup (struct procfs_dir *dir, const char *name, + struct node **node); + +#endif /* __PROCFS_H__ */ diff --git a/procfs_dir.c b/procfs_dir.c new file mode 100644 index 00000000..b9b0410e --- /dev/null +++ b/procfs_dir.c @@ -0,0 +1,575 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + procfs_dir.c -- This file contains definitions to perform + directory operations such as creating, + removing and refreshing directories. + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + + A portion of the code in this file is based on ftpfs code + present in the hurd repositories copyrighted to FSF. The + Copyright notice from that file is given below. + + Copyright (C) 1997,98,2002 Free Software Foundation, Inc. + Written by Miles Bader + This file is part of the GNU Hurd. +*/ + + +#include +#include +#include +#include +#include + +#include "procfs.h" + +/* Initial HASHTABLE length for the new directories to be created. */ +#define INIT_HTABLE_LEN 5 + +struct procfs_dir_entry **cur_entry; + +/* Return in DIR a new procfs directory, in the filesystem FS, + with node NODE and path PATH. */ +error_t procfs_dir_create (struct procfs *fs, struct node *node, + const char *path, struct procfs_dir **dir) +{ + struct procfs_dir *new = malloc (sizeof (struct procfs_dir)); + if (!new) + return ENOMEM; + struct procfs_dir_entry **htable = calloc (INIT_HTABLE_LEN, + sizeof (struct procfs_dir_entry *)); + if (!htable) + return ENOMEM; + + /* Hold a reference to the new dir's node. */ + spin_lock (&netfs_node_refcnt_lock); + node->references++; + spin_unlock (&netfs_node_refcnt_lock); + + new->num_entries = 0; + new->num_live_entries = 0; + new->htable_len = INIT_HTABLE_LEN; + new->htable = htable; + new->ordered = NULL; + new->fs_path = path; + new->fs = fs; + new->node = node; + new->stat_timestamp = 0; + new->name_timestamp = 0; + + *dir = new; + + return 0; +} + +/* Put the directory entry DIR_ENTRY into the hash table HTABLE. */ +static void +insert (struct procfs_dir_entry *dir_entry, + struct procfs_dir_entry **htable, size_t htable_len) +{ + struct procfs_dir_entry **new_htable = &htable[dir_entry->hv % htable_len]; + if (*new_htable) + (*new_htable)->self_p = &dir_entry->next; + dir_entry->next = *new_htable; + dir_entry->self_p = new_htable; + *new_htable = dir_entry; +} + +/* Calculate NAME's hash value. */ +static size_t +hash (const char *name) +{ + size_t hash_value = 0; + while (*name) + hash_value = ((hash_value << 5) + *name++) & 0xFFFFFF; + return hash_value; +} + +/* Extend the existing hashtable for DIR to accomodate values for new length + NEW_LEN. We retain all the previous entries. */ +static error_t +rehash (struct procfs_dir *dir, size_t new_len) +{ + int count; + size_t old_len = dir->htable_len; + struct procfs_dir_entry **old_htable = dir->htable; + struct procfs_dir_entry **new_htable = (struct procfs_dir_entry **) + malloc (new_len * sizeof (struct procfs_dir_entry *)); + + if (! new_htable) + return ENOMEM; + + bzero (new_htable, new_len * sizeof (struct procfs_dir_entry *)); + + for (count = 0; count < old_len; count++) + while (old_htable[count]) + { + struct procfs_dir_entry *dir_entry = old_htable[count]; + + /* Remove DIR_ENTRY from the old table */ + old_htable[count] = dir_entry->next; + + insert (dir_entry, new_htable, new_len); + } + + free (old_htable); + + dir->htable = new_htable; + dir->htable_len = new_len; + + return 0; +} + +/* Lookup NAME in DIR and return its entry. If there is no such entry, and + DNEW, the decision variable, is true, then a new entry is allocated and + returned, otherwise 0 is returned (if DNEW is true then 0 can be returned + if a memory allocation error occurs). */ +struct procfs_dir_entry * +lookup_entry (struct procfs_dir *dir, const char *name, int dnew) +{ + size_t hv = hash (name); + struct procfs_dir_entry *dir_entry = dir->htable[hv % dir->htable_len]; + + while (dir_entry && strcmp (name, dir_entry->name) != 0) + dir_entry = dir_entry->next; + + if (!dir_entry && dnew) + { + if (dir->num_entries > dir->htable_len) + /* Grow the hash table. */ + if (rehash (dir, (dir->htable_len + 1) * 2 - 1) != 0) + return 0; + + dir_entry = + (struct procfs_dir_entry *) malloc (sizeof (struct procfs_dir_entry)); + + if (dir_entry) + { + dir_entry->hv = hv; + dir_entry->name = strdup (name); + dir_entry->node = 0; + dir_entry->dir = dir; + dir_entry->stat_timestamp = 0; + bzero (&dir_entry->stat, sizeof dir_entry->stat); + dir_entry->symlink_target = 0; + dir_entry->noent = 0; + dir_entry->valid = 0; + dir_entry->name_timestamp = 0; + dir_entry->ordered_next = 0; + dir_entry->ordered_self_p = 0; + dir_entry->next = 0; + dir_entry->self_p = 0; + insert (dir_entry, dir->htable, dir->htable_len); + dir->num_entries++; + } + } + + return dir_entry; +} + + +/* Lookup NAME in DIR, returning its entry, or an error. + *NODE will contain the result node, locked, and with + an additional reference, or 0 if an error occurs. */ +error_t procfs_dir_lookup (struct procfs_dir *dir, const char *name, + struct node **node) +{ + struct procfs_dir_entry *dir_entry = 0; + error_t err = 0; + char *fs_path = dir->fs_path; + + struct timeval tv; + maptime_read (procfs_maptime, &tv); + + time_t timestamp = tv.tv_sec; + + if (*name == '\0' || strcmp (name, ".") == 0) + /* Current directory -- just add an additional reference to DIR's node + and return it. */ + { + netfs_nref (dir->node); + *node = dir->node; + return 0; + } + else if (strcmp (name, "..") == 0) + /* Parent directory. */ + { + if (dir->node->nn->dir_entry) + { + *node = dir->node->nn->dir_entry->dir->node; + mutex_lock (&(*node)->lock); + netfs_nref (*node); + } + else + { + err = ENOENT; /* No .. */ + *node = 0; + } + + mutex_unlock (&dir->node->lock); + + return err; + } + + err = procfs_dir_refresh (dir, dir->node == dir->fs->root); + if (!err && !dir_entry) + dir_entry = lookup_entry (dir, name, 0); + + if (! err) + { + if (dir_entry && !dir_entry->noent) + /* We've got a dir entry, get a node for it. */ + { + /* If there's already a node, add a ref so that it doesn't go + away. */ + spin_lock (&netfs_node_refcnt_lock); + if (dir_entry->node) + dir_entry->node->references++; + spin_unlock (&netfs_node_refcnt_lock); + + if (! dir_entry->node) + /* No node; make one and install it into E. */ + { + if (! fs_path) + err = EROFS; + + if (! err) + { + err = procfs_create_node (dir_entry, fs_path, &dir_entry->node); + + if (!err && dir->num_live_entries++ == 0) + /* Keep a reference to dir's node corresponding to + children. */ + { + spin_lock (&netfs_node_refcnt_lock); + dir->node->references++; + spin_unlock (&netfs_node_refcnt_lock); + } + } + } + + if (! err) + { + *node = dir_entry->node; + /* We have to unlock DIR's node before locking the child node + because the locking order is always child-parent. We know + the child node won't go away because we already hold the + additional reference to it. */ + mutex_unlock (&dir->node->lock); + mutex_lock (&dir_entry->node->lock); + } + } + else + err = ENOENT; + } + + if (err) + { + *node = 0; + mutex_unlock (&dir->node->lock); + } + +#if 0 + if (fs_path) + free (fs_path); +#endif + + return err; +} + +/* Lookup the null name in DIR, and return a node for it in NODE. Unlike + procfs_dir_lookup, this won't attempt to validate the existance of the + entry (to avoid opening a new connection if possible) -- that will happen + the first time the entry is refreshed. Also unlink ftpfs_dir_lookup, this + function doesn't expect DIR to be locked, and won't return *NODE locked. + This function is only used for bootstrapping the root node. */ +error_t +procfs_dir_null_lookup (struct procfs_dir *dir, struct node **node) +{ + struct procfs_dir_entry *dir_entry; + error_t err = 0; + + dir_entry = lookup_entry (dir, "", 1); + if (! dir_entry) + return ENOMEM; + + if (! dir_entry->noent) + /* We've got a dir entry, get a node for it. */ + { + /* If there's already a node, add a ref so that it doesn't go away. */ + spin_lock (&netfs_node_refcnt_lock); + if (dir_entry->node) + dir_entry->node->references++; + spin_unlock (&netfs_node_refcnt_lock); + + if (! dir_entry->node) + /* No node; make one and install it into DIR_ENTRY. */ + { + err = procfs_create_node (dir_entry, dir->fs_path, &dir_entry->node); + + if (!err && dir->num_live_entries++ == 0) + /* Keep a reference to dir's node corresponding to children. */ + { + spin_lock (&netfs_node_refcnt_lock); + dir->node->references++; + spin_unlock (&netfs_node_refcnt_lock); + } + } + + if (! err) + *node = dir_entry->node; + } + else + err = ENOENT; + + return err; +} + +/* Remove the specified DIR and free all its allocated + storage. */ +void procfs_dir_remove (struct procfs_dir *dir) +{ + + /* STUB */ + + return 0; +} + +/* Make all the directory entries invalid */ +static void +make_dir_invalid (struct procfs_dir *dir) +{ + int count; + size_t len = dir->htable_len; + struct procfs_dir_entry **htable = dir->htable; + struct procfs_dir_entry *dir_entry; + + for (count = 0; count < len; count++) + { + dir_entry = htable[count]; + while (dir_entry) + { + dir_entry->valid = 0; + dir_entry = dir_entry->next; + } + } +} + +/* Checks if the DIR name is in list of + Active pids. */ +int is_in_pid_list (struct procfs_dir *dir) +{ + int dir_name; + int count; + pid_t *pids = NULL; + int pidslen = 0; + error_t err; + + if (dir->node->nn) + { + dir_name = atoi (dir->node->nn->dir_entry->name); + err = proc_getallpids (getproc (), &pids, &pidslen); + + for (count = 0; count < pidslen; ++count) + if (pids[count] == dir_name) + return 1; + } + + return 0; + +} + +/* Checks if DIR is a directory that + represents a pid. */ +int check_parent (struct procfs_dir *dir) +{ + if (dir == dir->fs->root) + return 0; + else + if (is_in_pid_list (dir)) + return 1; + else + return 0; + +} + +/* Refresh DIR. */ +error_t procfs_dir_refresh (struct procfs_dir *dir, int isroot) +{ + error_t err; + int is_parent_pid; + struct node *node; + make_dir_invalid (dir); + + struct timeval tv; + maptime_read (procfs_maptime, &tv); + + time_t timestamp = tv.tv_sec; + cur_entry = &dir->ordered; + if (isroot) + err = procfs_fill_root_dir(dir, timestamp); + else + { + err = update_dir_entries (dir, timestamp); + is_parent_pid = check_parent (dir); + if (is_parent_pid) + err = procfs_create_files (dir, &node, timestamp); + } + + return err; +} + +/* Update the directory entry for NAME to reflect STAT and SYMLINK_TARGET. + This also creates a valid linked list of entries imposing ordering on + them. */ +struct procfs_dir_entry* +update_entries_list (struct procfs_dir *dir, const char *name, + const struct stat *stat, time_t timestamp, + const char *symlink_target) +{ + ino_t ino; + struct procfs_dir_entry *dir_entry = lookup_entry (dir, name, 1); + struct procfs *fs = dir->fs; + + if (! dir_entry) + return ENOMEM; + + if (dir_entry->stat.st_ino) + ino = dir_entry->stat.st_ino; + else + ino = fs->next_inode++; + + dir_entry->name_timestamp = timestamp; + + if (stat) + /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */ + { + dir_entry->stat = *stat; + dir_entry->stat_timestamp = timestamp; + + if (!dir_entry->symlink_target || !symlink_target + || strcmp (dir_entry->symlink_target, symlink_target) != 0) + { + if (dir_entry->symlink_target) + free (dir_entry->symlink_target); + dir_entry->symlink_target = symlink_target ? strdup (symlink_target) : 0; + } + } + + /* The st_ino field is always valid. */ + dir_entry->stat.st_ino = ino; + dir_entry->stat.st_fsid = fs->fsid; + dir_entry->stat.st_fstype = PROCFILESYSTEM; + + dir_entry->valid = 1; + + if (! dir_entry->ordered_self_p) + /* Position DIR_ENTRY in the ordered chain following the previously seen entry. */ + { + /* The PREV_ENTRY_NEXT_P field holds a pointer to the NEXT-field of the + previous entry, or a pointer to the ORDERED field in the directory. */ + dir_entry->ordered_self_p = cur_entry; + + if (*dir_entry->ordered_self_p) + /* Update the self_p pointer of the previous successor. */ + (*dir_entry->ordered_self_p)->ordered_self_p = &dir_entry->ordered_next; + + /* DIR_ENTRY comes before the previous successor. */ + dir_entry->ordered_next = *dir_entry->ordered_self_p; + + *dir_entry->ordered_self_p = dir_entry; /* Put DIR_ENTRY there. */ + } + + /* Put the next entry after this one. */ + cur_entry = &dir_entry->ordered_next; + + return dir_entry; +} + +/* Fills DIR, the root directory with all the pids of + processes running in the system as directories. */ +error_t +procfs_fill_root_dir(struct procfs_dir *dir, time_t timestamp) +{ + error_t err; + char *data; + pid_t *pids; + int pidslen; + struct stat *stat = (struct stat *) malloc (sizeof (struct stat)); + stat->st_mode = S_IFDIR; + + int count; + char *dir_name_pid; + struct node *node; + struct procfs_dir *new_dir; + struct procfs_dir_entry *dir_entry; + + pids = NULL; + pidslen = 0; + err = proc_getallpids (getproc (), &pids, &pidslen); + + if (!err) + { + for (count = 0; count < pidslen; count++) + { + if (asprintf (&dir_name_pid, "%d", pids[count]) == -1) + return errno; + +#if 0 + node = (struct node *) malloc (sizeof (struct node)); + new_dir = (struct procfs_dir *) malloc (sizeof (struct procfs_dir )); + + if (! node || ! new_dir ) + return ENOMEM; +#endif + dir_entry = update_entries_list (dir, dir_name_pid, + stat, timestamp, NULL); + err = procfs_create_node (dir_entry, dir_name_pid, &node); + + procfs_dir_create (dir->fs, node, + dir_name_pid, &new_dir); + free(dir_name_pid); + } + } + + if ((err = procfs_create_uptime (dir, &node, timestamp)) != 0) + return err; + + if ((err = procfs_create_stat (dir, &node, timestamp)) != 0) + return err; + + if ((err = procfs_create_version (dir, &node, timestamp)) != 0) + return err; + + if ((err = procfs_create_meminfo (dir, &node, timestamp)) != 0) + return err; + + if ((err = procfs_create_loadavg (dir, &node, timestamp)) != 0) + return err; + + return 0; +} + +error_t update_dir_entries (struct procfs_dir *dir) +{ + /* STUB */ + return 0; +} diff --git a/procfs_nonpid_files.c b/procfs_nonpid_files.c new file mode 100644 index 00000000..104ae8b3 --- /dev/null +++ b/procfs_nonpid_files.c @@ -0,0 +1,462 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + procfs_nonpid_files.c -- This file contains function definitions + to create and update the non-Per PID + files and their contents. + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + + A portion of the code in this file is based on vmstat.c code + present in the hurd repositories copyrighted to FSF. The + Copyright notice from that file is given below. + + Copyright (C) 1997,98,2002 Free Software Foundation, Inc. + Written by Miles Bader + This file is part of the GNU Hurd. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "procfs.h" + +typedef long long val_t; +#define BADVAL ((val_t) - 1LL) + +/* default pager port (must be privileged to fetch this). */ +mach_port_t def_pager; +struct default_pager_info def_pager_info; + +error_t procfs_create_uptime (struct procfs_dir *dir, + struct node **node, + time_t timestamp) +{ + int err; + char *file_name, *file_path; + struct procfs_dir_entry *dir_entry; + + if (asprintf (&file_name, "%s", "uptime") == -1) + return errno; + if (asprintf (&file_path, "%s", "uptime") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + + return err; +} + +error_t procfs_create_version(struct procfs_dir *dir, + struct node **node, + time_t timestamp) +{ + int err; + char *file_name, *file_path; + struct procfs_dir_entry *dir_entry; + + if (asprintf (&file_name, "%s", "version") == -1) + return errno; + if (asprintf (&file_path, "%s", "version") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + + return 0; +} + +error_t procfs_create_stat (struct procfs_dir *dir, + struct node **node, + time_t timestamp) +{ + int err; + char *file_name, *file_path; + struct procfs_dir_entry *dir_entry; + + if (asprintf (&file_name, "%s", "stat") == -1) + return errno; + if (asprintf (&file_path, "%s", "stat") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + + return err; +} + +error_t procfs_create_meminfo (struct procfs_dir *dir, + struct node **node, + time_t timestamp) +{ + int err; + char *file_name, *file_path; + struct procfs_dir_entry *dir_entry; + + if (asprintf (&file_name, "%s", "meminfo") == -1) + return errno; + if (asprintf (&file_path, "%s", "meminfo") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + + return err; +} + +error_t procfs_create_loadavg (struct procfs_dir *dir, + struct node **node, + time_t timestamp) +{ + int err; + char *file_name, *file_path; + struct procfs_dir_entry *dir_entry; + + if (asprintf (&file_name, "%s", "loadavg") == -1) + return errno; + if (asprintf (&file_path, "%s", "loadavg") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + + return err; +} + +error_t get_uptime (double *uptime_secs) +{ + struct timeval boot_time, uptime, now; + error_t err; + struct proc_stat *ps; + + err = _proc_stat_create (1, ps_context, &ps); + + if (err) + return err; + + err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); + if (!err && !(ps->flags & PSTAT_TASK_BASIC)) + err = EGRATUITOUS; + + if (! err) + { + time_value_t *const tv = &proc_stat_task_basic_info (ps)->creation_time; + boot_time.tv_sec = tv->seconds; + boot_time.tv_usec = tv->microseconds; + if (gettimeofday (&now, 0) < 0) + error (0, errno, "gettimeofday"); + timersub (&now, &boot_time, &uptime); + *uptime_secs = (double)uptime.tv_sec + ((double)uptime.tv_usec / 1000000); + } + + _proc_stat_free (ps); + return err; +} + +error_t get_total_times (double *total_user_time_secs , double *total_system_time_secs ) +{ + error_t err; + pid_t *pids; + int pidslen = 0, count; + struct proc_stat *ps; + struct task_thread_times_info live_threads_times; + + *total_user_time_secs = 0; + *total_system_time_secs = 0; + + pids = NULL; + err = proc_getallpids (getproc (), &pids, &pidslen); + + if (!err) + for (count = 0; count < pidslen; count++) + { + err = _proc_stat_create (pids[count], ps_context, &ps); + if (err) + return err; + + err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); + if (!err && !(ps->flags & PSTAT_TASK_BASIC)) + err = EGRATUITOUS; + + if (! err) + { + *total_user_time_secs += + ((double) (proc_stat_task_basic_info (ps)->user_time.seconds)) + + (((double) (proc_stat_task_basic_info (ps)->user_time.microseconds)) / + (1000 * 1000)); + + *total_system_time_secs += + ((double) (proc_stat_task_basic_info (ps)->system_time.seconds)) + + (((double) (proc_stat_task_basic_info (ps)->system_time.microseconds)) / (1000 * 1000)); + + error_t err = set_field_value (ps, PSTAT_TASK); + if (! err) + { + err = get_task_thread_times (ps->task, &live_threads_times); + if (! err) + { + *total_user_time_secs += ((double) (live_threads_times.user_time.seconds)) + + (((double) (live_threads_times.user_time.microseconds)) / (1000 * 1000)); + *total_system_time_secs += ((double) (live_threads_times.system_time.seconds)) + + (((double) (live_threads_times.system_time.microseconds)) / (1000 * 1000)); + + } + } + } + _proc_stat_free (ps); + } + + return err; +} + +error_t procfs_write_nonpid_stat (struct dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + char *stat_data; + error_t err; + double uptime_secs, total_user_time_secs; + double total_system_time_secs, idle_time_secs; + + err = get_uptime (&uptime_secs); + if (! err) + { + err = get_total_times (&total_user_time_secs, &total_system_time_secs); + if (! err) + { + idle_time_secs = uptime_secs - + total_system_time_secs; + + /* If the values are multiplied by 100, it iss done so to adjust + values in seconds to jiffies. */ + if (asprintf (&stat_data, "cpu %ld %ld %ld %ld %ld %ld %d %d %d\n" + "cpu0 %ld %ld %ld %ld %ld %ld %d %d %d\n" + "intr %ld %ld %ld %ld %ld %ld %d %d %d\n", + (long)(total_user_time_secs * 100), 0, + (long)(total_system_time_secs * 100), + (long) (idle_time_secs * 100), 0, 0, 0, 0, + 0, (long)(total_user_time_secs * 100), 0, + (long)(total_system_time_secs * 100), + (long)(idle_time_secs * 100), 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == -1) + return errno; + } + } + + memcpy (data, stat_data, strlen(stat_data)); + *len = strlen (data); + + free (stat_data); + return err; +} + +/* Makes sure the default pager port and associated + info exists, and returns 0 if not (after printing + an error). */ +static int +ensure_def_pager_info () +{ + error_t err; + + if (def_pager == MACH_PORT_NULL) + { + mach_port_t host; + + err = get_privileged_ports (&host, 0); + if (err == EPERM) + { + /* We are not root, so try opening the /servers file. */ + def_pager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); + if (def_pager == MACH_PORT_NULL) + { + error (0, errno, _SERVERS_DEFPAGER); + return 0; + } + } + if (def_pager == MACH_PORT_NULL) + { + if (err) + { + error (0, err, "get_privileged_ports"); + return 0; + } + + err = vm_set_default_memory_manager (host, &def_pager); + mach_port_deallocate (mach_task_self (), host); + + if (err) + { + error (0, err, "vm_set_default_memory_manager"); + return 0; + } + } + } + + if (!MACH_PORT_VALID (def_pager)) + { + if (def_pager == MACH_PORT_NULL) + { + error (0, 0, + "No default pager running, so no swap information available"); + def_pager = MACH_PORT_DEAD; /* so we don't try again */ + } + return 0; + } + + err = default_pager_info (def_pager, &def_pager_info); + if (err) + error (0, err, "default_pager_info"); + return (err == 0); +} + +#define SWAP_FIELD(getter, expr) \ + static val_t getter () \ + { return ensure_def_pager_info () ? (val_t) (expr) : BADVAL; } + +SWAP_FIELD (get_swap_size, def_pager_info.dpi_total_space) +SWAP_FIELD (get_swap_free, def_pager_info.dpi_free_space) +SWAP_FIELD (get_swap_page_size, def_pager_info.dpi_page_size) +SWAP_FIELD (get_swap_active, (def_pager_info.dpi_total_space + - def_pager_info.dpi_free_space)) + +error_t procfs_write_nonpid_meminfo (struct dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + char *meminfo_data; + error_t err; + struct vm_statistics vmstats; + + err = vm_statistics (mach_task_self (), &vmstats); + + unsigned long mem_size = ((vmstats.free_count + + vmstats.active_count + vmstats.inactive_count + + vmstats.wire_count) * vmstats.pagesize) / 1024; + + if (! err) + if (asprintf (&meminfo_data, "MemTotal:\t%lu kB\n" + "MemFree:\t%lu kB\n" + "Buffers:\t%ld kB\n" + "Cached:\t\t%ld kB\n" + "SwapCached:\t%ld kB\n" + "Active:\t\t%lu kB\n" + "Inactive:\t%lu kB\n" + "HighTotal:\t%lu kB\n" + "HighFree:\t%lu kB\n" + "LowTotal:\t%lu kB\n" + "LowFree:\t%lu kB\n" + "SwapTotal:\t%lu kB\n" + "SwapFree:\t%lu kB\n", + mem_size, (PAGES_TO_BYTES(vmstats.free_count)) / 1024 , 0, 0, 0, + (PAGES_TO_BYTES(vmstats.active_count)) / 1024, + (PAGES_TO_BYTES(vmstats.inactive_count)) / 1024, 0, 0, 0, 0, + get_swap_size (), get_swap_free ()) == -1) + return errno; + + memcpy (data, meminfo_data, strlen(meminfo_data)); + *len = strlen (data); + + free (meminfo_data); + return err; +} + +error_t procfs_write_nonpid_loadavg (struct dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + char *loadavg_data; + error_t err; + processor_set_info_t info; + natural_t *count; + struct host_load_info *load; + mach_port_t host; + + err = ps_host_load_info (&load); + if (err) + error (0, err, "ps_host_load_info"); + + if (! err) + if (asprintf (&loadavg_data, "%.2f %.2f %.2f %d/%d %d\n", + (double)load->avenrun[0] / (double)LOAD_SCALE, + (double)load->avenrun[1] / (double)LOAD_SCALE, + (double)load->avenrun[2] / (double)LOAD_SCALE, 0, 0, 0) == -1) + return errno; + + memcpy (data, loadavg_data, strlen(loadavg_data)); + *len = strlen (data); + + free (loadavg_data); + return err; +} + +error_t procfs_write_nonpid_uptime (struct dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + char *uptime_data; + error_t err; + double uptime_secs, total_user_time_secs; + double total_system_time_secs, idle_time_secs; + + err = get_uptime (&uptime_secs); + if (! err) + { + err = get_total_times (&total_user_time_secs, + &total_system_time_secs); + if (! err) + { + idle_time_secs = uptime_secs - + total_system_time_secs; + + if (asprintf (&uptime_data, "%.2f %.2f \n", + uptime_secs, idle_time_secs) == -1) + return errno; + } + } + + + memcpy (data, uptime_data, strlen(uptime_data)); + *len = strlen (data); + + free (uptime_data); + return err; +} diff --git a/procfs_pid.h b/procfs_pid.h new file mode 100644 index 00000000..811cca84 --- /dev/null +++ b/procfs_pid.h @@ -0,0 +1,86 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + procfs_pid.h -- This is the header file of which contains defintions + for structure of directory with PID as the name and + structure of each file in this directory. + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. +*/ + +#ifndef __PROCFS_PID_H__ +#define __PROCFS_PID_H__ + +struct procfs_pid_files +{ + struct procfs_cwd *procfs_cwd; + struct procfs_environ *procfs_environ; + struct procfs_cpu *procfs_cpu; + struct procfs_root *procfs_root; + struct procfs_exe *procfs_exe; + struct procfs_stat *_procfs_stat; + struct procfs_statm *procfs_statm; +}; + +struct procfs_stat +{ + pid_t pid; + char *comm; + char *state; + pid_t ppid; + pid_t pgid; + pid_t sid; + int tty_nr; + pid_t tty_pgrp; + unsigned flags; + long unsigned minflt; + long unsigned cminflt; + long unsigned majflt; + long unsigned cmajflt; + time_t utime; + time_t stime; + time_t cutime; + time_t cstime; + long priority; + long nice; + long num_threads; + long itrealvalue; + long long unsigned starttime; + long unsigned vsize; + long rss; + long unsigned rlim; + long unsigned startcode; + long unsigned endcode; + long unsigned startstack; + long unsigned kstkesp; + long unsigned kstkeip; + long unsigned signal; + long unsigned blocked; + long unsigned sigignore; + long unsigned sigcatch; + long unsigned wchan; + long unsigned nswap; + long unsigned cnswap; + int exit_signal; + int processor; + unsigned rt_priority; + unsigned policy; + long long unsigned delayacct_blkio_ticks; +}; + +#endif diff --git a/procfs_pid_files.c b/procfs_pid_files.c new file mode 100644 index 00000000..1012510b --- /dev/null +++ b/procfs_pid_files.c @@ -0,0 +1,570 @@ +/* procfs -- a translator for providing GNU/Linux compatible + proc pseudo-filesystem + + procfs_pid_files.c -- This file contains definitions to perform + file operations such as creating, writing to, + reading from and removing files that holds + information for each process with PID + + Copyright (C) 2008, FSF. + Written as a Summer of Code Project + + + procfs is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + procfs is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + + A portion of the code in this file is based on ftpfs code + present in the hurd repositories copyrighted to FSF. The + Copyright notice from that file is given below. + +*/ + +#include +#include +#include +#include +#include +#include + +#include "procfs.h" +#include "procfs_pid.h" + +/* Update the files named NAME within the directory named + PID also with SYMLINK TARGET if necessary. */ +struct procfs_dir_entry* +update_pid_entries (struct procfs_dir *dir, const char *name, + time_t timestamp, + const char *symlink_target) +{ + struct procfs_dir_entry *dir_entry; + struct stat *stat = (struct stat *) malloc (sizeof (struct stat)); + stat->st_mode = S_IFREG; + + dir_entry = update_entries_list (dir, name, stat, + timestamp, symlink_target); + + return dir_entry; +} + +/* Creates files to store process information for DIR + whose names are pids and returns these files in *NODE. */ +error_t +procfs_create_files (struct procfs_dir *dir, + struct node **node, + time_t timestamp) +{ + int err; + char *file_name, *file_path; + struct procfs_dir_entry *dir_entry; + + if (asprintf (&file_name, "%s", "stat") == -1) + return errno; + if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "stat") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + + if (asprintf (&file_name, "%s", "status") == -1) + return errno; + if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "status") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + + if (asprintf (&file_name, "%s", "cmdline") == -1) + return errno; + if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "cmdline") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + + if (asprintf (&file_name, "%s", "statm") == -1) + return errno; + if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "statm") == -1) + return errno; + + dir_entry = update_pid_entries (dir, file_name, timestamp, NULL); + err = procfs_create_node (dir_entry, file_path, node); + + free (file_name); + free (file_path); + +#if 0 + nodes_list = &node_stat; + nodes_list++; + node = nodes_list; +#endif + + return err; +} + +/* Check if the PSTAT_FLAG is set in the corresponding PS + structure, if not set it and check again and return error + status accordingly. */ +error_t set_field_value (struct proc_stat *ps, int pstat_flag) +{ + error_t err; + + if (! (ps->flags & pstat_flag)) + { + err = proc_stat_set_flags (ps, pstat_flag); + if (err) + return err; + + /* This second check is done since ps.h specifies to + do so since the previous call would not have set + the required value. */ + if (! (ps->flags & pstat_flag)) + return EGRATUITOUS; + } + + return 0; +} + +/* Adjusts TIME_VAL structure having Seconds and + Microseconds into the value in jiffies. The + value of jiffy is a hack to adjust to what + procps uses. */ +time_t adjust_jiffy_time (time_value_t time_val) +{ + time_t jiffy_time = time_val.seconds * JIFFY_ADJUST; + jiffy_time += (time_val.microseconds * JIFFY_ADJUST) + / (1000 * 1000); + + return jiffy_time; +} + +/* Extract the user and system time for the live threads of + the process. This information is directly retrieved from + MACH since neither libps not proc makes this available. */ +error_t get_task_thread_times (task_t task, + struct task_thread_times_info *live_threads_times) +{ + error_t err; + size_t tkcount = TASK_THREAD_TIMES_INFO_COUNT; + + err = task_info (task, TASK_THREAD_TIMES_INFO, + (task_info_t) live_threads_times, &tkcount); + if (err == MACH_SEND_INVALID_DEST) + err = ESRCH; + + return err; +} + +/* Obtains the User Time in UTIME and System Time in STIME from + MACH directly since this is neither made available by libps + nor by proc server. */ +error_t get_live_threads_time (struct proc_stat *ps, + time_t *utime, time_t *stime) +{ + struct task_thread_times_info live_threads_times; + error_t err = set_field_value (ps, PSTAT_TASK); + + if (! err) + { + err = get_task_thread_times (ps->task, &live_threads_times); + if (! err) + { + *utime = adjust_jiffy_time ( + live_threads_times.user_time); + *stime = adjust_jiffy_time ( + live_threads_times.system_time); + } + } + + return err; +} + +/* Get the data for stat file into the structure + PROCFS_STAT. */ +error_t get_stat_data (pid_t pid, + struct procfs_stat **procfs_stat) +{ + error_t err; + struct procfs_stat *new = (struct procfs_stat *) + malloc (sizeof (struct procfs_stat)); + + struct proc_stat *ps; + time_t utime, stime; + + err = _proc_stat_create (pid, ps_context, &ps); + + new->pid = pid; + + if (! err) + { + err = set_field_value (ps, PSTAT_ARGS); + if (! err) + asprintf (&new->comm, "%s", ps->args); + } + + err = set_field_value (ps, PSTAT_STATE); + if (! err) + { + if (ps->state & PSTAT_STATE_P_STOP) + new->state = strdup ("T"); + if (ps->state & PSTAT_STATE_P_ZOMBIE) + new->state = strdup ("Z"); + if (ps->state & PSTAT_STATE_P_FG) + new->state = strdup ("+"); + if (ps->state & PSTAT_STATE_P_SESSLDR) + new->state = strdup ("s"); + if (ps->state & PSTAT_STATE_P_LOGINLDR) + new->state = strdup ("l"); + if (ps->state & PSTAT_STATE_P_FORKED) + new->state = strdup ("f"); + if (ps->state & PSTAT_STATE_P_NOMSG) + new->state = strdup ("m"); + if (ps->state & PSTAT_STATE_P_NOPARENT) + new->state = strdup ("p"); + if (ps->state & PSTAT_STATE_P_ORPHAN) + new->state = strdup ("o"); + if (ps->state & PSTAT_STATE_P_TRACE) + new->state = strdup ("x"); + if (ps->state & PSTAT_STATE_P_WAIT) + new->state = strdup ("w"); + if (ps->state & PSTAT_STATE_P_GETMSG) + new->state = strdup ("g"); + } + + err = set_field_value (ps, PSTAT_PROC_INFO); + if (! err) + { + new->ppid = ps->proc_info->ppid; + new->pgid = ps->proc_info->pgrp; + new->sid = ps->proc_info->session; + new->tty_pgrp = ps->proc_info->pgrp; + } + else + { + new->ppid = 0; + new->pgid = 0; + new->sid = 0; + new->tty_pgrp = 0; + } + + err = set_field_value (ps, PSTAT_STATE); + if (! err) + new->flags = ps->state; + else + new->flags = 0; + + err = set_field_value (ps, PSTAT_TASK_EVENTS); + if (! err) + { + new->minflt = ps->task_events_info->faults; + new->majflt = ps->task_events_info->pageins; + } + else + { + new->minflt = 0; + new->majflt = 0; + } + + /* This seems to be a bit inconsistent with setting of other + fields in this code. There are two reasons for this. + 1. The actual information required is not made available + by libps which should be directly obtained from MACH. + 2. The same code which is required to get the information + have to be reused in procfs_nonpid_files.c */ + err = get_live_threads_time (ps, &utime, &stime); + if (! err) + { + new->utime = utime; + new->stime = stime; + } + else + { + new->utime = 0; + new->stime = 0; + } + + err = set_field_value (ps, PSTAT_TASK_BASIC); + if (! err) + { + new->cutime = adjust_jiffy_time ( + ps->task_basic_info->user_time); + new->cstime = adjust_jiffy_time ( + ps->task_basic_info->system_time); + + new->priority = ps->task_basic_info->base_priority; + new->starttime = adjust_jiffy_time ( + ps->task_basic_info->creation_time); + + new->vsize = ps->task_basic_info->virtual_size; + new->rss = ps->task_basic_info->resident_size; + } + else + { + new->cutime = 0; + new->cstime = 0; + new->priority = 0; + new->starttime = 0; + new->vsize = 0; + new->rss = 0; + } + + new->nice = getpriority (0, pid); + + err = set_field_value (ps, PSTAT_NUM_THREADS); + if (! err) + new->num_threads = ps->num_threads; + else + new->num_threads = 0; + + /* Not Supported in Linux 2.6 or later. */ + new->tty_nr = 0; + new->itrealvalue = 0; + new->nswap = 0; + new->cnswap = 0; + + /* Temporarily set to 0 until correct + values are found .*/ + new->cminflt = 0; + new->cmajflt = 0; + new->rlim = 0; + new->startcode = 0; + new->endcode = 0; + new->startstack = 0; + new->kstkesp = 0; + new->kstkeip = 0; + new->signal = 0; + new->blocked = 0; + new->sigignore = 0; + new->sigcatch = 0; + new->wchan = 0; + new->exit_signal = 0; + new->processor = 0; + new->rt_priority = 0; + new->policy = 0; + new->delayacct_blkio_ticks = 0; + + *procfs_stat = new; + _proc_stat_free (ps); + + return err; +} + +/* Writes required process information to stat file + within the directory represented by pid. Return + the data in DATA and actual length to be written + in LEN. */ +error_t +procfs_write_stat_file (struct procfs_dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + error_t err; + char *stat_data; + struct procfs_stat *procfs_stat; + pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name); + + err = get_stat_data (pid, &procfs_stat); + + if (asprintf (&stat_data, "%d (%s) %s %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %llu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu \n", + procfs_stat->pid, procfs_stat->comm, + procfs_stat->state, procfs_stat->ppid, + procfs_stat->pgid, procfs_stat->sid, + procfs_stat->tty_nr, procfs_stat->tty_pgrp, + procfs_stat->flags, procfs_stat->minflt, + procfs_stat->cminflt, procfs_stat->majflt, + procfs_stat->cmajflt, procfs_stat->utime, + procfs_stat->stime, procfs_stat->cutime, + procfs_stat->cstime, procfs_stat->priority, + procfs_stat->nice, procfs_stat->num_threads, + procfs_stat->itrealvalue, procfs_stat->starttime, + procfs_stat->vsize, BYTES_TO_PAGES(procfs_stat->rss), + procfs_stat->rlim, procfs_stat->startcode, + procfs_stat->endcode, procfs_stat->startstack, + procfs_stat->kstkesp, procfs_stat->kstkeip, + procfs_stat->signal, procfs_stat->blocked, + procfs_stat->sigignore, procfs_stat->sigcatch, + procfs_stat->wchan, procfs_stat->nswap, + procfs_stat->cnswap, procfs_stat->exit_signal, + procfs_stat->processor, procfs_stat->rt_priority, + procfs_stat->policy, + procfs_stat->delayacct_blkio_ticks) == -1) + return errno; + + + memcpy (data, stat_data, strlen(stat_data)); + *len = strlen (data); + + free (stat_data); + free (procfs_stat); + + return err; +} + +/* Writes required process's command line information + to cmline file within the directory represented by + pid. Return the data in DATA and actual length to + be written in LEN. */ +error_t +procfs_write_cmdline_file (struct procfs_dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + char *cmdline_data; + error_t err; + struct proc_stat *ps; + pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name); + err = _proc_stat_create (pid, ps_context, &ps); + + err = set_field_value (ps, PSTAT_ARGS); + + if (! err) + if (asprintf (&cmdline_data, "%s \n", ps->args) == -1) + return errno; + + memcpy (data, cmdline_data, strlen(cmdline_data)); + *len = strlen (data); + + _proc_stat_free (ps); + free (cmdline_data); + return err; +} + +/* Writes required process's information that is represented by + stat and statm in a human readable format to status file + within the directory represented by pid. Return the data + in DATA and actual length to be written in LEN. */ +error_t +procfs_write_status_file (struct procfs_dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + char *status_data; + error_t err; + struct proc_stat *ps; + struct procfs_stat *procfs_stat; + + pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name); + err = _proc_stat_create (pid, ps_context, &ps); + + err = get_stat_data (pid, &procfs_stat); + + if (! err) + if (asprintf (&status_data, "Name:\t%s\nState:\t%s\nTgid:\t%d\nPid:\t%d\n", procfs_stat->comm, procfs_stat->state, procfs_stat->pid, procfs_stat->pid) == -1) + return errno; + + memcpy (data, status_data, strlen(status_data)); + *len = strlen (data); + + _proc_stat_free (ps); + + free (status_data); + free (procfs_stat); + + return err; +} + +/* Writes required process information to statm file + within the directory represented by pid. Return + the data in DATA and actual length to be written + in LEN. */ +error_t +procfs_write_statm_file (struct procfs_dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + char *statm_data; + error_t err; + struct proc_stat *ps; + struct procfs_stat *procfs_stat; + + pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name); + err = _proc_stat_create (pid, ps_context, &ps); + + err = get_stat_data (pid, &procfs_stat); + + if (! err) + if (asprintf (&statm_data, "%lu %ld %d %d %d %d %d\n", + BYTES_TO_PAGES(procfs_stat->vsize), + BYTES_TO_PAGES(procfs_stat->rss), + 0, 0, 0, 0, 0) == -1) + return errno; + + memcpy (data, statm_data, strlen(statm_data)); + *len = strlen (data); + + _proc_stat_free (ps); + + free (statm_data); + free (procfs_stat); + + return err; +} + +/* Writes required process information to each of files + within directory represented by pid, for files specified + by NODE. Return the data in DATA and actual length of + data in LEN. */ +error_t +procfs_write_files_contents (struct node *node, + off_t offset, size_t *len, void *data) +{ + error_t err; + + if (! strcmp (node->nn->dir_entry->name, "stat")) + if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) + err = procfs_write_nonpid_stat (node->nn->dir_entry, + offset, len, data); + else + err = procfs_write_stat_file (node->nn->dir_entry, + offset, len, data); + + if (! strcmp (node->nn->dir_entry->name, "cmdline")) + err = procfs_write_cmdline_file (node->nn->dir_entry, + offset, len, data); + + if (! strcmp (node->nn->dir_entry->name, "status")) + err = procfs_write_status_file (node->nn->dir_entry, + offset, len, data); + + if (! strcmp (node->nn->dir_entry->name, "statm")) + err = procfs_write_statm_file (node->nn->dir_entry, + offset, len, data); + + if (! strcmp (node->nn->dir_entry->name, "meminfo")) + if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) + err = procfs_write_nonpid_meminfo (node->nn->dir_entry, + offset, len, data); + else + err = ENOENT; + + if (! strcmp (node->nn->dir_entry->name, "loadavg")) + if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) + err = procfs_write_nonpid_loadavg (node->nn->dir_entry, + offset, len, data); + else + err = ENOENT; + + if (! strcmp (node->nn->dir_entry->name, "uptime")) + if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) + err = procfs_write_nonpid_uptime (node->nn->dir_entry, + offset, len, data); + else + err = ENOENT; + + return err; +} -- cgit v1.2.3 From 802ea16ef91b30bc4fa9a61779b5c3602219bb1d Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Mon, 18 Aug 2008 16:53:00 +0200 Subject: 2008-08-18 Madhusudan.C.S * procfs/ChangeLog: ChangeLog: Revised. --- ChangeLog | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 36f6bf5c..eb175529 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,20 +1,20 @@ 2008-08-14 Madhusudan.C.S - * ChangeLog: New file added to procfs - * AUTHORS: New file added to procfs - * COPYING: New file added to procfs - * README: New file added to procfs - * Makefile: New file added to procfs - * bootstrap.c: New file added to procfs - * netfs.c: New file added to procfs - * node.c: New file added to procfs - * procfs.c: New file added to procfs - * procfs.h: New file added to procfs - * procfs_dir.c: New file added to procfs - * procfs_nonpid_files.c: New file added to procfs - * procfs_pid.h: New file added to procfs - * procfs_pid_files.c: New file added to procfs + * ChangeLog: New file. + * AUTHORS: New file. + * COPYING: New file. + * README: New file. + * Makefile: New file. + * bootstrap.c: New file. + * netfs.c: New file. + * node.c: New file. + * procfs.c: New file. + * procfs.h: New file. + * procfs_dir.c: New file. + * procfs_nonpid_files.c: New file. + * procfs_pid.h: New file. + * procfs_pid_files.c: New file. 2008-05-13 Madhusudan.C.S - * /sources/hurd/procfs: New directory added to the repository + * /sources/hurd/procfs: New directory added to the repository. -- cgit v1.2.3 From 0419c2813a91d0746cc70f4ecab33efca4b9440c Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Mon, 18 Aug 2008 17:15:00 +0200 Subject: 2008-08-14 Madhusudan.C.S * procfs.h: (jiffy_t): New typedef. * procfs_pid.h: "procfs.h" is included. (struct procfs_pid_files): Changed all the occurrences of time_t to jiffy_t. * procfs_pid_files.c: Removed "procfs.h". (adjust_jiffy_time): Changed return type from time_t to jiffy_t. Changed the type of jiffy_time variable from time_t to jiffy_t. (get_live_threads_time): Changed the type of utime and stime from time_t to jiffy_t. (get_stat_data): Changed the type of utime and stime from time_t to jiffy_t. --- ChangeLog | 15 +++++++++++++++ procfs.h | 2 ++ procfs_pid.h | 10 ++++++---- procfs_pid_files.c | 9 ++++----- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index eb175529..7cf16618 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2008-08-14 Madhusudan.C.S + * procfs.h: (jiffy_t): New typedef. + * procfs_pid.h: "procfs.h" is included. + (struct procfs_pid_files): Changed all the + occurrences of time_t to jiffy_t. + * procfs_pid_files.c: Removed "procfs.h". + (adjust_jiffy_time): Changed return type + from time_t to jiffy_t. + Changed the type of jiffy_time variable + from time_t to jiffy_t. + (get_live_threads_time): Changed the type + of utime and stime from time_t to jiffy_t. + (get_stat_data): Changed the type of utime + and stime from time_t to jiffy_t. + 2008-08-14 Madhusudan.C.S * ChangeLog: New file. diff --git a/procfs.h b/procfs.h index 95d4ee35..fa2fb7f7 100644 --- a/procfs.h +++ b/procfs.h @@ -53,6 +53,8 @@ #include #include +typedef unsigned long long jiffy_t; + /* A single entry in a directory. */ struct procfs_dir_entry { diff --git a/procfs_pid.h b/procfs_pid.h index 811cca84..566c83ea 100644 --- a/procfs_pid.h +++ b/procfs_pid.h @@ -26,6 +26,8 @@ #ifndef __PROCFS_PID_H__ #define __PROCFS_PID_H__ +#include "procfs.h" + struct procfs_pid_files { struct procfs_cwd *procfs_cwd; @@ -52,10 +54,10 @@ struct procfs_stat long unsigned cminflt; long unsigned majflt; long unsigned cmajflt; - time_t utime; - time_t stime; - time_t cutime; - time_t cstime; + jiffy_t utime; + jiffy_t stime; + jiffy_t cutime; + jiffy_t cstime; long priority; long nice; long num_threads; diff --git a/procfs_pid_files.c b/procfs_pid_files.c index 1012510b..55cc52c1 100644 --- a/procfs_pid_files.c +++ b/procfs_pid_files.c @@ -37,7 +37,6 @@ #include #include -#include "procfs.h" #include "procfs_pid.h" /* Update the files named NAME within the directory named @@ -148,9 +147,9 @@ error_t set_field_value (struct proc_stat *ps, int pstat_flag) Microseconds into the value in jiffies. The value of jiffy is a hack to adjust to what procps uses. */ -time_t adjust_jiffy_time (time_value_t time_val) +jiffy_t adjust_jiffy_time (time_value_t time_val) { - time_t jiffy_time = time_val.seconds * JIFFY_ADJUST; + jiffy_t jiffy_time = time_val.seconds * JIFFY_ADJUST; jiffy_time += (time_val.microseconds * JIFFY_ADJUST) / (1000 * 1000); @@ -178,7 +177,7 @@ error_t get_task_thread_times (task_t task, MACH directly since this is neither made available by libps nor by proc server. */ error_t get_live_threads_time (struct proc_stat *ps, - time_t *utime, time_t *stime) + jiffy_t *utime, jiffy_t *stime) { struct task_thread_times_info live_threads_times; error_t err = set_field_value (ps, PSTAT_TASK); @@ -208,7 +207,7 @@ error_t get_stat_data (pid_t pid, malloc (sizeof (struct procfs_stat)); struct proc_stat *ps; - time_t utime, stime; + jiffy_t utime, stime; err = _proc_stat_create (pid, ps_context, &ps); -- cgit v1.2.3 From a027dcc41d381a3c76a5b20d4d55ad9821af2308 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Mon, 18 Aug 2008 17:27:00 +0200 Subject: 2008-08-18 Madhusudan.C.S * procfs_nonpid_files.c: (procfs_write_nonpid_version): New function. * procfs_pid_files.c: (procfs_write_files_contents): Add a check to find if the read is requested for the version file and corresponding a call to it. --- ChangeLog | 8 ++++++++ procfs_nonpid_files.c | 18 +++++++++++++++++- procfs_pid_files.c | 7 +++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 7cf16618..6079292d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2008-08-18 Madhusudan.C.S + * procfs_nonpid_files.c: + (procfs_write_nonpid_version): New function. + * procfs_pid_files.c: + (procfs_write_files_contents): Add a check + to find if the read is requested for the + version file and corresponding a call to it. + 2008-08-14 Madhusudan.C.S * procfs.h: (jiffy_t): New typedef. * procfs_pid.h: "procfs.h" is included. diff --git a/procfs_nonpid_files.c b/procfs_nonpid_files.c index 104ae8b3..ab1341ff 100644 --- a/procfs_nonpid_files.c +++ b/procfs_nonpid_files.c @@ -269,7 +269,7 @@ error_t procfs_write_nonpid_stat (struct dir_entry *dir_entry, idle_time_secs = uptime_secs - total_system_time_secs; - /* If the values are multiplied by 100, it iss done so to adjust + /* If the values are multiplied by 100, it is done so to adjust values in seconds to jiffies. */ if (asprintf (&stat_data, "cpu %ld %ld %ld %ld %ld %ld %d %d %d\n" "cpu0 %ld %ld %ld %ld %ld %ld %d %d %d\n" @@ -460,3 +460,19 @@ error_t procfs_write_nonpid_uptime (struct dir_entry *dir_entry, free (uptime_data); return err; } + +error_t procfs_write_nonpid_version (struct dir_entry *dir_entry, + off_t offset, size_t *len, void *data) +{ + char *version_data; + error_t err = 0; + + if (asprintf (&version_data, "Linux version 2.6.18\n", NULL) == -1) + return errno; + + memcpy (data, version_data, strlen(version_data)); + *len = strlen (data); + + free (version_data); + return err; +} diff --git a/procfs_pid_files.c b/procfs_pid_files.c index 55cc52c1..3ad77340 100644 --- a/procfs_pid_files.c +++ b/procfs_pid_files.c @@ -565,5 +565,12 @@ procfs_write_files_contents (struct node *node, else err = ENOENT; + if (! strcmp (node->nn->dir_entry->name, "version")) + if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) + err = procfs_write_nonpid_version (node->nn->dir_entry, + offset, len, data); + else + err = ENOENT; + return err; } -- cgit v1.2.3 From 5bb07a3cb626b18aedfa463b6b344bf0fd963e71 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Mon, 18 Aug 2008 19:34:00 +0200 Subject: 2008-08-18 Madhusudan.C.S * procfs_nonpid_files.c: (get_uptime): Changed the parameter type from double to struct timeval. Changed the parameter name from uptime_secs to uptime. Removed uptime variable. Changed timersub to use the passed pointer instead of the local variable. Removed the calculation of uptime_secs. (get_total_times): Changed the parameters type from double to struct timeval. Changed the parameters name from total_user_time_secs to total_user_time and total_system_time_secs to total_system_time. New variables total_user_time_tmp, total_system_time_tmp and tmpval of type struct timeval. Call timerclear to clear the tmp variables. Remove calculation of times in seconds and do the same on struct timeval variables throughout using the timeradd macro. Assign values of temporary local variables to the pointers passed as parameters. (procfs_write_nonpid_stat): Replaced variables that hold time in seconds with struct timeval type variables and jiffy_t type variables. Argument to get_uptime changed from uptime_secs to uptime. Arguments to get_total_times changed from total_user_time_secs to total_user_time and total_system_time_secs to total_system_time. Replace arithematic time subtraction with timersub macro. Convert all the times in struct timeval type variables to jiffy_t type. Changed the type casting for the asprintf arguments to be compatible with jiffy_t type. (procfs_write_nonpid_uptime): Replaced variables that hold time in seconds with struct timeval type variables. Argument to get_uptime changed from uptime_secs to uptime. Arguments to get_total_times changed from total_user_time_secs to total_user_time and total_system_time_secs to total_system_time. Replace arithematic time subtraction with timersub macro. Convert all the times in struct timeval type variables to seconds. --- ChangeLog | 53 +++++++++++++++++++ procfs_nonpid_files.c | 138 +++++++++++++++++++++++++++++++------------------- 2 files changed, 140 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6079292d..2e027f58 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,56 @@ +2008-08-18 Madhusudan.C.S + * procfs_nonpid_files.c: (get_uptime): + Changed the parameter type from double to + struct timeval. + Changed the parameter name from uptime_secs + to uptime. + Removed uptime variable. + Changed timersub to use the passed pointer + instead of the local variable. + Removed the calculation of uptime_secs. + (get_total_times): Changed the parameters + type from double to struct timeval. + Changed the parameters name from + total_user_time_secs to total_user_time + and total_system_time_secs to + total_system_time. + New variables total_user_time_tmp, + total_system_time_tmp and tmpval of type + struct timeval. + Call timerclear to clear the tmp variables. + Remove calculation of times in seconds and + do the same on struct timeval variables + throughout using the timeradd macro. + Assign values of temporary local variables + to the pointers passed as parameters. + (procfs_write_nonpid_stat): Replaced + variables that hold time in seconds with + struct timeval type variables and jiffy_t + type variables. + Argument to get_uptime changed from + uptime_secs to uptime. + Arguments to get_total_times changed from + total_user_time_secs to total_user_time and + total_system_time_secs to total_system_time. + Replace arithematic time subtraction with + timersub macro. + Convert all the times in struct timeval type + variables to jiffy_t type. + Changed the type casting for the asprintf + arguments to be compatible with jiffy_t type. + (procfs_write_nonpid_uptime): Replaced + variables that hold time in seconds with + struct timeval type variables. + Argument to get_uptime changed from + uptime_secs to uptime. + Arguments to get_total_times changed from + total_user_time_secs to total_user_time and + total_system_time_secs to total_system_time. + Replace arithematic time subtraction with + timersub macro. + Convert all the times in struct timeval type + variables to seconds. + 2008-08-18 Madhusudan.C.S * procfs_nonpid_files.c: (procfs_write_nonpid_version): New function. diff --git a/procfs_nonpid_files.c b/procfs_nonpid_files.c index ab1341ff..9cb0c57a 100644 --- a/procfs_nonpid_files.c +++ b/procfs_nonpid_files.c @@ -166,9 +166,9 @@ error_t procfs_create_loadavg (struct procfs_dir *dir, return err; } -error_t get_uptime (double *uptime_secs) +error_t get_uptime (struct timeval *uptime) { - struct timeval boot_time, uptime, now; + struct timeval boot_time, now; error_t err; struct proc_stat *ps; @@ -188,15 +188,15 @@ error_t get_uptime (double *uptime_secs) boot_time.tv_usec = tv->microseconds; if (gettimeofday (&now, 0) < 0) error (0, errno, "gettimeofday"); - timersub (&now, &boot_time, &uptime); - *uptime_secs = (double)uptime.tv_sec + ((double)uptime.tv_usec / 1000000); + timersub (&now, &boot_time, uptime); } _proc_stat_free (ps); return err; } -error_t get_total_times (double *total_user_time_secs , double *total_system_time_secs ) +error_t get_total_times (struct timeval *total_user_time, + struct timeval *total_system_time) { error_t err; pid_t *pids; @@ -204,8 +204,12 @@ error_t get_total_times (double *total_user_time_secs , double *total_system_tim struct proc_stat *ps; struct task_thread_times_info live_threads_times; - *total_user_time_secs = 0; - *total_system_time_secs = 0; + struct timeval total_user_time_tmp; + struct timeval total_system_time_tmp; + struct timeval tmpval; + + timerclear (&total_user_time_tmp); + timerclear (&total_system_time_tmp); pids = NULL; err = proc_getallpids (getproc (), &pids, &pidslen); @@ -222,33 +226,40 @@ error_t get_total_times (double *total_user_time_secs , double *total_system_tim err = EGRATUITOUS; if (! err) - { - *total_user_time_secs += - ((double) (proc_stat_task_basic_info (ps)->user_time.seconds)) + - (((double) (proc_stat_task_basic_info (ps)->user_time.microseconds)) / - (1000 * 1000)); + { + tmpval.tv_sec = proc_stat_task_basic_info (ps)->user_time.seconds; + tmpval.tv_usec = proc_stat_task_basic_info (ps)->user_time.seconds; + timeradd (&total_user_time_tmp, &tmpval, &total_user_time_tmp); + + tmpval.tv_sec = proc_stat_task_basic_info (ps)->system_time.seconds; + tmpval.tv_usec = proc_stat_task_basic_info (ps)->system_time.seconds; + timeradd (&total_system_time_tmp, &tmpval, &total_system_time_tmp); - *total_system_time_secs += - ((double) (proc_stat_task_basic_info (ps)->system_time.seconds)) + - (((double) (proc_stat_task_basic_info (ps)->system_time.microseconds)) / (1000 * 1000)); - error_t err = set_field_value (ps, PSTAT_TASK); if (! err) { err = get_task_thread_times (ps->task, &live_threads_times); if (! err) { - *total_user_time_secs += ((double) (live_threads_times.user_time.seconds)) + - (((double) (live_threads_times.user_time.microseconds)) / (1000 * 1000)); - *total_system_time_secs += ((double) (live_threads_times.system_time.seconds)) + - (((double) (live_threads_times.system_time.microseconds)) / (1000 * 1000)); - + tmpval.tv_sec = live_threads_times.user_time.seconds; + tmpval.tv_usec = live_threads_times.user_time.microseconds; + timeradd (&total_user_time_tmp, &tmpval, &total_user_time_tmp); + + tmpval.tv_sec = live_threads_times.system_time.seconds; + tmpval.tv_usec = live_threads_times.system_time.microseconds; + timeradd (&total_system_time_tmp, &tmpval, &total_system_time_tmp); } } } _proc_stat_free (ps); } - + + total_user_time->tv_sec = total_user_time_tmp.tv_sec; + total_user_time->tv_usec = total_user_time_tmp.tv_usec; + + total_system_time->tv_sec = total_system_time_tmp.tv_sec; + total_system_time->tv_usec = total_system_time_tmp.tv_usec; + return err; } @@ -257,30 +268,45 @@ error_t procfs_write_nonpid_stat (struct dir_entry *dir_entry, { char *stat_data; error_t err; - double uptime_secs, total_user_time_secs; - double total_system_time_secs, idle_time_secs; + jiffy_t total_user_time_jiffy, total_system_time_jiffy; + jiffy_t idle_time_jiffy; + struct timeval uptime, total_user_time, total_system_time; + struct timeval idle_time; - err = get_uptime (&uptime_secs); + err = get_uptime (&uptime); + if (! err) { - err = get_total_times (&total_user_time_secs, &total_system_time_secs); + err = get_total_times (&total_user_time, &total_system_time); + if (! err) { - idle_time_secs = uptime_secs - - total_system_time_secs; - - /* If the values are multiplied by 100, it is done so to adjust - values in seconds to jiffies. */ - if (asprintf (&stat_data, "cpu %ld %ld %ld %ld %ld %ld %d %d %d\n" - "cpu0 %ld %ld %ld %ld %ld %ld %d %d %d\n" - "intr %ld %ld %ld %ld %ld %ld %d %d %d\n", - (long)(total_user_time_secs * 100), 0, - (long)(total_system_time_secs * 100), - (long) (idle_time_secs * 100), 0, 0, 0, 0, - 0, (long)(total_user_time_secs * 100), 0, - (long)(total_system_time_secs * 100), - (long)(idle_time_secs * 100), 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == -1) + timersub (&uptime, &total_system_time, + &idle_time); + + total_user_time_jiffy = 100 * ((double) total_user_time.tv_sec + + (double) total_user_time.tv_usec / (1000 * 1000)); + total_system_time_jiffy = 100 * ((double) total_system_time.tv_sec + + (double) total_system_time.tv_usec / (1000 * 1000)); + idle_time_jiffy = 100 * ((double) idle_time.tv_sec + + (double) idle_time.tv_usec / (1000 * 1000)); + + if (asprintf (&stat_data, "cpu %llu %llu %llu %llu %llu %llu %d %d %d\n" + "cpu0 %llu %llu %llu %llu %llu %llu %d %d %d\n" + "intr %llu %llu %llu %llu %llu %llu %d %d %d\n", + total_user_time_jiffy, (long long unsigned) 0, + total_system_time_jiffy, idle_time_jiffy, + (long long unsigned) 0, (long long unsigned) 0, + 0, 0, 0, + total_user_time_jiffy, (long long unsigned) 0, + total_system_time_jiffy, idle_time_jiffy, + (long long unsigned) 0, (long long unsigned) 0, + 0, 0, 0, + (long long unsigned) 0, + (long long unsigned) 0, (long long unsigned) 0, (long long unsigned) 0, + (long long unsigned) 0, + (long long unsigned) 0, (long long unsigned) 0, + (long long unsigned) 0, (long long unsigned) 0) == -1) return errno; } } @@ -434,23 +460,33 @@ error_t procfs_write_nonpid_uptime (struct dir_entry *dir_entry, { char *uptime_data; error_t err; - double uptime_secs, total_user_time_secs; - double total_system_time_secs, idle_time_secs; + double uptime_secs, idle_time_secs; + + struct timeval uptime_val; + struct timeval uptime, total_user_time, total_system_time; + struct timeval idle_time; + - err = get_uptime (&uptime_secs); + err = get_uptime (&uptime); if (! err) { - err = get_total_times (&total_user_time_secs, - &total_system_time_secs); + err = get_total_times (&total_user_time, + &total_system_time); if (! err) { - idle_time_secs = uptime_secs - - total_system_time_secs; - - if (asprintf (&uptime_data, "%.2f %.2f \n", + timersub (&uptime, &total_system_time, + &idle_time); + + uptime_secs = (double) uptime.tv_sec + + (double) uptime.tv_usec / (1000 * 1000); + + idle_time_secs = (double) idle_time.tv_sec + + (double) idle_time.tv_usec / (1000 * 1000); + + if (asprintf (&uptime_data, "%.2f %.2f\n", uptime_secs, idle_time_secs) == -1) return errno; - } + } } -- cgit v1.2.3 From 151d56afe0d7d9eb527fb0a194f383435b2c0f65 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Mon, 18 Aug 2008 19:45:00 +0200 Subject: 2008-08-18 Madhusudan.C.S * README: Initial Documentation. --- ChangeLog | 3 +++ README | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/ChangeLog b/ChangeLog index 2e027f58..e5bb2414 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2008-08-18 Madhusudan.C.S + * README: Initial Documentation. + 2008-08-18 Madhusudan.C.S * procfs_nonpid_files.c: (get_uptime): Changed the parameter type from double to diff --git a/README b/README index e69de29b..28b4aa3e 100644 --- a/README +++ b/README @@ -0,0 +1,77 @@ +~~~~~~~~~~~~~~~ +1. Introduction +~~~~~~~~~~~~~~~ + + This program is called procfs, the /proc translator. This +program provides a GNU/Linux compatible /proc pseudo file- +system on GNU Hurd. This is called a translator since it +translates the process related information stored in the +MACH Microkernel which is made available through the proc +server and the libps library into a virtual filesystem. + +~~~~~~~ +2. Goal +~~~~~~~ + + The major goal of writing this translator was to make the +process related tools like pgrep, pkill, kill which are +packaged in procps, killall, pstree which are packaged in +psmisc and various other process related tools that rely +on GNU/Linux's /proc filesystem to run out of the box on +Hurd. + +~~~~~~~~~~ +3. Install +~~~~~~~~~~ + + To install this translator you can simply do the following. +Just cd to the top level directory of the hurd main source +tree. Run the configure script with the following command. +(In BASH) + +$ ./configure + +(If your shell is something else run the equivalent command). + +Now you can run make with procfs as a parameter if you want +to build only procfs. + +$ make procfs + +(This first builds all the dependencies of procfs and then +builds procfs as a part of Hurd main source tree.) + +After building the translator, you get a binary named procfs. +You need to set it as a translator. To do so you can type the +following command from the top level directory of Hurd main +source tree directory from where you ran make. + +$ settrans -fgap /proc procfs/procfs + +Viola! You are done setting up the translator. + +~~~~~~~~~~ +4. Testing +~~~~~~~~~~ + + Now in case you want to use the debian hurd binaries to work +with tools like pgrep, pkill, kill, htop etc, you can just +download the binaries which I have uploaded here: + +http://madhusudancs.info/procfs-testing-how-to-mini + +The patches to the source packages will be made available soon. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5. Reporting Bugs and contacts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + I request all of you to help me in identifying the bugs in +procfs or in the procps or htop packages, so that I can fix +them. You are also free to submit patches if you feel so. The +patches can be sent either to bug-hurd mailing list or to my +e-mail adress. The e-mail IDs are as follows: + +bug-hurd@gnu.org +madhusudancs@gmail.com +madhusudan@madhusudancs.info -- cgit v1.2.3 From 4cce52a1ec161008243f5bee63df7f6230c0f7e6 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Mon, 18 Aug 2008 20:26:00 +0200 Subject: 2008-08-18 Madhusudan.C.S * procfs_nonpid_files.c: (procfs_write_nonpid_stat): Changed to procfs_read_nonpid_stat. (procfs_write_nonpid_meminfo): Changed to procfs_read_nonpid_meminfo. (procfs_write_nonpid_loadavg): Changed to procfs_read_nonpid_loadavg. (procfs_write_nonpid_uptime): Changed to procfs_read_nonpid_uptime. (procfs_write_nonpid_version):Changed to procfs_read_nonpid_version. * procfs_pid_files.c: (procfs_write_stat_file): Changed to procfs_read_stat_file. Changed the comment correspondingly from Write to Read. (procfs_write_cmdline_file ): Changed to procfs_read_cmdline_file. Changed the comment correspondingly from Write to Read. (procfs_write_status_file): Changed to procfs_read_status_file. Changed the comment correspondingly from Write to Read. (procfs_write_statm_file): Changed to procfs_read_statm_file. Changed the comment correspondingly from Write to Read. (procfs_write_files_contents): Changed to procfs_read_files_contents. Changed the comment correspondingly from Write to Read. Changed the call to procfs_write_nonpid_stat to procfs_read_nonpid_stat. Changed the call to procfs_write_stat_file to procfs_read_stat_file. Changed the call to procfs_write_cmdline_file to procfs_read_cmdline_file. Changed the call to procfs_write_status_file to procfs_read_status_file. Changed the call to procfs_write_statm_file to procfs_read_statm_file. Changed the call to procfs_write_nonpid_meminfo to procfs_read_nonpid_meminfo. Changed the call to procfs_write_nonpid_loadavg to procfs_read_nonpid_loadavg. Changed the call to procfs_write_nonpid_uptime to procfs_read_nonpid_uptime. Changed the call to procfs_write_nonpid_version to procfs_read_nonpid_version. netfs.c: (netfs_attempt_read): Changed the call from procfs_write_files_contents to procfs_read_files_contents. --- ChangeLog | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ netfs.c | 2 +- procfs_nonpid_files.c | 10 +++++----- procfs_pid_files.c | 46 +++++++++++++++++++++--------------------- 4 files changed, 84 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index e5bb2414..730ad727 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,58 @@ +2008-08-18 Madhusudan.C.S + * procfs_nonpid_files.c: + (procfs_write_nonpid_stat): Changed to + procfs_read_nonpid_stat. + (procfs_write_nonpid_meminfo): Changed to + procfs_read_nonpid_meminfo. + (procfs_write_nonpid_loadavg): Changed to + procfs_read_nonpid_loadavg. + (procfs_write_nonpid_uptime): Changed to + procfs_read_nonpid_uptime. + (procfs_write_nonpid_version):Changed to + procfs_read_nonpid_version. + * procfs_pid_files.c: + (procfs_write_stat_file): Changed to + procfs_read_stat_file. + Changed the comment correspondingly from + Write to Read. + (procfs_write_cmdline_file ): Changed to + procfs_read_cmdline_file. + Changed the comment correspondingly from + Write to Read. + (procfs_write_status_file): Changed to + procfs_read_status_file. + Changed the comment correspondingly from + Write to Read. + (procfs_write_statm_file): Changed to + procfs_read_statm_file. + Changed the comment correspondingly from + Write to Read. + (procfs_write_files_contents): Changed to + procfs_read_files_contents. + Changed the comment correspondingly from + Write to Read. + Changed the call to procfs_write_nonpid_stat + to procfs_read_nonpid_stat. + Changed the call to procfs_write_stat_file + to procfs_read_stat_file. + Changed the call to procfs_write_cmdline_file + to procfs_read_cmdline_file. + Changed the call to procfs_write_status_file + to procfs_read_status_file. + Changed the call to procfs_write_statm_file + to procfs_read_statm_file. + Changed the call to procfs_write_nonpid_meminfo + to procfs_read_nonpid_meminfo. + Changed the call to procfs_write_nonpid_loadavg + to procfs_read_nonpid_loadavg. + Changed the call to procfs_write_nonpid_uptime + to procfs_read_nonpid_uptime. + Changed the call to procfs_write_nonpid_version + to procfs_read_nonpid_version. + netfs.c: (netfs_attempt_read): Changed + the call from procfs_write_files_contents + to procfs_read_files_contents. + 2008-08-18 Madhusudan.C.S * README: Initial Documentation. diff --git a/netfs.c b/netfs.c index 31ffe1b5..7e00e4d4 100644 --- a/netfs.c +++ b/netfs.c @@ -435,7 +435,7 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *node, if (! err) { if (*len > 0) - procfs_write_files_contents (node, offset, + procfs_read_files_contents (node, offset, len, data); if (*len > 0) if (offset >= *len) diff --git a/procfs_nonpid_files.c b/procfs_nonpid_files.c index 9cb0c57a..15c09190 100644 --- a/procfs_nonpid_files.c +++ b/procfs_nonpid_files.c @@ -263,7 +263,7 @@ error_t get_total_times (struct timeval *total_user_time, return err; } -error_t procfs_write_nonpid_stat (struct dir_entry *dir_entry, +error_t procfs_read_nonpid_stat (struct dir_entry *dir_entry, off_t offset, size_t *len, void *data) { char *stat_data; @@ -387,7 +387,7 @@ SWAP_FIELD (get_swap_page_size, def_pager_info.dpi_page_size) SWAP_FIELD (get_swap_active, (def_pager_info.dpi_total_space - def_pager_info.dpi_free_space)) -error_t procfs_write_nonpid_meminfo (struct dir_entry *dir_entry, +error_t procfs_read_nonpid_meminfo (struct dir_entry *dir_entry, off_t offset, size_t *len, void *data) { char *meminfo_data; @@ -427,7 +427,7 @@ error_t procfs_write_nonpid_meminfo (struct dir_entry *dir_entry, return err; } -error_t procfs_write_nonpid_loadavg (struct dir_entry *dir_entry, +error_t procfs_read_nonpid_loadavg (struct dir_entry *dir_entry, off_t offset, size_t *len, void *data) { char *loadavg_data; @@ -455,7 +455,7 @@ error_t procfs_write_nonpid_loadavg (struct dir_entry *dir_entry, return err; } -error_t procfs_write_nonpid_uptime (struct dir_entry *dir_entry, +error_t procfs_read_nonpid_uptime (struct dir_entry *dir_entry, off_t offset, size_t *len, void *data) { char *uptime_data; @@ -497,7 +497,7 @@ error_t procfs_write_nonpid_uptime (struct dir_entry *dir_entry, return err; } -error_t procfs_write_nonpid_version (struct dir_entry *dir_entry, +error_t procfs_read_nonpid_version (struct dir_entry *dir_entry, off_t offset, size_t *len, void *data) { char *version_data; diff --git a/procfs_pid_files.c b/procfs_pid_files.c index 3ad77340..ed3a3bdc 100644 --- a/procfs_pid_files.c +++ b/procfs_pid_files.c @@ -367,12 +367,12 @@ error_t get_stat_data (pid_t pid, return err; } -/* Writes required process information to stat file +/* Reads required process information from stat file within the directory represented by pid. Return the data in DATA and actual length to be written in LEN. */ error_t -procfs_write_stat_file (struct procfs_dir_entry *dir_entry, +procfs_read_stat_file (struct procfs_dir_entry *dir_entry, off_t offset, size_t *len, void *data) { error_t err; @@ -417,12 +417,12 @@ procfs_write_stat_file (struct procfs_dir_entry *dir_entry, return err; } -/* Writes required process's command line information - to cmline file within the directory represented by - pid. Return the data in DATA and actual length to - be written in LEN. */ +/* Reads required process's command line information + from cmline file within the directory represented + by pid. Return the data in DATA and actual length + to be written in LEN. */ error_t -procfs_write_cmdline_file (struct procfs_dir_entry *dir_entry, +procfs_read_cmdline_file (struct procfs_dir_entry *dir_entry, off_t offset, size_t *len, void *data) { char *cmdline_data; @@ -445,12 +445,12 @@ procfs_write_cmdline_file (struct procfs_dir_entry *dir_entry, return err; } -/* Writes required process's information that is represented by - stat and statm in a human readable format to status file +/* Reads required process's information that is represented by + stat and statm in a human readable format from status file within the directory represented by pid. Return the data in DATA and actual length to be written in LEN. */ error_t -procfs_write_status_file (struct procfs_dir_entry *dir_entry, +procfs_read_status_file (struct procfs_dir_entry *dir_entry, off_t offset, size_t *len, void *data) { char *status_data; @@ -478,12 +478,12 @@ procfs_write_status_file (struct procfs_dir_entry *dir_entry, return err; } -/* Writes required process information to statm file +/* Reads required process information from statm file within the directory represented by pid. Return the data in DATA and actual length to be written in LEN. */ error_t -procfs_write_statm_file (struct procfs_dir_entry *dir_entry, +procfs_read_statm_file (struct procfs_dir_entry *dir_entry, off_t offset, size_t *len, void *data) { char *statm_data; @@ -514,60 +514,60 @@ procfs_write_statm_file (struct procfs_dir_entry *dir_entry, return err; } -/* Writes required process information to each of files +/* Reads required process information from each of files within directory represented by pid, for files specified by NODE. Return the data in DATA and actual length of data in LEN. */ error_t -procfs_write_files_contents (struct node *node, +procfs_read_files_contents (struct node *node, off_t offset, size_t *len, void *data) { error_t err; if (! strcmp (node->nn->dir_entry->name, "stat")) if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) - err = procfs_write_nonpid_stat (node->nn->dir_entry, + err = procfs_read_nonpid_stat (node->nn->dir_entry, offset, len, data); else - err = procfs_write_stat_file (node->nn->dir_entry, + err = procfs_read_stat_file (node->nn->dir_entry, offset, len, data); if (! strcmp (node->nn->dir_entry->name, "cmdline")) - err = procfs_write_cmdline_file (node->nn->dir_entry, + err = procfs_read_cmdline_file (node->nn->dir_entry, offset, len, data); if (! strcmp (node->nn->dir_entry->name, "status")) - err = procfs_write_status_file (node->nn->dir_entry, + err = procfs_read_status_file (node->nn->dir_entry, offset, len, data); if (! strcmp (node->nn->dir_entry->name, "statm")) - err = procfs_write_statm_file (node->nn->dir_entry, + err = procfs_read_statm_file (node->nn->dir_entry, offset, len, data); if (! strcmp (node->nn->dir_entry->name, "meminfo")) if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) - err = procfs_write_nonpid_meminfo (node->nn->dir_entry, + err = procfs_read_nonpid_meminfo (node->nn->dir_entry, offset, len, data); else err = ENOENT; if (! strcmp (node->nn->dir_entry->name, "loadavg")) if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) - err = procfs_write_nonpid_loadavg (node->nn->dir_entry, + err = procfs_read_nonpid_loadavg (node->nn->dir_entry, offset, len, data); else err = ENOENT; if (! strcmp (node->nn->dir_entry->name, "uptime")) if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) - err = procfs_write_nonpid_uptime (node->nn->dir_entry, + err = procfs_read_nonpid_uptime (node->nn->dir_entry, offset, len, data); else err = ENOENT; if (! strcmp (node->nn->dir_entry->name, "version")) if (! strcmp (node->nn->dir_entry->dir->fs_path, "")) - err = procfs_write_nonpid_version (node->nn->dir_entry, + err = procfs_read_nonpid_version (node->nn->dir_entry, offset, len, data); else err = ENOENT; -- cgit v1.2.3 From 80e2bfc9ee29a5ecac3a9b8eaa2d470ae30222c9 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Fri, 29 Aug 2008 18:42:00 +0200 Subject: 2008-08-29 Madhusudan.C.S * Makefile: (Copyright): 1997, 2000 -> 2008 (CC): Removed. (CFLAGS): Removed. (INCLUDES): Removed. (all): Removed. ($(target)): Removed. (%.o): Removed. (HURDLIBS): -lnetfs -> netfs, -lfshelp -> fshelp, -liohelp -> iohelp, -lthreads -> threads, -lports -> ports, -lihash -> ihash, -lps -> ps, -lshouldbeinlibc -> shouldbeinlibc. (include): Add include ../Makeconf --- ChangeLog | 16 ++++++++++++++++ Makefile | 18 ++++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 730ad727..a56b992d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2008-08-29 Madhusudan.C.S + * Makefile: (Copyright): 1997, 2000 -> 2008 + (CC): Removed. + (CFLAGS): Removed. + (INCLUDES): Removed. + (all): Removed. + ($(target)): Removed. + (%.o): Removed. + (HURDLIBS): -lnetfs -> netfs, + -lfshelp -> fshelp, + -liohelp -> iohelp, -lthreads -> threads, + -lports -> ports, -lihash -> ihash, + -lps -> ps, + -lshouldbeinlibc -> shouldbeinlibc. + (include): Add include ../Makeconf + 2008-08-18 Madhusudan.C.S * procfs_nonpid_files.c: (procfs_write_nonpid_stat): Changed to diff --git a/Makefile b/Makefile index dae406d3..500a2371 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Makefile - for procfs # -# Copyright (C) 1997, 2000 Free Software Foundation, Inc. +# Copyright (C) 2008 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -21,20 +21,10 @@ makemode := server target = procfs -CC = gcc -CFLAGS = -Wall -g -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -INCLUDES = -I. - SRCS = procfs.c bootstrap.c netfs.c procfs_dir.c node.c procfs_pid_files.c procfs_nonpid_files.c LCLHDRS = procfs.h procfs_pid.h OBJS = $(SRCS:.c=.o) -HURDLIBS = -lnetfs -lfshelp -liohelp -lthreads -lports -lihash -lps -lshouldbeinlibc - -all: $(target) - -$(target): $(OBJS) - $(CC) $(CFLAGS) -o $(target) $(OBJS) $(HURDLIBS) - -%.o: %.c $(LCLHDRS) - $(CC) $(CFLAGS) $(INCLUDES) -o $@ -c $< +HURDLIBS = netfs fshelp iohelp threads ports ihash ps shouldbeinlibc + +include ../Makeconf -- cgit v1.2.3 From b33bac72fa26be8676a6da73e0b13f51bcd40034 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Fri, 29 Aug 2008 21:34:00 +0200 Subject: 2008-08-29 Madhusudan.C.S * ChangeLog: Revised. --- ChangeLog | 290 +++++++++++++++++++++++++++----------------------------------- 1 file changed, 127 insertions(+), 163 deletions(-) diff --git a/ChangeLog b/ChangeLog index a56b992d..91399f23 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,170 +1,134 @@ -2008-08-29 Madhusudan.C.S - * Makefile: (Copyright): 1997, 2000 -> 2008 - (CC): Removed. - (CFLAGS): Removed. - (INCLUDES): Removed. - (all): Removed. - ($(target)): Removed. - (%.o): Removed. - (HURDLIBS): -lnetfs -> netfs, - -lfshelp -> fshelp, - -liohelp -> iohelp, -lthreads -> threads, - -lports -> ports, -lihash -> ihash, - -lps -> ps, - -lshouldbeinlibc -> shouldbeinlibc. - (include): Add include ../Makeconf - -2008-08-18 Madhusudan.C.S - * procfs_nonpid_files.c: - (procfs_write_nonpid_stat): Changed to - procfs_read_nonpid_stat. - (procfs_write_nonpid_meminfo): Changed to - procfs_read_nonpid_meminfo. - (procfs_write_nonpid_loadavg): Changed to - procfs_read_nonpid_loadavg. - (procfs_write_nonpid_uptime): Changed to - procfs_read_nonpid_uptime. - (procfs_write_nonpid_version):Changed to - procfs_read_nonpid_version. - * procfs_pid_files.c: - (procfs_write_stat_file): Changed to - procfs_read_stat_file. - Changed the comment correspondingly from - Write to Read. - (procfs_write_cmdline_file ): Changed to - procfs_read_cmdline_file. - Changed the comment correspondingly from - Write to Read. - (procfs_write_status_file): Changed to - procfs_read_status_file. - Changed the comment correspondingly from - Write to Read. - (procfs_write_statm_file): Changed to - procfs_read_statm_file. - Changed the comment correspondingly from - Write to Read. - (procfs_write_files_contents): Changed to - procfs_read_files_contents. - Changed the comment correspondingly from - Write to Read. - Changed the call to procfs_write_nonpid_stat - to procfs_read_nonpid_stat. - Changed the call to procfs_write_stat_file - to procfs_read_stat_file. - Changed the call to procfs_write_cmdline_file - to procfs_read_cmdline_file. - Changed the call to procfs_write_status_file - to procfs_read_status_file. - Changed the call to procfs_write_statm_file - to procfs_read_statm_file. - Changed the call to procfs_write_nonpid_meminfo - to procfs_read_nonpid_meminfo. - Changed the call to procfs_write_nonpid_loadavg - to procfs_read_nonpid_loadavg. - Changed the call to procfs_write_nonpid_uptime - to procfs_read_nonpid_uptime. - Changed the call to procfs_write_nonpid_version - to procfs_read_nonpid_version. - netfs.c: (netfs_attempt_read): Changed - the call from procfs_write_files_contents - to procfs_read_files_contents. - -2008-08-18 Madhusudan.C.S - * README: Initial Documentation. +2008-08-29 Madhusudan.C.S -2008-08-18 Madhusudan.C.S - * procfs_nonpid_files.c: (get_uptime): - Changed the parameter type from double to - struct timeval. - Changed the parameter name from uptime_secs - to uptime. - Removed uptime variable. - Changed timersub to use the passed pointer - instead of the local variable. - Removed the calculation of uptime_secs. - (get_total_times): Changed the parameters - type from double to struct timeval. - Changed the parameters name from - total_user_time_secs to total_user_time - and total_system_time_secs to - total_system_time. - New variables total_user_time_tmp, - total_system_time_tmp and tmpval of type - struct timeval. - Call timerclear to clear the tmp variables. - Remove calculation of times in seconds and - do the same on struct timeval variables - throughout using the timeradd macro. - Assign values of temporary local variables - to the pointers passed as parameters. - (procfs_write_nonpid_stat): Replaced - variables that hold time in seconds with - struct timeval type variables and jiffy_t - type variables. - Argument to get_uptime changed from - uptime_secs to uptime. - Arguments to get_total_times changed from - total_user_time_secs to total_user_time and - total_system_time_secs to total_system_time. - Replace arithematic time subtraction with - timersub macro. - Convert all the times in struct timeval type - variables to jiffy_t type. - Changed the type casting for the asprintf - arguments to be compatible with jiffy_t type. - (procfs_write_nonpid_uptime): Replaced - variables that hold time in seconds with - struct timeval type variables. - Argument to get_uptime changed from - uptime_secs to uptime. - Arguments to get_total_times changed from - total_user_time_secs to total_user_time and - total_system_time_secs to total_system_time. - Replace arithematic time subtraction with - timersub macro. - Convert all the times in struct timeval type - variables to seconds. + * Makefile: (Copyright): 1997, 2000 -> 2008. + (CC): Removed. + (CFLAGS): Removed. + (INCLUDES): Removed. + (all): Removed. + ($(target)): Removed. + (%.o): Removed. + (HURDLIBS): -lnetfs -> netfs, -lfshelp -> fshelp, + -liohelp -> iohelp, -lthreads -> threads, -lports -> ports, + -lihash -> ihash, -lps -> ps, -lshouldbeinlibc -> shouldbeinlibc. + (include): Add include ../Makeconf -2008-08-18 Madhusudan.C.S - * procfs_nonpid_files.c: - (procfs_write_nonpid_version): New function. - * procfs_pid_files.c: - (procfs_write_files_contents): Add a check - to find if the read is requested for the - version file and corresponding a call to it. +2008-08-18 Madhusudan.C.S + + * procfs_nonpid_files.c: (procfs_write_nonpid_stat): Changed to + procfs_read_nonpid_stat. + (procfs_write_nonpid_meminfo): Changed to procfs_read_nonpid_meminfo. + (procfs_write_nonpid_loadavg): Changed to procfs_read_nonpid_loadavg. + (procfs_write_nonpid_uptime): Changed to procfs_read_nonpid_uptime. + (procfs_write_nonpid_version):Changed to procfs_read_nonpid_version. + * procfs_pid_files.c: (procfs_write_stat_file): Changed to + procfs_read_stat_file. + Changed the comment correspondingly from Write to Read. + (procfs_write_cmdline_file ): Changed to procfs_read_cmdline_file. + Changed the comment correspondingly from Write to Read. + (procfs_write_status_file): Changed to procfs_read_status_file. + Changed the comment correspondingly from Write to Read. + (procfs_write_statm_file): Changed to procfs_read_statm_file. + Changed the comment correspondingly from Write to Read. + (procfs_write_files_contents): Changed to procfs_read_files_contents. + Changed the comment correspondingly from Write to Read. + Changed the call to procfs_write_nonpid_stat to procfs_read_nonpid_stat. + Changed the call to procfs_write_stat_file to procfs_read_stat_file. + Changed the call to procfs_write_cmdline_file to + procfs_read_cmdline_file. + Changed the call to procfs_write_status_file to + procfs_read_status_file. + Changed the call to procfs_write_statm_file to + procfs_read_statm_file. + Changed the call to procfs_write_nonpid_meminfo to + procfs_read_nonpid_meminfo. + Changed the call to procfs_write_nonpid_loadavg to + procfs_read_nonpid_loadavg. + Changed the call to procfs_write_nonpid_uptime to + procfs_read_nonpid_uptime. + Changed the call to procfs_write_nonpid_version to + procfs_read_nonpid_version. + netfs.c: (netfs_attempt_read): Changed the call from + procfs_write_files_contents to procfs_read_files_contents. -2008-08-14 Madhusudan.C.S - * procfs.h: (jiffy_t): New typedef. - * procfs_pid.h: "procfs.h" is included. - (struct procfs_pid_files): Changed all the - occurrences of time_t to jiffy_t. - * procfs_pid_files.c: Removed "procfs.h". - (adjust_jiffy_time): Changed return type - from time_t to jiffy_t. - Changed the type of jiffy_time variable - from time_t to jiffy_t. - (get_live_threads_time): Changed the type - of utime and stime from time_t to jiffy_t. - (get_stat_data): Changed the type of utime - and stime from time_t to jiffy_t. +2008-08-18 Madhusudan.C.S + + * README: Initial Documentation. + +2008-08-18 Madhusudan.C.S + + * procfs_nonpid_files.c: (get_uptime): Changed the parameter type from + double to struct timeval. + Changed the parameter name from uptime_secs to uptime. + Removed uptime variable. + Changed timersub to use the passed pointer instead of the local + variable. + Removed the calculation of uptime_secs. + (get_total_times): Changed the parameters type from double to struct + timeval. + Changed the parameters name from total_user_time_secs to + total_user_time and total_system_time_secs to total_system_time. + New variables total_user_time_tmp, total_system_time_tmp and tmpval + of type struct timeval. + Call timerclear to clear the tmp variables. + Remove calculation of times in seconds and do the same on struct + timeval variables throughout using the timeradd macro. + Assign values of temporary local variables to the pointers passed + as parameters. + (procfs_write_nonpid_stat): Replaced variables that hold time in + seconds with struct timeval type variables and jiffy_t type variables. + Argument to get_uptime changed from uptime_secs to uptime. + Arguments to get_total_times changed from total_user_time_secs to + total_user_time and total_system_time_secs to total_system_time. + Replace arithematic time subtraction with timersub macro. + Convert all the times in struct timeval type variables to jiffy_t type. + Changed the type casting for the asprintf arguments to be compatible + with jiffy_t type. + (procfs_write_nonpid_uptime): Replaced variables that hold time in + seconds with struct timeval type variables. + Argument to get_uptime changed from uptime_secs to uptime. + Arguments to get_total_times changed from total_user_time_secs to + total_user_time and total_system_time_secs to total_system_time. + Replace arithematic time subtraction with timersub macro. + Convert all the times in struct timeval type variables to seconds. + +2008-08-18 Madhusudan.C.S + + * procfs_nonpid_files.c: (procfs_write_nonpid_version): New function. + * procfs_pid_files.c: (procfs_write_files_contents): Add a check + to find if the read is requested for the version file and + corresponding a call to it. + +2008-08-14 Madhusudan.C.S + + * procfs.h: (jiffy_t): New typedef. + * procfs_pid.h: "procfs.h" is included. + (struct procfs_pid_files): Changed all the occurrences of time_t to + jiffy_t. + * procfs_pid_files.c: Removed "procfs.h". + (adjust_jiffy_time): Changed return type from time_t to jiffy_t. + Changed the type of jiffy_time variable from time_t to jiffy_t. + (get_live_threads_time): Changed the type of utime and stime from + time_t to jiffy_t. + (get_stat_data): Changed the type of utime and stime from time_t to + jiffy_t. -2008-08-14 Madhusudan.C.S +2008-08-14 Madhusudan.C.S - * ChangeLog: New file. - * AUTHORS: New file. - * COPYING: New file. - * README: New file. - * Makefile: New file. - * bootstrap.c: New file. - * netfs.c: New file. - * node.c: New file. - * procfs.c: New file. - * procfs.h: New file. - * procfs_dir.c: New file. - * procfs_nonpid_files.c: New file. - * procfs_pid.h: New file. - * procfs_pid_files.c: New file. + * ChangeLog: New file. + * AUTHORS: New file. + * COPYING: New file. + * README: New file. + * Makefile: New file. + * bootstrap.c: New file. + * netfs.c: New file. + * node.c: New file. + * procfs.c: New file. + * procfs.h: New file. + * procfs_dir.c: New file. + * procfs_nonpid_files.c: New file. + * procfs_pid.h: New file. + * procfs_pid_files.c: New file. -2008-05-13 Madhusudan.C.S +2008-05-13 Madhusudan.C.S - * /sources/hurd/procfs: New directory added to the repository. + * /sources/hurd/procfs: New directory added to the repository. -- cgit v1.2.3 From fdd74966f222b67aa3161e809c6e9d8aacb16b12 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Sat, 30 Aug 2008 22:20:00 +0200 Subject: 2008-08-29 Madhusudan.C.S * AUTHORS: File removed. * COPYING: Likewise. * README: Likewise. --- AUTHORS | 10 -- COPYING | 340 -------------------------------------------------------------- ChangeLog | 6 ++ README | 77 -------------- 4 files changed, 6 insertions(+), 427 deletions(-) delete mode 100644 AUTHORS delete mode 100644 COPYING delete mode 100644 README diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 622b6488..00000000 --- a/AUTHORS +++ /dev/null @@ -1,10 +0,0 @@ -Written By -~~~~~~~~~~ - -Madhusudan.C.S - - -Mentored By -~~~~~~~~~~~ - -Olaf Buddenhagen diff --git a/COPYING b/COPYING deleted file mode 100644 index 60549be5..00000000 --- a/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/ChangeLog b/ChangeLog index 91399f23..e22887dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2008-08-29 Madhusudan.C.S + + * AUTHORS: File removed. + * COPYING: Likewise. + * README: Likewise. + 2008-08-29 Madhusudan.C.S * Makefile: (Copyright): 1997, 2000 -> 2008. diff --git a/README b/README deleted file mode 100644 index 28b4aa3e..00000000 --- a/README +++ /dev/null @@ -1,77 +0,0 @@ -~~~~~~~~~~~~~~~ -1. Introduction -~~~~~~~~~~~~~~~ - - This program is called procfs, the /proc translator. This -program provides a GNU/Linux compatible /proc pseudo file- -system on GNU Hurd. This is called a translator since it -translates the process related information stored in the -MACH Microkernel which is made available through the proc -server and the libps library into a virtual filesystem. - -~~~~~~~ -2. Goal -~~~~~~~ - - The major goal of writing this translator was to make the -process related tools like pgrep, pkill, kill which are -packaged in procps, killall, pstree which are packaged in -psmisc and various other process related tools that rely -on GNU/Linux's /proc filesystem to run out of the box on -Hurd. - -~~~~~~~~~~ -3. Install -~~~~~~~~~~ - - To install this translator you can simply do the following. -Just cd to the top level directory of the hurd main source -tree. Run the configure script with the following command. -(In BASH) - -$ ./configure - -(If your shell is something else run the equivalent command). - -Now you can run make with procfs as a parameter if you want -to build only procfs. - -$ make procfs - -(This first builds all the dependencies of procfs and then -builds procfs as a part of Hurd main source tree.) - -After building the translator, you get a binary named procfs. -You need to set it as a translator. To do so you can type the -following command from the top level directory of Hurd main -source tree directory from where you ran make. - -$ settrans -fgap /proc procfs/procfs - -Viola! You are done setting up the translator. - -~~~~~~~~~~ -4. Testing -~~~~~~~~~~ - - Now in case you want to use the debian hurd binaries to work -with tools like pgrep, pkill, kill, htop etc, you can just -download the binaries which I have uploaded here: - -http://madhusudancs.info/procfs-testing-how-to-mini - -The patches to the source packages will be made available soon. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -5. Reporting Bugs and contacts -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - I request all of you to help me in identifying the bugs in -procfs or in the procps or htop packages, so that I can fix -them. You are also free to submit patches if you feel so. The -patches can be sent either to bug-hurd mailing list or to my -e-mail adress. The e-mail IDs are as follows: - -bug-hurd@gnu.org -madhusudancs@gmail.com -madhusudan@madhusudancs.info -- cgit v1.2.3 From 7a8b315855fc8edba2003fa69cb86cf664f4c2ec Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Sat, 30 Aug 2008 22:48:00 +0200 Subject: 2008-08-30 Madhusudan.C.S * procfs_dir.c: (procfs_dir_create): Assign newly created directory to its pointer in netnode. (procfs_dir_remove): Removed function. (free_entry): New function. (ordered_unlink): Likewise. (delete): Likewise. (sweep): Likewise. (procfs_dir_entries_remove): Likewise. (is_in_pid_list): Removed call to make_dir_invalid (). (procfs_fill_root_dir): struct stat *stat -> struct stat stat. Add Read and Execute permissions to all in stat.st_mode. Set stat.st_nlink to 1. Set stat.st_size to 0. Add struct proc_stat *ps definition. Set struct proc_stat data from _proc_stat_create () function and set stat.st_uid and stat.st_gid from the data in that structure. * procfs_pid_files.c: (update_pid_entries): Add Read permissions to all in stat->st_mode. --- ChangeLog | 21 ++++++++++ procfs_dir.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++------- procfs_pid_files.c | 2 +- 3 files changed, 126 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index e22887dd..0e62b6f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2008-08-30 Madhusudan.C.S + + * procfs_dir.c: (procfs_dir_create): Assign newly created directory to + its pointer in netnode. + (procfs_dir_remove): Removed function. + (free_entry): New function. + (ordered_unlink): Likewise. + (delete): Likewise. + (sweep): Likewise. + (procfs_dir_entries_remove): Likewise. + (is_in_pid_list): Removed call to make_dir_invalid (). + (procfs_fill_root_dir): struct stat *stat -> struct stat stat. + Add Read and Execute permissions to all in stat.st_mode. + Set stat.st_nlink to 1. + Set stat.st_size to 0. + Add struct proc_stat *ps definition. + Set struct proc_stat data from _proc_stat_create () function and + set stat.st_uid and stat.st_gid from the data in that structure. + * procfs_pid_files.c: (update_pid_entries): Add Read permissions + to all in stat->st_mode. + 2008-08-29 Madhusudan.C.S * AUTHORS: File removed. diff --git a/procfs_dir.c b/procfs_dir.c index b9b0410e..f76e6a4b 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -77,6 +77,9 @@ error_t procfs_dir_create (struct procfs *fs, struct node *node, *dir = new; + if (fs->root != 0) + node->nn->dir = new; + return 0; } @@ -343,14 +346,50 @@ procfs_dir_null_lookup (struct procfs_dir *dir, struct node **node) return err; } -/* Remove the specified DIR and free all its allocated - storage. */ -void procfs_dir_remove (struct procfs_dir *dir) +/* Free the directory entry DIR_ENTRY and all resources it consumes. */ +void +free_entry (struct procfs_dir_entry *dir_entry) { - /* STUB */ + assert (! dir_entry->self_p); /* We should only free deleted nodes. */ + free (dir_entry->name); + if (dir_entry->symlink_target) + free (dir_entry->symlink_target); + free (dir_entry->node->nn->dir); + free (dir_entry->node->nn); + free (dir_entry->node); + free (dir_entry); +} + +/* Remove DIR_ENTRY from its position in the ordered_next chain. */ +static void +ordered_unlink (struct procfs_dir_entry *dir_entry) +{ + if (dir_entry->ordered_self_p) + *dir_entry->ordered_self_p = dir_entry->ordered_next; + if (dir_entry->ordered_next) + dir_entry->ordered_next->self_p = dir_entry->ordered_self_p; +} + +/* Delete DIR_ENTRY from its directory, freeing any resources it holds. */ +static void +delete (struct procfs_dir_entry *dir_entry, struct procfs_dir *dir) +{ + dir->num_entries--; - return 0; + /* Take out of the hash chain. */ + if (dir_entry->self_p) + *dir_entry->self_p = dir_entry->next; + if (dir_entry->next) + dir_entry->next->self_p = dir_entry->self_p; + + /* Take out of the directory ordered list. */ + ordered_unlink (dir_entry); + + /* If there's a node attached, we'll delete the entry whenever it goes + away, otherwise, just delete it now. */ + if (! dir_entry->node) + free_entry (dir_entry); } /* Make all the directory entries invalid */ @@ -373,6 +412,41 @@ make_dir_invalid (struct procfs_dir *dir) } } +/* Delete any entries in DIR which don't have their valid bit set. */ +static void +sweep (struct procfs_dir *dir) +{ + size_t len = dir->htable_len, i; + struct procfs_dir_entry **htable = dir->htable, *dir_entry; + + for (i = 0; i < len; i++) + { + dir_entry = htable[i]; + while (dir_entry) + { + if (!dir_entry->valid && !dir_entry->noent && dir->num_entries) + delete (dir_entry, dir); + dir_entry = dir_entry->next; + } + if (htable[i]) + { + free (htable[i]); + htable[i] = 0; + } + + } + +} + +/* Remove the specified DIR and free all its allocated + storage. */ +void procfs_dir_entries_remove (struct procfs_dir *dir) +{ + /* Free all entries. */ + make_dir_invalid (dir); + sweep (dir); +} + /* Checks if the DIR name is in list of Active pids. */ int is_in_pid_list (struct procfs_dir *dir) @@ -417,7 +491,6 @@ error_t procfs_dir_refresh (struct procfs_dir *dir, int isroot) error_t err; int is_parent_pid; struct node *node; - make_dir_invalid (dir); struct timeval tv; maptime_read (procfs_maptime, &tv); @@ -513,14 +586,18 @@ procfs_fill_root_dir(struct procfs_dir *dir, time_t timestamp) char *data; pid_t *pids; int pidslen; - struct stat *stat = (struct stat *) malloc (sizeof (struct stat)); - stat->st_mode = S_IFDIR; + struct stat stat; + stat.st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH; + stat.st_nlink = 1; + stat.st_size = 0; int count; char *dir_name_pid; struct node *node; struct procfs_dir *new_dir; struct procfs_dir_entry *dir_entry; + struct proc_stat *ps; pids = NULL; pidslen = 0; @@ -540,13 +617,25 @@ procfs_fill_root_dir(struct procfs_dir *dir, time_t timestamp) if (! node || ! new_dir ) return ENOMEM; #endif - dir_entry = update_entries_list (dir, dir_name_pid, - stat, timestamp, NULL); - err = procfs_create_node (dir_entry, dir_name_pid, &node); - - procfs_dir_create (dir->fs, node, - dir_name_pid, &new_dir); - free(dir_name_pid); + err = _proc_stat_create (pids[count], ps_context, &ps); + if (! err) + { + err = set_field_value (ps, PSTAT_PROC_INFO); + if (! err) + { + stat.st_uid = proc_stat_proc_info (ps)->owner; + stat.st_gid = proc_stat_proc_info (ps)->pgrp; + + dir_entry = update_entries_list (dir, dir_name_pid, + &stat, timestamp, NULL); + err = procfs_create_node (dir_entry, dir_name_pid, &node); + + procfs_dir_create (dir->fs, node, + dir_name_pid, &new_dir); + free(dir_name_pid); + _proc_stat_free (ps); + } + } } } diff --git a/procfs_pid_files.c b/procfs_pid_files.c index ed3a3bdc..46861531 100644 --- a/procfs_pid_files.c +++ b/procfs_pid_files.c @@ -48,7 +48,7 @@ update_pid_entries (struct procfs_dir *dir, const char *name, { struct procfs_dir_entry *dir_entry; struct stat *stat = (struct stat *) malloc (sizeof (struct stat)); - stat->st_mode = S_IFREG; + stat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; dir_entry = update_entries_list (dir, name, stat, timestamp, symlink_target); -- cgit v1.2.3 From 7e7201c453ad7faa5d26625ca9d263ee625ef5ed Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Tue, 2 Sep 2008 00:33:00 +0200 Subject: 2008-09-02 Madhusudan.C.S * netfs.c: (netfs_get_dirents): Add call to procfs_dir_entries_remove(). --- ChangeLog | 5 +++++ netfs.c | 1 + 2 files changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index 0e62b6f4..1750c16d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2008-09-02 Madhusudan.C.S + + * netfs.c: (netfs_get_dirents): Add call to + procfs_dir_entries_remove(). + 2008-08-30 Madhusudan.C.S * procfs_dir.c: (procfs_dir_create): Assign newly created directory to diff --git a/netfs.c b/netfs.c index 7e00e4d4..4f6fd5ce 100644 --- a/netfs.c +++ b/netfs.c @@ -278,6 +278,7 @@ netfs_get_dirents (struct iouser *cred, struct node *dir, return ENOTDIR; } + procfs_dir_entries_remove (dir->nn->dir); return err; } -- cgit v1.2.3 From e58f247f9c3a95ea2805fe132777d192ef51f6d1 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Fri, 12 Dec 2008 01:48:00 +0100 Subject: 2008-12-12 Samuel Thibault * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Divide by 1024 value returned by get_swap_size and get_swap_free to get kilobytes. --- ChangeLog | 6 ++++++ procfs_nonpid_files.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 1750c16d..67a106b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2008-12-12 Samuel Thibault + + * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Divide by + 1024 value returned by get_swap_size and get_swap_free to get + kilobytes. + 2008-09-02 Madhusudan.C.S * netfs.c: (netfs_get_dirents): Add call to diff --git a/procfs_nonpid_files.c b/procfs_nonpid_files.c index 15c09190..d8b3a7d1 100644 --- a/procfs_nonpid_files.c +++ b/procfs_nonpid_files.c @@ -417,7 +417,7 @@ error_t procfs_read_nonpid_meminfo (struct dir_entry *dir_entry, mem_size, (PAGES_TO_BYTES(vmstats.free_count)) / 1024 , 0, 0, 0, (PAGES_TO_BYTES(vmstats.active_count)) / 1024, (PAGES_TO_BYTES(vmstats.inactive_count)) / 1024, 0, 0, 0, 0, - get_swap_size (), get_swap_free ()) == -1) + get_swap_size () / 1024, get_swap_free () / 1024) == -1) return errno; memcpy (data, meminfo_data, strlen(meminfo_data)); -- cgit v1.2.3 From edb4593c38d421b5d538b221a991b50c36fdba15 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Mon, 31 May 2010 02:23:00 +0200 Subject: 2010-05-31 Samuel Thibault * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Print swap sizes using %llu. --- ChangeLog | 5 +++++ procfs_nonpid_files.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 67a106b8..47cbeaf2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-05-31 Samuel Thibault + + * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Print swap sizes + using %llu. + 2008-12-12 Samuel Thibault * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Divide by diff --git a/procfs_nonpid_files.c b/procfs_nonpid_files.c index d8b3a7d1..2c1209ee 100644 --- a/procfs_nonpid_files.c +++ b/procfs_nonpid_files.c @@ -412,8 +412,8 @@ error_t procfs_read_nonpid_meminfo (struct dir_entry *dir_entry, "HighFree:\t%lu kB\n" "LowTotal:\t%lu kB\n" "LowFree:\t%lu kB\n" - "SwapTotal:\t%lu kB\n" - "SwapFree:\t%lu kB\n", + "SwapTotal:\t%llu kB\n" + "SwapFree:\t%llu kB\n", mem_size, (PAGES_TO_BYTES(vmstats.free_count)) / 1024 , 0, 0, 0, (PAGES_TO_BYTES(vmstats.active_count)) / 1024, (PAGES_TO_BYTES(vmstats.inactive_count)) / 1024, 0, 0, 0, 0, -- cgit v1.2.3 From d76613f23eec2a66f4727c69fcec3b5e3be3da22 Mon Sep 17 00:00:00 2001 From: Thomas Schwinge Date: Sun, 1 Aug 2010 13:28:31 +0200 Subject: Switch to the new ChangeLog style. * ChangeLog: Wipe out content, and add instructions about how to get it back. --- ChangeLog | 179 ++------------------------------------------------------------ 1 file changed, 4 insertions(+), 175 deletions(-) diff --git a/ChangeLog b/ChangeLog index 47cbeaf2..0cd74d02 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,177 +1,6 @@ -2010-05-31 Samuel Thibault +edb4593c38d421b5d538b221a991b50c36fdba15 is the last commit imported from CVS. +All commits after that one have valid author and committer information. - * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Print swap sizes - using %llu. +Use this to examine the change log for earlier changes: -2008-12-12 Samuel Thibault - - * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Divide by - 1024 value returned by get_swap_size and get_swap_free to get - kilobytes. - -2008-09-02 Madhusudan.C.S - - * netfs.c: (netfs_get_dirents): Add call to - procfs_dir_entries_remove(). - -2008-08-30 Madhusudan.C.S - - * procfs_dir.c: (procfs_dir_create): Assign newly created directory to - its pointer in netnode. - (procfs_dir_remove): Removed function. - (free_entry): New function. - (ordered_unlink): Likewise. - (delete): Likewise. - (sweep): Likewise. - (procfs_dir_entries_remove): Likewise. - (is_in_pid_list): Removed call to make_dir_invalid (). - (procfs_fill_root_dir): struct stat *stat -> struct stat stat. - Add Read and Execute permissions to all in stat.st_mode. - Set stat.st_nlink to 1. - Set stat.st_size to 0. - Add struct proc_stat *ps definition. - Set struct proc_stat data from _proc_stat_create () function and - set stat.st_uid and stat.st_gid from the data in that structure. - * procfs_pid_files.c: (update_pid_entries): Add Read permissions - to all in stat->st_mode. - -2008-08-29 Madhusudan.C.S - - * AUTHORS: File removed. - * COPYING: Likewise. - * README: Likewise. - -2008-08-29 Madhusudan.C.S - - * Makefile: (Copyright): 1997, 2000 -> 2008. - (CC): Removed. - (CFLAGS): Removed. - (INCLUDES): Removed. - (all): Removed. - ($(target)): Removed. - (%.o): Removed. - (HURDLIBS): -lnetfs -> netfs, -lfshelp -> fshelp, - -liohelp -> iohelp, -lthreads -> threads, -lports -> ports, - -lihash -> ihash, -lps -> ps, -lshouldbeinlibc -> shouldbeinlibc. - (include): Add include ../Makeconf - -2008-08-18 Madhusudan.C.S - - * procfs_nonpid_files.c: (procfs_write_nonpid_stat): Changed to - procfs_read_nonpid_stat. - (procfs_write_nonpid_meminfo): Changed to procfs_read_nonpid_meminfo. - (procfs_write_nonpid_loadavg): Changed to procfs_read_nonpid_loadavg. - (procfs_write_nonpid_uptime): Changed to procfs_read_nonpid_uptime. - (procfs_write_nonpid_version):Changed to procfs_read_nonpid_version. - * procfs_pid_files.c: (procfs_write_stat_file): Changed to - procfs_read_stat_file. - Changed the comment correspondingly from Write to Read. - (procfs_write_cmdline_file ): Changed to procfs_read_cmdline_file. - Changed the comment correspondingly from Write to Read. - (procfs_write_status_file): Changed to procfs_read_status_file. - Changed the comment correspondingly from Write to Read. - (procfs_write_statm_file): Changed to procfs_read_statm_file. - Changed the comment correspondingly from Write to Read. - (procfs_write_files_contents): Changed to procfs_read_files_contents. - Changed the comment correspondingly from Write to Read. - Changed the call to procfs_write_nonpid_stat to procfs_read_nonpid_stat. - Changed the call to procfs_write_stat_file to procfs_read_stat_file. - Changed the call to procfs_write_cmdline_file to - procfs_read_cmdline_file. - Changed the call to procfs_write_status_file to - procfs_read_status_file. - Changed the call to procfs_write_statm_file to - procfs_read_statm_file. - Changed the call to procfs_write_nonpid_meminfo to - procfs_read_nonpid_meminfo. - Changed the call to procfs_write_nonpid_loadavg to - procfs_read_nonpid_loadavg. - Changed the call to procfs_write_nonpid_uptime to - procfs_read_nonpid_uptime. - Changed the call to procfs_write_nonpid_version to - procfs_read_nonpid_version. - netfs.c: (netfs_attempt_read): Changed the call from - procfs_write_files_contents to procfs_read_files_contents. - -2008-08-18 Madhusudan.C.S - - * README: Initial Documentation. - -2008-08-18 Madhusudan.C.S - - * procfs_nonpid_files.c: (get_uptime): Changed the parameter type from - double to struct timeval. - Changed the parameter name from uptime_secs to uptime. - Removed uptime variable. - Changed timersub to use the passed pointer instead of the local - variable. - Removed the calculation of uptime_secs. - (get_total_times): Changed the parameters type from double to struct - timeval. - Changed the parameters name from total_user_time_secs to - total_user_time and total_system_time_secs to total_system_time. - New variables total_user_time_tmp, total_system_time_tmp and tmpval - of type struct timeval. - Call timerclear to clear the tmp variables. - Remove calculation of times in seconds and do the same on struct - timeval variables throughout using the timeradd macro. - Assign values of temporary local variables to the pointers passed - as parameters. - (procfs_write_nonpid_stat): Replaced variables that hold time in - seconds with struct timeval type variables and jiffy_t type variables. - Argument to get_uptime changed from uptime_secs to uptime. - Arguments to get_total_times changed from total_user_time_secs to - total_user_time and total_system_time_secs to total_system_time. - Replace arithematic time subtraction with timersub macro. - Convert all the times in struct timeval type variables to jiffy_t type. - Changed the type casting for the asprintf arguments to be compatible - with jiffy_t type. - (procfs_write_nonpid_uptime): Replaced variables that hold time in - seconds with struct timeval type variables. - Argument to get_uptime changed from uptime_secs to uptime. - Arguments to get_total_times changed from total_user_time_secs to - total_user_time and total_system_time_secs to total_system_time. - Replace arithematic time subtraction with timersub macro. - Convert all the times in struct timeval type variables to seconds. - -2008-08-18 Madhusudan.C.S - - * procfs_nonpid_files.c: (procfs_write_nonpid_version): New function. - * procfs_pid_files.c: (procfs_write_files_contents): Add a check - to find if the read is requested for the version file and - corresponding a call to it. - -2008-08-14 Madhusudan.C.S - - * procfs.h: (jiffy_t): New typedef. - * procfs_pid.h: "procfs.h" is included. - (struct procfs_pid_files): Changed all the occurrences of time_t to - jiffy_t. - * procfs_pid_files.c: Removed "procfs.h". - (adjust_jiffy_time): Changed return type from time_t to jiffy_t. - Changed the type of jiffy_time variable from time_t to jiffy_t. - (get_live_threads_time): Changed the type of utime and stime from - time_t to jiffy_t. - (get_stat_data): Changed the type of utime and stime from time_t to - jiffy_t. - -2008-08-14 Madhusudan.C.S - - * ChangeLog: New file. - * AUTHORS: New file. - * COPYING: New file. - * README: New file. - * Makefile: New file. - * bootstrap.c: New file. - * netfs.c: New file. - * node.c: New file. - * procfs.c: New file. - * procfs.h: New file. - * procfs_dir.c: New file. - * procfs_nonpid_files.c: New file. - * procfs_pid.h: New file. - * procfs_pid_files.c: New file. - -2008-05-13 Madhusudan.C.S - - * /sources/hurd/procfs: New directory added to the repository. + $ git show edb4593c38d421b5d538b221a991b50c36fdba15:ChangeLog -- cgit v1.2.3 From 5d011324a721d7cf3babfe6aa068e247c7f27f42 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Fri, 13 Aug 2010 19:05:13 +0200 Subject: update_pid_entries(): fix awkwardly indented uninitialized memory leak * procfs_pid_files.c (update_pid_entries): Replace dynamic allocation of `stat' with automatic allocation. Memset `stat' to 0. --- procfs_pid_files.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/procfs_pid_files.c b/procfs_pid_files.c index 46861531..d44a2a22 100644 --- a/procfs_pid_files.c +++ b/procfs_pid_files.c @@ -46,14 +46,12 @@ update_pid_entries (struct procfs_dir *dir, const char *name, time_t timestamp, const char *symlink_target) { - struct procfs_dir_entry *dir_entry; - struct stat *stat = (struct stat *) malloc (sizeof (struct stat)); - stat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + struct stat stat; - dir_entry = update_entries_list (dir, name, stat, - timestamp, symlink_target); + memset (&stat, 0, sizeof stat); + stat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - return dir_entry; + return update_entries_list (dir, name, &stat, timestamp, symlink_target); } /* Creates files to store process information for DIR -- cgit v1.2.3 From 5af547e7fc9ddc0b472b99c6bdb7341f6204e43f Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Fri, 13 Aug 2010 19:05:14 +0200 Subject: Set an appropriate st_mode on symlinks. * procfs_pid_files.c (update_pid_entries): When symlink_target is not NULL, set st_size to the symlink length and st_mode to S_IFLNK | 0777. --- procfs_pid_files.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/procfs_pid_files.c b/procfs_pid_files.c index d44a2a22..26a0af33 100644 --- a/procfs_pid_files.c +++ b/procfs_pid_files.c @@ -49,7 +49,16 @@ update_pid_entries (struct procfs_dir *dir, const char *name, struct stat stat; memset (&stat, 0, sizeof stat); - stat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + if (symlink_target) + { + stat.st_size = strlen (symlink_target); + stat.st_mode = S_IFLNK | 0777; + } + else + { + stat.st_size = 0; + stat.st_mode = S_IFREG | 0444; + } return update_entries_list (dir, name, &stat, timestamp, symlink_target); } -- cgit v1.2.3 From 1b55547480eff13851f2d53ff96afbace11c6004 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 16 Aug 2010 13:36:52 +0200 Subject: Implement /proc/mounts as a symlink to /etc/mtab * procfs_nonpid_files.c (procfs_create_mounts): New function. * procfs_dir.c (procfs_fill_root_dir): Call procfs_create_mounts. Signed-off-by: Jeremie Koenig --- procfs_dir.c | 3 +++ procfs_nonpid_files.c | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/procfs_dir.c b/procfs_dir.c index f76e6a4b..bd1e49d6 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -654,6 +654,9 @@ procfs_fill_root_dir(struct procfs_dir *dir, time_t timestamp) if ((err = procfs_create_loadavg (dir, &node, timestamp)) != 0) return err; + if ((err = procfs_create_mounts (dir, &node, timestamp)) != 0) + return err; + return 0; } diff --git a/procfs_nonpid_files.c b/procfs_nonpid_files.c index 2c1209ee..f1300666 100644 --- a/procfs_nonpid_files.c +++ b/procfs_nonpid_files.c @@ -166,6 +166,19 @@ error_t procfs_create_loadavg (struct procfs_dir *dir, return err; } +error_t procfs_create_mounts (struct procfs_dir *dir, + struct node **node, + time_t timestamp) +{ + struct procfs_dir_entry *dir_entry; + int err; + + dir_entry = update_pid_entries (dir, "mounts", timestamp, "/etc/mtab"); + err = procfs_create_node (dir_entry, "mounts", node); + + return err; +} + error_t get_uptime (struct timeval *uptime) { struct timeval boot_time, now; -- cgit v1.2.3 From d938e96e59a41d5eaa11040513815b757e58eb0c Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 09:43:29 +0000 Subject: Basic infrastructure * procfs.h: New file; basic interfaces for procfs nodes. * procfs.c: New file; implement the basic infrastructure. * netfs.c: New file; bridge libnetfs and the procfs interfaces. * main.c: New file; mostly a "Hello, World!" for now. * Makefile: New file; standalone for now. --- Makefile | 17 +++ main.c | 58 ++++++++++ netfs.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs.c | 90 +++++++++++++++ procfs.h | 40 +++++++ 5 files changed, 596 insertions(+) create mode 100644 Makefile create mode 100644 main.c create mode 100644 netfs.c create mode 100644 procfs.c create mode 100644 procfs.h diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..4ae336a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +TARGET = procfs +OBJS = procfs.o netfs.o main.o +LIBS = -lnetfs + +CC = gcc +CFLAGS = -Wall -g +CPPFLAGS = + +CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + +clean: + $(RM) $(TARGET) $(OBJS) diff --git a/main.c b/main.c new file mode 100644 index 00000000..cafd0c9a --- /dev/null +++ b/main.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include "procfs.h" + +static error_t get_contents (void *hook, void **contents, size_t *contents_len) +{ + static const char hello[] = "Hello, World!\n"; + *contents = (void *) hello; + *contents_len = sizeof hello - 1; + return 0; +} + +static error_t get_entries (void *hook, void **contents, size_t *contents_len) +{ + static const char entries[] = "hello"; + *contents = (void *) entries; + *contents_len = sizeof entries; + return 0; +} + +static error_t lookup (void *hook, const char *name, struct node **np) +{ + static const struct procfs_node_ops ops = { .get_contents = get_contents }; + + if (strcmp (name, "hello")) + return ENOENT; + + *np = procfs_make_node (&ops, NULL); + if (! *np) + return ENOMEM; + + return 0; +} + +int main (int argc, char **argv) +{ + static const struct procfs_node_ops ops = { + .get_contents = get_entries, + .lookup = lookup, + }; + mach_port_t bootstrap; + + argp_parse (&netfs_std_startup_argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "Must be started as a translator"); + + netfs_init (); + netfs_root_node = procfs_make_node (&ops, NULL); + + netfs_startup (bootstrap, 0); + for (;;) + netfs_server_loop (); +} + diff --git a/netfs.c b/netfs.c new file mode 100644 index 00000000..58665085 --- /dev/null +++ b/netfs.c @@ -0,0 +1,391 @@ +#include +#include +#include +#include +#include +#include +#include "procfs.h" + +#define PROCFS_SERVER_NAME "procfs" +#define PROCFS_SERVER_VERSION "0.1.0" +#define PROCFS_MAXSYMLINKS 16 + + +/* Interesting libnetfs callback functions. */ + +/* The user must define this variable. Set this to the name of the + filesystem server. */ +char *netfs_server_name = PROCFS_SERVER_NAME; + +/* The user must define this variables. Set this to be the server + version number. */ +char *netfs_server_version = PROCFS_SERVER_VERSION; + +/* Maximum number of symlinks to follow before returning ELOOP. */ +int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; + +/* The user must define this function. Read from the locked file NP + for user CRED starting at OFFSET and continuing for up to *LEN + bytes. Put the data at DATA. Set *LEN to the amount successfully + read upon return. */ +error_t netfs_attempt_read (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + char *contents; + size_t contents_len; + error_t err; + + err = procfs_get_contents (np, (void **) &contents, &contents_len); + if (err) + return err; + + contents += offset; + contents_len -= offset; + + if (*len > contents_len) + *len = contents_len; + if (*len < 0) + *len = 0; + + memcpy (data, contents, *len); + return 0; +} + +/* The user must define this function. Read the contents of locked + node NP (a symlink), for USER, into BUF. */ +error_t netfs_attempt_readlink (struct iouser *user, struct node *np, + char *buf) +{ + return EIO; +} + +/* Helper function for netfs_get_dirents() below. CONTENTS is an argz + vector of directory entry names, as returned by procfs_get_contents(). + Convert at most NENTRIES of them to dirent structures, put them in + DATA (if not NULL), write the number of entries processed in *AMT and + return the required/used space in DATACNT. */ +static int putentries (char *contents, size_t contents_len, int nentries, + char *data, mach_msg_type_number_t *datacnt) +{ + int i; + + *datacnt = 0; + for (i = 0; contents_len && (nentries < 0 || i < nentries); i++) + { + int namlen = strlen (contents); + int reclen = sizeof (struct dirent) + namlen; + + if (data) + { + struct dirent *d = (struct dirent *) (data + *datacnt); + d->d_fileno = 42; /* XXX */ + d->d_namlen = namlen; + d->d_reclen = reclen; + d->d_type = DT_UNKNOWN; + strcpy (d->d_name, contents); + } + + *datacnt += reclen; + contents += namlen + 1; + contents_len -= namlen + 1; + } + + return i; +} + +/* The user must define this function. Fill the array *DATA of size + BUFSIZE with up to NENTRIES dirents from DIR (which is locked) + starting with entry ENTRY for user CRED. The number of entries in + the array is stored in *AMT and the number of bytes in *DATACNT. + If the supplied buffer is not large enough to hold the data, it + should be grown. */ +error_t netfs_get_dirents (struct iouser *cred, struct node *dir, + int entry, int nentries, char **data, + mach_msg_type_number_t *datacnt, + vm_size_t bufsize, int *amt) +{ + char *contents; + size_t contents_len; + error_t err; + + err = procfs_get_contents (dir, (void **) &contents, &contents_len); + if (err) + return err; + + /* We depend on the fact that CONTENTS is terminated. */ + assert (contents_len == 0 || contents[contents_len - 1] == '\0'); + + /* Skip to the first requested entry. */ + while (contents_len && entry--) + { + int ofs = strlen (contents) + 1; + contents += ofs; + contents_len -= ofs; + } + + /* Allocate a buffer if necessary. */ + putentries (contents, contents_len, nentries, NULL, datacnt); + if (bufsize < *datacnt) + { + void *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); + if (n == MAP_FAILED) + return ENOMEM; + + *data = n; + } + + /* Do the actual conversion. */ + *amt = putentries (contents, contents_len, nentries, *data, datacnt); + + return 0; +} + +/* The user must define this function. Lookup NAME in DIR (which is + locked) for USER; set *NP to the found name upon return. If the + name was not found, then return ENOENT. On any error, clear *NP. + (*NP, if found, should be locked and a reference to it generated. + This call should unlock DIR no matter what.) */ +error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **np) +{ + error_t err; + + err = procfs_lookup (dir, name, np); + if (! err) + mutex_lock (&(*np)->lock); + + mutex_unlock (&dir->lock); + return err; +} + +/* The user must define this function. Node NP has no more references; + free all its associated storage. */ +void netfs_node_norefs (struct node *np) +{ + procfs_cleanup (np); + free (np); +} + + +/* Libnetfs callbacks managed with libfshelp. */ + +/* The user must define this function. Locked node NP is being opened + by USER, with FLAGS. NEWNODE is nonzero if we just created this + node. Return an error if we should not permit the open to complete + because of a permission restriction. */ +error_t netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = 0; + if (!err && (flags & O_READ)) + err = fshelp_access (&np->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&np->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&np->nn_stat, S_IEXEC, user); + return err; +} + +/* The user must define this function. Return the valid access + types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for + locked file NP and user CRED. */ +error_t netfs_report_access (struct iouser *cred, struct node *np, + int *types) +{ + *types = 0; + if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + return 0; +} + + +/* Trivial or unsupported libnetfs callbacks. */ + +/* The user must define this function. Make sure that NP->nn_stat is + filled with the most current information. CRED identifies the user + responsible for the operation. NP is locked. */ +error_t netfs_validate_stat (struct node *np, struct iouser *cred) +{ + return 0; +} + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NP, to change + the owner to UID and the group to GID. */ +error_t netfs_attempt_chown (struct iouser *cred, struct node *np, + uid_t uid, uid_t gid) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chauthor + call for the user specified by CRED on locked node NP, thereby + changing the author to AUTHOR. */ +error_t netfs_attempt_chauthor (struct iouser *cred, struct node *np, + uid_t author) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NODE, to change + the mode to MODE. Unlike the normal Unix and Hurd meaning of + chmod, this function is also used to attempt to change files into + other types. If such a transition is attempted which is + impossible, then return EOPNOTSUPP. */ +error_t netfs_attempt_chmod (struct iouser *cred, struct node *np, + mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn locked node NP + (user CRED) into a symlink with target NAME. */ +error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn NODE (user + CRED) into a device. TYPE is either S_IFBLK or S_IFCHR. NP is + locked. */ +error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np, + mode_t type, dev_t indexes) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chflags + call for the user specified by CRED on locked node NP, to change + the flags to FLAGS. */ +error_t netfs_attempt_chflags (struct iouser *cred, struct node *np, + int flags) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a utimes + call for the user specified by CRED on locked node NP, to change + the atime to ATIME and the mtime to MTIME. If ATIME or MTIME is + null, then set to the current time. */ +error_t netfs_attempt_utimes (struct iouser *cred, struct node *np, + struct timespec *atime, struct timespec *mtime) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to set the + size of the locked file NP (for user CRED) to SIZE bytes long. */ +error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, + loff_t size) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to fetch + filesystem status information for the remote filesystem, for the + user CRED. NP is locked. */ +error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, + fsys_statfsbuf_t *st) +{ + return ENOSYS; +} + +/* The user must define this function. This should sync the locked + file NP completely to disk, for the user CRED. If WAIT is set, + return only after the sync is completely finished. */ +error_t netfs_attempt_sync (struct iouser *cred, struct node *np, + int wait) +{ + return 0; +} + +/* The user must define this function. This should sync the entire + remote filesystem. If WAIT is set, return only after the sync is + completely finished. */ +error_t netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + +/* The user must define this function. Delete NAME in DIR (which is + locked) for USER. */ +error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to rename the + directory FROMDIR to TODIR. Note that neither of the specific nodes + are locked. */ +error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, + char *fromname, struct node *todir, + char *toname, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a new + directory named NAME in DIR (which is locked) for USER with mode + MODE. */ +error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, + char *name, mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to remove directory + named NAME in DIR (which is locked) for USER. */ +error_t netfs_attempt_rmdir (struct iouser *user, + struct node *dir, char *name) +{ + return EROFS; +} + + +/* The user must define this function. Create a link in DIR with name + NAME to FILE for USER. Note that neither DIR nor FILE are + locked. If EXCL is set, do not delete the target. Return EEXIST if + NAME is already found in DIR. */ +error_t netfs_attempt_link (struct iouser *user, struct node *dir, + struct node *file, char *name, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create an anonymous + file related to DIR (which is locked) for USER with MODE. Set *NP + to the returned file upon success. No matter what, unlock DIR. */ +error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, + mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a file named + NAME in DIR (which is locked) for USER with MODE. Set *NP to the + new node upon return. On any error, clear *NP. *NP should be + locked on success; no matter what, unlock DIR before returning. */ +error_t netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Write to the locked file NP + for user CRED starting at OFSET and continuing for up to *LEN bytes + from DATA. Set *LEN to the amount successfully written upon + return. */ +error_t netfs_attempt_write (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + return EROFS; +} + + diff --git a/procfs.c b/procfs.c new file mode 100644 index 00000000..304befba --- /dev/null +++ b/procfs.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include "procfs.h" + +struct netnode +{ + const struct procfs_node_ops *ops; + void *hook; + + /* (cached) contents of the node */ + void *contents; + size_t contents_len; +}; + +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) +{ + struct netnode *nn; + struct node *np; + + nn = malloc (sizeof *nn); + if (! nn) + return NULL; + + memset (nn, 0, sizeof *nn); + nn->ops = ops; + nn->hook = hook; + + np = netfs_make_node (nn); + if (! np) + { + free (nn); + return NULL; + } + + np->nn = nn; + memset (&np->nn_stat, 0, sizeof np->nn_stat); + np->nn_translated = 0; + + if (np->nn->ops->lookup) + np->nn_stat.st_mode = S_IFDIR | 0555; + else + np->nn_stat.st_mode = S_IFREG | 0444; + + return np; +} + +error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) +{ + if (! np->nn->contents && np->nn->ops->get_contents) + { + void *contents; + size_t contents_len; + error_t err; + + err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); + if (err) + return err; + + np->nn->contents = contents; + np->nn->contents_len = contents_len; + } + + *data = np->nn->contents; + *data_len = np->nn->contents_len; + return 0; +} + +error_t procfs_lookup (struct node *np, const char *name, struct node **npp) +{ + error_t err = ENOENT; + + if (np->nn->ops->lookup) + err = np->nn->ops->lookup (np->nn->hook, name, npp); + + return err; +} + +void procfs_cleanup (struct node *np) +{ + if (np->nn->contents && np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->contents); + + if (np->nn->ops->cleanup) + np->nn->ops->cleanup (np->nn->hook); + + free (np->nn); +} diff --git a/procfs.h b/procfs.h new file mode 100644 index 00000000..0557b6d1 --- /dev/null +++ b/procfs.h @@ -0,0 +1,40 @@ +#include +#include + + +/* Interface for the procfs side. */ + +/* Any of these callback functions can be omitted, in which case + reasonable defaults will be used. The initial file mode and type + depend on whether a lookup function is provided, but can be + overridden in update_stat(). */ +struct procfs_node_ops +{ + /* Fetch the contents of a node. A pointer to the contents should be + returned in *CONTENTS and their length in *CONTENTS_LEN. The exact + nature of these data depends on whether the node is a regular file, + symlink or directory, as determined by the file mode in + netnode->nn_stat. For regular files and symlinks, they are what + you would expect; for directories, they are an argz vector of the + names of the entries. */ + error_t (*get_contents) (void *hook, void **contents, size_t *contents_len); + void (*cleanup_contents) (void *contents); + + /* Lookup NAME in this directory, and store the result in *np. The + returned node should be created by lookup() using procfs_make_node() + or a derived function. */ + error_t (*lookup) (void *hook, const char *name, struct node **np); + + /* Destroy this node. */ + void (*cleanup) (void *hook); +}; + +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); + + +/* Interface for the libnetfs side. */ + +error_t procfs_get_contents (struct node *np, void **data, size_t *data_len); +error_t procfs_lookup (struct node *np, const char *name, struct node **npp); +void procfs_cleanup (struct node *np); + -- cgit v1.2.3 From 0a4c7a1ca8bff458eb11322d2c94ec9ffd832524 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 10:10:22 +0000 Subject: Add a helper module for simple regular files * procfs_file.h: New file, declares procfs_file_make_node. * procfs_file.c: New file, implements procfs_file_make_node. * main.c: Use them. * Makefile: Add the procfs_file module. --- Makefile | 2 +- main.c | 13 ++----------- procfs_file.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs_file.h | 6 ++++++ 4 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 procfs_file.c create mode 100644 procfs_file.h diff --git a/Makefile b/Makefile index 4ae336a3..e7282ce7 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ TARGET = procfs -OBJS = procfs.o netfs.o main.o +OBJS = procfs.o netfs.o procfs_file.o main.o LIBS = -lnetfs CC = gcc diff --git a/main.c b/main.c index cafd0c9a..457cf6a7 100644 --- a/main.c +++ b/main.c @@ -3,14 +3,7 @@ #include #include #include "procfs.h" - -static error_t get_contents (void *hook, void **contents, size_t *contents_len) -{ - static const char hello[] = "Hello, World!\n"; - *contents = (void *) hello; - *contents_len = sizeof hello - 1; - return 0; -} +#include "procfs_file.h" static error_t get_entries (void *hook, void **contents, size_t *contents_len) { @@ -22,12 +15,10 @@ static error_t get_entries (void *hook, void **contents, size_t *contents_len) static error_t lookup (void *hook, const char *name, struct node **np) { - static const struct procfs_node_ops ops = { .get_contents = get_contents }; - if (strcmp (name, "hello")) return ENOENT; - *np = procfs_make_node (&ops, NULL); + *np = procfs_file_make_node ("Hello, World!\n", -1, NULL); if (! *np) return ENOMEM; diff --git a/procfs_file.c b/procfs_file.c new file mode 100644 index 00000000..62419ee5 --- /dev/null +++ b/procfs_file.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include "procfs.h" +#include "procfs_file.h" + +struct procfs_file +{ + void *contents; + size_t len; + void (*cleanup)(void *contents); +}; + +error_t +procfs_file_getcontents (void *hook, void **contents, size_t *contents_len) +{ + struct procfs_file *f = hook; + + *contents = f->contents; + *contents_len = f->len; + return 0; +} + +void +procfs_file_cleanup (void *hook) +{ + struct procfs_file *f = hook; + + if (f->cleanup) + f->cleanup (f->contents); + + free (f); +} + +struct node * +procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)) +{ + static const struct procfs_node_ops ops = { + .get_contents = procfs_file_getcontents, + .cleanup = procfs_file_cleanup, + }; + struct procfs_file *f; + struct node *np; + + f = malloc (sizeof *f); + if (! f) + return NULL; + + f->contents = contents; + f->len = (len >= 0) ? len : strlen (f->contents); + f->cleanup = cleanup; + + np = procfs_make_node (&ops, f); + if (! np) + free (f); + + return np; +} + diff --git a/procfs_file.h b/procfs_file.h new file mode 100644 index 00000000..b615db93 --- /dev/null +++ b/procfs_file.h @@ -0,0 +1,6 @@ +/* Create a new regular file with the given CONTENTS. If LEN is negative, + CONTENTS is considered as a string and the file stops at the first + nul char. If CLEANUP is non-NULL, it is passed CONTENTS when the + node is destroyed. */ +struct node * +procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)); -- cgit v1.2.3 From 56ba0ac1e3a3f32763060ab862a6f0054c8a50bf Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 10:26:12 +0000 Subject: Implement simple directories * procfs_dir.h: New file; declare the procfs_dir_make_node function, based on the procfs_dir_entry structure. * procfs_dir.c: New file; implement simple directories. * Makefile: Add the procfs_dir module. * main.c: Use it. --- Makefile | 2 +- main.c | 29 ++++++---------------- procfs_dir.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs_dir.h | 15 ++++++++++++ 4 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 procfs_dir.c create mode 100644 procfs_dir.h diff --git a/Makefile b/Makefile index e7282ce7..ab6e406c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ TARGET = procfs -OBJS = procfs.o netfs.o procfs_file.o main.o +OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o main.o LIBS = -lnetfs CC = gcc diff --git a/main.c b/main.c index 457cf6a7..62e440d4 100644 --- a/main.c +++ b/main.c @@ -4,32 +4,19 @@ #include #include "procfs.h" #include "procfs_file.h" +#include "procfs_dir.h" -static error_t get_entries (void *hook, void **contents, size_t *contents_len) +static struct node *make_file (void *dir_hook, void *ent_hook) { - static const char entries[] = "hello"; - *contents = (void *) entries; - *contents_len = sizeof entries; - return 0; -} - -static error_t lookup (void *hook, const char *name, struct node **np) -{ - if (strcmp (name, "hello")) - return ENOENT; - - *np = procfs_file_make_node ("Hello, World!\n", -1, NULL); - if (! *np) - return ENOMEM; - - return 0; + return procfs_file_make_node (ent_hook, -1, NULL); } int main (int argc, char **argv) { - static const struct procfs_node_ops ops = { - .get_contents = get_entries, - .lookup = lookup, + static const struct procfs_dir_entry entries[] = { + { "hello", make_file, "Hello, World!\n" }, + { "goodbye", make_file, "Goodbye, cruel World!\n" }, + { } }; mach_port_t bootstrap; @@ -40,7 +27,7 @@ int main (int argc, char **argv) error (1, 0, "Must be started as a translator"); netfs_init (); - netfs_root_node = procfs_make_node (&ops, NULL); + netfs_root_node = procfs_dir_make_node (entries, NULL); netfs_startup (bootstrap, 0); for (;;) diff --git a/procfs_dir.c b/procfs_dir.c new file mode 100644 index 00000000..4d4faa28 --- /dev/null +++ b/procfs_dir.c @@ -0,0 +1,79 @@ +#include +#include +#include "procfs.h" +#include "procfs_dir.h" + +struct procfs_dir_node +{ + const struct procfs_dir_entry *entries; + void *hook; +}; + +static error_t +procfs_dir_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct procfs_dir_node *dn = hook; + const struct procfs_dir_entry *ent; + char *pos; + + *contents_len = 0; + for (ent = dn->entries; ent->name; ent++) + *contents_len += strlen (ent->name) + 1; + + *contents = malloc (*contents_len); + if (! *contents) + return ENOMEM; + + pos = *contents; + for (ent = dn->entries; ent->name; ent++) + { + strcpy (pos, ent->name); + pos += strlen (ent->name) + 1; + } + + return 0; +} + +static error_t +procfs_dir_lookup (void *hook, const char *name, struct node **np) +{ + struct procfs_dir_node *dn = hook; + const struct procfs_dir_entry *ent; + + for (ent = dn->entries; ent->name && strcmp (name, ent->name); ent++); + if (! ent->name) + return ENOENT; + + *np = ent->make_node (dn->hook, ent->hook); + if (! *np) + return ENOMEM; + + return 0; +} + +struct node * +procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = procfs_dir_get_contents, + .lookup = procfs_dir_lookup, + .cleanup_contents = free, + .cleanup = free, + }; + struct procfs_dir_node *dn; + struct node *np; + + dn = malloc (sizeof *dn); + if (! dn) + return NULL; + + dn->entries = entries; + dn->hook = dir_hook; + + np = procfs_make_node (&ops, dn); + if (! np) + free (dn); + + return np; +} + diff --git a/procfs_dir.h b/procfs_dir.h new file mode 100644 index 00000000..1ba45ad0 --- /dev/null +++ b/procfs_dir.h @@ -0,0 +1,15 @@ + +/* Each entry associates a name with a callback function for creating new + nodes corresponding to that entry. */ +struct procfs_dir_entry +{ + const char *name; + struct node *(*make_node)(void *dir_hook, void *entry_hook); + void *hook; +}; + +/* A simple directory is built from a table of entries. The table is + terminated by a null NAME pointer. */ +struct node * +procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook); + -- cgit v1.2.3 From 3907bbbcfb806799a5349e46a2b804307a0e9836 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 12:49:33 +0000 Subject: Add the list of processes as a directory * proclist.h, proclist.c: New files. * main.c: Add a proclist directory based on them. * Makefile: Include the proclist module. --- Makefile | 2 +- main.c | 8 +++++++ proclist.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ proclist.h | 2 ++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 proclist.c create mode 100644 proclist.h diff --git a/Makefile b/Makefile index ab6e406c..1fe7415b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ TARGET = procfs -OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o main.o +OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o proclist.o main.o LIBS = -lnetfs CC = gcc diff --git a/main.c b/main.c index 62e440d4..bc5c865c 100644 --- a/main.c +++ b/main.c @@ -1,21 +1,29 @@ #include +#include #include #include #include #include "procfs.h" #include "procfs_file.h" #include "procfs_dir.h" +#include "proclist.h" static struct node *make_file (void *dir_hook, void *ent_hook) { return procfs_file_make_node (ent_hook, -1, NULL); } +static struct node *make_proclist (void *dir_hook, void *ent_hook) +{ + return proclist_make_node (getproc ()); +} + int main (int argc, char **argv) { static const struct procfs_dir_entry entries[] = { { "hello", make_file, "Hello, World!\n" }, { "goodbye", make_file, "Goodbye, cruel World!\n" }, + { "proclist", make_proclist, }, { } }; mach_port_t bootstrap; diff --git a/proclist.c b/proclist.c new file mode 100644 index 00000000..4dd6ab31 --- /dev/null +++ b/proclist.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include "procfs.h" +#include "procfs_file.h" +#include "procfs_dir.h" + +#define PID_STR_SIZE (3 * sizeof (pid_t) + 1) + +struct proclist_node +{ + process_t process; +}; + +static error_t +proclist_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct proclist_node *pl = hook; + pidarray_t pids; + mach_msg_type_number_t num_pids; + error_t err; + int i; + + num_pids = 0; + err = proc_getallpids (pl->process, &pids, &num_pids); + if (err) + return EIO; + + *contents = malloc (num_pids * PID_STR_SIZE); + if (*contents) + { + *contents_len = 0; + for (i=0; i < num_pids; i++) + { + int n = sprintf (*contents + *contents_len, "%d", pids[i]); + assert (n >= 0); + *contents_len += (n + 1); + } + } + else + err = ENOMEM; + + vm_deallocate (mach_task_self (), (vm_address_t) pids, num_pids * sizeof pids[0]); + return err; +} + +static error_t +proclist_lookup (void *hook, const char *name, struct node **np) +{ + *np = procfs_file_make_node ("Ceci n'est pas un processus\n", -1, NULL); + return *np ? 0 : ENOMEM; +} + +struct node * +proclist_make_node (process_t process) +{ + static const struct procfs_node_ops ops = { + .get_contents = proclist_get_contents, + .lookup = proclist_lookup, + .cleanup_contents = free, + .cleanup = free, + }; + struct proclist_node *pl; + struct node *np; + + pl = malloc (sizeof *pl); + if (! pl) + return NULL; + + memset (pl, 0, sizeof *pl); + pl->process = process; + + np = procfs_make_node (&ops, pl); + if (! np) + free (pl); + + return np; +} + diff --git a/proclist.h b/proclist.h new file mode 100644 index 00000000..a766d50e --- /dev/null +++ b/proclist.h @@ -0,0 +1,2 @@ +#include +struct node *proclist_make_node (process_t process); -- cgit v1.2.3 From ac75e3648e6a9c3a8cf45d5fa491abd3a3ab3613 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 16:48:55 +0000 Subject: Fix the failure semantics of procfs_make_node * procfs.c (procfs_make_node): Invoke the cleanup callback on failure, so that callers don't have to. * procfs.h: Document the change. * procfs_dir.c (procfs_dir_make_node), procfs_file.c (procfs_file_make_node), proclist.c (proclist_make_node): Update to reflect the change. --- procfs.c | 14 +++++++++----- procfs.h | 2 ++ procfs_dir.c | 7 +------ procfs_file.c | 7 +------ proclist.c | 7 +------ 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/procfs.c b/procfs.c index 304befba..0c57686b 100644 --- a/procfs.c +++ b/procfs.c @@ -22,7 +22,7 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) nn = malloc (sizeof *nn); if (! nn) - return NULL; + goto fail; memset (nn, 0, sizeof *nn); nn->ops = ops; @@ -30,10 +30,7 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) np = netfs_make_node (nn); if (! np) - { - free (nn); - return NULL; - } + goto fail; np->nn = nn; memset (&np->nn_stat, 0, sizeof np->nn_stat); @@ -45,6 +42,13 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) np->nn_stat.st_mode = S_IFREG | 0444; return np; + +fail: + if (ops->cleanup) + ops->cleanup (hook); + + free (nn); + return NULL; } error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) diff --git a/procfs.h b/procfs.h index 0557b6d1..21b0f93d 100644 --- a/procfs.h +++ b/procfs.h @@ -29,6 +29,8 @@ struct procfs_node_ops void (*cleanup) (void *hook); }; +/* Create a new node and return it. Returns NULL if it fails to allocate + enough memory. In this case, ops->cleanup will be invoked. */ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); diff --git a/procfs_dir.c b/procfs_dir.c index 4d4faa28..62a45b1b 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -61,7 +61,6 @@ procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook) .cleanup = free, }; struct procfs_dir_node *dn; - struct node *np; dn = malloc (sizeof *dn); if (! dn) @@ -70,10 +69,6 @@ procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook) dn->entries = entries; dn->hook = dir_hook; - np = procfs_make_node (&ops, dn); - if (! np) - free (dn); - - return np; + return procfs_make_node (&ops, dn); } diff --git a/procfs_file.c b/procfs_file.c index 62419ee5..cb0488e9 100644 --- a/procfs_file.c +++ b/procfs_file.c @@ -40,7 +40,6 @@ procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)) .cleanup = procfs_file_cleanup, }; struct procfs_file *f; - struct node *np; f = malloc (sizeof *f); if (! f) @@ -50,10 +49,6 @@ procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)) f->len = (len >= 0) ? len : strlen (f->contents); f->cleanup = cleanup; - np = procfs_make_node (&ops, f); - if (! np) - free (f); - - return np; + return procfs_make_node (&ops, f); } diff --git a/proclist.c b/proclist.c index 4dd6ab31..16cef9d6 100644 --- a/proclist.c +++ b/proclist.c @@ -63,7 +63,6 @@ proclist_make_node (process_t process) .cleanup = free, }; struct proclist_node *pl; - struct node *np; pl = malloc (sizeof *pl); if (! pl) @@ -72,10 +71,6 @@ proclist_make_node (process_t process) memset (pl, 0, sizeof *pl); pl->process = process; - np = procfs_make_node (&ops, pl); - if (! np) - free (pl); - - return np; + return procfs_make_node (&ops, pl); } -- cgit v1.2.3 From ba139824fa06a97f2a3b1cc4c6085d10a83ec2b9 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 17:27:54 +0000 Subject: More cleanup possibilities * procfs.c, procfs.h: Extend the signature of the cleanup_contents callback in the procfs_node_ops structure to include the hook and contents_len. (cleanup_contents_with_free, cleanup_contents_with_vm_deallocate): New functions, can be used as a cleanup_contents callback for simple cases. * procfs_dir.c, procfs_dir.h (procfs_dir_make_node): Update, add a cleanup callback, make sure the cleanup callback is invoked if there is an error. * proclist.c (proclist_make_node), main.c (main): Update to match the new interfaces. --- main.c | 2 +- procfs.c | 15 ++++++++++++++- procfs.h | 6 +++++- procfs_dir.c | 27 +++++++++++++++++++++++---- procfs_dir.h | 9 +++++++-- proclist.c | 2 +- 6 files changed, 51 insertions(+), 10 deletions(-) diff --git a/main.c b/main.c index bc5c865c..4350eff5 100644 --- a/main.c +++ b/main.c @@ -35,7 +35,7 @@ int main (int argc, char **argv) error (1, 0, "Must be started as a translator"); netfs_init (); - netfs_root_node = procfs_dir_make_node (entries, NULL); + netfs_root_node = procfs_dir_make_node (entries, NULL, NULL); netfs_startup (bootstrap, 0); for (;;) diff --git a/procfs.c b/procfs.c index 0c57686b..755e0519 100644 --- a/procfs.c +++ b/procfs.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "procfs.h" @@ -15,6 +16,18 @@ struct netnode size_t contents_len; }; +void +procfs_cleanup_contents_with_free (void *hook, void *cont, size_t len) +{ + free (cont); +} + +void +procfs_cleanup_contents_with_vm_deallocate (void *hook, void *cont, size_t len) +{ + vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); +} + struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) { struct netnode *nn; @@ -85,7 +98,7 @@ error_t procfs_lookup (struct node *np, const char *name, struct node **npp) void procfs_cleanup (struct node *np) { if (np->nn->contents && np->nn->ops->cleanup_contents) - np->nn->ops->cleanup_contents (np->nn->contents); + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); if (np->nn->ops->cleanup) np->nn->ops->cleanup (np->nn->hook); diff --git a/procfs.h b/procfs.h index 21b0f93d..3cb3223d 100644 --- a/procfs.h +++ b/procfs.h @@ -18,7 +18,7 @@ struct procfs_node_ops you would expect; for directories, they are an argz vector of the names of the entries. */ error_t (*get_contents) (void *hook, void **contents, size_t *contents_len); - void (*cleanup_contents) (void *contents); + void (*cleanup_contents) (void *hook, void *contents, size_t contents_len); /* Lookup NAME in this directory, and store the result in *np. The returned node should be created by lookup() using procfs_make_node() @@ -29,6 +29,10 @@ struct procfs_node_ops void (*cleanup) (void *hook); }; +/* These helper functions can be used as procfs_node_ops.cleanup_contents. */ +void procfs_cleanup_contents_with_free (void *, void *, size_t); +void procfs_cleanup_contents_with_vm_deallocate (void *, void *, size_t); + /* Create a new node and return it. Returns NULL if it fails to allocate enough memory. In this case, ops->cleanup will be invoked. */ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); diff --git a/procfs_dir.c b/procfs_dir.c index 62a45b1b..b7fb28fa 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -7,6 +7,7 @@ struct procfs_dir_node { const struct procfs_dir_entry *entries; void *hook; + void (*cleanup) (void *hook); }; static error_t @@ -51,23 +52,41 @@ procfs_dir_lookup (void *hook, const char *name, struct node **np) return 0; } +static void +procfs_dir_cleanup (void *hook) +{ + struct procfs_dir_node *dn = hook; + + if (dn->cleanup) + dn->cleanup (dn->hook); + + free (dn); +} + struct node * -procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook) +procfs_dir_make_node (const struct procfs_dir_entry *entries, + void *dir_hook, void (*cleanup) (void *dir_hook)) { static const struct procfs_node_ops ops = { .get_contents = procfs_dir_get_contents, .lookup = procfs_dir_lookup, - .cleanup_contents = free, - .cleanup = free, + .cleanup_contents = procfs_cleanup_contents_with_free, + .cleanup = procfs_dir_cleanup, }; struct procfs_dir_node *dn; dn = malloc (sizeof *dn); if (! dn) - return NULL; + { + if (cleanup) + cleanup (dir_hook); + + return NULL; + } dn->entries = entries; dn->hook = dir_hook; + dn->cleanup = cleanup; return procfs_make_node (&ops, dn); } diff --git a/procfs_dir.h b/procfs_dir.h index 1ba45ad0..4eb934e0 100644 --- a/procfs_dir.h +++ b/procfs_dir.h @@ -9,7 +9,12 @@ struct procfs_dir_entry }; /* A simple directory is built from a table of entries. The table is - terminated by a null NAME pointer. */ + terminated by a null NAME pointer. The DIR_HOOK is passed the + MAKE_NODE callback function of looked up procfs_dir_entries, and to + the provided CLEANUP function when the directory is destroyed. + Returns the new directory node. If not enough memory can be + allocated, CLEANUP is invoked immediately and NULL is returned. */ struct node * -procfs_dir_make_node (const struct procfs_dir_entry *entries, void *dir_hook); +procfs_dir_make_node (const struct procfs_dir_entry *entries, + void *dir_hook, void (*cleanup) (void *dir_hook)); diff --git a/proclist.c b/proclist.c index 16cef9d6..e009ebdd 100644 --- a/proclist.c +++ b/proclist.c @@ -59,7 +59,7 @@ proclist_make_node (process_t process) static const struct procfs_node_ops ops = { .get_contents = proclist_get_contents, .lookup = proclist_lookup, - .cleanup_contents = free, + .cleanup_contents = procfs_cleanup_contents_with_free, .cleanup = free, }; struct proclist_node *pl; -- cgit v1.2.3 From c62d5ff73ceaad21bb0784ed6098d307a625b10d Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 20:34:58 +0000 Subject: Add real process nodes * process.c, process.h: New files, implement a process directory with cmdline and environ files. * Makefile: Add the process module. * proclist.c: Replace stub pid files with the real thing. --- Makefile | 2 +- process.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ process.h | 2 ++ proclist.c | 13 ++++++++--- 4 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 process.c create mode 100644 process.h diff --git a/Makefile b/Makefile index 1fe7415b..23815d7f 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ TARGET = procfs -OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o proclist.o main.o +OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o process.o proclist.o main.o LIBS = -lnetfs CC = gcc diff --git a/process.c b/process.c new file mode 100644 index 00000000..90ea511b --- /dev/null +++ b/process.c @@ -0,0 +1,79 @@ +#include +#include +#include "procfs.h" +#include "procfs_dir.h" +#include "process.h" + +struct process_node { + process_t procserv; + pid_t pid; +}; + + +/* The proc_getprocargs() and proc_getprocenv() calls have the same + prototype and we use them in the same way; namely, publish the data + they return as-is. We take advantage of this to have common code and + use a function pointer as the procfs_dir "entry hook" to choose the + call to use on a file by file basis. */ + +struct process_argz_node +{ + struct process_node pn; + error_t (*getargz) (process_t, pid_t, void **, mach_msg_type_number_t *); +}; + +static error_t +process_argz_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct process_argz_node *pz = hook; + error_t err; + + *contents_len = 0; + err = pz->getargz (pz->pn.procserv, pz->pn.pid, contents, contents_len); + if (err) + return EIO; + + return 0; +} + +static struct node * +process_argz_make_node (void *dir_hook, void *entry_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = process_argz_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_vm_deallocate, + .cleanup = free, + }; + struct process_argz_node *zn; + + zn = malloc (sizeof *zn); + if (! zn) + return NULL; + + memcpy (&zn->pn, dir_hook, sizeof zn->pn); + zn->getargz = entry_hook; + + return procfs_make_node (&ops, zn); +} + + +struct node * +process_make_node (process_t procserv, pid_t pid) +{ + static const struct procfs_dir_entry entries[] = { + { "cmdline", process_argz_make_node, proc_getprocargs, }, + { "environ", process_argz_make_node, proc_getprocenv, }, + { NULL, } + }; + struct process_node *pn; + + pn = malloc (sizeof *pn); + if (! pn) + return NULL; + + pn->procserv = procserv; + pn->pid = pid; + + return procfs_dir_make_node (entries, pn, free); +} + diff --git a/process.h b/process.h new file mode 100644 index 00000000..8ca7c851 --- /dev/null +++ b/process.h @@ -0,0 +1,2 @@ +struct node * +process_make_node (process_t procserv, pid_t pid); diff --git a/proclist.c b/proclist.c index e009ebdd..148e4bc3 100644 --- a/proclist.c +++ b/proclist.c @@ -4,8 +4,7 @@ #include #include #include "procfs.h" -#include "procfs_file.h" -#include "procfs_dir.h" +#include "process.h" #define PID_STR_SIZE (3 * sizeof (pid_t) + 1) @@ -49,7 +48,15 @@ proclist_get_contents (void *hook, void **contents, size_t *contents_len) static error_t proclist_lookup (void *hook, const char *name, struct node **np) { - *np = procfs_file_make_node ("Ceci n'est pas un processus\n", -1, NULL); + struct proclist_node *pl = hook; + char *endp; + pid_t pid; + + pid = strtol (name, &endp, 10); + if (name[0] == '0' || !name[0] || *endp) + return ENOENT; + + *np = process_make_node (pl->process, pid); return *np ? 0 : ENOMEM; } -- cgit v1.2.3 From 2fb5b93b4cd56fd68b525371ba63b0933b61d03a Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Tue, 17 Aug 2010 22:20:32 +0000 Subject: Fetch process information and reject the non-existing ones * process.c, process.h (process_make_node): Make static, include a procinfo structure into the node information. (process_lookup_pid): New function, replaces process_make_node as the outer interface, returns an error for non-existing processes. * proclist.c (proclist_lookup): Convert to the new interface. --- process.c | 34 +++++++++++++++++++++++++++++++--- process.h | 10 ++++++++-- proclist.c | 12 +++++++++--- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/process.c b/process.c index 90ea511b..612ed493 100644 --- a/process.c +++ b/process.c @@ -7,6 +7,7 @@ struct process_node { process_t procserv; pid_t pid; + struct procinfo info; }; @@ -57,8 +58,8 @@ process_argz_make_node (void *dir_hook, void *entry_hook) } -struct node * -process_make_node (process_t procserv, pid_t pid) +static struct node * +process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) { static const struct procfs_dir_entry entries[] = { { "cmdline", process_argz_make_node, proc_getprocargs, }, @@ -73,7 +74,34 @@ process_make_node (process_t procserv, pid_t pid) pn->procserv = procserv; pn->pid = pid; + memcpy (&pn->info, info, sizeof pn->info); - return procfs_dir_make_node (entries, pn, free); + return procfs_dir_make_node (entries, pn, process_cleanup); } +error_t +process_lookup_pid (process_t procserv, pid_t pid, struct node **np) +{ + procinfo_t info; + size_t info_sz; + data_t tw; + size_t tw_sz; + int flags; + error_t err; + + tw_sz = info_sz = 0, flags = 0; + err = proc_getprocinfo (procserv, pid, &flags, &info, &info_sz, &tw, &tw_sz); + if (err == ESRCH) + return ENOENT; + if (err) + return EIO; + + assert (info_sz * sizeof *info >= sizeof (struct procinfo)); + *np = process_make_node (procserv, pid, (struct procinfo *) info); + vm_deallocate (mach_task_self (), (vm_address_t) info, info_sz); + + if (! *np) + return ENOMEM; + + return 0; +} diff --git a/process.h b/process.h index 8ca7c851..abdaaa3b 100644 --- a/process.h +++ b/process.h @@ -1,2 +1,8 @@ -struct node * -process_make_node (process_t procserv, pid_t pid); +#include + +/* Create a node for a directory representing information available at + the proc server PROC for the given PID. On success, returns the + newly created node in *NP. */ +error_t +process_lookup_pid (process_t proc, pid_t pid, struct node **np); + diff --git a/proclist.c b/proclist.c index 148e4bc3..75b61a2e 100644 --- a/proclist.c +++ b/proclist.c @@ -52,12 +52,18 @@ proclist_lookup (void *hook, const char *name, struct node **np) char *endp; pid_t pid; + /* Self-lookups should not end up here. */ + assert (name[0]); + + /* No leading zeros allowed */ + if (name[0] == '0' && name[1]) + return ENOENT; + pid = strtol (name, &endp, 10); - if (name[0] == '0' || !name[0] || *endp) + if (*endp) return ENOENT; - *np = process_make_node (pl->process, pid); - return *np ? 0 : ENOMEM; + return process_lookup_pid (pl->process, pid, np); } struct node * -- cgit v1.2.3 From 56d301e212ac1964223cff8ef9c34889cb1a6e75 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 18 Aug 2010 20:44:54 +0000 Subject: Fuse the proclist into the root node * dircat.c, dircat.h: New files, merge directories. * Makefile: Add the dircat module. * main.c: Use dircat to merge the proclist into the root directory, instead of having it as a stand-alone one. * procfs.h, procfs.c: Add a "refresh hack" to have the contents of the root directory recreated on each request. * proclist.c (proclist_make_node): Enable the hack in question. --- Makefile | 3 +- dircat.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dircat.h | 9 ++++++ main.c | 41 +++++++++++++++++++------- procfs.c | 8 +++++ procfs.h | 8 +++++ proclist.c | 1 + 7 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 dircat.c create mode 100644 dircat.h diff --git a/Makefile b/Makefile index 23815d7f..97a61ed4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ TARGET = procfs -OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o process.o proclist.o main.o +OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o \ + process.o proclist.o dircat.o main.o LIBS = -lnetfs CC = gcc diff --git a/dircat.c b/dircat.c new file mode 100644 index 00000000..857ba72b --- /dev/null +++ b/dircat.c @@ -0,0 +1,99 @@ +#include +#include +#include "procfs.h" + +struct dircat_node +{ + struct node **dirs; +}; + +static error_t +dircat_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct dircat_node *dcn = hook; + int i, sz, pos; + error_t err; + + pos = 0; + *contents = malloc (sz = 512); + + for (i=0; dcn->dirs[i]; i++) + { + void *subcon; + size_t sublen; + + err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); + if (err) + { + free (*contents); + *contents = NULL; + return err; + } + + while (pos + sublen > sz) + *contents = realloc (*contents, sz *= 2); + + memcpy (*contents + pos, subcon, sublen); + pos += sublen; + } + + *contents_len = pos; + return 0; +} + +static error_t +dircat_lookup (void *hook, const char *name, struct node **np) +{ + struct dircat_node *dcn = hook; + error_t err; + int i; + + err = ENOENT; + for (i=0; err && dcn->dirs[i]; i++) + err = procfs_lookup (dcn->dirs[i], name, np); + + return err; +} + +static void +dircat_release_dirs (struct node **dirs) +{ + int i; + + for (i=0; dirs[i]; i++) + netfs_nrele (dirs[i]); +} + +static void +dircat_cleanup (void *hook) +{ + struct dircat_node *dcn = hook; + + dircat_release_dirs (dcn->dirs); + free (dcn); +} + +struct node * +dircat_make_node (struct node **dirs) +{ + static struct procfs_node_ops ops = { + .get_contents = dircat_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_free, + .lookup = dircat_lookup, + .cleanup = dircat_cleanup, + /* necessary so that it propagates to proclist */ + .enable_refresh_hack_and_break_readdir = 1, + }; + struct dircat_node *dcn; + + dcn = malloc (sizeof *dcn); + if (! dcn) + { + dircat_release_dirs (dirs); + return NULL; + } + + dcn->dirs = dirs; + return procfs_make_node (&ops, dcn); +} + diff --git a/dircat.h b/dircat.h new file mode 100644 index 00000000..cb228526 --- /dev/null +++ b/dircat.h @@ -0,0 +1,9 @@ +/* Append the contents of multiple directories. DIRS is a + NULL-terminated array of directory nodes. One reference is consumed + for each of them, even on ENOMEM, in which case NULL is returned. + DIRS has to be static data for now, or at list remain available and + unchanged for the duration of the created node's life. Strange + things will happen if they have entries with the same name or if one + of them is not a directory. */ +struct node * +dircat_make_node (struct node **dirs); diff --git a/main.c b/main.c index 4350eff5..e08bbdb7 100644 --- a/main.c +++ b/main.c @@ -7,25 +7,46 @@ #include "procfs_file.h" #include "procfs_dir.h" #include "proclist.h" +#include "dircat.h" -static struct node *make_file (void *dir_hook, void *ent_hook) +static struct node * +make_file (void *dir_hook, void *ent_hook) { return procfs_file_make_node (ent_hook, -1, NULL); } -static struct node *make_proclist (void *dir_hook, void *ent_hook) +error_t +root_make_node (struct node **np) { - return proclist_make_node (getproc ()); + static const struct procfs_dir_entry static_entries[] = { + { "hello", make_file, "Hello, World!\n" }, + { "goodbye", make_file, "Goodbye, cruel World!\n" }, + }; + /* We never have two root nodes alive simultaneously, so it's ok to + have this as static data. */ + static struct node *root_dirs[3]; + + root_dirs[0] = procfs_dir_make_node (static_entries, NULL, NULL); + if (! root_dirs[0]) + return ENOMEM; + + root_dirs[1] = proclist_make_node (getproc ()); + if (! root_dirs[1]) + { + netfs_nrele (root_dirs[0]); + return ENOMEM; + } + + root_dirs[2] = NULL; + *np = dircat_make_node (root_dirs); + if (! *np) + return ENOMEM; + + return 0; } int main (int argc, char **argv) { - static const struct procfs_dir_entry entries[] = { - { "hello", make_file, "Hello, World!\n" }, - { "goodbye", make_file, "Goodbye, cruel World!\n" }, - { "proclist", make_proclist, }, - { } - }; mach_port_t bootstrap; argp_parse (&netfs_std_startup_argp, argc, argv, 0, 0, 0); @@ -35,7 +56,7 @@ int main (int argc, char **argv) error (1, 0, "Must be started as a translator"); netfs_init (); - netfs_root_node = procfs_dir_make_node (entries, NULL, NULL); + root_make_node (&netfs_root_node); netfs_startup (bootstrap, 0); for (;;) diff --git a/procfs.c b/procfs.c index 755e0519..573bb72f 100644 --- a/procfs.c +++ b/procfs.c @@ -66,6 +66,14 @@ fail: error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) { + if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) + { + if (np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, + np->nn->contents_len); + np->nn->contents = NULL; + } + if (! np->nn->contents && np->nn->ops->get_contents) { void *contents; diff --git a/procfs.h b/procfs.h index 3cb3223d..b44db6e3 100644 --- a/procfs.h +++ b/procfs.h @@ -27,6 +27,14 @@ struct procfs_node_ops /* Destroy this node. */ void (*cleanup) (void *hook); + + /* FIXME: This is needed because the root node is persistent, and we + want the list of processes to be updated. However, this means that + readdir() on the root node runs the risk of returning incoherent + results if done in multiple runs and processes are added/removed in + the meantime. The right way to fix this is probably to add a + getroot() user hook function to libnetfs. */ + int enable_refresh_hack_and_break_readdir; }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ diff --git a/proclist.c b/proclist.c index 75b61a2e..94a7a04d 100644 --- a/proclist.c +++ b/proclist.c @@ -74,6 +74,7 @@ proclist_make_node (process_t process) .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, .cleanup = free, + .enable_refresh_hack_and_break_readdir = 1, }; struct proclist_node *pl; -- cgit v1.2.3 From ef370b0967024ced9296ff2028abc1b154c0a951 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 18 Aug 2010 21:06:08 +0000 Subject: Set the owner of process directories. * process.c (process_make_node): Use the owner_uid from the procinfo structure to set the owner of the created directory. --- process.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index 612ed493..411f4c4a 100644 --- a/process.c +++ b/process.c @@ -67,6 +67,7 @@ process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) { NULL, } }; struct process_node *pn; + struct node *np; pn = malloc (sizeof *pn); if (! pn) @@ -76,7 +77,10 @@ process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) pn->pid = pid; memcpy (&pn->info, info, sizeof pn->info); - return procfs_dir_make_node (entries, pn, process_cleanup); + np = procfs_dir_make_node (entries, pn, process_cleanup); + np->nn_stat.st_uid = pn->info.owner; + + return np; } error_t -- cgit v1.2.3 From 97e598086b37583585b455d2b804b6e49d97ea85 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 19 Aug 2010 03:12:27 +0000 Subject: Add a basic [pid]/stat file * process.c: Add a basic stat file. --- process.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/process.c b/process.c index 411f4c4a..2b89ef81 100644 --- a/process.c +++ b/process.c @@ -1,3 +1,4 @@ +#include #include #include #include "procfs.h" @@ -57,6 +58,113 @@ process_argz_make_node (void *dir_hook, void *entry_hook) return procfs_make_node (&ops, zn); } +/* The other files don't need any information besides the data in struct + process_node. Furthermore, their contents don't have any nul byte. + Consequently, we use a simple "multiplexer" based on the information + below. */ + +struct process_file_node +{ + struct process_node pn; + error_t (*get_contents) (struct process_node *pn, char **contents); +}; + +static char mapstate (int hurd_state) +{ + return '?'; +} + +static error_t +process_file_gc_stat (struct process_node *pn, char **contents) +{ + char *argz; + size_t argz_len; + int len; + + argz = NULL, argz_len = 0; + proc_getprocargs(pn->procserv, pn->pid, &argz, &argz_len); + + len = asprintf (contents, + "%d (%s) %c " /* pid, command, state */ + "%d %d %d " /* ppid, pgid, session */ + "%d %d " /* controling tty stuff */ + "%u " /* flags, as defined by */ + "%lu %lu %lu %lu " /* page fault counts */ + "%lu %lu %ld %ld " /* user/sys times, in sysconf(_SC_CLK_TCK) */ + "%ld %ld " /* scheduler params (priority, nice) */ + "%ld %ld " /* number of threads, [obsolete] */ + "%llu " /* start time since boot (jiffies) */ + "%lu %ld %lu " /* virtual size, rss, rss limit */ + "%lu %lu %lu %lu %lu " /* some vm addresses (code, stack, sp, pc) */ + "%lu %lu %lu %lu " /* pending, blocked, ignored and caught sigs */ + "%lu " /* wait channel */ + "%lu %lu " /* swap usage (not maintained in Linux) */ + "%d " /* exit signal, to be sent to the parent */ + "%d " /* last processor used */ + "%u %u " /* RT priority and policy */ + "%llu " /* aggregated block I/O delay */ + "\n", + pn->pid, argz ?: "", mapstate (pn->info.state), + pn->info.ppid, pn->info.pgrp, pn->info.session, + 0, 0, + 0, + 0L, 0L, 0L, 0L, + 0L, 0L, 0L, 0L, + 0L, 0L, + 0L, 0L, + 0LL, + 0L, 0L, 0L, + 0L, 0L, 0L, 0L, 0L, + 0L, 0L, 0L, 0L, + 0L, + 0L, 0L, + 0, + 0, + 0, 0, + 0LL); + + vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len); + + if (len < 0) + return ENOMEM; + + return 0; +} + +static error_t +process_file_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct process_file_node *fn = hook; + error_t err; + + err = fn->get_contents (&fn->pn, (char **) contents); + if (err) + return err; + + *contents_len = strlen (*contents); + return 0; +} + +static struct node * +process_file_make_node (void *dir_hook, void *entry_hook) +{ + static const struct procfs_node_ops ops = { + .get_contents = process_file_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_free, + .cleanup = free, + }; + struct process_file_node *fn; + + fn = malloc (sizeof *fn); + if (! fn) + return NULL; + + memcpy (&fn->pn, dir_hook, sizeof fn->pn); + fn->get_contents = entry_hook; + + return procfs_make_node (&ops, fn); +} + static struct node * process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) @@ -64,6 +172,7 @@ process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) static const struct procfs_dir_entry entries[] = { { "cmdline", process_argz_make_node, proc_getprocargs, }, { "environ", process_argz_make_node, proc_getprocenv, }, + { "stat", process_file_make_node, process_file_gc_stat, }, { NULL, } }; struct process_node *pn; -- cgit v1.2.3 From 4ac169deaa1eb4c8df4a49fc940700971fa5863a Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 19 Aug 2010 04:52:17 +0000 Subject: Add the dot entries to directories * procfs_dir.c (procfs_dir_get_contents): Prepend the . and .. entries to the ones from the given table. --- procfs_dir.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/procfs_dir.c b/procfs_dir.c index b7fb28fa..431fea3e 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -13,11 +13,12 @@ struct procfs_dir_node static error_t procfs_dir_get_contents (void *hook, void **contents, size_t *contents_len) { + static const char dot_dotdot[] = ".\0.."; struct procfs_dir_node *dn = hook; const struct procfs_dir_entry *ent; char *pos; - *contents_len = 0; + *contents_len = sizeof dot_dotdot; for (ent = dn->entries; ent->name; ent++) *contents_len += strlen (ent->name) + 1; @@ -25,7 +26,8 @@ procfs_dir_get_contents (void *hook, void **contents, size_t *contents_len) if (! *contents) return ENOMEM; - pos = *contents; + memcpy (*contents, dot_dotdot, sizeof dot_dotdot); + pos = *contents + sizeof dot_dotdot; for (ent = dn->entries; ent->name; ent++) { strcpy (pos, ent->name); -- cgit v1.2.3 From c6c770c33eb0f23e7e8e04976c711653cdf1a41d Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 19 Aug 2010 07:48:30 +0000 Subject: Implement lookup for . and .. * procfs.c (procfs_lookup): Keep track of the parent directory, implement the lookup of the dot-directories. (procfs_cleanup): Release the reference to the parent node, if applicable. * procfs.h: Add a comment about the parent reference. * netfs.c (netfs_attempt_lookup): Lock the looked up node after the directory has been unlocked, in case they are the same. --- netfs.c | 3 ++- procfs.c | 26 ++++++++++++++++++++++++-- procfs.h | 5 ++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/netfs.c b/netfs.c index 58665085..6b105f91 100644 --- a/netfs.c +++ b/netfs.c @@ -151,10 +151,11 @@ error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, error_t err; err = procfs_lookup (dir, name, np); + mutex_unlock (&dir->lock); + if (! err) mutex_lock (&(*np)->lock); - mutex_unlock (&dir->lock); return err; } diff --git a/procfs.c b/procfs.c index 573bb72f..c5f19497 100644 --- a/procfs.c +++ b/procfs.c @@ -14,6 +14,9 @@ struct netnode /* (cached) contents of the node */ void *contents; size_t contents_len; + + /* parent directory, if applicable */ + struct node *parent; }; void @@ -97,8 +100,24 @@ error_t procfs_lookup (struct node *np, const char *name, struct node **npp) { error_t err = ENOENT; - if (np->nn->ops->lookup) - err = np->nn->ops->lookup (np->nn->hook, name, npp); + if (err && ! strcmp (name, ".")) + { + netfs_nref(*npp = np); + err = 0; + } + + if (err && np->nn->parent && ! strcmp (name, "..")) + { + netfs_nref(*npp = np->nn->parent); + err = 0; + } + + if (err && np->nn->ops->lookup) + { + err = np->nn->ops->lookup (np->nn->hook, name, npp); + if (! err) + netfs_nref ((*npp)->nn->parent = np); + } return err; } @@ -111,5 +130,8 @@ void procfs_cleanup (struct node *np) if (np->nn->ops->cleanup) np->nn->ops->cleanup (np->nn->hook); + if (np->nn->parent) + netfs_nrele (np->nn->parent); + free (np->nn); } diff --git a/procfs.h b/procfs.h index b44db6e3..42eed0e2 100644 --- a/procfs.h +++ b/procfs.h @@ -22,7 +22,10 @@ struct procfs_node_ops /* Lookup NAME in this directory, and store the result in *np. The returned node should be created by lookup() using procfs_make_node() - or a derived function. */ + or a derived function. Note that the parent will be kept alive as + long as the child exists, so you can safely reference the parent's + data from the child. You may want to consider locking if there's + any mutation going on, though. */ error_t (*lookup) (void *hook, const char *name, struct node **np); /* Destroy this node. */ -- cgit v1.2.3 From 2c7dcef74c3259d2d5db7a11f5c77d18d3a51e85 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 19 Aug 2010 23:10:11 +0000 Subject: Invent path-based inode numbers * procfs.h, procfs.c (procfs_make_ino): New function, invents an inode number by hashing the parent's and the name of an entry. (procfs_lookup): Use it to assign an inode number to child nodes at lookup time. * main.c (root_make_node): Assign an arbitrary inode number to the root directory. --- main.c | 4 ++++ procfs.c | 28 +++++++++++++++++++++++++++- procfs.h | 5 +++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index e08bbdb7..15602819 100644 --- a/main.c +++ b/main.c @@ -42,6 +42,10 @@ root_make_node (struct node **np) if (! *np) return ENOMEM; + /* Since this one is not created through proc_lookup(), we have to affect an + inode number to it. */ + (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; + return 0; } diff --git a/procfs.c b/procfs.c index c5f19497..4cce46b9 100644 --- a/procfs.c +++ b/procfs.c @@ -67,6 +67,29 @@ fail: return NULL; } +/* FIXME: possibly not the fastest hash function... */ +ino64_t +procfs_make_ino (struct node *np, const char *filename) +{ + unsigned short x[3]; + + if (! strcmp (filename, ".")) + return np->nn_stat.st_ino; + if (! strcmp (filename, "..")) + return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 42; + + assert (sizeof np->nn_stat.st_ino > sizeof x); + memcpy (x, &np->nn_stat.st_ino, sizeof x); + + while (*filename) + { + x[0] ^= *(filename++); + jrand48 (x); + } + + return jrand48 (x); +} + error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) { if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) @@ -116,7 +139,10 @@ error_t procfs_lookup (struct node *np, const char *name, struct node **npp) { err = np->nn->ops->lookup (np->nn->hook, name, npp); if (! err) - netfs_nref ((*npp)->nn->parent = np); + { + (*npp)->nn_stat.st_ino = procfs_make_ino (np, name); + netfs_nref ((*npp)->nn->parent = np); + } } return err; diff --git a/procfs.h b/procfs.h index 42eed0e2..4c9d828b 100644 --- a/procfs.h +++ b/procfs.h @@ -51,6 +51,11 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); /* Interface for the libnetfs side. */ +/* Get the inode number which will be given to a child of NP named FILENAME. + This allows us to retreive them for readdir() without creating the + corresponding child nodes. */ +ino64_t procfs_make_ino (struct node *np, const char *filename); + error_t procfs_get_contents (struct node *np, void **data, size_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); -- cgit v1.2.3 From 086569ee636d91a820aa23031dda3cb74bab9505 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 09:25:27 +0000 Subject: Handle the ref counter spinlock on cleanup * netfs.c (netfs_node_norefs): Handle the reference counters spinlock so as to avoid deadlocking on reentry. --- netfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/netfs.c b/netfs.c index 6b105f91..e98af752 100644 --- a/netfs.c +++ b/netfs.c @@ -163,8 +163,12 @@ error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, free all its associated storage. */ void netfs_node_norefs (struct node *np) { + spin_unlock (&netfs_node_refcnt_lock); + procfs_cleanup (np); free (np); + + spin_lock (&netfs_node_refcnt_lock); } -- cgit v1.2.3 From 1f1661d6a5b6f22acb48460b5304e29af2a0a554 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 09:26:09 +0000 Subject: Use libps and enhance [pid]/stat * Makefile: Add libps to the $(LIBS). * proclist.c, proclist.h: Embed the proc server port in a ps_context structure. (proclist_make_node): Change to prototype to allow for the possibility of error. Rename to proclist_create_node to reflect the change and non-triviality. * process.c, process.h: Revamp. Use a full-blown procstat structure instead of just the procinfo fetched from the process server. Use the additional data to complement [pid]/stat. (process_lookup_pid): Get a ps_context structure instead of a port to the process server. * main.c (root_make_node): Convert to the new interface for proclist_create_node. --- Makefile | 2 +- main.c | 7 +- process.c | 267 ++++++++++++++++++++++++++++++++----------------------------- process.h | 8 +- procfs.c | 2 +- proclist.c | 35 ++++---- proclist.h | 2 +- 7 files changed, 164 insertions(+), 159 deletions(-) diff --git a/Makefile b/Makefile index 97a61ed4..48013f95 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ TARGET = procfs OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o \ process.o proclist.o dircat.o main.o -LIBS = -lnetfs +LIBS = -lnetfs -lps CC = gcc CFLAGS = -Wall -g diff --git a/main.c b/main.c index 15602819..75e1cda3 100644 --- a/main.c +++ b/main.c @@ -25,16 +25,17 @@ root_make_node (struct node **np) /* We never have two root nodes alive simultaneously, so it's ok to have this as static data. */ static struct node *root_dirs[3]; + error_t err; root_dirs[0] = procfs_dir_make_node (static_entries, NULL, NULL); if (! root_dirs[0]) return ENOMEM; - root_dirs[1] = proclist_make_node (getproc ()); - if (! root_dirs[1]) + err = proclist_create_node (getproc (), &root_dirs[1]); + if (err) { netfs_nrele (root_dirs[0]); - return ENOMEM; + return err; } root_dirs[2] = NULL; diff --git a/process.c b/process.c index 2b89ef81..a1cc0f61 100644 --- a/process.c +++ b/process.c @@ -1,100 +1,75 @@ #include #include +#include #include +#include +#include +#include #include "procfs.h" #include "procfs_dir.h" #include "process.h" + +/* Implementations for the process_file_desc.get_contents callback. */ -struct process_node { - process_t procserv; - pid_t pid; - struct procinfo info; -}; - - -/* The proc_getprocargs() and proc_getprocenv() calls have the same - prototype and we use them in the same way; namely, publish the data - they return as-is. We take advantage of this to have common code and - use a function pointer as the procfs_dir "entry hook" to choose the - call to use on a file by file basis. */ - -struct process_argz_node +static char * +process_file_gc_cmdline (struct proc_stat *ps, size_t *len) { - struct process_node pn; - error_t (*getargz) (process_t, pid_t, void **, mach_msg_type_number_t *); -}; + *len = proc_stat_args_len(ps); + return proc_stat_args(ps); +} -static error_t -process_argz_get_contents (void *hook, void **contents, size_t *contents_len) +static char * +process_file_gc_environ (struct proc_stat *ps, size_t *len) { - struct process_argz_node *pz = hook; - error_t err; - - *contents_len = 0; - err = pz->getargz (pz->pn.procserv, pz->pn.pid, contents, contents_len); - if (err) - return EIO; - - return 0; + *len = proc_stat_args_len(ps); + return proc_stat_args(ps); } -static struct node * -process_argz_make_node (void *dir_hook, void *entry_hook) +static char state_char (struct proc_stat *ps) { - static const struct procfs_node_ops ops = { - .get_contents = process_argz_get_contents, - .cleanup_contents = procfs_cleanup_contents_with_vm_deallocate, - .cleanup = free, - }; - struct process_argz_node *zn; - - zn = malloc (sizeof *zn); - if (! zn) - return NULL; + int i; - memcpy (&zn->pn, dir_hook, sizeof zn->pn); - zn->getargz = entry_hook; + for (i = 0; (1 << i) & (PSTAT_STATE_P_STATES | PSTAT_STATE_T_STATES); i++) + if (proc_stat_state (ps) & (1 << i)) + return proc_stat_state_tags[i]; - return procfs_make_node (&ops, zn); + return '?'; } -/* The other files don't need any information besides the data in struct - process_node. Furthermore, their contents don't have any nul byte. - Consequently, we use a simple "multiplexer" based on the information - below. */ - -struct process_file_node +static long int sc_tv (time_value_t tv) { - struct process_node pn; - error_t (*get_contents) (struct process_node *pn, char **contents); -}; + double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; + return usecs * sysconf(_SC_CLK_TCK) / 1000000; +} -static char mapstate (int hurd_state) +static long long int jiff_tv (time_value_t tv) { - return '?'; + double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; + /* Let's say a jiffy is 1/100 of a second.. */ + return usecs * 100 / 1000000; } -static error_t -process_file_gc_stat (struct process_node *pn, char **contents) +static char * +process_file_gc_stat (struct proc_stat *ps, size_t *len) { - char *argz; - size_t argz_len; - int len; - - argz = NULL, argz_len = 0; - proc_getprocargs(pn->procserv, pn->pid, &argz, &argz_len); - - len = asprintf (contents, + struct procinfo *pi = proc_stat_proc_info (ps); + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); + char *contents; + + /* See proc(5) for more information about the contents of each field for the + Linux procfs. */ + *len = asprintf (&contents, "%d (%s) %c " /* pid, command, state */ "%d %d %d " /* ppid, pgid, session */ "%d %d " /* controling tty stuff */ "%u " /* flags, as defined by */ "%lu %lu %lu %lu " /* page fault counts */ "%lu %lu %ld %ld " /* user/sys times, in sysconf(_SC_CLK_TCK) */ - "%ld %ld " /* scheduler params (priority, nice) */ - "%ld %ld " /* number of threads, [obsolete] */ + "%d %d " /* scheduler params (priority, nice) */ + "%d %ld " /* number of threads, [obsolete] */ "%llu " /* start time since boot (jiffies) */ - "%lu %ld %lu " /* virtual size, rss, rss limit */ + "%lu %ld %lu " /* virtual size (bytes), rss (pages), rss lim */ "%lu %lu %lu %lu %lu " /* some vm addresses (code, stack, sp, pc) */ "%lu %lu %lu %lu " /* pending, blocked, ignored and caught sigs */ "%lu " /* wait channel */ @@ -104,44 +79,71 @@ process_file_gc_stat (struct process_node *pn, char **contents) "%u %u " /* RT priority and policy */ "%llu " /* aggregated block I/O delay */ "\n", - pn->pid, argz ?: "", mapstate (pn->info.state), - pn->info.ppid, pn->info.pgrp, pn->info.session, - 0, 0, - 0, - 0L, 0L, 0L, 0L, - 0L, 0L, 0L, 0L, - 0L, 0L, - 0L, 0L, - 0LL, - 0L, 0L, 0L, + proc_stat_pid (ps), proc_stat_args (ps), state_char (ps), + pi->ppid, pi->pgrp, pi->session, + 0, 0, /* no such thing as a major:minor for ctty */ + 0, /* no such thing as CLONE_* flags on Hurd */ + 0L, 0L, 0L, 0L, /* TASK_EVENTS_INFO is unavailable on GNU Mach */ + sc_tv (thbi->user_time), sc_tv (thbi->system_time), + 0L, 0L, /* cumulative time for children */ + MACH_PRIORITY_TO_NICE(thbi->base_priority) + 20, + MACH_PRIORITY_TO_NICE(thbi->base_priority), + pi->nthreads, 0L, + jiff_tv (thbi->creation_time), /* FIXME: ... since boot */ + (long unsigned int) tbi->virtual_size, + (long unsigned int) tbi->resident_size / PAGE_SIZE, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, - 0L, + (long unsigned int) proc_stat_thread_rpc (ps), /* close enough */ 0L, 0L, 0, 0, 0, 0, 0LL); - vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len); + return len >= 0 ? contents : NULL; +} - if (len < 0) - return ENOMEM; - return 0; -} +/* Describes a file in a process directory. This is used as an "entry hook" + * for our procfs_dir entry table, passed to process_file_make_node. */ +struct process_file_desc +{ + /* The proc_stat information required to get the contents of this file. */ + ps_flags_t needs; + + /* Once we have acquired the necessary information, there can be only + memory allocation errors, hence this simplified signature. */ + char *(*get_contents) (struct proc_stat *ps, size_t *len); + + /* The cmdline and environ contents don't need any cleaning since they are + part of a proc_stat structure. */ + int no_cleanup; +}; + +/* Information associated to an actual file node. */ +struct process_file_node +{ + const struct process_file_desc *desc; + struct proc_stat *ps; +}; static error_t process_file_get_contents (void *hook, void **contents, size_t *contents_len) { - struct process_file_node *fn = hook; + struct process_file_node *file = hook; error_t err; - err = fn->get_contents (&fn->pn, (char **) contents); + err = proc_stat_set_flags (file->ps, file->desc->needs); if (err) - return err; + return EIO; + if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) + return EIO; + + *contents = file->desc->get_contents (file->ps, contents_len); + if (! *contents) + return ENOMEM; - *contents_len = strlen (*contents); return 0; } @@ -153,68 +155,75 @@ process_file_make_node (void *dir_hook, void *entry_hook) .cleanup_contents = procfs_cleanup_contents_with_free, .cleanup = free, }; - struct process_file_node *fn; + static const struct procfs_node_ops ops_no_cleanup = { + .get_contents = process_file_get_contents, + .cleanup = free, + }; + struct process_file_node *f; - fn = malloc (sizeof *fn); - if (! fn) + f = malloc (sizeof *f); + if (! f) return NULL; - memcpy (&fn->pn, dir_hook, sizeof fn->pn); - fn->get_contents = entry_hook; + f->desc = entry_hook; + f->ps = dir_hook; - return procfs_make_node (&ops, fn); + return procfs_make_node (f->desc->no_cleanup ? &ops_no_cleanup : &ops, f); } -static struct node * -process_make_node (process_t procserv, pid_t pid, const struct procinfo *info) -{ - static const struct procfs_dir_entry entries[] = { - { "cmdline", process_argz_make_node, proc_getprocargs, }, - { "environ", process_argz_make_node, proc_getprocenv, }, - { "stat", process_file_make_node, process_file_gc_stat, }, - { NULL, } - }; - struct process_node *pn; - struct node *np; - - pn = malloc (sizeof *pn); - if (! pn) - return NULL; - - pn->procserv = procserv; - pn->pid = pid; - memcpy (&pn->info, info, sizeof pn->info); - - np = procfs_dir_make_node (entries, pn, process_cleanup); - np->nn_stat.st_uid = pn->info.owner; - - return np; -} +static struct procfs_dir_entry entries[] = { + { + .name = "cmdline", + .make_node = process_file_make_node, + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_cmdline, + .needs = PSTAT_ARGS, + .no_cleanup = 1, + }, + }, + { + .name = "environ", + .make_node = process_file_make_node, + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_environ, + .needs = PSTAT_ENV, + .no_cleanup = 1, + }, + }, + { + .name = "stat", + .make_node = process_file_make_node, + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_stat, + .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO + | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC + | PSTAT_THREAD_WAIT, + }, + }, + {} +}; error_t -process_lookup_pid (process_t procserv, pid_t pid, struct node **np) +process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) { - procinfo_t info; - size_t info_sz; - data_t tw; - size_t tw_sz; - int flags; + struct proc_stat *ps; error_t err; - tw_sz = info_sz = 0, flags = 0; - err = proc_getprocinfo (procserv, pid, &flags, &info, &info_sz, &tw, &tw_sz); + err = _proc_stat_create (pid, pc, &ps); if (err == ESRCH) return ENOENT; if (err) return EIO; - assert (info_sz * sizeof *info >= sizeof (struct procinfo)); - *np = process_make_node (procserv, pid, (struct procinfo *) info); - vm_deallocate (mach_task_self (), (vm_address_t) info, info_sz); + err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); + if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) + return EIO; + *np = procfs_dir_make_node (entries, ps, (void (*)(void *)) _proc_stat_free); if (! *np) return ENOMEM; + (*np)->nn_stat.st_uid = proc_stat_owner_uid (ps); return 0; } diff --git a/process.h b/process.h index abdaaa3b..8c2ee636 100644 --- a/process.h +++ b/process.h @@ -1,8 +1,8 @@ -#include +#include -/* Create a node for a directory representing information available at - the proc server PROC for the given PID. On success, returns the +/* Create a node for a directory representing the given PID, as published by + the proc server refrenced by the libps context PC. On success, returns the newly created node in *NP. */ error_t -process_lookup_pid (process_t proc, pid_t pid, struct node **np); +process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np); diff --git a/procfs.c b/procfs.c index 4cce46b9..0a235a76 100644 --- a/procfs.c +++ b/procfs.c @@ -87,7 +87,7 @@ procfs_make_ino (struct node *np, const char *filename) jrand48 (x); } - return jrand48 (x); + return (unsigned long) jrand48 (x); } error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) diff --git a/proclist.c b/proclist.c index 94a7a04d..56a3fdf9 100644 --- a/proclist.c +++ b/proclist.c @@ -3,27 +3,23 @@ #include #include #include +#include #include "procfs.h" #include "process.h" #define PID_STR_SIZE (3 * sizeof (pid_t) + 1) -struct proclist_node -{ - process_t process; -}; - static error_t proclist_get_contents (void *hook, void **contents, size_t *contents_len) { - struct proclist_node *pl = hook; + struct ps_context *pc = hook; pidarray_t pids; mach_msg_type_number_t num_pids; error_t err; int i; num_pids = 0; - err = proc_getallpids (pl->process, &pids, &num_pids); + err = proc_getallpids (pc->server, &pids, &num_pids); if (err) return EIO; @@ -48,7 +44,7 @@ proclist_get_contents (void *hook, void **contents, size_t *contents_len) static error_t proclist_lookup (void *hook, const char *name, struct node **np) { - struct proclist_node *pl = hook; + struct ps_context *pc = hook; char *endp; pid_t pid; @@ -63,28 +59,27 @@ proclist_lookup (void *hook, const char *name, struct node **np) if (*endp) return ENOENT; - return process_lookup_pid (pl->process, pid, np); + return process_lookup_pid (pc, pid, np); } -struct node * -proclist_make_node (process_t process) +error_t +proclist_create_node (process_t procserv, struct node **np) { static const struct procfs_node_ops ops = { .get_contents = proclist_get_contents, .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, - .cleanup = free, + .cleanup = (void (*)(void *)) ps_context_free, .enable_refresh_hack_and_break_readdir = 1, }; - struct proclist_node *pl; - - pl = malloc (sizeof *pl); - if (! pl) - return NULL; + struct ps_context *pc; + error_t err; - memset (pl, 0, sizeof *pl); - pl->process = process; + err = ps_context_create (procserv, &pc); + if (err) + return err; - return procfs_make_node (&ops, pl); + *np = procfs_make_node (&ops, pc); + return 0; } diff --git a/proclist.h b/proclist.h index a766d50e..1c7ab084 100644 --- a/proclist.h +++ b/proclist.h @@ -1,2 +1,2 @@ #include -struct node *proclist_make_node (process_t process); +error_t proclist_create_node (process_t procserv, struct node **np); -- cgit v1.2.3 From a45606d0036565c468b462a207575bf2b4077fd8 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 11:09:36 +0000 Subject: Implement symlinks * netfs.c (netfs_validate_stat): For symlinks, fetch the contents and propagate their length into the nn_stat.st_size field. (netfs_attempt_readlink): Implement using procfs_get_contents. --- netfs.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/netfs.c b/netfs.c index e98af752..a47861e5 100644 --- a/netfs.c +++ b/netfs.c @@ -24,6 +24,28 @@ char *netfs_server_version = PROCFS_SERVER_VERSION; /* Maximum number of symlinks to follow before returning ELOOP. */ int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; +/* The user must define this function. Make sure that NP->nn_stat is + filled with the most current information. CRED identifies the user + responsible for the operation. NP is locked. */ +error_t netfs_validate_stat (struct node *np, struct iouser *cred) +{ + void *contents; + size_t contents_len; + error_t err; + + /* Only symlinks need to have their size filled, before a read is + attempted. */ + if (! S_ISLNK (np->nn_stat.st_mode)) + return 0; + + err = procfs_get_contents (np, &contents, &contents_len); + if (err) + return err; + + np->nn_stat.st_size = contents_len; + return 0; +} + /* The user must define this function. Read from the locked file NP for user CRED starting at OFFSET and continuing for up to *LEN bytes. Put the data at DATA. Set *LEN to the amount successfully @@ -56,7 +78,17 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *np, error_t netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) { - return EIO; + char *contents; + size_t contents_len; + error_t err; + + err = procfs_get_contents (np, (void **) &contents, &contents_len); + if (err) + return err; + + assert (contents_len == np->nn_stat.st_size); + memcpy (buf, contents, contents_len); + return 0; } /* Helper function for netfs_get_dirents() below. CONTENTS is an argz @@ -210,14 +242,6 @@ error_t netfs_report_access (struct iouser *cred, struct node *np, /* Trivial or unsupported libnetfs callbacks. */ -/* The user must define this function. Make sure that NP->nn_stat is - filled with the most current information. CRED identifies the user - responsible for the operation. NP is locked. */ -error_t netfs_validate_stat (struct node *np, struct iouser *cred) -{ - return 0; -} - /* The user must define this function. This should attempt a chmod call for the user specified by CRED on locked node NP, to change the owner to UID and the group to GID. */ -- cgit v1.2.3 From 75f53efdc66be8c6ffac31bd17a309f9065e273c Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 11:21:59 +0000 Subject: Encapsulate access to node->nn_stat * procfs.c, procfs.h (procfs_node_chown, procfs_node_chmod, procfs_node_chtype): New functions, encapsulate access to some nn_stat fields. * process.c (process_lookup_pid): Use procfs_node_chown instead of direct access. --- process.c | 2 +- procfs.c | 19 +++++++++++++++++++ procfs.h | 13 +++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index a1cc0f61..779a2e18 100644 --- a/process.c +++ b/process.c @@ -224,6 +224,6 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) if (! *np) return ENOMEM; - (*np)->nn_stat.st_uid = proc_stat_owner_uid (ps); + procfs_node_chown (*np, proc_stat_owner_uid (ps)); return 0; } diff --git a/procfs.c b/procfs.c index 0a235a76..5396ecf7 100644 --- a/procfs.c +++ b/procfs.c @@ -67,6 +67,25 @@ fail: return NULL; } +void procfs_node_chown (struct node *np, uid_t owner) +{ + np->nn_stat.st_uid = owner; +} + +void procfs_node_chmod (struct node *np, mode_t mode) +{ + np->nn_stat.st_mode = (np->nn_stat.st_mode & S_IFMT) | mode; + np->nn_translated = np->nn_stat.st_mode; +} + +void procfs_node_chtype (struct node *np, mode_t type) +{ + np->nn_stat.st_mode = (np->nn_stat.st_mode & ~S_IFMT) | type; + np->nn_translated = np->nn_stat.st_mode; + if (type == S_IFLNK) + procfs_node_chmod (np, 0777); +} + /* FIXME: possibly not the fastest hash function... */ ino64_t procfs_make_ino (struct node *np, const char *filename) diff --git a/procfs.h b/procfs.h index 4c9d828b..4ab3b567 100644 --- a/procfs.h +++ b/procfs.h @@ -48,6 +48,19 @@ void procfs_cleanup_contents_with_vm_deallocate (void *, void *, size_t); enough memory. In this case, ops->cleanup will be invoked. */ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); +/* Set the owner of the node NP. Must be called right after the node + has been created. */ +void procfs_node_chown (struct node *np, uid_t owner); + +/* Set the permission bits of the node NP. Must be called right after + the node has been created. */ +void procfs_node_chmod (struct node *np, mode_t mode); + +/* Set the type of the node NP. If type is S_IFLNK, appropriate + permission bits will be set as well. Must be called right after the + node has been created. */ +void procfs_node_chtype (struct node *np, mode_t type); + /* Interface for the libnetfs side. */ -- cgit v1.2.3 From 80e439d146f661c416b7f42c4180b16aae7ac2f7 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 18:06:16 +0000 Subject: Set a restrictive mode on some sensitive files * process.c (process_file_make_node, entries): Set the environ and stat files as readable only by the owner of the process. --- process.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index 779a2e18..8955cf40 100644 --- a/process.c +++ b/process.c @@ -119,6 +119,9 @@ struct process_file_desc /* The cmdline and environ contents don't need any cleaning since they are part of a proc_stat structure. */ int no_cleanup; + + /* If specified, the file mode to be set with procfs_node_chmod(). */ + mode_t mode; }; /* Information associated to an actual file node. */ @@ -160,6 +163,7 @@ process_file_make_node (void *dir_hook, void *entry_hook) .cleanup = free, }; struct process_file_node *f; + struct node *np; f = malloc (sizeof *f); if (! f) @@ -168,7 +172,15 @@ process_file_make_node (void *dir_hook, void *entry_hook) f->desc = entry_hook; f->ps = dir_hook; - return procfs_make_node (f->desc->no_cleanup ? &ops_no_cleanup : &ops, f); + np = procfs_make_node (f->desc->no_cleanup ? &ops_no_cleanup : &ops, f); + if (! np) + return NULL; + + procfs_node_chown (np, proc_stat_owner_uid (f->ps)); + if (f->desc->mode) + procfs_node_chmod (np, f->desc->mode); + + return np; } @@ -189,6 +201,7 @@ static struct procfs_dir_entry entries[] = { .get_contents = process_file_gc_environ, .needs = PSTAT_ENV, .no_cleanup = 1, + .mode = 0400, }, }, { @@ -199,6 +212,7 @@ static struct procfs_dir_entry entries[] = { .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC | PSTAT_THREAD_WAIT, + .mode = 0400, }, }, {} -- cgit v1.2.3 From 2717c43bca6f920c4d3b0909c33bf3c1f76a70a9 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 18:08:17 +0000 Subject: New root files: version, uptime, stat * rootdir.c, rootdir.h: New files. * main.c: Use rootdir_create_node. * Makefile: Add the rootdir module. --- Makefile | 2 +- main.c | 21 ++------- rootdir.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rootdir.h | 2 + 4 files changed, 167 insertions(+), 17 deletions(-) create mode 100644 rootdir.c create mode 100644 rootdir.h diff --git a/Makefile b/Makefile index 48013f95..9820a76b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TARGET = procfs OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o \ - process.o proclist.o dircat.o main.o + process.o proclist.o rootdir.o dircat.o main.o LIBS = -lnetfs -lps CC = gcc diff --git a/main.c b/main.c index 75e1cda3..15ad60f4 100644 --- a/main.c +++ b/main.c @@ -4,34 +4,23 @@ #include #include #include "procfs.h" -#include "procfs_file.h" -#include "procfs_dir.h" #include "proclist.h" +#include "rootdir.h" #include "dircat.h" -static struct node * -make_file (void *dir_hook, void *ent_hook) -{ - return procfs_file_make_node (ent_hook, -1, NULL); -} - error_t root_make_node (struct node **np) { - static const struct procfs_dir_entry static_entries[] = { - { "hello", make_file, "Hello, World!\n" }, - { "goodbye", make_file, "Goodbye, cruel World!\n" }, - }; /* We never have two root nodes alive simultaneously, so it's ok to have this as static data. */ static struct node *root_dirs[3]; error_t err; - root_dirs[0] = procfs_dir_make_node (static_entries, NULL, NULL); - if (! root_dirs[0]) - return ENOMEM; + err = proclist_create_node (getproc (), &root_dirs[0]); + if (err) + return err; - err = proclist_create_node (getproc (), &root_dirs[1]); + err = rootdir_create_node (&root_dirs[1]); if (err) { netfs_nrele (root_dirs[0]); diff --git a/rootdir.c b/rootdir.c new file mode 100644 index 00000000..fb48b932 --- /dev/null +++ b/rootdir.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include "procfs.h" +#include "procfs_dir.h" + +/* This implements a directory node with the static files in /proc */ + +#define INIT_PID 1 + +static error_t +get_boottime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps; + error_t err; + + err = _proc_stat_create (INIT_PID, pc, &ps); + if (err) + return err; + + err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); + if (err || !(proc_stat_flags (ps) & PSTAT_TASK_BASIC)) + err = EIO; + + if (! err) + { + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + tv->tv_sec = tbi->creation_time.seconds; + tv->tv_usec = tbi->creation_time.microseconds; + } + + _proc_stat_free (ps); + return err; +} + +static error_t +rootdir_gc_version (void *hook, void **contents, size_t *contents_len) +{ + struct utsname uts; + int r; + + r = uname (&uts); + if (r < 0) + return errno; + + *contents_len = asprintf ((char **) contents, + "Linux version 2.6.1 (%s %s %s %s)\n", + uts.sysname, uts.release, uts.version, uts.machine); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + +/* Uptime -- we use the start time of init to deduce it. This is probably a bit + fragile, as any clock update will make the result inaccurate. */ +static error_t +rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) +{ + struct timeval time, boottime; + double up_secs; + error_t err; + + err = gettimeofday (&time, NULL); + if (err < 0) + return errno; + + err = get_boottime (hook, &boottime); + if (err) + return err; + + timersub (&time, &boottime, &time); + up_secs = time.tv_sec + time.tv_usec / 1000000.; + + /* The second field is the total idle time. As far as I know we don't + keep track of it. */ + *contents_len = asprintf ((char **) contents, "%.2lf %.2lf\n", up_secs, 0.); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + +static error_t +rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) +{ + struct timeval boottime; + struct vm_statistics vmstats; + error_t err; + + err = get_boottime (hook, &boottime); + if (err) + return err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + *contents_len = asprintf ((char **) contents, + /* Does Mach keeps track of any of this? */ + "cpu 0 0 0 0 0 0 0 0 0\n" + "cpu0 0 0 0 0 0 0 0 0 0\n" + "intr 0\n" + /* This we know. */ + "page %d %d\n" + "btime %lu\n", + vmstats.pageins, vmstats.pageouts, + boottime.tv_sec); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + +static struct node * +rootdir_file_make_node (void *dir_hook, void *entry_hook) +{ + return procfs_make_node (entry_hook, dir_hook); +} + +static struct procfs_dir_entry rootdir_entries[] = { + { + .name = "version", + .make_node = rootdir_file_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_version, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "uptime", + .make_node = rootdir_file_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_uptime, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "stat", + .make_node = rootdir_file_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_stat, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + {} +}; + +error_t +rootdir_create_node (struct node **np) +{ + struct ps_context *pc; + error_t err; + + err = ps_context_create (getproc (), &pc); + if (err) + return err; + + *np = procfs_dir_make_node (rootdir_entries, pc, + (void (*)(void *)) ps_context_free); + return 0; +} + diff --git a/rootdir.h b/rootdir.h new file mode 100644 index 00000000..a764b0fd --- /dev/null +++ b/rootdir.h @@ -0,0 +1,2 @@ +error_t +rootdir_create_node (struct node **np); -- cgit v1.2.3 From efb8515465dcb03526a35bf87b549cac97bbab8c Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 21 Aug 2010 19:18:59 +0000 Subject: Add loadavg * rootdir.c: Add the root file "loadavg". --- rootdir.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/rootdir.c b/rootdir.c index fb48b932..0985b63b 100644 --- a/rootdir.c +++ b/rootdir.c @@ -108,6 +108,28 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) return *contents_len >= 0 ? 0 : ENOMEM; } +static error_t +rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) +{ + host_load_info_data_t hli; + mach_msg_type_number_t cnt; + error_t err; + + cnt = HOST_LOAD_INFO_COUNT; + err = host_info (mach_host_self (), HOST_LOAD_INFO, (host_info_t) &hli, &cnt); + if (err) + return err; + + assert (cnt == HOST_LOAD_INFO_COUNT); + *contents_len = asprintf ((char **) contents, + "%.2f %.2f %.2f 1/0 0\n", + hli.avenrun[0] / (double) LOAD_SCALE, + hli.avenrun[1] / (double) LOAD_SCALE, + hli.avenrun[2] / (double) LOAD_SCALE); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + static struct node * rootdir_file_make_node (void *dir_hook, void *entry_hook) { @@ -139,6 +161,14 @@ static struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .name = "loadavg", + .make_node = rootdir_file_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_loadavg, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, {} }; -- cgit v1.2.3 From aa3bac054364aa2d3614ded28df4980639184319 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 00:55:11 +0000 Subject: Add a fake "self" symlink * rootdir.c: Add a fake "self" symlink which always points to init. --- rootdir.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/rootdir.c b/rootdir.c index 0985b63b..79928446 100644 --- a/rootdir.c +++ b/rootdir.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "procfs.h" #include "procfs_dir.h" @@ -130,12 +131,30 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) return *contents_len >= 0 ? 0 : ENOMEM; } +static error_t +rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) +{ + *contents = "1"; + *contents_len = strlen (*contents); + return 0; +} + + static struct node * rootdir_file_make_node (void *dir_hook, void *entry_hook) { return procfs_make_node (entry_hook, dir_hook); } +static struct node * +rootdir_symlink_make_node (void *dir_hook, void *entry_hook) +{ + struct node *np = procfs_make_node (entry_hook, dir_hook); + if (np) + procfs_node_chtype (np, S_IFLNK); + return np; +} + static struct procfs_dir_entry rootdir_entries[] = { { .name = "version", @@ -169,6 +188,13 @@ static struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .name = "self", + .make_node = rootdir_symlink_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_fakeself, + }, + }, {} }; -- cgit v1.2.3 From c68fc8b783804e093551d93cc1b8c10964cdaaeb Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 00:55:29 +0000 Subject: Add an empty meminfo root file * rootdir.c: Add "meminfo", empty for now. --- rootdir.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/rootdir.c b/rootdir.c index 79928446..17fdb93a 100644 --- a/rootdir.c +++ b/rootdir.c @@ -131,6 +131,13 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) return *contents_len >= 0 ? 0 : ENOMEM; } +static error_t +rootdir_gc_empty (void *hook, void **contents, size_t *contents_len) +{ + *contents_len = 0; + return 0; +} + static error_t rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) { @@ -188,6 +195,13 @@ static struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .name = "meminfo", + .make_node = rootdir_file_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_empty, + }, + }, { .name = "self", .make_node = rootdir_symlink_make_node, -- cgit v1.2.3 From f9ddb679942b6f4309d05b5462fe5cbb3d0a2beb Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sun, 22 Aug 2010 19:33:43 +0000 Subject: Play nice with the procps old_Hertz_hack * rootdir.c (rootdir_gc_uptime, rootdir_gc_stat): assume a completely idle rather than completely busy system, so that the idle seconds can be meaningfully divided by the idle jiffies by procps. --- rootdir.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/rootdir.c b/rootdir.c index 17fdb93a..50f6e071 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -74,8 +75,11 @@ rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) up_secs = time.tv_sec + time.tv_usec / 1000000.; /* The second field is the total idle time. As far as I know we don't - keep track of it. */ - *contents_len = asprintf ((char **) contents, "%.2lf %.2lf\n", up_secs, 0.); + keep track of it. However, procps uses it to compute "USER_HZ", and + proc(5) specifies that it should be equal to USER_HZ times the idle value + in ticks from /proc/stat. So we assume a completely idle system both here + and there to make that work. */ + *contents_len = asprintf ((char **) contents, "%.2lf %.2lf\n", up_secs, up_secs); return *contents_len >= 0 ? 0 : ENOMEM; } @@ -83,10 +87,15 @@ rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) static error_t rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) { - struct timeval boottime; + struct timeval boottime, time; struct vm_statistics vmstats; + unsigned long up_ticks; error_t err; + err = gettimeofday (&time, NULL); + if (err < 0) + return errno; + err = get_boottime (hook, &boottime); if (err) return err; @@ -95,14 +104,19 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) if (err) return EIO; + timersub (&time, &boottime, &time); + up_ticks = sysconf(_SC_CLK_TCK) * (time.tv_sec + time.tv_usec / 1000000.); + *contents_len = asprintf ((char **) contents, /* Does Mach keeps track of any of this? */ - "cpu 0 0 0 0 0 0 0 0 0\n" - "cpu0 0 0 0 0 0 0 0 0 0\n" + "cpu 0 0 0 %lu 0 0 0 0 0\n" + "cpu0 0 0 0 %lu 0 0 0 0 0\n" "intr 0\n" /* This we know. */ "page %d %d\n" "btime %lu\n", + up_ticks, + up_ticks, vmstats.pageins, vmstats.pageouts, boottime.tv_sec); -- cgit v1.2.3 From b3427143ae8dc628cb3748da7618700c6bd7ac9e Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sun, 22 Aug 2010 20:17:54 +0000 Subject: Add --clk-tck to set the clock unit * main.c (argp_parser, main): Add and parse the --clk-tck option. * main.h: Publish opt_clk_tck. * process.c (sc_tc): Use the user-provided clock frequency. * rootdir.c (rootdir_gc_stat): Likewise. --- main.c | 40 +++++++++++++++++++++++++++++++++++++++- main.h | 2 ++ process.c | 3 ++- rootdir.c | 3 ++- 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 main.h diff --git a/main.c b/main.c index 15ad60f4..a59ab47f 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -7,6 +8,42 @@ #include "proclist.h" #include "rootdir.h" #include "dircat.h" +#include "main.h" + +/* Command-line options */ +int opt_clk_tck; + +static error_t +argp_parser (int key, char *arg, struct argp_state *state) +{ + char *endp; + + switch (key) + { + case 'h': + opt_clk_tck = strtol (arg, &endp, 0); + if (*endp || ! *arg || opt_clk_tck <= 0) + error (1, 0, "--clk-tck: HZ should be a positive integer"); + break; + } + + return 0; +} + +struct argp argp = { + .options = (struct argp_option []) { + { "clk-tck", 'h', "HZ", 0, + "Unit used for the values expressed in system clock ticks " + "(default: sysconf(_SC_CLK_TCK))" }, + {} + }, + .parser = argp_parser, + .doc = "A virtual filesystem emulating the Linux procfs.", + .children = (struct argp_child []) { + { &netfs_std_startup_argp, }, + {} + }, +}; error_t root_make_node (struct node **np) @@ -43,7 +80,8 @@ int main (int argc, char **argv) { mach_port_t bootstrap; - argp_parse (&netfs_std_startup_argp, argc, argv, 0, 0, 0); + opt_clk_tck = sysconf(_SC_CLK_TCK); + argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) diff --git a/main.h b/main.h new file mode 100644 index 00000000..4b2ef9a1 --- /dev/null +++ b/main.h @@ -0,0 +1,2 @@ +/* Startup options */ +extern int opt_clk_tck; diff --git a/process.c b/process.c index 8955cf40..65f0e503 100644 --- a/process.c +++ b/process.c @@ -8,6 +8,7 @@ #include "procfs.h" #include "procfs_dir.h" #include "process.h" +#include "main.h" /* Implementations for the process_file_desc.get_contents callback. */ @@ -39,7 +40,7 @@ static char state_char (struct proc_stat *ps) static long int sc_tv (time_value_t tv) { double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; - return usecs * sysconf(_SC_CLK_TCK) / 1000000; + return usecs * opt_clk_tck / 1000000; } static long long int jiff_tv (time_value_t tv) diff --git a/rootdir.c b/rootdir.c index 50f6e071..1d9c083c 100644 --- a/rootdir.c +++ b/rootdir.c @@ -7,6 +7,7 @@ #include #include "procfs.h" #include "procfs_dir.h" +#include "main.h" /* This implements a directory node with the static files in /proc */ @@ -105,7 +106,7 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) return EIO; timersub (&time, &boottime, &time); - up_ticks = sysconf(_SC_CLK_TCK) * (time.tv_sec + time.tv_usec / 1000000.); + up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); *contents_len = asprintf ((char **) contents, /* Does Mach keeps track of any of this? */ -- cgit v1.2.3 From 5714e1cef2584410a7823c7ead9d2435141fb0c4 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sun, 22 Aug 2010 20:46:54 +0000 Subject: Add --stat-mode to override the perms for [pid]/stat * main.c (argp_parser, main): Add the --stat-mode option. * process.c (process_lookup_pid): Use it. --- main.c | 14 ++++++++++++++ main.h | 1 + process.c | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/main.c b/main.c index a59ab47f..09cffc76 100644 --- a/main.c +++ b/main.c @@ -12,6 +12,7 @@ /* Command-line options */ int opt_clk_tck; +mode_t opt_stat_mode; static error_t argp_parser (int key, char *arg, struct argp_state *state) @@ -25,6 +26,12 @@ argp_parser (int key, char *arg, struct argp_state *state) if (*endp || ! *arg || opt_clk_tck <= 0) error (1, 0, "--clk-tck: HZ should be a positive integer"); break; + + case 's': + opt_stat_mode = strtol (arg, &endp, 8); + if (*endp || ! *arg || opt_stat_mode & ~07777) + error (1, 0, "--stat-mode: MODE should be an octal mode"); + break; } return 0; @@ -35,6 +42,12 @@ struct argp argp = { { "clk-tck", 'h', "HZ", 0, "Unit used for the values expressed in system clock ticks " "(default: sysconf(_SC_CLK_TCK))" }, + { "stat-mode", 's', "MODE", 0, + "The [pid]/stat file publishes information which on Hurd is only " + "available to the process owner. " + "You can use this option to override its mode to be more permissive " + "for compatibility purposes. " + "(default: 0400)" }, {} }, .parser = argp_parser, @@ -81,6 +94,7 @@ int main (int argc, char **argv) mach_port_t bootstrap; opt_clk_tck = sysconf(_SC_CLK_TCK); + opt_stat_mode = 0400; argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); diff --git a/main.h b/main.h index 4b2ef9a1..6669d321 100644 --- a/main.h +++ b/main.h @@ -1,2 +1,3 @@ /* Startup options */ extern int opt_clk_tck; +extern mode_t opt_stat_mode; diff --git a/process.c b/process.c index 65f0e503..f7a7a577 100644 --- a/process.c +++ b/process.c @@ -206,6 +206,7 @@ static struct procfs_dir_entry entries[] = { }, }, { + /* Beware of the hack below, which requires this to be entries[2]. */ .name = "stat", .make_node = process_file_make_node, .hook = & (struct process_file_desc) { @@ -235,6 +236,10 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) return EIO; + /* FIXME: have a separate proc_desc structure for each file, so this can be + accessed in a more robust and straightforward way. */ + ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; + *np = procfs_dir_make_node (entries, ps, (void (*)(void *)) _proc_stat_free); if (! *np) return ENOMEM; -- cgit v1.2.3 From 4665f087fde174a9de3e1c3f3de090dd4bfa85e0 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sun, 22 Aug 2010 21:23:36 +0000 Subject: Add a fake-self option to control the self symlink * main.c (argp_parse, main): Add the --fake-self option. * main.h: Publish it. * rootdir.c (rootdir_gc_fakeself, rootdir_entries, rootdir_create_node): Use it. --- main.c | 17 +++++++++++++++++ main.h | 1 + rootdir.c | 30 +++++++++++++++++------------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/main.c b/main.c index 09cffc76..26f5248f 100644 --- a/main.c +++ b/main.c @@ -13,6 +13,7 @@ /* Command-line options */ int opt_clk_tck; mode_t opt_stat_mode; +pid_t opt_fake_self; static error_t argp_parser (int key, char *arg, struct argp_state *state) @@ -32,6 +33,17 @@ argp_parser (int key, char *arg, struct argp_state *state) if (*endp || ! *arg || opt_stat_mode & ~07777) error (1, 0, "--stat-mode: MODE should be an octal mode"); break; + + case 'S': + if (arg) + { + opt_fake_self = strtol (arg, &endp, 0); + if (*endp || ! *arg) + error (1, 0, "--fake-self: PID must be an integer"); + } + else + opt_fake_self = 1; + break; } return 0; @@ -48,6 +60,10 @@ struct argp argp = { "You can use this option to override its mode to be more permissive " "for compatibility purposes. " "(default: 0400)" }, + { "fake-self", 'S', "PID", OPTION_ARG_OPTIONAL, + "Provide a fake \"self\" symlink to the given PID, for compatibility " + "purposes. If PID is omitted, \"self\" will point to init. " + "(default: no self link)" }, {} }, .parser = argp_parser, @@ -95,6 +111,7 @@ int main (int argc, char **argv) opt_clk_tck = sysconf(_SC_CLK_TCK); opt_stat_mode = 0400; + opt_fake_self = -1; argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); diff --git a/main.h b/main.h index 6669d321..361380b3 100644 --- a/main.h +++ b/main.h @@ -1,3 +1,4 @@ /* Startup options */ extern int opt_clk_tck; extern mode_t opt_stat_mode; +extern pid_t opt_fake_self; diff --git a/rootdir.c b/rootdir.c index 1d9c083c..833fc150 100644 --- a/rootdir.c +++ b/rootdir.c @@ -156,9 +156,8 @@ rootdir_gc_empty (void *hook, void **contents, size_t *contents_len) static error_t rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) { - *contents = "1"; - *contents_len = strlen (*contents); - return 0; + *contents_len = asprintf ((char **) contents, "%d", opt_fake_self); + return *contents_len >= 0 ? 0 : ENOMEM; } @@ -177,7 +176,15 @@ rootdir_symlink_make_node (void *dir_hook, void *entry_hook) return np; } -static struct procfs_dir_entry rootdir_entries[] = { +static const struct procfs_dir_entry rootdir_entries[] = { + { + .name = "self", + .make_node = rootdir_symlink_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_fakeself, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, { .name = "version", .make_node = rootdir_file_make_node, @@ -217,13 +224,6 @@ static struct procfs_dir_entry rootdir_entries[] = { .get_contents = rootdir_gc_empty, }, }, - { - .name = "self", - .make_node = rootdir_symlink_make_node, - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_fakeself, - }, - }, {} }; @@ -231,14 +231,18 @@ error_t rootdir_create_node (struct node **np) { struct ps_context *pc; + const struct procfs_dir_entry *entries; error_t err; err = ps_context_create (getproc (), &pc); if (err) return err; - *np = procfs_dir_make_node (rootdir_entries, pc, - (void (*)(void *)) ps_context_free); + entries = rootdir_entries; + if (opt_fake_self < 0) + entries++; + + *np = procfs_dir_make_node (entries, pc, (void (*)(void *)) ps_context_free); return 0; } -- cgit v1.2.3 From 0ef322973a8b9dc9c0f5ba8155ad780016f3654f Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 00:52:24 +0000 Subject: Add meminfo and vmstat * rootdir.c: Add a (non-empty) meminfo and a vmstat file. --- rootdir.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/rootdir.c b/rootdir.c index 833fc150..f5b43ff0 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -9,7 +10,10 @@ #include "procfs_dir.h" #include "main.h" -/* This implements a directory node with the static files in /proc */ +/* This implements a directory node with the static files in /proc. + NB: the libps functions for host information return static storage; + using them would require locking and as a consequence it would be + more complicated, not simpler. */ #define INIT_PID 1 @@ -147,10 +151,83 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_empty (void *hook, void **contents, size_t *contents_len) +rootdir_gc_meminfo (void *hook, void **contents, size_t *contents_len) { - *contents_len = 0; - return 0; + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + struct vm_statistics vmstats; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf ((char **) contents, + "MemTotal: %14lu kB\n" + "MemFree: %14lu kB\n" + "Active: %14lu kB\n" + "Inactive: %14lu kB\n" + "Mlocked: %14lu kB\n" + , + /* TODO: check that these are really 1024-bytes kBs. */ + (long unsigned) hbi.memory_size / 1024, + (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, + (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024); + + return *contents_len >= 0 ? 0 : ENOMEM; +} + +static error_t +rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) +{ + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + struct vm_statistics vmstats; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf ((char **) contents, + "nr_free_pages %lu\n" + "nr_inactive_anon %lu\n" + "nr_active_anon %lu\n" + "nr_inactive_file %lu\n" + "nr_active_file %lu\n" + "nr_unevictable %lu\n" + "nr_mlock %lu\n" + "pgpgin %lu\n" + "pgpgout %lu\n" + "pgfault %lu\n", + (long unsigned) vmstats.free_count, + /* FIXME: how can we distinguish the anon/file pages? Maybe we can + ask the default pager how many it manages? */ + (long unsigned) vmstats.inactive_count, + (long unsigned) vmstats.active_count, + (long unsigned) 0, + (long unsigned) 0, + (long unsigned) vmstats.wire_count, + (long unsigned) vmstats.wire_count, + (long unsigned) vmstats.pageins, + (long unsigned) vmstats.pageouts, + (long unsigned) vmstats.faults); + + return *contents_len >= 0 ? 0 : ENOMEM; } static error_t @@ -221,7 +298,16 @@ static const struct procfs_dir_entry rootdir_entries[] = { .name = "meminfo", .make_node = rootdir_file_make_node, .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_empty, + .get_contents = rootdir_gc_meminfo, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, + { + .name = "vmstat", + .make_node = rootdir_file_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_vmstat, + .cleanup_contents = procfs_cleanup_contents_with_free, }, }, {} -- cgit v1.2.3 From e3707dc9deb28d5144836032a54dbc028247835d Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 05:19:19 +0000 Subject: Add a global cmdline file * main.c (argp_parser, main): Add the --kernel-pid option. * main.h: Publish it. * rootdir.c (rootdir_gc_cmdline): New function. --- main.c | 11 +++++++++++ main.h | 1 + rootdir.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/main.c b/main.c index 26f5248f..fee4867c 100644 --- a/main.c +++ b/main.c @@ -14,6 +14,7 @@ int opt_clk_tck; mode_t opt_stat_mode; pid_t opt_fake_self; +pid_t opt_kernel_pid; static error_t argp_parser (int key, char *arg, struct argp_state *state) @@ -44,6 +45,12 @@ argp_parser (int key, char *arg, struct argp_state *state) else opt_fake_self = 1; break; + + case 'k': + opt_kernel_pid = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_kernel_pid < 0) + error (1, 0, "--kernel-process: PID must be a positive integer"); + break; } return 0; @@ -64,6 +71,9 @@ struct argp argp = { "Provide a fake \"self\" symlink to the given PID, for compatibility " "purposes. If PID is omitted, \"self\" will point to init. " "(default: no self link)" }, + { "kernel-process", 'k', "PID", 0, + "Process identifier for the kernel, used to retreive its command line " + "(default: 2)" }, {} }, .parser = argp_parser, @@ -112,6 +122,7 @@ int main (int argc, char **argv) opt_clk_tck = sysconf(_SC_CLK_TCK); opt_stat_mode = 0400; opt_fake_self = -1; + opt_kernel_pid = 2; argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); diff --git a/main.h b/main.h index 361380b3..6ada229a 100644 --- a/main.h +++ b/main.h @@ -2,3 +2,4 @@ extern int opt_clk_tck; extern mode_t opt_stat_mode; extern pid_t opt_fake_self; +extern pid_t opt_kernel_pid; diff --git a/rootdir.c b/rootdir.c index f5b43ff0..865342ce 100644 --- a/rootdir.c +++ b/rootdir.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "procfs.h" #include "procfs_dir.h" @@ -230,6 +231,41 @@ rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) return *contents_len >= 0 ? 0 : ENOMEM; } +static error_t +rootdir_gc_cmdline (void *hook, void **contents, size_t *contents_len) +{ + struct ps_context *pc = hook; + struct proc_stat *ps; + error_t err; + + err = _proc_stat_create (opt_kernel_pid, pc, &ps); + if (err) + return EIO; + + err = proc_stat_set_flags (ps, PSTAT_ARGS); + if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS)) + { + err = EIO; + goto out; + } + + *contents_len = proc_stat_args_len (ps); + *contents = malloc (*contents_len); + if (! *contents) + { + err = ENOMEM; + goto out; + } + + memcpy (*contents, proc_stat_args (ps), *contents_len); + argz_stringify (*contents, *contents_len, ' '); + ((char *) *contents)[*contents_len - 1] = '\n'; + +out: + _proc_stat_free (ps); + return err; +} + static error_t rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) { @@ -310,6 +346,14 @@ static const struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .name = "cmdline", + .make_node = rootdir_file_make_node, + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_cmdline, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, {} }; -- cgit v1.2.3 From e873ff83afba08ff61a5e98ab90d5ee05ab5c601 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 05:44:37 +0000 Subject: Add statm to process directories * process.c: Add the statm file. --- process.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/process.c b/process.c index f7a7a577..4404d084 100644 --- a/process.c +++ b/process.c @@ -105,6 +105,20 @@ process_file_gc_stat (struct proc_stat *ps, size_t *len) return len >= 0 ? contents : NULL; } +static char * +process_file_gc_statm (struct proc_stat *ps, size_t *len) +{ + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + char *contents; + + *len = asprintf (&contents, + "%lu %lu 0 0 0 0 0\n", + tbi->virtual_size / sysconf(_SC_PAGE_SIZE), + tbi->resident_size / sysconf(_SC_PAGE_SIZE)); + + return len >= 0 ? contents : NULL; +} + /* Describes a file in a process directory. This is used as an "entry hook" * for our procfs_dir entry table, passed to process_file_make_node. */ @@ -217,6 +231,15 @@ static struct procfs_dir_entry entries[] = { .mode = 0400, }, }, + { + .name = "statm", + .make_node = process_file_make_node, + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_statm, + .needs = PSTAT_TASK_BASIC, + .mode = 0444, + }, + }, {} }; -- cgit v1.2.3 From abd1ad000a2861b46df445888365678686e31bcb Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 07:14:45 +0000 Subject: Add a status file to process directories * process.c: Add a status file. --- process.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/process.c b/process.c index 4404d084..b2b098ff 100644 --- a/process.c +++ b/process.c @@ -37,6 +37,27 @@ static char state_char (struct proc_stat *ps) return '?'; } +static const char *state_string (struct proc_stat *ps) +{ + static const char *const state_strings[] = { + "T (stopped)", + "Z (zombie)", + "R (running)", + "H (halted)", + "D (disk sleep)", + "S (sleeping)", + "I (idle)", + NULL + }; + int i; + + for (i = 0; state_strings[i]; i++) + if (proc_stat_state (ps) & (1 << i)) + return state_strings[i]; + + return "? (unknown)"; +} + static long int sc_tv (time_value_t tv) { double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; @@ -119,6 +140,42 @@ process_file_gc_statm (struct proc_stat *ps, size_t *len) return len >= 0 ? contents : NULL; } +static char * +process_file_gc_status (struct proc_stat *ps, size_t *len) +{ + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + char *contents; + + *len = asprintf (&contents, + "Name:\t%s\n" + "State:\t%s\n" + "Tgid:\t%u\n" + "Pid:\t%u\n" + "PPid:\t%u\n" + "Uid:\t%u\t%u\t%u\t%u\n" + "VmSize:\t%8u kB\n" + "VmPeak:\t%8u kB\n" + "VmRSS:\t%8u kB\n" + "VmHWM:\t%8u kB\n" /* ie. resident peak */ + "Threads:\t%u\n", + proc_stat_args (ps), + state_string (ps), + proc_stat_pid (ps), /* XXX will need more work for threads */ + proc_stat_pid (ps), + proc_stat_proc_info (ps)->ppid, + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + proc_stat_owner_uid (ps), + tbi->virtual_size / 1024, + tbi->virtual_size / 1024, + tbi->resident_size / 1024, + tbi->resident_size / 1024, + proc_stat_num_threads (ps)); + + return len >= 0 ? contents : NULL; +} + /* Describes a file in a process directory. This is used as an "entry hook" * for our procfs_dir entry table, passed to process_file_make_node. */ @@ -240,6 +297,16 @@ static struct procfs_dir_entry entries[] = { .mode = 0444, }, }, + { + .name = "status", + .make_node = process_file_make_node, + .hook = & (struct process_file_desc) { + .get_contents = process_file_gc_status, + .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO + | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS, + .mode = 0444, + }, + }, {} }; -- cgit v1.2.3 From 25bad336ed7af83bfb78b2a3534fdf223554bfc0 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 07:35:59 +0000 Subject: Add a --compatible option * main.c (argp_parser): Add --compatible, which sets the options required for compatibility with the procps tools. --- main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/main.c b/main.c index fee4867c..35d05c65 100644 --- a/main.c +++ b/main.c @@ -51,6 +51,12 @@ argp_parser (int key, char *arg, struct argp_state *state) if (*endp || ! *arg || (signed) opt_kernel_pid < 0) error (1, 0, "--kernel-process: PID must be a positive integer"); break; + + case 'c': + opt_clk_tck = 100; + opt_stat_mode = 0444; + opt_fake_self = 1; + break; } return 0; @@ -74,6 +80,9 @@ struct argp argp = { { "kernel-process", 'k', "PID", 0, "Process identifier for the kernel, used to retreive its command line " "(default: 2)" }, + { "compatible", 'c', NULL, 0, + "Try to be compatible with the Linux procps utilities. " + "Currently equivalent to -h 100 -s 0444 -S 1." }, {} }, .parser = argp_parser, -- cgit v1.2.3 From 33bfc249fd692910bb1e3f7a5820d31dfda0f284 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 08:24:43 +0000 Subject: Handle errors in main * main.c (main): Handle errors from argp_parse and root_make_node. --- main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 35d05c65..a1db4753 100644 --- a/main.c +++ b/main.c @@ -127,19 +127,24 @@ root_make_node (struct node **np) int main (int argc, char **argv) { mach_port_t bootstrap; + error_t err; opt_clk_tck = sysconf(_SC_CLK_TCK); opt_stat_mode = 0400; opt_fake_self = -1; opt_kernel_pid = 2; - argp_parse (&argp, argc, argv, 0, 0, 0); + err = argp_parse (&argp, argc, argv, 0, 0, 0); + if (err) + error (1, err, "Could not parse command line"); task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) error (1, 0, "Must be started as a translator"); netfs_init (); - root_make_node (&netfs_root_node); + err = root_make_node (&netfs_root_node); + if (err) + error (1, err, "Could not create the root node"); netfs_startup (bootstrap, 0); for (;;) -- cgit v1.2.3 From d67e76dbb63e277f0d503224e63e85cc5d91ac11 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 08:25:07 +0000 Subject: netfs_server_loop never returns * main.c: Don't call netfs_server_loop repeatedly; it should never return. --- main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index a1db4753..4be506fb 100644 --- a/main.c +++ b/main.c @@ -147,7 +147,8 @@ int main (int argc, char **argv) error (1, err, "Could not create the root node"); netfs_startup (bootstrap, 0); - for (;;) - netfs_server_loop (); + netfs_server_loop (); + + assert (0 /* netfs_server_loop returned after all */); } -- cgit v1.2.3 From 4f40fb6fbfa95c51b6abfceffcd0e1ce80824e70 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 09:05:30 +0000 Subject: Add a TODO-list * TODO: New file. --- TODO | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 00000000..b4a39b05 --- /dev/null +++ b/TODO @@ -0,0 +1,2 @@ +* Add swap information +* Threads in /proc/[pid]/task/[n] -- cgit v1.2.3 From 4deaca2ce2c4ed2c5ab53f57a7747c71126a8da7 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 09:06:01 +0000 Subject: Use a global ps_context * proclist.c, proclist.h (proclist_create_node): Use a ps_context passed by the caller; errors are no longer possible, rename to proclist_make_node and change the signature accordingly. * rootdir.c, rootdir.h (rootdir_create_node): Likewise. * main.c (main): Create the ps_context here and pass it to root_make_node. (root_make_node): Pass it to proclist_make_node and rootdir_make_node. --- main.c | 35 ++++++++++++++++++++++------------- proclist.c | 15 +++------------ proclist.h | 6 ++++-- rootdir.c | 13 +++---------- rootdir.h | 6 ++++-- 5 files changed, 36 insertions(+), 39 deletions(-) diff --git a/main.c b/main.c index 4be506fb..91156485 100644 --- a/main.c +++ b/main.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "procfs.h" #include "proclist.h" #include "rootdir.h" @@ -94,38 +95,42 @@ struct argp argp = { }; error_t -root_make_node (struct node **np) +root_make_node (struct ps_context *pc, struct node **np) { /* We never have two root nodes alive simultaneously, so it's ok to have this as static data. */ static struct node *root_dirs[3]; - error_t err; - err = proclist_create_node (getproc (), &root_dirs[0]); - if (err) - return err; + root_dirs[0] = proclist_make_node (pc); + if (! root_dirs[0]) + goto nomem; - err = rootdir_create_node (&root_dirs[1]); - if (err) - { - netfs_nrele (root_dirs[0]); - return err; - } + root_dirs[1] = rootdir_make_node (pc); + if (! root_dirs[1]) + goto nomem; root_dirs[2] = NULL; *np = dircat_make_node (root_dirs); if (! *np) - return ENOMEM; + goto nomem; /* Since this one is not created through proc_lookup(), we have to affect an inode number to it. */ (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; return 0; + +nomem: + if (root_dirs[1]) + netfs_nrele (root_dirs[1]); + if (root_dirs[0]) + netfs_nrele (root_dirs[0]); + return ENOMEM; } int main (int argc, char **argv) { + struct ps_context *pc; mach_port_t bootstrap; error_t err; @@ -137,12 +142,16 @@ int main (int argc, char **argv) if (err) error (1, err, "Could not parse command line"); + err = ps_context_create (getproc (), &pc); + if (err) + error (1, err, "Could not create libps context"); + task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) error (1, 0, "Must be started as a translator"); netfs_init (); - err = root_make_node (&netfs_root_node); + err = root_make_node (pc, &netfs_root_node); if (err) error (1, err, "Could not create the root node"); diff --git a/proclist.c b/proclist.c index 56a3fdf9..35422a81 100644 --- a/proclist.c +++ b/proclist.c @@ -62,24 +62,15 @@ proclist_lookup (void *hook, const char *name, struct node **np) return process_lookup_pid (pc, pid, np); } -error_t -proclist_create_node (process_t procserv, struct node **np) +struct node * +proclist_make_node (struct ps_context *pc) { static const struct procfs_node_ops ops = { .get_contents = proclist_get_contents, .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, - .cleanup = (void (*)(void *)) ps_context_free, .enable_refresh_hack_and_break_readdir = 1, }; - struct ps_context *pc; - error_t err; - - err = ps_context_create (procserv, &pc); - if (err) - return err; - - *np = procfs_make_node (&ops, pc); - return 0; + return procfs_make_node (&ops, pc); } diff --git a/proclist.h b/proclist.h index 1c7ab084..ce69dde3 100644 --- a/proclist.h +++ b/proclist.h @@ -1,2 +1,4 @@ -#include -error_t proclist_create_node (process_t procserv, struct node **np); +#include + +struct node * +proclist_make_node (struct ps_context *pc); diff --git a/rootdir.c b/rootdir.c index 865342ce..8062f0d3 100644 --- a/rootdir.c +++ b/rootdir.c @@ -357,22 +357,15 @@ static const struct procfs_dir_entry rootdir_entries[] = { {} }; -error_t -rootdir_create_node (struct node **np) +struct node +*rootdir_make_node (struct ps_context *pc) { - struct ps_context *pc; const struct procfs_dir_entry *entries; - error_t err; - - err = ps_context_create (getproc (), &pc); - if (err) - return err; entries = rootdir_entries; if (opt_fake_self < 0) entries++; - *np = procfs_dir_make_node (entries, pc, (void (*)(void *)) ps_context_free); - return 0; + return procfs_dir_make_node (entries, pc, NULL); } diff --git a/rootdir.h b/rootdir.h index a764b0fd..0caee258 100644 --- a/rootdir.h +++ b/rootdir.h @@ -1,2 +1,4 @@ -error_t -rootdir_create_node (struct node **np); +#include + +struct node * +rootdir_make_node (struct ps_context *pc); -- cgit v1.2.3 From 0439361f817c3f610c1f5ec859bd302867472e50 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:02:13 +0000 Subject: Fix the handling of processes without an owner * main.c (argp_parser): New option --anonymous-owner. * main.h: Publish it. * process.c (process_lookup_pid): Use it to set the file owner uid of non-owned processes. --- main.c | 22 ++++++++++++++++++++++ main.h | 1 + process.c | 4 +++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index 91156485..06c1da04 100644 --- a/main.c +++ b/main.c @@ -16,10 +16,12 @@ int opt_clk_tck; mode_t opt_stat_mode; pid_t opt_fake_self; pid_t opt_kernel_pid; +uid_t opt_anon_owner; static error_t argp_parser (int key, char *arg, struct argp_state *state) { + struct passwd *pw; char *endp; switch (key) @@ -58,6 +60,20 @@ argp_parser (int key, char *arg, struct argp_state *state) opt_stat_mode = 0444; opt_fake_self = 1; break; + + case 'a': + pw = getpwnam (arg); + if (pw) + { + opt_anon_owner = pw->pw_uid; + break; + } + + opt_anon_owner = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_anon_owner < 0) + error(1, 0, "--anonymous-owner: USER should be the a user name " + "or a numeric UID."); + break; } return 0; @@ -84,6 +100,11 @@ struct argp argp = { { "compatible", 'c', NULL, 0, "Try to be compatible with the Linux procps utilities. " "Currently equivalent to -h 100 -s 0444 -S 1." }, + { "anonymous-owner", 'a', "USER", 0, + "Make USER the owner of files related to processes without one. " + "Be aware that USER will be granted access to the environment and " + "other sensitive information about the processes in question. " + "(default: use uid 0)" }, {} }, .parser = argp_parser, @@ -138,6 +159,7 @@ int main (int argc, char **argv) opt_stat_mode = 0400; opt_fake_self = -1; opt_kernel_pid = 2; + opt_anon_owner = 0; err = argp_parse (&argp, argc, argv, 0, 0, 0); if (err) error (1, err, "Could not parse command line"); diff --git a/main.h b/main.h index 6ada229a..28d1b023 100644 --- a/main.h +++ b/main.h @@ -3,3 +3,4 @@ extern int opt_clk_tck; extern mode_t opt_stat_mode; extern pid_t opt_fake_self; extern pid_t opt_kernel_pid; +extern uid_t opt_anon_owner; diff --git a/process.c b/process.c index b2b098ff..7f5646ae 100644 --- a/process.c +++ b/process.c @@ -314,6 +314,7 @@ error_t process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) { struct proc_stat *ps; + int owner; error_t err; err = _proc_stat_create (pid, pc, &ps); @@ -334,6 +335,7 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) if (! *np) return ENOMEM; - procfs_node_chown (*np, proc_stat_owner_uid (ps)); + owner = proc_stat_owner_uid (ps); + procfs_node_chown (*np, owner >= 0 ? owner : opt_anon_owner); return 0; } -- cgit v1.2.3 From a269783a41ae8b18e5b24d934c96ce9e90de3067 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:02:48 +0000 Subject: Remove the unused procfs_file module * procfs_file.c, procfs_file.h: Remove. * Makefile: Remove procfs_file. --- Makefile | 2 +- procfs_file.c | 54 ------------------------------------------------------ procfs_file.h | 6 ------ 3 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 procfs_file.c delete mode 100644 procfs_file.h diff --git a/Makefile b/Makefile index 9820a76b..b8c055f6 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ TARGET = procfs -OBJS = procfs.o netfs.o procfs_file.o procfs_dir.o \ +OBJS = procfs.o netfs.o procfs_dir.o \ process.o proclist.o rootdir.o dircat.o main.o LIBS = -lnetfs -lps diff --git a/procfs_file.c b/procfs_file.c deleted file mode 100644 index cb0488e9..00000000 --- a/procfs_file.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include "procfs.h" -#include "procfs_file.h" - -struct procfs_file -{ - void *contents; - size_t len; - void (*cleanup)(void *contents); -}; - -error_t -procfs_file_getcontents (void *hook, void **contents, size_t *contents_len) -{ - struct procfs_file *f = hook; - - *contents = f->contents; - *contents_len = f->len; - return 0; -} - -void -procfs_file_cleanup (void *hook) -{ - struct procfs_file *f = hook; - - if (f->cleanup) - f->cleanup (f->contents); - - free (f); -} - -struct node * -procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)) -{ - static const struct procfs_node_ops ops = { - .get_contents = procfs_file_getcontents, - .cleanup = procfs_file_cleanup, - }; - struct procfs_file *f; - - f = malloc (sizeof *f); - if (! f) - return NULL; - - f->contents = contents; - f->len = (len >= 0) ? len : strlen (f->contents); - f->cleanup = cleanup; - - return procfs_make_node (&ops, f); -} - diff --git a/procfs_file.h b/procfs_file.h deleted file mode 100644 index b615db93..00000000 --- a/procfs_file.h +++ /dev/null @@ -1,6 +0,0 @@ -/* Create a new regular file with the given CONTENTS. If LEN is negative, - CONTENTS is considered as a string and the file stops at the first - nul char. If CLEANUP is non-NULL, it is passed CONTENTS when the - node is destroyed. */ -struct node * -procfs_file_make_node (void *contents, ssize_t len, void (*cleanup)(void *)); -- cgit v1.2.3 From 0f2bdacd6ad3dbcc905925dee12cb30918c33a11 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:24:29 +0000 Subject: Make contents a char* to avoid typecasts all over the place * procfs.h (procfs_cleanup_contents_with_free, procfs_cleanup_contents_with_vm_deallocate, procfs_get_contents, struct procfs_ops): Change CONTENTS from a void pointer to a char one. * dircat.c, netfs.c, process.c, procfs.c, procfs_dir.c, proclist.c, rootdir.c: Update. --- dircat.c | 4 ++-- netfs.c | 10 +++++----- process.c | 2 +- procfs.c | 10 +++++----- procfs.h | 10 +++++----- procfs_dir.c | 2 +- proclist.c | 2 +- rootdir.c | 32 ++++++++++++++++---------------- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/dircat.c b/dircat.c index 857ba72b..93bb2fed 100644 --- a/dircat.c +++ b/dircat.c @@ -8,7 +8,7 @@ struct dircat_node }; static error_t -dircat_get_contents (void *hook, void **contents, size_t *contents_len) +dircat_get_contents (void *hook, char **contents, size_t *contents_len) { struct dircat_node *dcn = hook; int i, sz, pos; @@ -19,7 +19,7 @@ dircat_get_contents (void *hook, void **contents, size_t *contents_len) for (i=0; dcn->dirs[i]; i++) { - void *subcon; + char *subcon; size_t sublen; err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); diff --git a/netfs.c b/netfs.c index a47861e5..6fd82a00 100644 --- a/netfs.c +++ b/netfs.c @@ -29,7 +29,7 @@ int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; responsible for the operation. NP is locked. */ error_t netfs_validate_stat (struct node *np, struct iouser *cred) { - void *contents; + char *contents; size_t contents_len; error_t err; @@ -57,7 +57,7 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *np, size_t contents_len; error_t err; - err = procfs_get_contents (np, (void **) &contents, &contents_len); + err = procfs_get_contents (np, &contents, &contents_len); if (err) return err; @@ -82,7 +82,7 @@ error_t netfs_attempt_readlink (struct iouser *user, struct node *np, size_t contents_len; error_t err; - err = procfs_get_contents (np, (void **) &contents, &contents_len); + err = procfs_get_contents (np, &contents, &contents_len); if (err) return err; @@ -140,7 +140,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, size_t contents_len; error_t err; - err = procfs_get_contents (dir, (void **) &contents, &contents_len); + err = procfs_get_contents (dir, &contents, &contents_len); if (err) return err; @@ -159,7 +159,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, putentries (contents, contents_len, nentries, NULL, datacnt); if (bufsize < *datacnt) { - void *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); + char *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); if (n == MAP_FAILED) return ENOMEM; diff --git a/process.c b/process.c index 7f5646ae..fa795522 100644 --- a/process.c +++ b/process.c @@ -204,7 +204,7 @@ struct process_file_node }; static error_t -process_file_get_contents (void *hook, void **contents, size_t *contents_len) +process_file_get_contents (void *hook, char **contents, size_t *contents_len) { struct process_file_node *file = hook; error_t err; diff --git a/procfs.c b/procfs.c index 5396ecf7..a5f52b50 100644 --- a/procfs.c +++ b/procfs.c @@ -12,7 +12,7 @@ struct netnode void *hook; /* (cached) contents of the node */ - void *contents; + char *contents; size_t contents_len; /* parent directory, if applicable */ @@ -20,13 +20,13 @@ struct netnode }; void -procfs_cleanup_contents_with_free (void *hook, void *cont, size_t len) +procfs_cleanup_contents_with_free (void *hook, char *cont, size_t len) { free (cont); } void -procfs_cleanup_contents_with_vm_deallocate (void *hook, void *cont, size_t len) +procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, size_t len) { vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); } @@ -109,7 +109,7 @@ procfs_make_ino (struct node *np, const char *filename) return (unsigned long) jrand48 (x); } -error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) +error_t procfs_get_contents (struct node *np, char **data, size_t *data_len) { if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) { @@ -121,7 +121,7 @@ error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) if (! np->nn->contents && np->nn->ops->get_contents) { - void *contents; + char *contents; size_t contents_len; error_t err; diff --git a/procfs.h b/procfs.h index 4ab3b567..8336ee85 100644 --- a/procfs.h +++ b/procfs.h @@ -17,8 +17,8 @@ struct procfs_node_ops netnode->nn_stat. For regular files and symlinks, they are what you would expect; for directories, they are an argz vector of the names of the entries. */ - error_t (*get_contents) (void *hook, void **contents, size_t *contents_len); - void (*cleanup_contents) (void *hook, void *contents, size_t contents_len); + error_t (*get_contents) (void *hook, char **contents, size_t *contents_len); + void (*cleanup_contents) (void *hook, char *contents, size_t contents_len); /* Lookup NAME in this directory, and store the result in *np. The returned node should be created by lookup() using procfs_make_node() @@ -41,8 +41,8 @@ struct procfs_node_ops }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ -void procfs_cleanup_contents_with_free (void *, void *, size_t); -void procfs_cleanup_contents_with_vm_deallocate (void *, void *, size_t); +void procfs_cleanup_contents_with_free (void *, char *, size_t); +void procfs_cleanup_contents_with_vm_deallocate (void *, char *, size_t); /* Create a new node and return it. Returns NULL if it fails to allocate enough memory. In this case, ops->cleanup will be invoked. */ @@ -69,7 +69,7 @@ void procfs_node_chtype (struct node *np, mode_t type); corresponding child nodes. */ ino64_t procfs_make_ino (struct node *np, const char *filename); -error_t procfs_get_contents (struct node *np, void **data, size_t *data_len); +error_t procfs_get_contents (struct node *np, char **data, size_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); diff --git a/procfs_dir.c b/procfs_dir.c index 431fea3e..4df46695 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -11,7 +11,7 @@ struct procfs_dir_node }; static error_t -procfs_dir_get_contents (void *hook, void **contents, size_t *contents_len) +procfs_dir_get_contents (void *hook, char **contents, size_t *contents_len) { static const char dot_dotdot[] = ".\0.."; struct procfs_dir_node *dn = hook; diff --git a/proclist.c b/proclist.c index 35422a81..b5acb26c 100644 --- a/proclist.c +++ b/proclist.c @@ -10,7 +10,7 @@ #define PID_STR_SIZE (3 * sizeof (pid_t) + 1) static error_t -proclist_get_contents (void *hook, void **contents, size_t *contents_len) +proclist_get_contents (void *hook, char **contents, size_t *contents_len) { struct ps_context *pc = hook; pidarray_t pids; diff --git a/rootdir.c b/rootdir.c index 8062f0d3..cd8949bf 100644 --- a/rootdir.c +++ b/rootdir.c @@ -44,7 +44,7 @@ get_boottime (struct ps_context *pc, struct timeval *tv) } static error_t -rootdir_gc_version (void *hook, void **contents, size_t *contents_len) +rootdir_gc_version (void *hook, char **contents, size_t *contents_len) { struct utsname uts; int r; @@ -53,7 +53,7 @@ rootdir_gc_version (void *hook, void **contents, size_t *contents_len) if (r < 0) return errno; - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, "Linux version 2.6.1 (%s %s %s %s)\n", uts.sysname, uts.release, uts.version, uts.machine); @@ -63,7 +63,7 @@ rootdir_gc_version (void *hook, void **contents, size_t *contents_len) /* Uptime -- we use the start time of init to deduce it. This is probably a bit fragile, as any clock update will make the result inaccurate. */ static error_t -rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) +rootdir_gc_uptime (void *hook, char **contents, size_t *contents_len) { struct timeval time, boottime; double up_secs; @@ -85,13 +85,13 @@ rootdir_gc_uptime (void *hook, void **contents, size_t *contents_len) proc(5) specifies that it should be equal to USER_HZ times the idle value in ticks from /proc/stat. So we assume a completely idle system both here and there to make that work. */ - *contents_len = asprintf ((char **) contents, "%.2lf %.2lf\n", up_secs, up_secs); + *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, up_secs); return *contents_len >= 0 ? 0 : ENOMEM; } static error_t -rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) +rootdir_gc_stat (void *hook, char **contents, size_t *contents_len) { struct timeval boottime, time; struct vm_statistics vmstats; @@ -113,7 +113,7 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) timersub (&time, &boottime, &time); up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, /* Does Mach keeps track of any of this? */ "cpu 0 0 0 %lu 0 0 0 0 0\n" "cpu0 0 0 0 %lu 0 0 0 0 0\n" @@ -130,7 +130,7 @@ rootdir_gc_stat (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) +rootdir_gc_loadavg (void *hook, char **contents, size_t *contents_len) { host_load_info_data_t hli; mach_msg_type_number_t cnt; @@ -142,7 +142,7 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) return err; assert (cnt == HOST_LOAD_INFO_COUNT); - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, "%.2f %.2f %.2f 1/0 0\n", hli.avenrun[0] / (double) LOAD_SCALE, hli.avenrun[1] / (double) LOAD_SCALE, @@ -152,7 +152,7 @@ rootdir_gc_loadavg (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_meminfo (void *hook, void **contents, size_t *contents_len) +rootdir_gc_meminfo (void *hook, char **contents, size_t *contents_len) { host_basic_info_data_t hbi; mach_msg_type_number_t cnt; @@ -169,7 +169,7 @@ rootdir_gc_meminfo (void *hook, void **contents, size_t *contents_len) return err; assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, "MemTotal: %14lu kB\n" "MemFree: %14lu kB\n" "Active: %14lu kB\n" @@ -187,7 +187,7 @@ rootdir_gc_meminfo (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) +rootdir_gc_vmstat (void *hook, char **contents, size_t *contents_len) { host_basic_info_data_t hbi; mach_msg_type_number_t cnt; @@ -204,7 +204,7 @@ rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) return err; assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf ((char **) contents, + *contents_len = asprintf (contents, "nr_free_pages %lu\n" "nr_inactive_anon %lu\n" "nr_active_anon %lu\n" @@ -232,7 +232,7 @@ rootdir_gc_vmstat (void *hook, void **contents, size_t *contents_len) } static error_t -rootdir_gc_cmdline (void *hook, void **contents, size_t *contents_len) +rootdir_gc_cmdline (void *hook, char **contents, size_t *contents_len) { struct ps_context *pc = hook; struct proc_stat *ps; @@ -259,7 +259,7 @@ rootdir_gc_cmdline (void *hook, void **contents, size_t *contents_len) memcpy (*contents, proc_stat_args (ps), *contents_len); argz_stringify (*contents, *contents_len, ' '); - ((char *) *contents)[*contents_len - 1] = '\n'; + (*contents)[*contents_len - 1] = '\n'; out: _proc_stat_free (ps); @@ -267,9 +267,9 @@ out: } static error_t -rootdir_gc_fakeself (void *hook, void **contents, size_t *contents_len) +rootdir_gc_fakeself (void *hook, char **contents, size_t *contents_len) { - *contents_len = asprintf ((char **) contents, "%d", opt_fake_self); + *contents_len = asprintf (contents, "%d", opt_fake_self); return *contents_len >= 0 ? 0 : ENOMEM; } -- cgit v1.2.3 From 6e202c432e2f16dfa83a7dc21b759c03623fa394 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:33:22 +0000 Subject: Detect asprintf's ENOMEM in procfs.c rather than everywhere * procfs.h: Make CONTENTS_LEN an ssize_t rather than a size_t, and document the change. * procfs.c (procfs_get_contents): Initialize CONTENTS_LEN to a negative value, and fail with ENOMEM if it's still negative after the callback returns. (everywhere): Update to ssize_t. * dircat.c, netfs.c, process.c, procfs_dir.c, proclist.c, rootdir.c: Update to ssize_t and the new GET_CONTENTS semantics. --- dircat.c | 4 ++-- netfs.c | 8 ++++---- process.c | 54 ++++++++++++++++++++---------------------------------- procfs.c | 13 ++++++++----- procfs.h | 14 ++++++++------ procfs_dir.c | 2 +- proclist.c | 2 +- rootdir.c | 30 +++++++++++++++--------------- 8 files changed, 59 insertions(+), 68 deletions(-) diff --git a/dircat.c b/dircat.c index 93bb2fed..bb66508a 100644 --- a/dircat.c +++ b/dircat.c @@ -8,7 +8,7 @@ struct dircat_node }; static error_t -dircat_get_contents (void *hook, char **contents, size_t *contents_len) +dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) { struct dircat_node *dcn = hook; int i, sz, pos; @@ -20,7 +20,7 @@ dircat_get_contents (void *hook, char **contents, size_t *contents_len) for (i=0; dcn->dirs[i]; i++) { char *subcon; - size_t sublen; + ssize_t sublen; err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); if (err) diff --git a/netfs.c b/netfs.c index 6fd82a00..e41e0623 100644 --- a/netfs.c +++ b/netfs.c @@ -30,7 +30,7 @@ int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; error_t netfs_validate_stat (struct node *np, struct iouser *cred) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; /* Only symlinks need to have their size filled, before a read is @@ -54,7 +54,7 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *np, loff_t offset, size_t *len, void *data) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; err = procfs_get_contents (np, &contents, &contents_len); @@ -79,7 +79,7 @@ error_t netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; err = procfs_get_contents (np, &contents, &contents_len); @@ -137,7 +137,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, vm_size_t bufsize, int *amt) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; err = procfs_get_contents (dir, &contents, &contents_len); diff --git a/process.c b/process.c index fa795522..624f79e7 100644 --- a/process.c +++ b/process.c @@ -12,18 +12,18 @@ /* Implementations for the process_file_desc.get_contents callback. */ -static char * -process_file_gc_cmdline (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_cmdline (struct proc_stat *ps, char **contents) { - *len = proc_stat_args_len(ps); - return proc_stat_args(ps); + *contents = proc_stat_args(ps); + return proc_stat_args_len(ps); } -static char * -process_file_gc_environ (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_environ (struct proc_stat *ps, char **contents) { - *len = proc_stat_args_len(ps); - return proc_stat_args(ps); + *contents = proc_stat_env(ps); + return proc_stat_env_len(ps); } static char state_char (struct proc_stat *ps) @@ -71,17 +71,16 @@ static long long int jiff_tv (time_value_t tv) return usecs * 100 / 1000000; } -static char * -process_file_gc_stat (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_stat (struct proc_stat *ps, char **contents) { struct procinfo *pi = proc_stat_proc_info (ps); task_basic_info_t tbi = proc_stat_task_basic_info (ps); thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); - char *contents; /* See proc(5) for more information about the contents of each field for the Linux procfs. */ - *len = asprintf (&contents, + return asprintf (contents, "%d (%s) %c " /* pid, command, state */ "%d %d %d " /* ppid, pgid, session */ "%d %d " /* controling tty stuff */ @@ -122,31 +121,23 @@ process_file_gc_stat (struct proc_stat *ps, size_t *len) 0, 0, 0, 0LL); - - return len >= 0 ? contents : NULL; } -static char * -process_file_gc_statm (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_statm (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); - char *contents; - - *len = asprintf (&contents, + return asprintf (contents, "%lu %lu 0 0 0 0 0\n", tbi->virtual_size / sysconf(_SC_PAGE_SIZE), tbi->resident_size / sysconf(_SC_PAGE_SIZE)); - - return len >= 0 ? contents : NULL; } -static char * -process_file_gc_status (struct proc_stat *ps, size_t *len) +static ssize_t +process_file_gc_status (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); - char *contents; - - *len = asprintf (&contents, + return asprintf (contents, "Name:\t%s\n" "State:\t%s\n" "Tgid:\t%u\n" @@ -172,8 +163,6 @@ process_file_gc_status (struct proc_stat *ps, size_t *len) tbi->resident_size / 1024, tbi->resident_size / 1024, proc_stat_num_threads (ps)); - - return len >= 0 ? contents : NULL; } @@ -186,7 +175,7 @@ struct process_file_desc /* Once we have acquired the necessary information, there can be only memory allocation errors, hence this simplified signature. */ - char *(*get_contents) (struct proc_stat *ps, size_t *len); + ssize_t (*get_contents) (struct proc_stat *ps, char **contents); /* The cmdline and environ contents don't need any cleaning since they are part of a proc_stat structure. */ @@ -204,7 +193,7 @@ struct process_file_node }; static error_t -process_file_get_contents (void *hook, char **contents, size_t *contents_len) +process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) { struct process_file_node *file = hook; error_t err; @@ -215,10 +204,7 @@ process_file_get_contents (void *hook, char **contents, size_t *contents_len) if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) return EIO; - *contents = file->desc->get_contents (file->ps, contents_len); - if (! *contents) - return ENOMEM; - + *contents_len = file->desc->get_contents (file->ps, contents); return 0; } diff --git a/procfs.c b/procfs.c index a5f52b50..6d15e4fd 100644 --- a/procfs.c +++ b/procfs.c @@ -13,20 +13,20 @@ struct netnode /* (cached) contents of the node */ char *contents; - size_t contents_len; + ssize_t contents_len; /* parent directory, if applicable */ struct node *parent; }; void -procfs_cleanup_contents_with_free (void *hook, char *cont, size_t len) +procfs_cleanup_contents_with_free (void *hook, char *cont, ssize_t len) { free (cont); } void -procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, size_t len) +procfs_cleanup_contents_with_vm_deallocate (void *hook, char *cont, ssize_t len) { vm_deallocate (mach_task_self (), (vm_address_t) cont, (vm_size_t) len); } @@ -109,7 +109,7 @@ procfs_make_ino (struct node *np, const char *filename) return (unsigned long) jrand48 (x); } -error_t procfs_get_contents (struct node *np, char **data, size_t *data_len) +error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) { if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) { @@ -122,12 +122,15 @@ error_t procfs_get_contents (struct node *np, char **data, size_t *data_len) if (! np->nn->contents && np->nn->ops->get_contents) { char *contents; - size_t contents_len; + ssize_t contents_len; error_t err; + contents_len = -1; err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); if (err) return err; + if (contents_len < 0) + return ENOMEM; np->nn->contents = contents; np->nn->contents_len = contents_len; diff --git a/procfs.h b/procfs.h index 8336ee85..e8ef18b3 100644 --- a/procfs.h +++ b/procfs.h @@ -16,9 +16,11 @@ struct procfs_node_ops symlink or directory, as determined by the file mode in netnode->nn_stat. For regular files and symlinks, they are what you would expect; for directories, they are an argz vector of the - names of the entries. */ - error_t (*get_contents) (void *hook, char **contents, size_t *contents_len); - void (*cleanup_contents) (void *hook, char *contents, size_t contents_len); + names of the entries. If upon return, *CONTENTS_LEN is negative or + unchanged, the call is considered to have failed because of a memory + allocation error. */ + error_t (*get_contents) (void *hook, char **contents, ssize_t *contents_len); + void (*cleanup_contents) (void *hook, char *contents, ssize_t contents_len); /* Lookup NAME in this directory, and store the result in *np. The returned node should be created by lookup() using procfs_make_node() @@ -41,8 +43,8 @@ struct procfs_node_ops }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ -void procfs_cleanup_contents_with_free (void *, char *, size_t); -void procfs_cleanup_contents_with_vm_deallocate (void *, char *, size_t); +void procfs_cleanup_contents_with_free (void *, char *, ssize_t); +void procfs_cleanup_contents_with_vm_deallocate (void *, char *, ssize_t); /* Create a new node and return it. Returns NULL if it fails to allocate enough memory. In this case, ops->cleanup will be invoked. */ @@ -69,7 +71,7 @@ void procfs_node_chtype (struct node *np, mode_t type); corresponding child nodes. */ ino64_t procfs_make_ino (struct node *np, const char *filename); -error_t procfs_get_contents (struct node *np, char **data, size_t *data_len); +error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); diff --git a/procfs_dir.c b/procfs_dir.c index 4df46695..8ec3f7a5 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -11,7 +11,7 @@ struct procfs_dir_node }; static error_t -procfs_dir_get_contents (void *hook, char **contents, size_t *contents_len) +procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len) { static const char dot_dotdot[] = ".\0.."; struct procfs_dir_node *dn = hook; diff --git a/proclist.c b/proclist.c index b5acb26c..38368feb 100644 --- a/proclist.c +++ b/proclist.c @@ -10,7 +10,7 @@ #define PID_STR_SIZE (3 * sizeof (pid_t) + 1) static error_t -proclist_get_contents (void *hook, char **contents, size_t *contents_len) +proclist_get_contents (void *hook, char **contents, ssize_t *contents_len) { struct ps_context *pc = hook; pidarray_t pids; diff --git a/rootdir.c b/rootdir.c index cd8949bf..364b0731 100644 --- a/rootdir.c +++ b/rootdir.c @@ -44,7 +44,7 @@ get_boottime (struct ps_context *pc, struct timeval *tv) } static error_t -rootdir_gc_version (void *hook, char **contents, size_t *contents_len) +rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { struct utsname uts; int r; @@ -57,13 +57,13 @@ rootdir_gc_version (void *hook, char **contents, size_t *contents_len) "Linux version 2.6.1 (%s %s %s %s)\n", uts.sysname, uts.release, uts.version, uts.machine); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } /* Uptime -- we use the start time of init to deduce it. This is probably a bit fragile, as any clock update will make the result inaccurate. */ static error_t -rootdir_gc_uptime (void *hook, char **contents, size_t *contents_len) +rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) { struct timeval time, boottime; double up_secs; @@ -87,11 +87,11 @@ rootdir_gc_uptime (void *hook, char **contents, size_t *contents_len) and there to make that work. */ *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, up_secs); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_stat (void *hook, char **contents, size_t *contents_len) +rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) { struct timeval boottime, time; struct vm_statistics vmstats; @@ -126,11 +126,11 @@ rootdir_gc_stat (void *hook, char **contents, size_t *contents_len) vmstats.pageins, vmstats.pageouts, boottime.tv_sec); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_loadavg (void *hook, char **contents, size_t *contents_len) +rootdir_gc_loadavg (void *hook, char **contents, ssize_t *contents_len) { host_load_info_data_t hli; mach_msg_type_number_t cnt; @@ -148,11 +148,11 @@ rootdir_gc_loadavg (void *hook, char **contents, size_t *contents_len) hli.avenrun[1] / (double) LOAD_SCALE, hli.avenrun[2] / (double) LOAD_SCALE); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_meminfo (void *hook, char **contents, size_t *contents_len) +rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) { host_basic_info_data_t hbi; mach_msg_type_number_t cnt; @@ -183,11 +183,11 @@ rootdir_gc_meminfo (void *hook, char **contents, size_t *contents_len) (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_vmstat (void *hook, char **contents, size_t *contents_len) +rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len) { host_basic_info_data_t hbi; mach_msg_type_number_t cnt; @@ -228,11 +228,11 @@ rootdir_gc_vmstat (void *hook, char **contents, size_t *contents_len) (long unsigned) vmstats.pageouts, (long unsigned) vmstats.faults); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } static error_t -rootdir_gc_cmdline (void *hook, char **contents, size_t *contents_len) +rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) { struct ps_context *pc = hook; struct proc_stat *ps; @@ -267,10 +267,10 @@ out: } static error_t -rootdir_gc_fakeself (void *hook, char **contents, size_t *contents_len) +rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) { *contents_len = asprintf (contents, "%d", opt_fake_self); - return *contents_len >= 0 ? 0 : ENOMEM; + return 0; } -- cgit v1.2.3 From 4646c4a3ef6171a0ddec4fcfe0c6aa34b50501cd Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 11:54:10 +0000 Subject: Improve the interface for dircat_make_node * dircat.c, dircat.h (dircat_make_node): Use an explicit array size for DIRS, fail with ENOMEM is any of them is NULL, and release the reference on the non-NULL nodes on any error. * main.c (root_make_node): Use the new interface. --- dircat.c | 37 +++++++++++++++++++++++-------------- dircat.h | 17 +++++++++-------- main.c | 27 ++++++--------------------- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/dircat.c b/dircat.c index bb66508a..d043486d 100644 --- a/dircat.c +++ b/dircat.c @@ -4,7 +4,8 @@ struct dircat_node { - struct node **dirs; + int num_dirs; + struct node *dirs[0]; }; static error_t @@ -17,7 +18,7 @@ dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) pos = 0; *contents = malloc (sz = 512); - for (i=0; dcn->dirs[i]; i++) + for (i=0; i < dcn->num_dirs; i++) { char *subcon; ssize_t sublen; @@ -49,19 +50,20 @@ dircat_lookup (void *hook, const char *name, struct node **np) int i; err = ENOENT; - for (i=0; err && dcn->dirs[i]; i++) + for (i=0; err && i < dcn->num_dirs; i++) err = procfs_lookup (dcn->dirs[i], name, np); return err; } static void -dircat_release_dirs (struct node **dirs) +dircat_release_dirs (struct node *const *dirs, int num_dirs) { int i; - for (i=0; dirs[i]; i++) - netfs_nrele (dirs[i]); + for (i=0; i < num_dirs; i++) + if (dirs[i]) + netfs_nrele (dirs[i]); } static void @@ -69,12 +71,12 @@ dircat_cleanup (void *hook) { struct dircat_node *dcn = hook; - dircat_release_dirs (dcn->dirs); + dircat_release_dirs (dcn->dirs, dcn->num_dirs); free (dcn); } struct node * -dircat_make_node (struct node **dirs) +dircat_make_node (struct node *const *dirs, int num_dirs) { static struct procfs_node_ops ops = { .get_contents = dircat_get_contents, @@ -85,15 +87,22 @@ dircat_make_node (struct node **dirs) .enable_refresh_hack_and_break_readdir = 1, }; struct dircat_node *dcn; + int i; + + for (i=0; i < num_dirs; i++) + if (! dirs[i]) + goto fail; - dcn = malloc (sizeof *dcn); + dcn = malloc (sizeof *dcn + num_dirs * sizeof dcn->dirs[0]); if (! dcn) - { - dircat_release_dirs (dirs); - return NULL; - } + goto fail; - dcn->dirs = dirs; + dcn->num_dirs = num_dirs; + memcpy (dcn->dirs, dirs, num_dirs * sizeof dcn->dirs[0]); return procfs_make_node (&ops, dcn); + +fail: + dircat_release_dirs (dirs, num_dirs); + return NULL; } diff --git a/dircat.h b/dircat.h index cb228526..951d2021 100644 --- a/dircat.h +++ b/dircat.h @@ -1,9 +1,10 @@ -/* Append the contents of multiple directories. DIRS is a - NULL-terminated array of directory nodes. One reference is consumed - for each of them, even on ENOMEM, in which case NULL is returned. - DIRS has to be static data for now, or at list remain available and - unchanged for the duration of the created node's life. Strange - things will happen if they have entries with the same name or if one - of them is not a directory. */ +/* Append the contents of NUM_DIRS directories. DIRS is an array of + directory nodes. One reference is consumed for each of them. If a + memory allocation error occurs, or if one of the directories is a + NULL pointer, the references are dropped immediately and NULL is + returned. The given DIRS array is duplicated and can therefore be + allocated on the caller's stack. Strange things will happen if some + elements of DIRS have entries with the same name or if one of them is + not a directory. */ struct node * -dircat_make_node (struct node **dirs); +dircat_make_node (struct node *const *dirs, int num_dirs); diff --git a/main.c b/main.c index 06c1da04..eaab9867 100644 --- a/main.c +++ b/main.c @@ -118,35 +118,20 @@ struct argp argp = { error_t root_make_node (struct ps_context *pc, struct node **np) { - /* We never have two root nodes alive simultaneously, so it's ok to - have this as static data. */ - static struct node *root_dirs[3]; + struct node *root_dirs[] = { + proclist_make_node (pc), + rootdir_make_node (pc), + }; - root_dirs[0] = proclist_make_node (pc); - if (! root_dirs[0]) - goto nomem; - - root_dirs[1] = rootdir_make_node (pc); - if (! root_dirs[1]) - goto nomem; - - root_dirs[2] = NULL; - *np = dircat_make_node (root_dirs); + *np = dircat_make_node (root_dirs, sizeof root_dirs / sizeof root_dirs[0]); if (! *np) - goto nomem; + return ENOMEM; /* Since this one is not created through proc_lookup(), we have to affect an inode number to it. */ (*np)->nn_stat.st_ino = * (uint32_t *) "PROC"; return 0; - -nomem: - if (root_dirs[1]) - netfs_nrele (root_dirs[1]); - if (root_dirs[0]) - netfs_nrele (root_dirs[0]); - return ENOMEM; } int main (int argc, char **argv) -- cgit v1.2.3 From f522af65aa004fea09705c74115836c6acd1cddb Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 14:15:54 +0000 Subject: Revamp procfs_dir * procfs_dir.c, procfs_dir.h: Revamp the interface to make the more complicated use cases somewhat less hackish. * process.c: Update, specify a default make_node function. * rootdir.c: Likewise; make this optional "self" link use case somewhat less hackish. --- process.c | 16 +++++++------ procfs_dir.c | 78 ++++++++++++++++++++++++++++++++++++++---------------------- procfs_dir.h | 48 +++++++++++++++++++++++++++---------- rootdir.c | 36 +++++++++++++++------------- 4 files changed, 113 insertions(+), 65 deletions(-) diff --git a/process.c b/process.c index 624f79e7..61688f7e 100644 --- a/process.c +++ b/process.c @@ -209,7 +209,7 @@ process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) } static struct node * -process_file_make_node (void *dir_hook, void *entry_hook) +process_file_make_node (void *dir_hook, const void *entry_hook) { static const struct procfs_node_ops ops = { .get_contents = process_file_get_contents, @@ -245,7 +245,6 @@ process_file_make_node (void *dir_hook, void *entry_hook) static struct procfs_dir_entry entries[] = { { .name = "cmdline", - .make_node = process_file_make_node, .hook = & (struct process_file_desc) { .get_contents = process_file_gc_cmdline, .needs = PSTAT_ARGS, @@ -254,7 +253,6 @@ static struct procfs_dir_entry entries[] = { }, { .name = "environ", - .make_node = process_file_make_node, .hook = & (struct process_file_desc) { .get_contents = process_file_gc_environ, .needs = PSTAT_ENV, @@ -265,7 +263,6 @@ static struct procfs_dir_entry entries[] = { { /* Beware of the hack below, which requires this to be entries[2]. */ .name = "stat", - .make_node = process_file_make_node, .hook = & (struct process_file_desc) { .get_contents = process_file_gc_stat, .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO @@ -276,7 +273,6 @@ static struct procfs_dir_entry entries[] = { }, { .name = "statm", - .make_node = process_file_make_node, .hook = & (struct process_file_desc) { .get_contents = process_file_gc_statm, .needs = PSTAT_TASK_BASIC, @@ -285,7 +281,6 @@ static struct procfs_dir_entry entries[] = { }, { .name = "status", - .make_node = process_file_make_node, .hook = & (struct process_file_desc) { .get_contents = process_file_gc_status, .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO @@ -299,6 +294,13 @@ static struct procfs_dir_entry entries[] = { error_t process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) { + static const struct procfs_dir_ops dir_ops = { + .entries = entries, + .cleanup = (void (*)(void *)) _proc_stat_free, + .entry_ops = { + .make_node = process_file_make_node, + }, + }; struct proc_stat *ps; int owner; error_t err; @@ -317,7 +319,7 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) accessed in a more robust and straightforward way. */ ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; - *np = procfs_dir_make_node (entries, ps, (void (*)(void *)) _proc_stat_free); + *np = procfs_dir_make_node (&dir_ops, ps); if (! *np) return ENOMEM; diff --git a/procfs_dir.c b/procfs_dir.c index 8ec3f7a5..dfe30a26 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -5,49 +5,71 @@ struct procfs_dir_node { - const struct procfs_dir_entry *entries; + const struct procfs_dir_ops *ops; void *hook; - void (*cleanup) (void *hook); }; +static int +entry_exists (struct procfs_dir_node *dir, const struct procfs_dir_entry *ent) +{ + if (ent->ops.exists) + return ent->ops.exists (dir->hook, ent->hook); + if (dir->ops->entry_ops.exists) + return dir->ops->entry_ops.exists (dir->hook, ent->hook); + + return 1; +} + static error_t procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len) { static const char dot_dotdot[] = ".\0.."; - struct procfs_dir_node *dn = hook; + struct procfs_dir_node *dir = hook; const struct procfs_dir_entry *ent; - char *pos; + int pos; - *contents_len = sizeof dot_dotdot; - for (ent = dn->entries; ent->name; ent++) - *contents_len += strlen (ent->name) + 1; + /* Evaluate how much space is needed. Note that we include the hidden + entries, just in case their status changes between now and then. */ + pos = sizeof dot_dotdot; + for (ent = dir->ops->entries; ent->name; ent++) + pos += strlen (ent->name) + 1; - *contents = malloc (*contents_len); + *contents = malloc (pos); if (! *contents) return ENOMEM; memcpy (*contents, dot_dotdot, sizeof dot_dotdot); - pos = *contents + sizeof dot_dotdot; - for (ent = dn->entries; ent->name; ent++) + pos = sizeof dot_dotdot; + for (ent = dir->ops->entries; ent->name; ent++) { - strcpy (pos, ent->name); + if (! entry_exists (dir, ent)) + continue; + + strcpy (*contents + pos, ent->name); pos += strlen (ent->name) + 1; } + *contents_len = pos; return 0; } static error_t procfs_dir_lookup (void *hook, const char *name, struct node **np) { - struct procfs_dir_node *dn = hook; + struct procfs_dir_node *dir = hook; const struct procfs_dir_entry *ent; - for (ent = dn->entries; ent->name && strcmp (name, ent->name); ent++); + for (ent = dir->ops->entries; ent->name && strcmp (name, ent->name); ent++); if (! ent->name) return ENOENT; - *np = ent->make_node (dn->hook, ent->hook); + if (ent->ops.make_node) + *np = ent->ops.make_node (dir->hook, ent->hook); + else if (dir->ops->entry_ops.make_node) + *np = dir->ops->entry_ops.make_node (dir->hook, ent->hook); + else + return EGRATUITOUS; + if (! *np) return ENOMEM; @@ -57,17 +79,16 @@ procfs_dir_lookup (void *hook, const char *name, struct node **np) static void procfs_dir_cleanup (void *hook) { - struct procfs_dir_node *dn = hook; + struct procfs_dir_node *dir = hook; - if (dn->cleanup) - dn->cleanup (dn->hook); + if (dir->ops->cleanup) + dir->ops->cleanup (dir->hook); - free (dn); + free (dir); } struct node * -procfs_dir_make_node (const struct procfs_dir_entry *entries, - void *dir_hook, void (*cleanup) (void *dir_hook)) +procfs_dir_make_node (const struct procfs_dir_ops *dir_ops, void *dir_hook) { static const struct procfs_node_ops ops = { .get_contents = procfs_dir_get_contents, @@ -75,21 +96,20 @@ procfs_dir_make_node (const struct procfs_dir_entry *entries, .cleanup_contents = procfs_cleanup_contents_with_free, .cleanup = procfs_dir_cleanup, }; - struct procfs_dir_node *dn; + struct procfs_dir_node *dir; - dn = malloc (sizeof *dn); - if (! dn) + dir = malloc (sizeof *dir); + if (! dir) { - if (cleanup) - cleanup (dir_hook); + if (dir_ops->cleanup) + dir_ops->cleanup (dir_hook); return NULL; } - dn->entries = entries; - dn->hook = dir_hook; - dn->cleanup = cleanup; + dir->ops = dir_ops; + dir->hook = dir_hook; - return procfs_make_node (&ops, dn); + return procfs_make_node (&ops, dir); } diff --git a/procfs_dir.h b/procfs_dir.h index 4eb934e0..12e99d2a 100644 --- a/procfs_dir.h +++ b/procfs_dir.h @@ -1,20 +1,44 @@ +/* This module provides an abstraction layer for implementing simple + directories with (mostly) static contents. The user defines the + contents of the directory by providing a table of entries and various + optional callback functions. */ -/* Each entry associates a name with a callback function for creating new - nodes corresponding to that entry. */ +/* These operations define how a given entry will behave. Either can be + omitted, both from the entry-specific operations and from the + directory-wide defaults. */ +struct procfs_dir_entry_ops +{ + /* Called when this entry is looked up to create a corresponding node. */ + struct node *(*make_node)(void *dir_hook, const void *entry_hook); + /* If this is provided and returns 0, this entry will be hidden. */ + int (*exists)(void *dir_hook, const void *entry_hook); +}; + +/* Describes an individual directory entry, associating a NAME with + * arbitrary HOOK data and node-specific OPS. */ struct procfs_dir_entry { const char *name; - struct node *(*make_node)(void *dir_hook, void *entry_hook); - void *hook; + const void *hook; + struct procfs_dir_entry_ops ops; +}; + +/* Describes a complete directory. ENTRIES is a table terminated by a + null NAME field. ENTRY_OPS provides default operations for the + entries which don't specify them. The optional CLEANUP function + should release all the resources associated with the directory hook. */ +struct procfs_dir_ops +{ + const struct procfs_dir_entry *entries; + void (*cleanup)(void *dir_hook); + struct procfs_dir_entry_ops entry_ops; }; -/* A simple directory is built from a table of entries. The table is - terminated by a null NAME pointer. The DIR_HOOK is passed the - MAKE_NODE callback function of looked up procfs_dir_entries, and to - the provided CLEANUP function when the directory is destroyed. - Returns the new directory node. If not enough memory can be - allocated, CLEANUP is invoked immediately and NULL is returned. */ +/* Create and return a new node for the directory described in OPS. + The DIR_HOOK is passed the MAKE_NODE callback function of looked up + entries, as well as to the CLEANUP callback when the node is + destroyed. If not enough memory can be allocated, OPS->CLEANUP is + invoked immediately and NULL is returned. */ struct node * -procfs_dir_make_node (const struct procfs_dir_entry *entries, - void *dir_hook, void (*cleanup) (void *dir_hook)); +procfs_dir_make_node (const struct procfs_dir_ops *ops, void *dir_hook); diff --git a/rootdir.c b/rootdir.c index 364b0731..dcee9a54 100644 --- a/rootdir.c +++ b/rootdir.c @@ -266,6 +266,12 @@ out: return err; } +static int +rootdir_fakeself_exists () +{ + return opt_fake_self >= 0; +} + static error_t rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) { @@ -275,13 +281,13 @@ rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) static struct node * -rootdir_file_make_node (void *dir_hook, void *entry_hook) +rootdir_file_make_node (void *dir_hook, const void *entry_hook) { return procfs_make_node (entry_hook, dir_hook); } static struct node * -rootdir_symlink_make_node (void *dir_hook, void *entry_hook) +rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) { struct node *np = procfs_make_node (entry_hook, dir_hook); if (np) @@ -292,15 +298,17 @@ rootdir_symlink_make_node (void *dir_hook, void *entry_hook) static const struct procfs_dir_entry rootdir_entries[] = { { .name = "self", - .make_node = rootdir_symlink_make_node, .hook = & (struct procfs_node_ops) { .get_contents = rootdir_gc_fakeself, .cleanup_contents = procfs_cleanup_contents_with_free, }, + .ops = { + .make_node = rootdir_symlink_make_node, + .exists = rootdir_fakeself_exists, + } }, { .name = "version", - .make_node = rootdir_file_make_node, .hook = & (struct procfs_node_ops) { .get_contents = rootdir_gc_version, .cleanup_contents = procfs_cleanup_contents_with_free, @@ -308,7 +316,6 @@ static const struct procfs_dir_entry rootdir_entries[] = { }, { .name = "uptime", - .make_node = rootdir_file_make_node, .hook = & (struct procfs_node_ops) { .get_contents = rootdir_gc_uptime, .cleanup_contents = procfs_cleanup_contents_with_free, @@ -316,7 +323,6 @@ static const struct procfs_dir_entry rootdir_entries[] = { }, { .name = "stat", - .make_node = rootdir_file_make_node, .hook = & (struct procfs_node_ops) { .get_contents = rootdir_gc_stat, .cleanup_contents = procfs_cleanup_contents_with_free, @@ -324,7 +330,6 @@ static const struct procfs_dir_entry rootdir_entries[] = { }, { .name = "loadavg", - .make_node = rootdir_file_make_node, .hook = & (struct procfs_node_ops) { .get_contents = rootdir_gc_loadavg, .cleanup_contents = procfs_cleanup_contents_with_free, @@ -332,7 +337,6 @@ static const struct procfs_dir_entry rootdir_entries[] = { }, { .name = "meminfo", - .make_node = rootdir_file_make_node, .hook = & (struct procfs_node_ops) { .get_contents = rootdir_gc_meminfo, .cleanup_contents = procfs_cleanup_contents_with_free, @@ -340,7 +344,6 @@ static const struct procfs_dir_entry rootdir_entries[] = { }, { .name = "vmstat", - .make_node = rootdir_file_make_node, .hook = & (struct procfs_node_ops) { .get_contents = rootdir_gc_vmstat, .cleanup_contents = procfs_cleanup_contents_with_free, @@ -348,7 +351,6 @@ static const struct procfs_dir_entry rootdir_entries[] = { }, { .name = "cmdline", - .make_node = rootdir_file_make_node, .hook = & (struct procfs_node_ops) { .get_contents = rootdir_gc_cmdline, .cleanup_contents = procfs_cleanup_contents_with_free, @@ -360,12 +362,12 @@ static const struct procfs_dir_entry rootdir_entries[] = { struct node *rootdir_make_node (struct ps_context *pc) { - const struct procfs_dir_entry *entries; - - entries = rootdir_entries; - if (opt_fake_self < 0) - entries++; - - return procfs_dir_make_node (entries, pc, NULL); + static const struct procfs_dir_ops ops = { + .entries = rootdir_entries, + .entry_ops = { + .make_node = rootdir_file_make_node, + }, + }; + return procfs_dir_make_node (&ops, pc); } -- cgit v1.2.3 From 7550343b09a94dc1c659ad74dc75b3b77e7ab0bc Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 16:46:04 +0000 Subject: Cleanup pass on process.c * process.c: Reorder some of the code. Improve comments. Jiffies and clock tick are the same thing, right? Replace the stat mode and cleanup hacks by more straightforward solutions. --- process.c | 122 ++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 43 deletions(-) diff --git a/process.c b/process.c index 61688f7e..1f9d5781 100644 --- a/process.c +++ b/process.c @@ -9,22 +9,20 @@ #include "procfs_dir.h" #include "process.h" #include "main.h" - -/* Implementations for the process_file_desc.get_contents callback. */ -static ssize_t -process_file_gc_cmdline (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_args(ps); - return proc_stat_args_len(ps); -} +/* This module implements the process directories and the files they + contain. A libps proc_stat structure is created for each process + node, and is used by the individual file content generators as a + source of information. Each possible file (cmdline, environ, ...) is + decribed in a process_file_desc structure, which specifies which bits + of information (ie. libps flags) it needs, and what function should + be used to generate the file's contents. -static ssize_t -process_file_gc_environ (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_env(ps); - return proc_stat_env_len(ps); -} + The content generators are defined first, followed by glue logic and + entry table. */ + + +/* Helper functions */ static char state_char (struct proc_stat *ps) { @@ -58,17 +56,26 @@ static const char *state_string (struct proc_stat *ps) return "? (unknown)"; } -static long int sc_tv (time_value_t tv) +static long long int timeval_jiffies (time_value_t tv) { - double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; - return usecs * opt_clk_tck / 1000000; + double secs = tv.seconds + tv.microseconds / 1000000.; + return secs * opt_clk_tck; } -static long long int jiff_tv (time_value_t tv) +/* Actual content generators */ + +static ssize_t +process_file_gc_cmdline (struct proc_stat *ps, char **contents) { - double usecs = ((double) tv.seconds) * 1000000 + tv.microseconds; - /* Let's say a jiffy is 1/100 of a second.. */ - return usecs * 100 / 1000000; + *contents = proc_stat_args(ps); + return proc_stat_args_len(ps); +} + +static ssize_t +process_file_gc_environ (struct proc_stat *ps, char **contents) +{ + *contents = proc_stat_env(ps); + return proc_stat_env_len(ps); } static ssize_t @@ -105,17 +112,18 @@ process_file_gc_stat (struct proc_stat *ps, char **contents) 0, 0, /* no such thing as a major:minor for ctty */ 0, /* no such thing as CLONE_* flags on Hurd */ 0L, 0L, 0L, 0L, /* TASK_EVENTS_INFO is unavailable on GNU Mach */ - sc_tv (thbi->user_time), sc_tv (thbi->system_time), + (long unsigned) timeval_jiffies (thbi->user_time), + (long unsigned) timeval_jiffies (thbi->system_time), 0L, 0L, /* cumulative time for children */ MACH_PRIORITY_TO_NICE(thbi->base_priority) + 20, MACH_PRIORITY_TO_NICE(thbi->base_priority), pi->nthreads, 0L, - jiff_tv (thbi->creation_time), /* FIXME: ... since boot */ - (long unsigned int) tbi->virtual_size, - (long unsigned int) tbi->resident_size / PAGE_SIZE, 0L, + timeval_jiffies (thbi->creation_time), /* FIXME: ... since boot */ + (long unsigned) tbi->virtual_size, + (long unsigned) tbi->resident_size / PAGE_SIZE, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, - (long unsigned int) proc_stat_thread_rpc (ps), /* close enough */ + (long unsigned) proc_stat_thread_rpc (ps), /* close enough */ 0L, 0L, 0, 0, @@ -127,6 +135,7 @@ static ssize_t process_file_gc_statm (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); + return asprintf (contents, "%lu %lu 0 0 0 0 0\n", tbi->virtual_size / sysconf(_SC_PAGE_SIZE), @@ -137,6 +146,7 @@ static ssize_t process_file_gc_status (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); + return asprintf (contents, "Name:\t%s\n" "State:\t%s\n" @@ -165,59 +175,70 @@ process_file_gc_status (struct proc_stat *ps, char **contents) proc_stat_num_threads (ps)); } + +/* Implementation of the file nodes. */ -/* Describes a file in a process directory. This is used as an "entry hook" - * for our procfs_dir entry table, passed to process_file_make_node. */ +/* Describes a file in the process directories. This structure is + filled in as an "entry hook" in our procfs_dir entry table and is + passed to the process_file_make_node function defined below. */ struct process_file_desc { /* The proc_stat information required to get the contents of this file. */ ps_flags_t needs; - /* Once we have acquired the necessary information, there can be only - memory allocation errors, hence this simplified signature. */ + /* Content generator to use for this file. Once we have acquired the + necessary information, there can be only memory allocation errors, + hence this simplified signature. */ ssize_t (*get_contents) (struct proc_stat *ps, char **contents); - /* The cmdline and environ contents don't need any cleaning since they are - part of a proc_stat structure. */ + /* The cmdline and environ contents don't need any cleaning since they + point directly into the proc_stat structure. */ int no_cleanup; /* If specified, the file mode to be set with procfs_node_chmod(). */ mode_t mode; }; -/* Information associated to an actual file node. */ struct process_file_node { const struct process_file_desc *desc; struct proc_stat *ps; }; +/* FIXME: lock the parent! */ static error_t process_file_get_contents (void *hook, char **contents, ssize_t *contents_len) { struct process_file_node *file = hook; error_t err; + /* Fetch the required information. */ err = proc_stat_set_flags (file->ps, file->desc->needs); if (err) return EIO; if ((proc_stat_flags (file->ps) & file->desc->needs) != file->desc->needs) return EIO; + /* Call the actual content generator (see the definitions below). */ *contents_len = file->desc->get_contents (file->ps, contents); return 0; } +static void +process_file_cleanup_contents (void *hook, char *contents, ssize_t len) +{ + struct process_file_node *file = hook; + + if (! file->desc->no_cleanup) + free (contents); +} + static struct node * process_file_make_node (void *dir_hook, const void *entry_hook) { static const struct procfs_node_ops ops = { .get_contents = process_file_get_contents, - .cleanup_contents = procfs_cleanup_contents_with_free, - .cleanup = free, - }; - static const struct procfs_node_ops ops_no_cleanup = { - .get_contents = process_file_get_contents, + .cleanup_contents = process_file_cleanup_contents, .cleanup = free, }; struct process_file_node *f; @@ -230,7 +251,7 @@ process_file_make_node (void *dir_hook, const void *entry_hook) f->desc = entry_hook; f->ps = dir_hook; - np = procfs_make_node (f->desc->no_cleanup ? &ops_no_cleanup : &ops, f); + np = procfs_make_node (&ops, f); if (! np) return NULL; @@ -241,6 +262,18 @@ process_file_make_node (void *dir_hook, const void *entry_hook) return np; } +/* Stat needs its own constructor in oreder to set its mode according to + the --stat-mode command-line option. */ +static struct node * +process_stat_make_node (void *dir_hook, const void *entry_hook) +{ + struct node *np = process_file_make_node (dir_hook, entry_hook); + if (np) procfs_node_chmod (np, opt_stat_mode); + return np; +} + + +/* Implementation of the process directory per se. */ static struct procfs_dir_entry entries[] = { { @@ -261,22 +294,22 @@ static struct procfs_dir_entry entries[] = { }, }, { - /* Beware of the hack below, which requires this to be entries[2]. */ .name = "stat", .hook = & (struct process_file_desc) { .get_contents = process_file_gc_stat, .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO | PSTAT_TASK | PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC | PSTAT_THREAD_WAIT, - .mode = 0400, }, + .ops = { + .make_node = process_stat_make_node, + } }, { .name = "statm", .hook = & (struct process_file_desc) { .get_contents = process_file_gc_statm, .needs = PSTAT_TASK_BASIC, - .mode = 0444, }, }, { @@ -285,7 +318,6 @@ static struct procfs_dir_entry entries[] = { .get_contents = process_file_gc_status, .needs = PSTAT_PID | PSTAT_ARGS | PSTAT_STATE | PSTAT_PROC_INFO | PSTAT_TASK_BASIC | PSTAT_OWNER_UID | PSTAT_NUM_THREADS, - .mode = 0444, }, }, {} @@ -319,6 +351,10 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) accessed in a more robust and straightforward way. */ ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; + /* FIXME: have a separate proc_desc structure for each file, so this can be + accessed in a more robust and straightforward way. */ + ((struct process_file_desc *) entries[2].hook)->mode = opt_stat_mode; + *np = procfs_dir_make_node (&dir_ops, ps); if (! *np) return ENOMEM; -- cgit v1.2.3 From 434f79cbfa00c7370724dc25091b5a42bdf8d9e1 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 19:04:48 +0000 Subject: Fix the global idle time * rootdir.c: Replace INIT_PID by KERNEL_PID, for boot time and idle time purposes. (get_idletime): New function, queries the kernel's idle thread. (rootdir_gc_uptime, rootdir_gc_stat): Use the new function and provide the real idle time. --- rootdir.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/rootdir.c b/rootdir.c index dcee9a54..728008e5 100644 --- a/rootdir.c +++ b/rootdir.c @@ -16,15 +16,16 @@ using them would require locking and as a consequence it would be more complicated, not simpler. */ -#define INIT_PID 1 +#define KERNEL_PID 2 +/* We get the boot time by using that of the kernel process. */ static error_t get_boottime (struct ps_context *pc, struct timeval *tv) { struct proc_stat *ps; error_t err; - err = _proc_stat_create (INIT_PID, pc, &ps); + err = _proc_stat_create (KERNEL_PID, pc, &ps); if (err) return err; @@ -43,6 +44,63 @@ get_boottime (struct ps_context *pc, struct timeval *tv) return err; } +/* We get the idle time by querying the kernel's idle thread. */ +static error_t +get_idletime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps, *pst; + thread_basic_info_t tbi; + error_t err; + int i; + + err = _proc_stat_create (KERNEL_PID, pc, &ps); + if (err) + return err; + + pst = NULL, tbi = NULL; + + err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); + if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) + { + err = EIO; + goto out; + } + + /* Look for the idle thread */ + for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE); i++) + { + if (pst) + _proc_stat_free (pst); + + pst = NULL, tbi = NULL; + if (i >= proc_stat_num_threads (ps)) + { + err = ESRCH; + goto out; + } + + err = proc_stat_thread_create (ps, i, &pst); + if (err) + continue; + + err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC); + if (err || ! (proc_stat_flags (pst) & PSTAT_THREAD_BASIC)) + continue; + + tbi = proc_stat_thread_basic_info (pst); + } + + /* We found it! */ + tv->tv_sec = tbi->system_time.seconds; + tv->tv_usec = tbi->system_time.microseconds; + err = 0; + +out: + if (pst) _proc_stat_free (pst); + _proc_stat_free (ps); + return err; +} + static error_t rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { @@ -65,8 +123,8 @@ rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) static error_t rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) { - struct timeval time, boottime; - double up_secs; + struct timeval time, boottime, idletime; + double up_secs, idle_secs; error_t err; err = gettimeofday (&time, NULL); @@ -77,15 +135,20 @@ rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) if (err) return err; + err = get_idletime (hook, &idletime); + if (err) + return err; + timersub (&time, &boottime, &time); up_secs = time.tv_sec + time.tv_usec / 1000000.; + idle_secs = idletime.tv_sec + idletime.tv_usec / 1000000.; /* The second field is the total idle time. As far as I know we don't keep track of it. However, procps uses it to compute "USER_HZ", and proc(5) specifies that it should be equal to USER_HZ times the idle value in ticks from /proc/stat. So we assume a completely idle system both here and there to make that work. */ - *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, up_secs); + *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); return 0; } @@ -93,9 +156,9 @@ rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) static error_t rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) { - struct timeval boottime, time; + struct timeval boottime, time, idletime; struct vm_statistics vmstats; - unsigned long up_ticks; + unsigned long up_ticks, idle_ticks; error_t err; err = gettimeofday (&time, NULL); @@ -106,23 +169,26 @@ rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) if (err) return err; + err = get_idletime (hook, &idletime); + if (err) + return err; + err = vm_statistics (mach_task_self (), &vmstats); if (err) return EIO; timersub (&time, &boottime, &time); up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); + idle_ticks = opt_clk_tck * (idletime.tv_sec + idletime.tv_usec / 1000000.); *contents_len = asprintf (contents, - /* Does Mach keeps track of any of this? */ - "cpu 0 0 0 %lu 0 0 0 0 0\n" - "cpu0 0 0 0 %lu 0 0 0 0 0\n" + "cpu %lu 0 0 %lu 0 0 0 0 0\n" + "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" "intr 0\n" - /* This we know. */ "page %d %d\n" "btime %lu\n", - up_ticks, - up_ticks, + up_ticks - idle_ticks, idle_ticks, + up_ticks - idle_ticks, idle_ticks, vmstats.pageins, vmstats.pageouts, boottime.tv_sec); -- cgit v1.2.3 From 7b027a34676880bded1a05a5c47bfa85a6e79092 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 20:21:52 +0000 Subject: Make sure the clock never runs backwards. * process.c, rootdir.c: When converting timeval structures into seconds or jiffies, make sure that floating point rounding errors don't make the clock the result jump backwards on second boundaries. --- process.c | 4 ++-- rootdir.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/process.c b/process.c index 1f9d5781..68e7bc1d 100644 --- a/process.c +++ b/process.c @@ -58,8 +58,8 @@ static const char *state_string (struct proc_stat *ps) static long long int timeval_jiffies (time_value_t tv) { - double secs = tv.seconds + tv.microseconds / 1000000.; - return secs * opt_clk_tck; + double secs = tv.seconds * 1000000. + tv.microseconds; + return secs * opt_clk_tck / 1000000.; } /* Actual content generators */ diff --git a/rootdir.c b/rootdir.c index 728008e5..c6ddd87c 100644 --- a/rootdir.c +++ b/rootdir.c @@ -140,8 +140,8 @@ rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) return err; timersub (&time, &boottime, &time); - up_secs = time.tv_sec + time.tv_usec / 1000000.; - idle_secs = idletime.tv_sec + idletime.tv_usec / 1000000.; + up_secs = (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; + idle_secs = (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; /* The second field is the total idle time. As far as I know we don't keep track of it. However, procps uses it to compute "USER_HZ", and @@ -178,8 +178,8 @@ rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) return EIO; timersub (&time, &boottime, &time); - up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); - idle_ticks = opt_clk_tck * (idletime.tv_sec + idletime.tv_usec / 1000000.); + up_ticks = opt_clk_tck * (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; + idle_ticks = opt_clk_tck * (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; *contents_len = asprintf (contents, "cpu %lu 0 0 %lu 0 0 0 0 0\n" -- cgit v1.2.3 From b3d304167929bc199e6789fd67ffea035e7fdbf1 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 20:43:16 +0000 Subject: Refresh nodes when they're read from the start. This is necessary for top, for instance, which keeps some files open and re-reads them regularly. As an extra bonus we can drop the refresh hack. * procfs.c, procfs.h: Remove the refresh hack. (procfs_refresh): New function, invalidates the cached contents. * netfs.c (netfs_attempt_read, netfs_get_dirents): Call procfs_refresh when the read is from offset 0. * proclist.c (proclist_make_node): Remove the refresh hack. * dircat.c (dircat_make_node): Likewise. (dircat_get_contents): Use procfs_refresh to avoid keeping old data from the component nodes. --- dircat.c | 5 +++-- netfs.c | 6 ++++++ procfs.c | 19 +++++++++---------- procfs.h | 13 +++++-------- proclist.c | 1 - 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/dircat.c b/dircat.c index d043486d..d24e8345 100644 --- a/dircat.c +++ b/dircat.c @@ -23,6 +23,9 @@ dircat_get_contents (void *hook, char **contents, ssize_t *contents_len) char *subcon; ssize_t sublen; + /* Make sure we're not getting some old stuff. */ + procfs_refresh (dcn->dirs[i]); + err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); if (err) { @@ -83,8 +86,6 @@ dircat_make_node (struct node *const *dirs, int num_dirs) .cleanup_contents = procfs_cleanup_contents_with_free, .lookup = dircat_lookup, .cleanup = dircat_cleanup, - /* necessary so that it propagates to proclist */ - .enable_refresh_hack_and_break_readdir = 1, }; struct dircat_node *dcn; int i; diff --git a/netfs.c b/netfs.c index e41e0623..02a4bf54 100644 --- a/netfs.c +++ b/netfs.c @@ -57,6 +57,9 @@ error_t netfs_attempt_read (struct iouser *cred, struct node *np, ssize_t contents_len; error_t err; + if (offset == 0) + procfs_refresh (np); + err = procfs_get_contents (np, &contents, &contents_len); if (err) return err; @@ -139,6 +142,9 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, char *contents; ssize_t contents_len; error_t err; + + if (entry == 0) + procfs_refresh (dir); err = procfs_get_contents (dir, &contents, &contents_len); if (err) diff --git a/procfs.c b/procfs.c index 6d15e4fd..f7b28347 100644 --- a/procfs.c +++ b/procfs.c @@ -111,14 +111,6 @@ procfs_make_ino (struct node *np, const char *filename) error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) { - if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) - { - if (np->nn->ops->cleanup_contents) - np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, - np->nn->contents_len); - np->nn->contents = NULL; - } - if (! np->nn->contents && np->nn->ops->get_contents) { char *contents; @@ -141,6 +133,14 @@ error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len) return 0; } +void procfs_refresh (struct node *np) +{ + if (np->nn->contents && np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); + + np->nn->contents = NULL; +} + error_t procfs_lookup (struct node *np, const char *name, struct node **npp) { error_t err = ENOENT; @@ -172,8 +172,7 @@ error_t procfs_lookup (struct node *np, const char *name, struct node **npp) void procfs_cleanup (struct node *np) { - if (np->nn->contents && np->nn->ops->cleanup_contents) - np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, np->nn->contents_len); + procfs_refresh (np); if (np->nn->ops->cleanup) np->nn->ops->cleanup (np->nn->hook); diff --git a/procfs.h b/procfs.h index e8ef18b3..a9665f97 100644 --- a/procfs.h +++ b/procfs.h @@ -32,14 +32,6 @@ struct procfs_node_ops /* Destroy this node. */ void (*cleanup) (void *hook); - - /* FIXME: This is needed because the root node is persistent, and we - want the list of processes to be updated. However, this means that - readdir() on the root node runs the risk of returning incoherent - results if done in multiple runs and processes are added/removed in - the meantime. The right way to fix this is probably to add a - getroot() user hook function to libnetfs. */ - int enable_refresh_hack_and_break_readdir; }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ @@ -71,6 +63,11 @@ void procfs_node_chtype (struct node *np, mode_t type); corresponding child nodes. */ ino64_t procfs_make_ino (struct node *np, const char *filename); +/* Forget the current cached contents for the node. This is done before reads + from offset 0, to ensure that the data are recent even for utilities such as + top which keep some nodes open. */ +void procfs_refresh (struct node *np); + error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); diff --git a/proclist.c b/proclist.c index 38368feb..fe5d0cff 100644 --- a/proclist.c +++ b/proclist.c @@ -69,7 +69,6 @@ proclist_make_node (struct ps_context *pc) .get_contents = proclist_get_contents, .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, - .enable_refresh_hack_and_break_readdir = 1, }; return procfs_make_node (&ops, pc); } -- cgit v1.2.3 From ca8277c6f507e54a043b84a8df9c55ac39a83c6e Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 05:46:49 +0000 Subject: Fix leak in error case * process.c (process_lookup_pid): Fix leak in error case. --- process.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index 68e7bc1d..47e34708 100644 --- a/process.c +++ b/process.c @@ -345,7 +345,10 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) - return EIO; + { + _proc_stat_free (ps); + return EIO; + } /* FIXME: have a separate proc_desc structure for each file, so this can be accessed in a more robust and straightforward way. */ -- cgit v1.2.3 From e1c831a43ac1945db5a52ee6873856b9399dca76 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 05:47:08 +0000 Subject: Add a PROFILE mode * Makefile: Change FOOFLAGS defaults to appropriate values when $(PROFILE) is defined. * rootdir.c: Add an "exit" file, which causes exit to be called when looked up, so that profiling data can be written to disk. --- Makefile | 10 +++++++++- rootdir.c | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b8c055f6..a397522f 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,21 @@ 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) -o $@ $^ $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) clean: $(RM) $(TARGET) $(OBJS) diff --git a/rootdir.c b/rootdir.c index c6ddd87c..204bc02b 100644 --- a/rootdir.c +++ b/rootdir.c @@ -422,6 +422,15 @@ static const struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, +#ifdef PROFILE + /* In order to get a usable gmon.out file, we must apparently use exit(). */ + { + .name = "exit", + .ops = { + .make_node = exit, + }, + }, +#endif {} }; -- cgit v1.2.3 From 164db73bb35173f45a7eaba1984c3169cba778e8 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 12:03:16 +0000 Subject: Add swap information to the top-level stat file * rootdir.c (rootdir_gc_meminfo): Add swap information. * TODO: Update. --- TODO | 1 - rootdir.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index b4a39b05..241c61ae 100644 --- a/TODO +++ b/TODO @@ -1,2 +1 @@ -* Add swap information * Threads in /proc/[pid]/task/[n] diff --git a/rootdir.c b/rootdir.c index 204bc02b..419f774e 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,7 +1,10 @@ #include #include +#include +#include #include #include +#include #include #include #include @@ -101,6 +104,22 @@ out: return err; } +static error_t +get_swapinfo (default_pager_info_t *info) +{ + mach_port_t defpager; + error_t err; + + defpager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); + if (defpager == MACH_PORT_NULL) + return errno; + + err = default_pager_info (defpager, info); + mach_port_deallocate (mach_task_self (), defpager); + + return err; +} + static error_t rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { @@ -223,6 +242,7 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) host_basic_info_data_t hbi; mach_msg_type_number_t cnt; struct vm_statistics vmstats; + default_pager_info_t swap; error_t err; err = vm_statistics (mach_task_self (), &vmstats); @@ -234,6 +254,10 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) if (err) return err; + err = get_swapinfo (&swap); + if (err) + return err; + assert (cnt == HOST_BASIC_INFO_COUNT); *contents_len = asprintf (contents, "MemTotal: %14lu kB\n" @@ -241,13 +265,17 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) "Active: %14lu kB\n" "Inactive: %14lu kB\n" "Mlocked: %14lu kB\n" + "SwapTotal:%14lu kB\n" + "SwapFree: %14lu kB\n" , /* TODO: check that these are really 1024-bytes kBs. */ (long unsigned) hbi.memory_size / 1024, (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, - (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024); + (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024, + (long unsigned) swap.dpi_total_space / 1024, + (long unsigned) swap.dpi_free_space / 1024); return 0; } -- cgit v1.2.3 From 0f3c9c2640ffd76f1d60f16dce6d16c47aac98b4 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 19:04:24 +0000 Subject: Add some comments in rootdir.c * rootdir.c: Add page breaks to separate sections and add header comments for them. --- rootdir.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/rootdir.c b/rootdir.c index 419f774e..2a2ab5a5 100644 --- a/rootdir.c +++ b/rootdir.c @@ -21,6 +21,9 @@ #define KERNEL_PID 2 + +/* Helper functions */ + /* We get the boot time by using that of the kernel process. */ static error_t get_boottime (struct ps_context *pc, struct timeval *tv) @@ -120,6 +123,9 @@ get_swapinfo (default_pager_info_t *info) return err; } + +/* Content generators */ + static error_t rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { @@ -137,8 +143,6 @@ rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) return 0; } -/* Uptime -- we use the start time of init to deduce it. This is probably a bit - fragile, as any clock update will make the result inaccurate. */ static error_t rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) { @@ -373,10 +377,16 @@ rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) return 0; } + +/* Glue logic and entries table */ static struct node * rootdir_file_make_node (void *dir_hook, const void *entry_hook) { + /* The entry hook we use is actually a procfs_node_ops for the file to be + created. The hook associated to these newly created files (and passed + to the generators above as a consequence) is always the same global + ps_context, which we get from rootdir_make_node as the directory hook. */ return procfs_make_node (entry_hook, dir_hook); } -- cgit v1.2.3 From e25cfb3f3a843122df7e70e97992a97ffe81717a Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 25 Aug 2010 19:15:35 +0000 Subject: Update the to-do list. * TODO: Add more entries. --- TODO | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 241c61ae..952d67bc 100644 --- a/TODO +++ b/TODO @@ -1 +1,24 @@ -* Threads in /proc/[pid]/task/[n] +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. + -- cgit v1.2.3 From 5c7310fd853620627b57c72ddcc3b212f29f6056 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Thu, 26 Aug 2010 23:03:40 +0000 Subject: Add copyright notices * dircat.c, dircat.h, main.c, main.h, netfs.c, process.c, process.h, procfs.c, procfs.h, procfs_dir.c, procfs_dir.h, proclist.c, proclist.h, rootdir.c, rootdir.h: Add copyright notices. --- dircat.c | 19 +++++++++++++++++++ dircat.h | 19 +++++++++++++++++++ main.c | 19 +++++++++++++++++++ main.h | 19 +++++++++++++++++++ netfs.c | 19 +++++++++++++++++++ process.c | 19 +++++++++++++++++++ process.h | 19 +++++++++++++++++++ procfs.c | 19 +++++++++++++++++++ procfs.h | 19 +++++++++++++++++++ procfs_dir.c | 19 +++++++++++++++++++ procfs_dir.h | 19 +++++++++++++++++++ proclist.c | 19 +++++++++++++++++++ proclist.h | 19 +++++++++++++++++++ rootdir.c | 19 +++++++++++++++++++ rootdir.h | 19 +++++++++++++++++++ 15 files changed, 285 insertions(+) diff --git a/dircat.c b/dircat.c index d24e8345..5a60899a 100644 --- a/dircat.c +++ b/dircat.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, concatenation of two directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include "procfs.h" diff --git a/dircat.h b/dircat.h index 951d2021..4177b384 100644 --- a/dircat.h +++ b/dircat.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, concatenation of two directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* Append the contents of NUM_DIRS directories. DIRS is an array of directory nodes. One reference is consumed for each of them. If a memory allocation error occurs, or if one of the directories is a diff --git a/main.c b/main.c index eaab9867..94fc227d 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, main program. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/main.h b/main.h index 28d1b023..4e28b7eb 100644 --- a/main.h +++ b/main.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, command-line options set by main.c. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* Startup options */ extern int opt_clk_tck; extern mode_t opt_stat_mode; diff --git a/netfs.c b/netfs.c index 02a4bf54..24a6603f 100644 --- a/netfs.c +++ b/netfs.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, interface with libnetfs. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/process.c b/process.c index 47e34708..6652a4e9 100644 --- a/process.c +++ b/process.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, implementation of process directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/process.h b/process.h index 8c2ee636..b230a281 100644 --- a/process.h +++ b/process.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, implementation of process directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include /* Create a node for a directory representing the given PID, as published by diff --git a/procfs.c b/procfs.c index f7b28347..ee52de7a 100644 --- a/procfs.c +++ b/procfs.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, basic infrastructure. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/procfs.h b/procfs.h index a9665f97..64782ec4 100644 --- a/procfs.h +++ b/procfs.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, basic infrastructure. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include diff --git a/procfs_dir.c b/procfs_dir.c index dfe30a26..c250aa48 100644 --- a/procfs_dir.c +++ b/procfs_dir.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include "procfs.h" diff --git a/procfs_dir.h b/procfs_dir.h index 12e99d2a..94c5b019 100644 --- a/procfs_dir.h +++ b/procfs_dir.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* This module provides an abstraction layer for implementing simple directories with (mostly) static contents. The user defines the contents of the directory by providing a table of entries and various diff --git a/proclist.c b/proclist.c index fe5d0cff..58b942dc 100644 --- a/proclist.c +++ b/proclist.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, list of processes as a directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/proclist.h b/proclist.h index ce69dde3..bfe95b3d 100644 --- a/proclist.h +++ b/proclist.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, list of processes as a directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include struct node * diff --git a/rootdir.c b/rootdir.c index 2a2ab5a5..da068cf4 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include #include #include diff --git a/rootdir.h b/rootdir.h index 0caee258..6980da8f 100644 --- a/rootdir.h +++ b/rootdir.h @@ -1,3 +1,22 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include struct node * -- cgit v1.2.3 From 8dc9884fc74243a6810fdcac603e1941962b666a Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 1 Sep 2010 10:19:49 +0000 Subject: Use the user-provided kernel PID for uptime * rootdir.c (get_boottime, get_idletime): replace KERNEL_PID with the opt_kernel_pid command-line option. * main.c (argp): Document the change. --- main.c | 3 ++- rootdir.c | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index 94fc227d..3a976ccc 100644 --- a/main.c +++ b/main.c @@ -114,7 +114,8 @@ struct argp argp = { "purposes. If PID is omitted, \"self\" will point to init. " "(default: no self link)" }, { "kernel-process", 'k', "PID", 0, - "Process identifier for the kernel, used to retreive its command line " + "Process identifier for the kernel, used to retreive its command " + "line, as well as the global up and idle times. " "(default: 2)" }, { "compatible", 'c', NULL, 0, "Try to be compatible with the Linux procps utilities. " diff --git a/rootdir.c b/rootdir.c index da068cf4..15ef8bce 100644 --- a/rootdir.c +++ b/rootdir.c @@ -38,8 +38,6 @@ using them would require locking and as a consequence it would be more complicated, not simpler. */ -#define KERNEL_PID 2 - /* Helper functions */ @@ -50,7 +48,7 @@ get_boottime (struct ps_context *pc, struct timeval *tv) struct proc_stat *ps; error_t err; - err = _proc_stat_create (KERNEL_PID, pc, &ps); + err = _proc_stat_create (opt_kernel_pid, pc, &ps); if (err) return err; @@ -78,7 +76,7 @@ get_idletime (struct ps_context *pc, struct timeval *tv) error_t err; int i; - err = _proc_stat_create (KERNEL_PID, pc, &ps); + err = _proc_stat_create (opt_kernel_pid, pc, &ps); if (err) return err; -- cgit v1.2.3 From b0db9bc55ce9b66d0fba511239c278d1a257a9ef Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Wed, 1 Sep 2010 10:49:11 +0000 Subject: Use 2 instead of 42 as the parent inode number * procfs.c (procfs_make_ino): Use 2 rather than 42 as a temporary hack, since 2 is the root's inode with ext2fs. --- procfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procfs.c b/procfs.c index ee52de7a..ae5a6769 100644 --- a/procfs.c +++ b/procfs.c @@ -114,7 +114,7 @@ procfs_make_ino (struct node *np, const char *filename) if (! strcmp (filename, ".")) return np->nn_stat.st_ino; if (! strcmp (filename, "..")) - return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 42; + return np->nn->parent ? np->nn->parent->nn_stat.st_ino : /* FIXME: */ 2; assert (sizeof np->nn_stat.st_ino > sizeof x); memcpy (x, &np->nn_stat.st_ino, sizeof x); -- cgit v1.2.3 From f4dfd14bb77fd884a0e5afe306c677e7e873f41d Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 22 Feb 2011 21:06:36 +0100 Subject: Add Buffers and Cached to /proc/meminfo * rootdir.c (rootdir_gc_meminfo): Add Buffers and Cached, set to 0. --- rootdir.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rootdir.c b/rootdir.c index 15ef8bce..1fa71b03 100644 --- a/rootdir.c +++ b/rootdir.c @@ -283,15 +283,18 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) *contents_len = asprintf (contents, "MemTotal: %14lu kB\n" "MemFree: %14lu kB\n" + "Buffers: %14lu kB\n" + "Cached: %14lu kB\n" "Active: %14lu kB\n" "Inactive: %14lu kB\n" "Mlocked: %14lu kB\n" "SwapTotal:%14lu kB\n" "SwapFree: %14lu kB\n" , - /* TODO: check that these are really 1024-bytes kBs. */ (long unsigned) hbi.memory_size / 1024, (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, + 0, + 0, (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, (long unsigned) vmstats.wire_count * PAGE_SIZE / 1024, -- cgit v1.2.3 From d9fc76bd4e8c9a459fad7152135b738496318415 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 27 Dec 2011 17:31:20 +0100 Subject: Do not use msgport information * process.c (process_lookup_pid): Set PSTAT_NO_MSGPORT flag. * rootdir.c (get_boottime, get_idletime, rootdir_gc_cmdline): Set PSTAT_NO_MSGPORT flag. --- process.c | 2 ++ rootdir.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/process.c b/process.c index 6652a4e9..e812a1f1 100644 --- a/process.c +++ b/process.c @@ -362,6 +362,8 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) if (err) return EIO; + proc_stat_set_flags (ps, PSTAT_NO_MSGPORT); + err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) { diff --git a/rootdir.c b/rootdir.c index 1fa71b03..018de617 100644 --- a/rootdir.c +++ b/rootdir.c @@ -52,6 +52,8 @@ get_boottime (struct ps_context *pc, struct timeval *tv) if (err) return err; + proc_stat_set_flags (ps, PSTAT_NO_MSGPORT); + err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); if (err || !(proc_stat_flags (ps) & PSTAT_TASK_BASIC)) err = EIO; @@ -82,6 +84,8 @@ get_idletime (struct ps_context *pc, struct timeval *tv) pst = NULL, tbi = NULL; + proc_stat_set_flags (ps, PSTAT_NO_MSGPORT); + err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) { @@ -360,6 +364,8 @@ rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) if (err) return EIO; + proc_stat_set_flags (ps, PSTAT_NO_MSGPORT); + err = proc_stat_set_flags (ps, PSTAT_ARGS); if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS)) { -- cgit v1.2.3 From f8471a5a6b20693d3c7afc0645de62c842b82bd3 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 27 Dec 2011 18:13:00 +0100 Subject: Revert "Do not use msgport information" This reverts commit d9fc76bd4e8c9a459fad7152135b738496318415, as it breaks /proc/pid/stat --- process.c | 2 -- rootdir.c | 6 ------ 2 files changed, 8 deletions(-) diff --git a/process.c b/process.c index e812a1f1..6652a4e9 100644 --- a/process.c +++ b/process.c @@ -362,8 +362,6 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) if (err) return EIO; - proc_stat_set_flags (ps, PSTAT_NO_MSGPORT); - err = proc_stat_set_flags (ps, PSTAT_OWNER_UID); if (err || ! (proc_stat_flags (ps) & PSTAT_OWNER_UID)) { diff --git a/rootdir.c b/rootdir.c index 018de617..1fa71b03 100644 --- a/rootdir.c +++ b/rootdir.c @@ -52,8 +52,6 @@ get_boottime (struct ps_context *pc, struct timeval *tv) if (err) return err; - proc_stat_set_flags (ps, PSTAT_NO_MSGPORT); - err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); if (err || !(proc_stat_flags (ps) & PSTAT_TASK_BASIC)) err = EIO; @@ -84,8 +82,6 @@ get_idletime (struct ps_context *pc, struct timeval *tv) pst = NULL, tbi = NULL; - proc_stat_set_flags (ps, PSTAT_NO_MSGPORT); - err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) { @@ -364,8 +360,6 @@ rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) if (err) return EIO; - proc_stat_set_flags (ps, PSTAT_NO_MSGPORT); - err = proc_stat_set_flags (ps, PSTAT_ARGS); if (err || ! (proc_stat_flags (ps) & PSTAT_ARGS)) { -- cgit v1.2.3 From 4d9dc03bbc42fdfdac0dc72f2bfac1f37f70e8b7 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sat, 14 Jan 2012 14:47:58 +0100 Subject: PID stat/status: show only the file name of processes The Linux /proc fs shows only the file name of processes in the `stat' and `status' files of every process directory, so adapt also procfs to show process file names. Add a new `args_filename` function, much similar to GNU's `basename' but returning the original string also when the resulting file name is an empty string. * process.c (args_filename): New function. (process_file_gc_stat): Wrap the `proc_stat_args' result with `args_filename'. (process_file_gc_status): Likewise. --- process.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/process.c b/process.c index 6652a4e9..17a38ea8 100644 --- a/process.c +++ b/process.c @@ -81,6 +81,12 @@ static long long int timeval_jiffies (time_value_t tv) return secs * opt_clk_tck / 1000000.; } +static const char *args_filename (const char *name) +{ + char *sp = strrchr (name, '/'); + return sp != NULL && *(sp + 1) != '\0' ? sp + 1 : name; +} + /* Actual content generators */ static ssize_t @@ -126,7 +132,7 @@ process_file_gc_stat (struct proc_stat *ps, char **contents) "%u %u " /* RT priority and policy */ "%llu " /* aggregated block I/O delay */ "\n", - proc_stat_pid (ps), proc_stat_args (ps), state_char (ps), + proc_stat_pid (ps), args_filename (proc_stat_args (ps)), state_char (ps), pi->ppid, pi->pgrp, pi->session, 0, 0, /* no such thing as a major:minor for ctty */ 0, /* no such thing as CLONE_* flags on Hurd */ @@ -178,7 +184,7 @@ process_file_gc_status (struct proc_stat *ps, char **contents) "VmRSS:\t%8u kB\n" "VmHWM:\t%8u kB\n" /* ie. resident peak */ "Threads:\t%u\n", - proc_stat_args (ps), + args_filename (proc_stat_args (ps)), state_string (ps), proc_stat_pid (ps), /* XXX will need more work for threads */ proc_stat_pid (ps), -- cgit v1.2.3 From da62c4ea16e14fac7e298385a681043820d69ac4 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Mon, 10 Sep 2012 18:14:48 +0200 Subject: PID stat/status: show only the first word If a process changed its title to a multiword string, show only the first word of it. * process.c (args_filename_length): New function. (process_file_gc_stat): Use args_filename_length. (process_file_gc_status): Likewise. --- process.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/process.c b/process.c index 17a38ea8..68eb50a0 100644 --- a/process.c +++ b/process.c @@ -87,6 +87,11 @@ static const char *args_filename (const char *name) return sp != NULL && *(sp + 1) != '\0' ? sp + 1 : name; } +static int args_filename_length (const char *name) +{ + return strchrnul (name, ' ') - name; +} + /* Actual content generators */ static ssize_t @@ -109,11 +114,12 @@ 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); + const char *fn = args_filename (proc_stat_args (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 (%.*s) %c " /* pid, command, state */ "%d %d %d " /* ppid, pgid, session */ "%d %d " /* controling tty stuff */ "%u " /* flags, as defined by */ @@ -132,7 +138,7 @@ process_file_gc_stat (struct proc_stat *ps, char **contents) "%u %u " /* RT priority and policy */ "%llu " /* aggregated block I/O delay */ "\n", - proc_stat_pid (ps), args_filename (proc_stat_args (ps)), state_char (ps), + proc_stat_pid (ps), args_filename_length (fn), fn, 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 */ @@ -171,9 +177,10 @@ static ssize_t process_file_gc_status (struct proc_stat *ps, char **contents) { task_basic_info_t tbi = proc_stat_task_basic_info (ps); + const char *fn = args_filename (proc_stat_args (ps)); return asprintf (contents, - "Name:\t%s\n" + "Name:\t%.*s\n" "State:\t%s\n" "Tgid:\t%u\n" "Pid:\t%u\n" @@ -184,7 +191,7 @@ process_file_gc_status (struct proc_stat *ps, char **contents) "VmRSS:\t%8u kB\n" "VmHWM:\t%8u kB\n" /* ie. resident peak */ "Threads:\t%u\n", - args_filename (proc_stat_args (ps)), + args_filename_length (fn), fn, state_string (ps), proc_stat_pid (ps), /* XXX will need more work for threads */ proc_stat_pid (ps), -- cgit v1.2.3 From b5014c2caaef8ee061c47b907a599526d9dbe875 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Fri, 19 Oct 2012 00:32:18 +0200 Subject: Fix procfs link Makefile (LIBS): Add -lfshelp --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a397522f..c69cb206 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ TARGET = procfs OBJS = procfs.o netfs.o procfs_dir.o \ process.o proclist.o rootdir.o dircat.o main.o -LIBS = -lnetfs -lps +LIBS = -lnetfs -lps -lfshelp CC = gcc CFLAGS = -Wall -g -- cgit v1.2.3 From 9828a0bb4c6e69f401738013e4adfb4da95afc03 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Nov 2012 22:47:15 +0100 Subject: Move procfs to pthreads --- netfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/netfs.c b/netfs.c index 24a6603f..a4d9f9c2 100644 --- a/netfs.c +++ b/netfs.c @@ -208,10 +208,10 @@ error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, error_t err; err = procfs_lookup (dir, name, np); - mutex_unlock (&dir->lock); + pthread_mutex_unlock (&dir->lock); if (! err) - mutex_lock (&(*np)->lock); + pthread_mutex_lock (&(*np)->lock); return err; } @@ -220,12 +220,12 @@ error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, free all its associated storage. */ void netfs_node_norefs (struct node *np) { - spin_unlock (&netfs_node_refcnt_lock); + pthread_spin_unlock (&netfs_node_refcnt_lock); procfs_cleanup (np); free (np); - spin_lock (&netfs_node_refcnt_lock); + pthread_spin_lock (&netfs_node_refcnt_lock); } -- cgit v1.2.3 From 1b7ad5c5d601b6388f0fc871b4fa42d231c05400 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Thu, 6 Dec 2012 17:51:58 +0100 Subject: Simple implementation of statfs Initial implementation of statfs reply, just returning the filesystem type and its id. * netfs.c: Include and . (netfs_attempt_statfs): Implement. --- netfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netfs.c b/netfs.c index a4d9f9c2..c139d11a 100644 --- a/netfs.c +++ b/netfs.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "procfs.h" #define PROCFS_SERVER_NAME "procfs" @@ -347,7 +349,10 @@ error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, fsys_statfsbuf_t *st) { - return ENOSYS; + memset (st, 0, sizeof *st); + st->f_type = FSTYPE_PROC; + st->f_fsid = getpid (); + return 0; } /* The user must define this function. This should sync the locked -- cgit v1.2.3 From 3081d01a686922957fa298a3101f3cb1e8200ce7 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 12 Jan 2013 16:23:09 +0100 Subject: Report VM cache size in meminfo * rootdir.c: Include and . (rootdir_gc_meminfo): Call vm_cache_statistics and return the VM cache size. --- rootdir.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rootdir.c b/rootdir.c index 1fa71b03..31e2d8c6 100644 --- a/rootdir.c +++ b/rootdir.c @@ -17,8 +17,10 @@ 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 @@ -263,6 +265,7 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) host_basic_info_data_t hbi; mach_msg_type_number_t cnt; struct vm_statistics vmstats; + struct vm_cache_statistics cache_stats; default_pager_info_t swap; error_t err; @@ -270,6 +273,10 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) if (err) return EIO; + err = vm_cache_statistics (mach_task_self (), &cache_stats); + 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) @@ -294,7 +301,7 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) (long unsigned) hbi.memory_size / 1024, (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, 0, - 0, + (long unsigned) cache_stats.cache_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, -- cgit v1.2.3 From 9288839793069e589615e4bb47257b63501586ae Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sat, 2 Mar 2013 17:36:36 +0100 Subject: Remove old hack for the "stat" permissions 7550343b09a94dc1c659ad74dc75b3b77e7ab0bc added a better way to set the permissions of that file, so this old hack is not needed anymore. * process.c (process_lookup_pid): Do not change the mode of ENTRIES[2].hook. --- process.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/process.c b/process.c index 68eb50a0..c5ef7d8a 100644 --- a/process.c +++ b/process.c @@ -382,14 +382,6 @@ process_lookup_pid (struct ps_context *pc, pid_t pid, struct node **np) 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; -- cgit v1.2.3 From 26b18d8e7d8bb5f1bf9ae20e40e8b291db3cebd6 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sat, 29 Jun 2013 22:10:09 +0200 Subject: procfs: fix the error handling in argp_parser Do not exit using error (1, ..) but gracefully handle the error using argp_error. Also fix a typo ("the a user") while touching these lines. * procfs/main.c (argp_parser): Proper error handling. (argp_parser): Fix typo. --- main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index 3a976ccc..f33ace9a 100644 --- a/main.c +++ b/main.c @@ -48,13 +48,13 @@ argp_parser (int key, char *arg, struct argp_state *state) 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"); + argp_error (state, "--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"); + argp_error (state, "--stat-mode: MODE should be an octal mode"); break; case 'S': @@ -62,7 +62,7 @@ argp_parser (int key, char *arg, struct argp_state *state) { opt_fake_self = strtol (arg, &endp, 0); if (*endp || ! *arg) - error (1, 0, "--fake-self: PID must be an integer"); + argp_error (state, "--fake-self: PID must be an integer"); } else opt_fake_self = 1; @@ -71,7 +71,7 @@ argp_parser (int key, char *arg, struct argp_state *state) 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"); + argp_error (state, "--kernel-process: PID must be a positive integer"); break; case 'c': @@ -90,8 +90,8 @@ argp_parser (int key, char *arg, struct argp_state *state) 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."); + argp_error (state, "--anonymous-owner: USER should be " + "a user name or a numeric UID."); break; } -- cgit v1.2.3 From ea6ea2f554bc81b856d5286c23c2b4c8ba303fb9 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sat, 29 Jun 2013 22:13:16 +0200 Subject: procfs: keep old config values if the parsing fails Previously if strtol failed the previous configuration value would get overwritten. Prevent this by storing the result in a temporary variable and update the configuration if the argument was parsed correctly and passed the sanity checks. * procfs/main.c (argp_parser): Keep old configuration in case a malformed value is encountered. --- main.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/main.c b/main.c index f33ace9a..0892d369 100644 --- a/main.c +++ b/main.c @@ -42,36 +42,45 @@ argp_parser (int key, char *arg, struct argp_state *state) { struct passwd *pw; char *endp; + long int v; switch (key) { case 'h': - opt_clk_tck = strtol (arg, &endp, 0); - if (*endp || ! *arg || opt_clk_tck <= 0) + v = strtol (arg, &endp, 0); + if (*endp || ! *arg || v <= 0) argp_error (state, "--clk-tck: HZ should be a positive integer"); + else + opt_clk_tck = v; break; case 's': - opt_stat_mode = strtol (arg, &endp, 8); - if (*endp || ! *arg || opt_stat_mode & ~07777) + v = strtol (arg, &endp, 8); + if (*endp || ! *arg || (mode_t) v & ~07777) argp_error (state, "--stat-mode: MODE should be an octal mode"); + else + opt_stat_mode = v; break; case 'S': if (arg) { - opt_fake_self = strtol (arg, &endp, 0); + v = strtol (arg, &endp, 0); if (*endp || ! *arg) argp_error (state, "--fake-self: PID must be an integer"); + else + opt_fake_self = v; } else opt_fake_self = 1; break; case 'k': - opt_kernel_pid = strtol (arg, &endp, 0); + v = strtol (arg, &endp, 0); if (*endp || ! *arg || (signed) opt_kernel_pid < 0) argp_error (state, "--kernel-process: PID must be a positive integer"); + else + opt_kernel_pid = v; break; case 'c': @@ -88,10 +97,12 @@ argp_parser (int key, char *arg, struct argp_state *state) break; } - opt_anon_owner = strtol (arg, &endp, 0); - if (*endp || ! *arg || (signed) opt_anon_owner < 0) + v = strtol (arg, &endp, 0); + if (*endp || ! *arg || v < 0) argp_error (state, "--anonymous-owner: USER should be " "a user name or a numeric UID."); + else + opt_anon_owner = v; break; } -- cgit v1.2.3 From fd1f64a2e9e794a0184238bf3a0c4b7f5c6f963c Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sat, 29 Jun 2013 22:14:13 +0200 Subject: procfs: enable fsys_set_options Make procfs translators handle fsys_set_options requests by pointing netfs_runtime_argp to our argp struct. * procfs/main.c (netfs_runtime_argp): New variable. --- main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main.c b/main.c index 0892d369..1b19c013 100644 --- a/main.c +++ b/main.c @@ -146,6 +146,9 @@ struct argp argp = { }, }; +/* Used by netfs_set_options to handle runtime option parsing. */ +struct argp *netfs_runtime_argp = &argp; + error_t root_make_node (struct ps_context *pc, struct node **np) { -- cgit v1.2.3 From 669dadfa07a5e63db8d9e572603872be88db52a3 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sat, 20 Jul 2013 17:47:05 +0200 Subject: procfs: Ignore arguments for compatibility with Linux' procfs. Ignore the --nodev, --noexec and --nosuid arguments. * procfs/main.c (argp_parser): Ignore some arguments for compatibility. --- main.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/main.c b/main.c index 1b19c013..90b3e92c 100644 --- a/main.c +++ b/main.c @@ -37,6 +37,10 @@ pid_t opt_fake_self; pid_t opt_kernel_pid; uid_t opt_anon_owner; +#define NODEV_KEY -1 /* <= 0, so no short option. */ +#define NOEXEC_KEY -2 /* Likewise. */ +#define NOSUID_KEY -3 /* Likewise. */ + static error_t argp_parser (int key, char *arg, struct argp_state *state) { @@ -104,6 +108,18 @@ argp_parser (int key, char *arg, struct argp_state *state) else opt_anon_owner = v; break; + + case NODEV_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; + + case NOEXEC_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; + + case NOSUID_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; } return 0; @@ -136,6 +152,12 @@ struct argp argp = { "Be aware that USER will be granted access to the environment and " "other sensitive information about the processes in question. " "(default: use uid 0)" }, + { "nodev", NODEV_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + { "noexec", NOEXEC_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + { "nosuid", NOSUID_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, {} }, .parser = argp_parser, -- cgit v1.2.3 From b1a3f96a8139c5ed3aac7b8a77b06ba4c0a877c7 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 16 Jul 2013 10:51:20 +0200 Subject: Fix procfs_node_chmod Formerly only the mode_t bits cut out by S_IFMT were preserved, but this does not include S_IPTRANS. Fix this by preserving everything but the permission bits. * procfs.c (procfs_node_chmod): Fix bitmask. --- procfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procfs.c b/procfs.c index ae5a6769..aa785d35 100644 --- a/procfs.c +++ b/procfs.c @@ -93,7 +93,7 @@ void procfs_node_chown (struct node *np, uid_t 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_stat.st_mode = (np->nn_stat.st_mode & ~ALLPERMS) | mode; np->nn_translated = np->nn_stat.st_mode; } -- cgit v1.2.3 From 641249f6b143c4e22825e18e0dbe22bb04d21138 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 16 Jul 2013 10:51:21 +0200 Subject: Fix default file ownership Formerly all nodes were created so that they were owned by root:root by default. This causes problems if the procfs translator is running as unprivileged user and serves passive translator records, because the file ownership is being used as credentials for starting translators. This obviously fails if the procfs translator is not root but is trying to acquire credentials including uid/gid 0. Fix this by using the uid/gid of the procfs translator by default. procfs.c (procfs_make_node): Fix default node ownership. --- procfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/procfs.c b/procfs.c index aa785d35..44e4f7e7 100644 --- a/procfs.c +++ b/procfs.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -76,6 +77,9 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) else np->nn_stat.st_mode = S_IFREG | 0444; + np->nn_stat.st_uid = getuid (); + np->nn_stat.st_gid = getgid (); + return np; fail: -- cgit v1.2.3 From 890f987c15029d7e29f7deea0bba0b8d4ecaeaeb Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 16 Jul 2013 10:51:22 +0200 Subject: Add the necessary infrastructure to serve passive translators * netfs.c (netfs_get_translator): New function. * procfs.c (procfs_get_translator): Likewise. * procfs.h (struct procfs_node_ops): New field get_translator. (procfs_get_translator): New function declaration. --- netfs.c | 11 +++++++++++ procfs.c | 12 ++++++++++++ procfs.h | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/netfs.c b/netfs.c index c139d11a..276c57cc 100644 --- a/netfs.c +++ b/netfs.c @@ -230,6 +230,17 @@ void netfs_node_norefs (struct node *np) pthread_spin_lock (&netfs_node_refcnt_lock); } +/* The user may define this function (but should define it together + with netfs_set_translator). For locked node NODE with S_IPTRANS + set in its mode, look up the name of its translator. Store the + name into newly malloced storage, and return it in *ARGZ; set + *ARGZ_LEN to the total length. */ +error_t netfs_get_translator (struct node *np, char **argz, + size_t *argz_len) +{ + return procfs_get_translator (np, argz, argz_len); +} + /* Libnetfs callbacks managed with libfshelp. */ diff --git a/procfs.c b/procfs.c index 44e4f7e7..cae4a519 100644 --- a/procfs.c +++ b/procfs.c @@ -205,3 +205,15 @@ void procfs_cleanup (struct node *np) free (np->nn); } + +error_t procfs_get_translator (struct node *np, + char **argz, + size_t *argz_len) +{ + if (np->nn->ops->get_translator) + return np->nn->ops->get_translator (np->nn->hook, argz, argz_len); + + *argz = NULL; + *argz_len = 0; + return 0; +} diff --git a/procfs.h b/procfs.h index 64782ec4..d04bbad7 100644 --- a/procfs.h +++ b/procfs.h @@ -51,6 +51,9 @@ struct procfs_node_ops /* Destroy this node. */ void (*cleanup) (void *hook); + + /* Get the passive translator record. */ + error_t (*get_translator) (void *hook, char **argz, size_t *argz_len); }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ @@ -91,3 +94,6 @@ 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); +/* Get the passive translator record if any. */ +error_t procfs_get_translator (struct node *np, char **argz, size_t *argz_len); + -- cgit v1.2.3 From 92132dda0f13b2f4e82048797acfb7c34da2f53f Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Mon, 22 Jul 2013 22:57:38 +0200 Subject: Fix profile link * Makefile [PROFILE] (LIBS): Replace -lthreads with -lpthread --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c69cb206..9ab2ab88 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ ifdef PROFILE CFLAGS= -g -pg CPPFLAGS= -DPROFILE LDFLAGS= -static -LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lthreads -lihash -lshouldbeinlibc +LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lpthread -lihash -lshouldbeinlibc endif CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -- cgit v1.2.3 From 07b0785c207cd46751abcb17cf44168621bbc479 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 22 Jul 2013 17:54:36 +0200 Subject: Makefile: add -lpthread * Makefile (LIBS): Link with libpthread. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9ab2ab88..5c51c1d2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ TARGET = procfs OBJS = procfs.o netfs.o procfs_dir.o \ process.o proclist.o rootdir.o dircat.o main.o -LIBS = -lnetfs -lps -lfshelp +LIBS = -lnetfs -lps -lfshelp -lpthread CC = gcc CFLAGS = -Wall -g -- cgit v1.2.3 From 7802531da03626cfded497239e29a8ec2e4a3285 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 22 Jul 2013 17:54:37 +0200 Subject: rootdir.c: Fix compiler warning * rootdir.c (rootdir_gc_meminfo): Use unsigned long constant. --- rootdir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootdir.c b/rootdir.c index 31e2d8c6..9fc326af 100644 --- a/rootdir.c +++ b/rootdir.c @@ -300,7 +300,7 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) , (long unsigned) hbi.memory_size / 1024, (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, - 0, + 0UL, (long unsigned) cache_stats.cache_count * PAGE_SIZE / 1024, (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, -- cgit v1.2.3 From fc7055f2461fa92bc17d67f02058399dca5736ae Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 22 Jul 2013 17:54:38 +0200 Subject: rootdir.c: Fix declaration of rootdir_fakeself_exists. * rootdir.c (rootdir_fakeself_exists): Use the correct arguments for this kind of function. --- rootdir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootdir.c b/rootdir.c index 9fc326af..f234dd03 100644 --- a/rootdir.c +++ b/rootdir.c @@ -392,7 +392,7 @@ out: } static int -rootdir_fakeself_exists () +rootdir_fakeself_exists (void *dir_hook, const void *entry_hook) { return opt_fake_self >= 0; } -- cgit v1.2.3 From eda9e61107ed34a5f7a566fe6835a48c48aec1d3 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 26 Aug 2013 19:32:24 +0200 Subject: Fix argument parsing The switch statement was missing the default case returning ARGP_ERR_UNKNOWN. This patch adds that. * main.c (argp_parser): Add default case. --- main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main.c b/main.c index 90b3e92c..2bfa5c20 100644 --- a/main.c +++ b/main.c @@ -120,6 +120,9 @@ argp_parser (int key, char *arg, struct argp_state *state) case NOSUID_KEY: /* Ignored for compatibility with Linux' procfs. */ ;; + + default: + return ARGP_ERR_UNKNOWN; } return 0; -- cgit v1.2.3 From 2baaf773daa797048ea96573fa01786d129f00fb Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Fri, 28 Jun 2013 18:37:43 +0200 Subject: procfs: handle the --update parameter Split the argument handling into a common part and one for fsys_update_options. Handle the --update parameter; for procfs this is a no-op. * procfs/main.c (common_options): New variable. (runtime_argp_parser): Handle --update. (startup_argp): New variable. (netfs_runtime_argp_): New variable. --- main.c | 103 ++++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 33 deletions(-) diff --git a/main.c b/main.c index 2bfa5c20..b678a713 100644 --- a/main.c +++ b/main.c @@ -128,45 +128,82 @@ argp_parser (int key, char *arg, struct argp_state *state) return 0; } +struct argp_option common_options[] = { + { "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)" }, + { "nodev", NODEV_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + { "noexec", NOEXEC_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + { "nosuid", NOSUID_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + {} +}; + 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)" }, - { "nodev", NODEV_KEY, NULL, 0, - "Ignored for compatibility with Linux' procfs." }, - { "noexec", NOEXEC_KEY, NULL, 0, - "Ignored for compatibility with Linux' procfs." }, - { "nosuid", NOSUID_KEY, NULL, 0, - "Ignored for compatibility with Linux' procfs." }, + .options = common_options, + .parser = argp_parser, + .doc = "A virtual filesystem emulating the Linux procfs.", + .children = (struct argp_child []) { + { &netfs_std_startup_argp, }, {} }, +}; + +static error_t +runtime_argp_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'u': + /* do nothing */ + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +struct argp runtime_argp = { + .options = (struct argp_option []) { + { "update", 'u', NULL, 0, "remount; for procfs this does nothing" }, + {}, + }, + .parser = runtime_argp_parser, +}; + +struct argp netfs_runtime_argp_ = { + .options = common_options, .parser = argp_parser, .doc = "A virtual filesystem emulating the Linux procfs.", .children = (struct argp_child []) { - { &netfs_std_startup_argp, }, + { &runtime_argp, }, + { &netfs_std_runtime_argp, }, {} }, }; -- cgit v1.2.3 From 2f13ab11aa71a2b8fb6e045a9ff81b7b2252cbe0 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Fri, 28 Jun 2013 18:37:44 +0200 Subject: procfs: define macros for the default argument values Define a macro for the default value of each command line parameter. This allows one to generate a minimal response to fsys_get_options requests. * procfs/main.c: New macro definitions for default values. --- main.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index b678a713..fc4c5e5a 100644 --- a/main.c +++ b/main.c @@ -37,6 +37,13 @@ pid_t opt_fake_self; pid_t opt_kernel_pid; uid_t opt_anon_owner; +/* Default values */ +#define OPT_CLK_TCK sysconf(_SC_CLK_TCK) +#define OPT_STAT_MODE 0400 +#define OPT_FAKE_SELF -1 +#define OPT_KERNEL_PID 2 +#define OPT_ANON_OWNER 0 + #define NODEV_KEY -1 /* <= 0, so no short option. */ #define NOEXEC_KEY -2 /* Likewise. */ #define NOSUID_KEY -3 /* Likewise. */ @@ -236,11 +243,11 @@ int main (int argc, char **argv) mach_port_t bootstrap; error_t err; - opt_clk_tck = sysconf(_SC_CLK_TCK); - opt_stat_mode = 0400; - opt_fake_self = -1; - opt_kernel_pid = 2; - opt_anon_owner = 0; + opt_clk_tck = OPT_CLK_TCK; + opt_stat_mode = OPT_STAT_MODE; + opt_fake_self = OPT_FAKE_SELF; + opt_kernel_pid = OPT_KERNEL_PID; + opt_anon_owner = OPT_ANON_OWNER; err = argp_parse (&argp, argc, argv, 0, 0, 0); if (err) error (1, err, "Could not parse command line"); -- cgit v1.2.3 From 883e9a3bcbd0de620272c79b6a081b42b0832ef4 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Fri, 28 Jun 2013 18:37:45 +0200 Subject: procfs: provide a more meaningful response to fsys_get_options Implement our own netfs_append_args function that provides the appropriate command line flags if the current values differ from the default values. * procfs/main.c (netfs_append_args): New function. --- main.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/main.c b/main.c index fc4c5e5a..54e96823 100644 --- a/main.c +++ b/main.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "procfs.h" @@ -218,6 +219,47 @@ struct argp netfs_runtime_argp_ = { /* Used by netfs_set_options to handle runtime option parsing. */ struct argp *netfs_runtime_argp = &argp; +/* Return an argz string describing the current options. Fill *ARGZ + with a pointer to newly malloced storage holding the list and *LEN + to the length of that storage. */ +error_t +netfs_append_args (char **argz, size_t *argz_len) +{ + char buf[80]; + error_t err = 0; + +#define FOPT(opt, default, fmt, args...) \ + do { \ + if (! err && opt != default) \ + { \ + snprintf (buf, sizeof buf, fmt, ## args); \ + err = argz_add (argz, argz_len, buf); \ + } \ + } while (0) + + FOPT (opt_clk_tck, OPT_CLK_TCK, + "--clk-tck=%d", opt_clk_tck); + + FOPT (opt_stat_mode, OPT_STAT_MODE, + "--stat-mode=%o", opt_stat_mode); + + FOPT (opt_fake_self, OPT_FAKE_SELF, + "--fake-self=%d", opt_fake_self); + + FOPT (opt_anon_owner, OPT_ANON_OWNER, + "--anonymous-owner=%d", opt_anon_owner); + + FOPT (opt_kernel_pid, OPT_KERNEL_PID, + "--kernel-process=%d", opt_kernel_pid); + +#undef FOPT + + if (! err) + err = netfs_append_std_options (argz, argz_len); + + return err; +} + error_t root_make_node (struct ps_context *pc, struct node **np) { -- cgit v1.2.3 From 690beb4d6679c66f45d09883408cb860337e31c2 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 5 Aug 2013 12:11:30 +0200 Subject: Make start_code and end_code available in /proc/*/stat This makes killall5 actually kill any processes not marked as important. * process.c (process_file_gc_stat): Fix {start,end}_code. --- process.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/process.c b/process.c index c5ef7d8a..269a18bb 100644 --- a/process.c +++ b/process.c @@ -116,6 +116,26 @@ process_file_gc_stat (struct proc_stat *ps, char **contents) thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); const char *fn = args_filename (proc_stat_args (ps)); + vm_address_t start_code = 1; /* 0 would make killall5.c consider it + a kernel process, thus use 1 as + default. */ + vm_address_t end_code = 1; + process_t p; + error_t err = proc_pid2proc (ps->context->server, ps->pid, &p); + if (! err) + { + boolean_t essential = 0; + proc_is_important (p, &essential); + if (essential) + start_code = end_code = 0; /* To make killall5.c consider it a + kernel process that is to be + left alone. */ + else + proc_get_code (p, &start_code, &end_code); + + mach_port_deallocate (mach_task_self (), p); + } + /* See proc(5) for more information about the contents of each field for the Linux procfs. */ return asprintf (contents, @@ -152,7 +172,9 @@ process_file_gc_stat (struct proc_stat *ps, char **contents) 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, + start_code, + end_code, + 0L, 0L, 0L, 0L, 0L, 0L, 0L, (long unsigned) proc_stat_thread_rpc (ps), /* close enough */ 0L, 0L, -- cgit v1.2.3 From 969078c9755b7e28dcabe277629f757e20a19f1a Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Mon, 22 Jul 2013 17:54:39 +0200 Subject: Serve mounts node with a passive translator record Make mounts a node with a passive translator record so that the mtab translator is started on access if it is available. * rootdir.c (MTAB_TRANSLATOR): New macro. (rootdir_mounts_get_translator): New function. (rootdir_mounts_exists): New function. (rootdir_translator_make_node): Likewise. (rootdir_entries): Add "mounts" node. --- rootdir.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/rootdir.c b/rootdir.c index f234dd03..34bf91ce 100644 --- a/rootdir.c +++ b/rootdir.c @@ -1,5 +1,5 @@ /* Hurd /proc filesystem, permanent files of the root directory. - Copyright (C) 2010 Free Software Foundation, Inc. + Copyright (C) 2010,13 Free Software Foundation, Inc. This file is part of the GNU Hurd. @@ -404,6 +404,31 @@ rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) return 0; } +/* The mtab translator to use by default for the "mounts" node. */ +#define MTAB_TRANSLATOR "/hurd/mtab" + +static error_t +rootdir_mounts_get_translator (void *hook, char **argz, size_t *argz_len) +{ + static const char const mtab_argz[] = MTAB_TRANSLATOR "\0/"; + + *argz = malloc (sizeof mtab_argz); + if (! *argz) + return ENOMEM; + + memcpy (*argz, mtab_argz, sizeof mtab_argz); + *argz_len = sizeof mtab_argz; + return 0; +} + +static int +rootdir_mounts_exists (void *dir_hook, const void *entry_hook) +{ + static int translator_exists = -1; + if (translator_exists == -1) + translator_exists = access (MTAB_TRANSLATOR, F_OK|X_OK) == 0; + return translator_exists; +} /* Glue logic and entries table */ @@ -426,6 +451,18 @@ rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) return np; } +static struct node * +rootdir_translator_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_IFREG | S_IPTRANS); + procfs_node_chmod (np, 0444); + } + return np; +} + static const struct procfs_dir_entry rootdir_entries[] = { { .name = "self", @@ -487,6 +524,16 @@ static const struct procfs_dir_entry rootdir_entries[] = { .cleanup_contents = procfs_cleanup_contents_with_free, }, }, + { + .name = "mounts", + .hook = & (struct procfs_node_ops) { + .get_translator = rootdir_mounts_get_translator, + }, + .ops = { + .make_node = rootdir_translator_make_node, + .exists = rootdir_mounts_exists, + } + }, #ifdef PROFILE /* In order to get a usable gmon.out file, we must apparently use exit(). */ { -- cgit v1.2.3 From c87688a05a8b49479ee10127470cc60acebead4a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 4 Oct 2013 20:27:45 +0200 Subject: Fix mtab translator leak The node structure for the `mounts' entry is created and destroyed on each access, despite the fact a translator is activated on it. Fix the issue by caching it. * rootdir.c (rootdir_mounts_node): New variable. (rootdir_mounts_node_lock): Likewise. (rootdir_mounts_make_node): New function. (rootdir_translator_make_node): Remove function. (rootdir_entries): Replace `rootdir_translator_make_node' with `rootdir_mounts_make_node'. --- rootdir.c | 55 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/rootdir.c b/rootdir.c index 34bf91ce..94b0ddb2 100644 --- a/rootdir.c +++ b/rootdir.c @@ -407,6 +407,47 @@ rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) /* The mtab translator to use by default for the "mounts" node. */ #define MTAB_TRANSLATOR "/hurd/mtab" +static struct node *rootdir_mounts_node; +static pthread_spinlock_t rootdir_mounts_node_lock = + PTHREAD_SPINLOCK_INITIALIZER; + +static struct node * +rootdir_mounts_make_node (void *dir_hook, const void *entry_hook) +{ + struct node *np, *prev; + + pthread_spin_lock (&rootdir_mounts_node_lock); + np = rootdir_mounts_node; + pthread_spin_unlock (&rootdir_mounts_node_lock); + + if (np != NULL) + { + netfs_nref (np); + return np; + } + + np = procfs_make_node (entry_hook, dir_hook); + if (np == NULL) + return NULL; + + procfs_node_chtype (np, S_IFREG | S_IPTRANS); + procfs_node_chmod (np, 0444); + + pthread_spin_lock (&rootdir_mounts_node_lock); + prev = rootdir_mounts_node; + if (rootdir_mounts_node == NULL) + rootdir_mounts_node = np; + pthread_spin_unlock (&rootdir_mounts_node_lock); + + if (prev != NULL) + { + procfs_cleanup (np); + np = prev; + } + + return np; +} + static error_t rootdir_mounts_get_translator (void *hook, char **argz, size_t *argz_len) { @@ -451,18 +492,6 @@ rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) return np; } -static struct node * -rootdir_translator_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_IFREG | S_IPTRANS); - procfs_node_chmod (np, 0444); - } - return np; -} - static const struct procfs_dir_entry rootdir_entries[] = { { .name = "self", @@ -530,7 +559,7 @@ static const struct procfs_dir_entry rootdir_entries[] = { .get_translator = rootdir_mounts_get_translator, }, .ops = { - .make_node = rootdir_translator_make_node, + .make_node = rootdir_mounts_make_node, .exists = rootdir_mounts_exists, } }, -- cgit v1.2.3 From 122224d32894a8465a09561fc0a5220f5bb9c2c7 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Thu, 22 May 2014 20:25:22 +0200 Subject: Implement /proc/slabinfo Add a node 'slabinfo' to the root directory that contains information about the slab allocator used in GNU Mach. The formatting code has been taken from Richard Braun's standalone client available here: git://darnassus.sceen.net/rbraun/slabinfo.git * rootdir.c (rootdir_gc_slabinfo): New function. (rootdir_entries): Add node 'slabinfo'. * Makefile (OBJS): Add mach_debugUser.o. (rootdir.o): Add an explicit dependency on mach_debug_U.h. Furthermore, add rules to create both functions. --- Makefile | 22 ++++++++++++++++++- rootdir.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5c51c1d2..28205962 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TARGET = procfs OBJS = procfs.o netfs.o procfs_dir.o \ - process.o proclist.o rootdir.o dircat.o main.o + process.o proclist.o rootdir.o dircat.o main.o mach_debugUser.o LIBS = -lnetfs -lps -lfshelp -lpthread CC = gcc @@ -19,8 +19,28 @@ CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 all: $(TARGET) +rootdir.o: rootdir.c mach_debug_U.h + $(TARGET): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) clean: $(RM) $(TARGET) $(OBJS) + +# This is the gist of the MIG user stub handling from Hurd's build +# system: + +# Where to find .defs files. +vpath %.defs /usr/include/mach_debug + +MIG = mig +MIGCOM = $(MIG) -cc cat - /dev/null +MIGCOMFLAGS := -subrprefix __ + +%.udefsi: %.defs + $(CPP) -x c $(CPPFLAGS) $(MIGUFLAGS) $($*-MIGUFLAGS) \ + $< -o $*.udefsi + +%_U.h %User.c: %.udefsi + $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMUFLAGS) $($*-MIGCOMUFLAGS) < $< \ + -user $*User.c -server /dev/null -header $*_U.h diff --git a/rootdir.c b/rootdir.c index 94b0ddb2..076885c9 100644 --- a/rootdir.c +++ b/rootdir.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,8 @@ #include "procfs_dir.h" #include "main.h" +#include "mach_debug_U.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 @@ -470,6 +473,69 @@ rootdir_mounts_exists (void *dir_hook, const void *entry_hook) translator_exists = access (MTAB_TRANSLATOR, F_OK|X_OK) == 0; return translator_exists; } + +static error_t +rootdir_gc_slabinfo (void *hook, char **contents, ssize_t *contents_len) +{ + error_t err; + FILE *m; + const char header[] = + "cache obj slab bufs objs bufs" + " total reclaimable\n" + "name flags size size /slab usage count" + " memory memory\n"; + cache_info_array_t cache_info; + size_t mem_usage, mem_reclaimable, mem_total, mem_total_reclaimable; + mach_msg_type_number_t cache_info_count; + int i; + + cache_info = NULL; + cache_info_count = 0; + + err = host_slab_info (mach_host_self(), &cache_info, &cache_info_count); + if (err) + return err; + + m = open_memstream (contents, contents_len); + if (m == NULL) + { + err = ENOMEM; + goto out; + } + + fprintf (m, "%s", header); + + mem_total = 0; + mem_total_reclaimable = 0; + + for (i = 0; i < cache_info_count; i++) + { + mem_usage = (cache_info[i].nr_slabs * cache_info[i].slab_size) + >> 10; + mem_total += mem_usage; + mem_reclaimable = (cache_info[i].flags & CACHE_FLAGS_NO_RECLAIM) + ? 0 : (cache_info[i].nr_free_slabs + * cache_info[i].slab_size) >> 10; + mem_total_reclaimable += mem_reclaimable; + fprintf (m, + "%-21s %04x %7zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", + cache_info[i].name, cache_info[i].flags, + cache_info[i].obj_size, cache_info[i].slab_size >> 10, + cache_info[i].bufs_per_slab, cache_info[i].nr_objs, + cache_info[i].nr_bufs, mem_usage, mem_reclaimable); + } + + fprintf (m, "total: %zuk, reclaimable: %zuk\n", + mem_total, mem_total_reclaimable); + + fclose (m); + *contents_len += 1; /* For the terminating 0. */ + + out: + vm_deallocate (mach_task_self (), + cache_info, cache_info_count * sizeof *cache_info); + return err; +} /* Glue logic and entries table */ @@ -563,6 +629,13 @@ static const struct procfs_dir_entry rootdir_entries[] = { .exists = rootdir_mounts_exists, } }, + { + .name = "slabinfo", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_slabinfo, + .cleanup_contents = procfs_cleanup_contents_with_free, + }, + }, #ifdef PROFILE /* In order to get a usable gmon.out file, we must apparently use exit(). */ { -- cgit v1.2.3 From 65ebcc40e55dfb3ee776383891f8a6b15b176d27 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 28 May 2014 14:37:58 +0200 Subject: Do not include the terminating zero in /proc/slabinfo * rootdir.c (rootdir_gc_slabinfo): Do not zero-terminate the generated content. --- rootdir.c | 1 - 1 file changed, 1 deletion(-) diff --git a/rootdir.c b/rootdir.c index 076885c9..0b131192 100644 --- a/rootdir.c +++ b/rootdir.c @@ -529,7 +529,6 @@ rootdir_gc_slabinfo (void *hook, char **contents, ssize_t *contents_len) mem_total, mem_total_reclaimable); fclose (m); - *contents_len += 1; /* For the terminating 0. */ out: vm_deallocate (mach_task_self (), -- cgit v1.2.3 From aac4aaf42372f61c78061711916c81a9d5bcb42d Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sun, 15 Jun 2014 18:25:59 +0200 Subject: Prepare the procfs translator to be merged into the Hurd sources Move the procfs translator to its own subdirectory 'procfs'. This is the last commit to this repository. Development of the procfs translator will continue in the main Hurd repository. * procfs/Makefile: Replace the standalone Makefile with the one from the Debian packaging repository. --- ChangeLog | 6 - Makefile | 46 ---- TODO | 24 -- dircat.c | 128 ---------- dircat.h | 29 --- main.c | 315 ------------------------- main.h | 25 -- netfs.c | 461 ------------------------------------ process.c | 414 -------------------------------- process.h | 27 --- procfs.c | 219 ----------------- procfs.h | 99 -------- procfs/ChangeLog | 6 + procfs/Makefile | 34 +++ procfs/TODO | 24 ++ procfs/dircat.c | 128 ++++++++++ procfs/dircat.h | 29 +++ procfs/main.c | 315 +++++++++++++++++++++++++ procfs/main.h | 25 ++ procfs/netfs.c | 461 ++++++++++++++++++++++++++++++++++++ procfs/process.c | 414 ++++++++++++++++++++++++++++++++ procfs/process.h | 27 +++ procfs/procfs.c | 219 +++++++++++++++++ procfs/procfs.h | 99 ++++++++ procfs/procfs_dir.c | 134 +++++++++++ procfs/procfs_dir.h | 63 +++++ procfs/proclist.c | 94 ++++++++ procfs/proclist.h | 23 ++ procfs/rootdir.c | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++ procfs/rootdir.h | 23 ++ procfs_dir.c | 134 ----------- procfs_dir.h | 63 ----- proclist.c | 94 -------- proclist.h | 23 -- rootdir.c | 661 ---------------------------------------------------- rootdir.h | 23 -- 36 files changed, 2779 insertions(+), 2791 deletions(-) delete mode 100644 ChangeLog delete mode 100644 Makefile delete mode 100644 TODO 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/ChangeLog 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/ChangeLog b/ChangeLog deleted file mode 100644 index 0cd74d02..00000000 --- a/ChangeLog +++ /dev/null @@ -1,6 +0,0 @@ -edb4593c38d421b5d538b221a991b50c36fdba15 is the last commit imported from CVS. -All commits after that one have valid author and committer information. - -Use this to examine the change log for earlier changes: - - $ git show edb4593c38d421b5d538b221a991b50c36fdba15:ChangeLog diff --git a/Makefile b/Makefile deleted file mode 100644 index 28205962..00000000 --- a/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -TARGET = procfs -OBJS = procfs.o netfs.o procfs_dir.o \ - process.o proclist.o rootdir.o dircat.o main.o mach_debugUser.o -LIBS = -lnetfs -lps -lfshelp -lpthread - -CC = gcc -CFLAGS = -Wall -g -CPPFLAGS = -LDFLAGS = - -ifdef PROFILE -CFLAGS= -g -pg -CPPFLAGS= -DPROFILE -LDFLAGS= -static -LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lpthread -lihash -lshouldbeinlibc -endif - -CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 - -all: $(TARGET) - -rootdir.o: rootdir.c mach_debug_U.h - -$(TARGET): $(OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) - -clean: - $(RM) $(TARGET) $(OBJS) - -# This is the gist of the MIG user stub handling from Hurd's build -# system: - -# Where to find .defs files. -vpath %.defs /usr/include/mach_debug - -MIG = mig -MIGCOM = $(MIG) -cc cat - /dev/null -MIGCOMFLAGS := -subrprefix __ - -%.udefsi: %.defs - $(CPP) -x c $(CPPFLAGS) $(MIGUFLAGS) $($*-MIGUFLAGS) \ - $< -o $*.udefsi - -%_U.h %User.c: %.udefsi - $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMUFLAGS) $($*-MIGCOMUFLAGS) < $< \ - -user $*User.c -server /dev/null -header $*_U.h diff --git a/TODO b/TODO deleted file mode 100644 index 952d67bc..00000000 --- a/TODO +++ /dev/null @@ -1,24 +0,0 @@ -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/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 54e96823..00000000 --- a/main.c +++ /dev/null @@ -1,315 +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 -#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; - -/* Default values */ -#define OPT_CLK_TCK sysconf(_SC_CLK_TCK) -#define OPT_STAT_MODE 0400 -#define OPT_FAKE_SELF -1 -#define OPT_KERNEL_PID 2 -#define OPT_ANON_OWNER 0 - -#define NODEV_KEY -1 /* <= 0, so no short option. */ -#define NOEXEC_KEY -2 /* Likewise. */ -#define NOSUID_KEY -3 /* Likewise. */ - -static error_t -argp_parser (int key, char *arg, struct argp_state *state) -{ - struct passwd *pw; - char *endp; - long int v; - - switch (key) - { - case 'h': - v = strtol (arg, &endp, 0); - if (*endp || ! *arg || v <= 0) - argp_error (state, "--clk-tck: HZ should be a positive integer"); - else - opt_clk_tck = v; - break; - - case 's': - v = strtol (arg, &endp, 8); - if (*endp || ! *arg || (mode_t) v & ~07777) - argp_error (state, "--stat-mode: MODE should be an octal mode"); - else - opt_stat_mode = v; - break; - - case 'S': - if (arg) - { - v = strtol (arg, &endp, 0); - if (*endp || ! *arg) - argp_error (state, "--fake-self: PID must be an integer"); - else - opt_fake_self = v; - } - else - opt_fake_self = 1; - break; - - case 'k': - v = strtol (arg, &endp, 0); - if (*endp || ! *arg || (signed) opt_kernel_pid < 0) - argp_error (state, "--kernel-process: PID must be a positive integer"); - else - opt_kernel_pid = v; - 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; - } - - v = strtol (arg, &endp, 0); - if (*endp || ! *arg || v < 0) - argp_error (state, "--anonymous-owner: USER should be " - "a user name or a numeric UID."); - else - opt_anon_owner = v; - break; - - case NODEV_KEY: - /* Ignored for compatibility with Linux' procfs. */ - ;; - - case NOEXEC_KEY: - /* Ignored for compatibility with Linux' procfs. */ - ;; - - case NOSUID_KEY: - /* Ignored for compatibility with Linux' procfs. */ - ;; - - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -struct argp_option common_options[] = { - { "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)" }, - { "nodev", NODEV_KEY, NULL, 0, - "Ignored for compatibility with Linux' procfs." }, - { "noexec", NOEXEC_KEY, NULL, 0, - "Ignored for compatibility with Linux' procfs." }, - { "nosuid", NOSUID_KEY, NULL, 0, - "Ignored for compatibility with Linux' procfs." }, - {} -}; - -struct argp argp = { - .options = common_options, - .parser = argp_parser, - .doc = "A virtual filesystem emulating the Linux procfs.", - .children = (struct argp_child []) { - { &netfs_std_startup_argp, }, - {} - }, -}; - -static error_t -runtime_argp_parser (int key, char *arg, struct argp_state *state) -{ - switch (key) - { - case 'u': - /* do nothing */ - break; - - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -struct argp runtime_argp = { - .options = (struct argp_option []) { - { "update", 'u', NULL, 0, "remount; for procfs this does nothing" }, - {}, - }, - .parser = runtime_argp_parser, -}; - -struct argp netfs_runtime_argp_ = { - .options = common_options, - .parser = argp_parser, - .doc = "A virtual filesystem emulating the Linux procfs.", - .children = (struct argp_child []) { - { &runtime_argp, }, - { &netfs_std_runtime_argp, }, - {} - }, -}; - -/* Used by netfs_set_options to handle runtime option parsing. */ -struct argp *netfs_runtime_argp = &argp; - -/* Return an argz string describing the current options. Fill *ARGZ - with a pointer to newly malloced storage holding the list and *LEN - to the length of that storage. */ -error_t -netfs_append_args (char **argz, size_t *argz_len) -{ - char buf[80]; - error_t err = 0; - -#define FOPT(opt, default, fmt, args...) \ - do { \ - if (! err && opt != default) \ - { \ - snprintf (buf, sizeof buf, fmt, ## args); \ - err = argz_add (argz, argz_len, buf); \ - } \ - } while (0) - - FOPT (opt_clk_tck, OPT_CLK_TCK, - "--clk-tck=%d", opt_clk_tck); - - FOPT (opt_stat_mode, OPT_STAT_MODE, - "--stat-mode=%o", opt_stat_mode); - - FOPT (opt_fake_self, OPT_FAKE_SELF, - "--fake-self=%d", opt_fake_self); - - FOPT (opt_anon_owner, OPT_ANON_OWNER, - "--anonymous-owner=%d", opt_anon_owner); - - FOPT (opt_kernel_pid, OPT_KERNEL_PID, - "--kernel-process=%d", opt_kernel_pid); - -#undef FOPT - - if (! err) - err = netfs_append_std_options (argz, argz_len); - - return err; -} - -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 = OPT_CLK_TCK; - opt_stat_mode = OPT_STAT_MODE; - opt_fake_self = OPT_FAKE_SELF; - opt_kernel_pid = OPT_KERNEL_PID; - opt_anon_owner = OPT_ANON_OWNER; - 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 276c57cc..00000000 --- a/netfs.c +++ /dev/null @@ -1,461 +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 -#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); - pthread_mutex_unlock (&dir->lock); - - if (! err) - pthread_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) -{ - pthread_spin_unlock (&netfs_node_refcnt_lock); - - procfs_cleanup (np); - free (np); - - pthread_spin_lock (&netfs_node_refcnt_lock); -} - -/* The user may define this function (but should define it together - with netfs_set_translator). For locked node NODE with S_IPTRANS - set in its mode, look up the name of its translator. Store the - name into newly malloced storage, and return it in *ARGZ; set - *ARGZ_LEN to the total length. */ -error_t netfs_get_translator (struct node *np, char **argz, - size_t *argz_len) -{ - return procfs_get_translator (np, argz, argz_len); -} - - -/* 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) -{ - memset (st, 0, sizeof *st); - st->f_type = FSTYPE_PROC; - st->f_fsid = getpid (); - return 0; -} - -/* 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 269a18bb..00000000 --- a/process.c +++ /dev/null @@ -1,414 +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.; -} - -static const char *args_filename (const char *name) -{ - char *sp = strrchr (name, '/'); - return sp != NULL && *(sp + 1) != '\0' ? sp + 1 : name; -} - -static int args_filename_length (const char *name) -{ - return strchrnul (name, ' ') - name; -} - -/* Actual content generators */ - -static ssize_t -process_file_gc_cmdline (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_args(ps); - return proc_stat_args_len(ps); -} - -static ssize_t -process_file_gc_environ (struct proc_stat *ps, char **contents) -{ - *contents = proc_stat_env(ps); - return proc_stat_env_len(ps); -} - -static ssize_t -process_file_gc_stat (struct proc_stat *ps, char **contents) -{ - struct procinfo *pi = proc_stat_proc_info (ps); - task_basic_info_t tbi = proc_stat_task_basic_info (ps); - thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); - const char *fn = args_filename (proc_stat_args (ps)); - - vm_address_t start_code = 1; /* 0 would make killall5.c consider it - a kernel process, thus use 1 as - default. */ - vm_address_t end_code = 1; - process_t p; - error_t err = proc_pid2proc (ps->context->server, ps->pid, &p); - if (! err) - { - boolean_t essential = 0; - proc_is_important (p, &essential); - if (essential) - start_code = end_code = 0; /* To make killall5.c consider it a - kernel process that is to be - left alone. */ - else - proc_get_code (p, &start_code, &end_code); - - mach_port_deallocate (mach_task_self (), p); - } - - /* 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), args_filename_length (fn), fn, 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, - start_code, - end_code, - 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); - const char *fn = args_filename (proc_stat_args (ps)); - - return asprintf (contents, - "Name:\t%.*s\n" - "State:\t%s\n" - "Tgid:\t%u\n" - "Pid:\t%u\n" - "PPid:\t%u\n" - "Uid:\t%u\t%u\t%u\t%u\n" - "VmSize:\t%8u kB\n" - "VmPeak:\t%8u kB\n" - "VmRSS:\t%8u kB\n" - "VmHWM:\t%8u kB\n" /* ie. resident peak */ - "Threads:\t%u\n", - args_filename_length (fn), fn, - 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; - } - - *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 cae4a519..00000000 --- a/procfs.c +++ /dev/null @@ -1,219 +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 -#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; - - np->nn_stat.st_uid = getuid (); - np->nn_stat.st_gid = getgid (); - - 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 & ~ALLPERMS) | 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); -} - -error_t procfs_get_translator (struct node *np, - char **argz, - size_t *argz_len) -{ - if (np->nn->ops->get_translator) - return np->nn->ops->get_translator (np->nn->hook, argz, argz_len); - - *argz = NULL; - *argz_len = 0; - return 0; -} diff --git a/procfs.h b/procfs.h deleted file mode 100644 index d04bbad7..00000000 --- a/procfs.h +++ /dev/null @@ -1,99 +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); - - /* Get the passive translator record. */ - error_t (*get_translator) (void *hook, char **argz, size_t *argz_len); -}; - -/* 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); - -/* Get the passive translator record if any. */ -error_t procfs_get_translator (struct node *np, char **argz, size_t *argz_len); - diff --git a/procfs/ChangeLog b/procfs/ChangeLog new file mode 100644 index 00000000..0cd74d02 --- /dev/null +++ b/procfs/ChangeLog @@ -0,0 +1,6 @@ +edb4593c38d421b5d538b221a991b50c36fdba15 is the last commit imported from CVS. +All commits after that one have valid author and committer information. + +Use this to examine the change log for earlier changes: + + $ git show edb4593c38d421b5d538b221a991b50c36fdba15:ChangeLog diff --git a/procfs/Makefile b/procfs/Makefile new file mode 100644 index 00000000..6d7ca483 --- /dev/null +++ b/procfs/Makefile @@ -0,0 +1,34 @@ +# Makefile - for procfs +# +# Copyright (C) 2008 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +dir := procfs +makemode := server + +target = procfs + +SRCS = procfs.c netfs.c procfs_dir.c process.c proclist.c rootdir.c dircat.c main.c mach_debugUser.c +LCLHDRS = dircat.h main.h process.h procfs.h procfs_dir.h proclist.h rootdir.h + +OBJS = $(SRCS:.c=.o) +HURDLIBS = netfs fshelp iohelp ps ports ihash shouldbeinlibc +OTHERLIBS = -lpthread + +# Where to find .defs files. +vpath %.defs /usr/include/mach_debug + +include ../Makeconf 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..54e96823 --- /dev/null +++ b/procfs/main.c @@ -0,0 +1,315 @@ +/* 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 +#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; + +/* Default values */ +#define OPT_CLK_TCK sysconf(_SC_CLK_TCK) +#define OPT_STAT_MODE 0400 +#define OPT_FAKE_SELF -1 +#define OPT_KERNEL_PID 2 +#define OPT_ANON_OWNER 0 + +#define NODEV_KEY -1 /* <= 0, so no short option. */ +#define NOEXEC_KEY -2 /* Likewise. */ +#define NOSUID_KEY -3 /* Likewise. */ + +static error_t +argp_parser (int key, char *arg, struct argp_state *state) +{ + struct passwd *pw; + char *endp; + long int v; + + switch (key) + { + case 'h': + v = strtol (arg, &endp, 0); + if (*endp || ! *arg || v <= 0) + argp_error (state, "--clk-tck: HZ should be a positive integer"); + else + opt_clk_tck = v; + break; + + case 's': + v = strtol (arg, &endp, 8); + if (*endp || ! *arg || (mode_t) v & ~07777) + argp_error (state, "--stat-mode: MODE should be an octal mode"); + else + opt_stat_mode = v; + break; + + case 'S': + if (arg) + { + v = strtol (arg, &endp, 0); + if (*endp || ! *arg) + argp_error (state, "--fake-self: PID must be an integer"); + else + opt_fake_self = v; + } + else + opt_fake_self = 1; + break; + + case 'k': + v = strtol (arg, &endp, 0); + if (*endp || ! *arg || (signed) opt_kernel_pid < 0) + argp_error (state, "--kernel-process: PID must be a positive integer"); + else + opt_kernel_pid = v; + 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; + } + + v = strtol (arg, &endp, 0); + if (*endp || ! *arg || v < 0) + argp_error (state, "--anonymous-owner: USER should be " + "a user name or a numeric UID."); + else + opt_anon_owner = v; + break; + + case NODEV_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; + + case NOEXEC_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; + + case NOSUID_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +struct argp_option common_options[] = { + { "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)" }, + { "nodev", NODEV_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + { "noexec", NOEXEC_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + { "nosuid", NOSUID_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + {} +}; + +struct argp argp = { + .options = common_options, + .parser = argp_parser, + .doc = "A virtual filesystem emulating the Linux procfs.", + .children = (struct argp_child []) { + { &netfs_std_startup_argp, }, + {} + }, +}; + +static error_t +runtime_argp_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'u': + /* do nothing */ + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +struct argp runtime_argp = { + .options = (struct argp_option []) { + { "update", 'u', NULL, 0, "remount; for procfs this does nothing" }, + {}, + }, + .parser = runtime_argp_parser, +}; + +struct argp netfs_runtime_argp_ = { + .options = common_options, + .parser = argp_parser, + .doc = "A virtual filesystem emulating the Linux procfs.", + .children = (struct argp_child []) { + { &runtime_argp, }, + { &netfs_std_runtime_argp, }, + {} + }, +}; + +/* Used by netfs_set_options to handle runtime option parsing. */ +struct argp *netfs_runtime_argp = &argp; + +/* Return an argz string describing the current options. Fill *ARGZ + with a pointer to newly malloced storage holding the list and *LEN + to the length of that storage. */ +error_t +netfs_append_args (char **argz, size_t *argz_len) +{ + char buf[80]; + error_t err = 0; + +#define FOPT(opt, default, fmt, args...) \ + do { \ + if (! err && opt != default) \ + { \ + snprintf (buf, sizeof buf, fmt, ## args); \ + err = argz_add (argz, argz_len, buf); \ + } \ + } while (0) + + FOPT (opt_clk_tck, OPT_CLK_TCK, + "--clk-tck=%d", opt_clk_tck); + + FOPT (opt_stat_mode, OPT_STAT_MODE, + "--stat-mode=%o", opt_stat_mode); + + FOPT (opt_fake_self, OPT_FAKE_SELF, + "--fake-self=%d", opt_fake_self); + + FOPT (opt_anon_owner, OPT_ANON_OWNER, + "--anonymous-owner=%d", opt_anon_owner); + + FOPT (opt_kernel_pid, OPT_KERNEL_PID, + "--kernel-process=%d", opt_kernel_pid); + +#undef FOPT + + if (! err) + err = netfs_append_std_options (argz, argz_len); + + return err; +} + +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 = OPT_CLK_TCK; + opt_stat_mode = OPT_STAT_MODE; + opt_fake_self = OPT_FAKE_SELF; + opt_kernel_pid = OPT_KERNEL_PID; + opt_anon_owner = OPT_ANON_OWNER; + 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..276c57cc --- /dev/null +++ b/procfs/netfs.c @@ -0,0 +1,461 @@ +/* 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 +#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); + pthread_mutex_unlock (&dir->lock); + + if (! err) + pthread_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) +{ + pthread_spin_unlock (&netfs_node_refcnt_lock); + + procfs_cleanup (np); + free (np); + + pthread_spin_lock (&netfs_node_refcnt_lock); +} + +/* The user may define this function (but should define it together + with netfs_set_translator). For locked node NODE with S_IPTRANS + set in its mode, look up the name of its translator. Store the + name into newly malloced storage, and return it in *ARGZ; set + *ARGZ_LEN to the total length. */ +error_t netfs_get_translator (struct node *np, char **argz, + size_t *argz_len) +{ + return procfs_get_translator (np, argz, argz_len); +} + + +/* 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) +{ + memset (st, 0, sizeof *st); + st->f_type = FSTYPE_PROC; + st->f_fsid = getpid (); + return 0; +} + +/* 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..269a18bb --- /dev/null +++ b/procfs/process.c @@ -0,0 +1,414 @@ +/* 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.; +} + +static const char *args_filename (const char *name) +{ + char *sp = strrchr (name, '/'); + return sp != NULL && *(sp + 1) != '\0' ? sp + 1 : name; +} + +static int args_filename_length (const char *name) +{ + return strchrnul (name, ' ') - name; +} + +/* Actual content generators */ + +static ssize_t +process_file_gc_cmdline (struct proc_stat *ps, char **contents) +{ + *contents = proc_stat_args(ps); + return proc_stat_args_len(ps); +} + +static ssize_t +process_file_gc_environ (struct proc_stat *ps, char **contents) +{ + *contents = proc_stat_env(ps); + return proc_stat_env_len(ps); +} + +static ssize_t +process_file_gc_stat (struct proc_stat *ps, char **contents) +{ + struct procinfo *pi = proc_stat_proc_info (ps); + task_basic_info_t tbi = proc_stat_task_basic_info (ps); + thread_basic_info_t thbi = proc_stat_thread_basic_info (ps); + const char *fn = args_filename (proc_stat_args (ps)); + + vm_address_t start_code = 1; /* 0 would make killall5.c consider it + a kernel process, thus use 1 as + default. */ + vm_address_t end_code = 1; + process_t p; + error_t err = proc_pid2proc (ps->context->server, ps->pid, &p); + if (! err) + { + boolean_t essential = 0; + proc_is_important (p, &essential); + if (essential) + start_code = end_code = 0; /* To make killall5.c consider it a + kernel process that is to be + left alone. */ + else + proc_get_code (p, &start_code, &end_code); + + mach_port_deallocate (mach_task_self (), p); + } + + /* 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), args_filename_length (fn), fn, 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, + start_code, + end_code, + 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); + const char *fn = args_filename (proc_stat_args (ps)); + + return asprintf (contents, + "Name:\t%.*s\n" + "State:\t%s\n" + "Tgid:\t%u\n" + "Pid:\t%u\n" + "PPid:\t%u\n" + "Uid:\t%u\t%u\t%u\t%u\n" + "VmSize:\t%8u kB\n" + "VmPeak:\t%8u kB\n" + "VmRSS:\t%8u kB\n" + "VmHWM:\t%8u kB\n" /* ie. resident peak */ + "Threads:\t%u\n", + args_filename_length (fn), fn, + 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; + } + + *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..cae4a519 --- /dev/null +++ b/procfs/procfs.c @@ -0,0 +1,219 @@ +/* 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 +#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; + + np->nn_stat.st_uid = getuid (); + np->nn_stat.st_gid = getgid (); + + 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 & ~ALLPERMS) | 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); +} + +error_t procfs_get_translator (struct node *np, + char **argz, + size_t *argz_len) +{ + if (np->nn->ops->get_translator) + return np->nn->ops->get_translator (np->nn->hook, argz, argz_len); + + *argz = NULL; + *argz_len = 0; + return 0; +} diff --git a/procfs/procfs.h b/procfs/procfs.h new file mode 100644 index 00000000..d04bbad7 --- /dev/null +++ b/procfs/procfs.h @@ -0,0 +1,99 @@ +/* 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); + + /* Get the passive translator record. */ + error_t (*get_translator) (void *hook, char **argz, size_t *argz_len); +}; + +/* 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); + +/* Get the passive translator record if any. */ +error_t procfs_get_translator (struct node *np, char **argz, size_t *argz_len); + 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..0b131192 --- /dev/null +++ b/procfs/rootdir.c @@ -0,0 +1,661 @@ +/* Hurd /proc filesystem, permanent files of the root directory. + Copyright (C) 2010,13 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 +#include +#include +#include "procfs.h" +#include "procfs_dir.h" +#include "main.h" + +#include "mach_debug_U.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; + struct vm_cache_statistics cache_stats; + default_pager_info_t swap; + error_t err; + + err = vm_statistics (mach_task_self (), &vmstats); + if (err) + return EIO; + + err = vm_cache_statistics (mach_task_self (), &cache_stats); + if (err) + return EIO; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + if (err) + return err; + + err = get_swapinfo (&swap); + if (err) + return err; + + assert (cnt == HOST_BASIC_INFO_COUNT); + *contents_len = asprintf (contents, + "MemTotal: %14lu kB\n" + "MemFree: %14lu kB\n" + "Buffers: %14lu kB\n" + "Cached: %14lu kB\n" + "Active: %14lu kB\n" + "Inactive: %14lu kB\n" + "Mlocked: %14lu kB\n" + "SwapTotal:%14lu kB\n" + "SwapFree: %14lu kB\n" + , + (long unsigned) hbi.memory_size / 1024, + (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, + 0UL, + (long unsigned) cache_stats.cache_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 (void *dir_hook, const void *entry_hook) +{ + 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; +} + +/* The mtab translator to use by default for the "mounts" node. */ +#define MTAB_TRANSLATOR "/hurd/mtab" + +static struct node *rootdir_mounts_node; +static pthread_spinlock_t rootdir_mounts_node_lock = + PTHREAD_SPINLOCK_INITIALIZER; + +static struct node * +rootdir_mounts_make_node (void *dir_hook, const void *entry_hook) +{ + struct node *np, *prev; + + pthread_spin_lock (&rootdir_mounts_node_lock); + np = rootdir_mounts_node; + pthread_spin_unlock (&rootdir_mounts_node_lock); + + if (np != NULL) + { + netfs_nref (np); + return np; + } + + np = procfs_make_node (entry_hook, dir_hook); + if (np == NULL) + return NULL; + + procfs_node_chtype (np, S_IFREG | S_IPTRANS); + procfs_node_chmod (np, 0444); + + pthread_spin_lock (&rootdir_mounts_node_lock); + prev = rootdir_mounts_node; + if (rootdir_mounts_node == NULL) + rootdir_mounts_node = np; + pthread_spin_unlock (&rootdir_mounts_node_lock); + + if (prev != NULL) + { + procfs_cleanup (np); + np = prev; + } + + return np; +} + +static error_t +rootdir_mounts_get_translator (void *hook, char **argz, size_t *argz_len) +{ + static const char const mtab_argz[] = MTAB_TRANSLATOR "\0/"; + + *argz = malloc (sizeof mtab_argz); + if (! *argz) + return ENOMEM; + + memcpy (*argz, mtab_argz, sizeof mtab_argz); + *argz_len = sizeof mtab_argz; + return 0; +} + +static int +rootdir_mounts_exists (void *dir_hook, const void *entry_hook) +{ + static int translator_exists = -1; + if (translator_exists == -1) + translator_exists = access (MTAB_TRANSLATOR, F_OK|X_OK) == 0; + return translator_exists; +} + +static error_t +rootdir_gc_slabinfo (void *hook, char **contents, ssize_t *contents_len) +{ + error_t err; + FILE *m; + const char header[] = + "cache obj slab bufs objs bufs" + " total reclaimable\n" + "name flags size size /slab usage count" + " memory memory\n"; + cache_info_array_t cache_info; + size_t mem_usage, mem_reclaimable, mem_total, mem_total_reclaimable; + mach_msg_type_number_t cache_info_count; + int i; + + cache_info = NULL; + cache_info_count = 0; + + err = host_slab_info (mach_host_self(), &cache_info, &cache_info_count); + if (err) + return err; + + m = open_memstream (contents, contents_len); + if (m == NULL) + { + err = ENOMEM; + goto out; + } + + fprintf (m, "%s", header); + + mem_total = 0; + mem_total_reclaimable = 0; + + for (i = 0; i < cache_info_count; i++) + { + mem_usage = (cache_info[i].nr_slabs * cache_info[i].slab_size) + >> 10; + mem_total += mem_usage; + mem_reclaimable = (cache_info[i].flags & CACHE_FLAGS_NO_RECLAIM) + ? 0 : (cache_info[i].nr_free_slabs + * cache_info[i].slab_size) >> 10; + mem_total_reclaimable += mem_reclaimable; + fprintf (m, + "%-21s %04x %7zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", + cache_info[i].name, cache_info[i].flags, + cache_info[i].obj_size, cache_info[i].slab_size >> 10, + cache_info[i].bufs_per_slab, cache_info[i].nr_objs, + cache_info[i].nr_bufs, mem_usage, mem_reclaimable); + } + + fprintf (m, "total: %zuk, reclaimable: %zuk\n", + mem_total, mem_total_reclaimable); + + fclose (m); + + out: + vm_deallocate (mach_task_self (), + cache_info, cache_info_count * sizeof *cache_info); + return err; +} + +/* 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, + }, + }, + { + .name = "mounts", + .hook = & (struct procfs_node_ops) { + .get_translator = rootdir_mounts_get_translator, + }, + .ops = { + .make_node = rootdir_mounts_make_node, + .exists = rootdir_mounts_exists, + } + }, + { + .name = "slabinfo", + .hook = & (struct procfs_node_ops) { + .get_contents = rootdir_gc_slabinfo, + .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 0b131192..00000000 --- a/rootdir.c +++ /dev/null @@ -1,661 +0,0 @@ -/* Hurd /proc filesystem, permanent files of the root directory. - Copyright (C) 2010,13 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 -#include -#include -#include "procfs.h" -#include "procfs_dir.h" -#include "main.h" - -#include "mach_debug_U.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; - struct vm_cache_statistics cache_stats; - default_pager_info_t swap; - error_t err; - - err = vm_statistics (mach_task_self (), &vmstats); - if (err) - return EIO; - - err = vm_cache_statistics (mach_task_self (), &cache_stats); - if (err) - return EIO; - - cnt = HOST_BASIC_INFO_COUNT; - err = host_info (mach_host_self (), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); - if (err) - return err; - - err = get_swapinfo (&swap); - if (err) - return err; - - assert (cnt == HOST_BASIC_INFO_COUNT); - *contents_len = asprintf (contents, - "MemTotal: %14lu kB\n" - "MemFree: %14lu kB\n" - "Buffers: %14lu kB\n" - "Cached: %14lu kB\n" - "Active: %14lu kB\n" - "Inactive: %14lu kB\n" - "Mlocked: %14lu kB\n" - "SwapTotal:%14lu kB\n" - "SwapFree: %14lu kB\n" - , - (long unsigned) hbi.memory_size / 1024, - (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, - 0UL, - (long unsigned) cache_stats.cache_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 (void *dir_hook, const void *entry_hook) -{ - 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; -} - -/* The mtab translator to use by default for the "mounts" node. */ -#define MTAB_TRANSLATOR "/hurd/mtab" - -static struct node *rootdir_mounts_node; -static pthread_spinlock_t rootdir_mounts_node_lock = - PTHREAD_SPINLOCK_INITIALIZER; - -static struct node * -rootdir_mounts_make_node (void *dir_hook, const void *entry_hook) -{ - struct node *np, *prev; - - pthread_spin_lock (&rootdir_mounts_node_lock); - np = rootdir_mounts_node; - pthread_spin_unlock (&rootdir_mounts_node_lock); - - if (np != NULL) - { - netfs_nref (np); - return np; - } - - np = procfs_make_node (entry_hook, dir_hook); - if (np == NULL) - return NULL; - - procfs_node_chtype (np, S_IFREG | S_IPTRANS); - procfs_node_chmod (np, 0444); - - pthread_spin_lock (&rootdir_mounts_node_lock); - prev = rootdir_mounts_node; - if (rootdir_mounts_node == NULL) - rootdir_mounts_node = np; - pthread_spin_unlock (&rootdir_mounts_node_lock); - - if (prev != NULL) - { - procfs_cleanup (np); - np = prev; - } - - return np; -} - -static error_t -rootdir_mounts_get_translator (void *hook, char **argz, size_t *argz_len) -{ - static const char const mtab_argz[] = MTAB_TRANSLATOR "\0/"; - - *argz = malloc (sizeof mtab_argz); - if (! *argz) - return ENOMEM; - - memcpy (*argz, mtab_argz, sizeof mtab_argz); - *argz_len = sizeof mtab_argz; - return 0; -} - -static int -rootdir_mounts_exists (void *dir_hook, const void *entry_hook) -{ - static int translator_exists = -1; - if (translator_exists == -1) - translator_exists = access (MTAB_TRANSLATOR, F_OK|X_OK) == 0; - return translator_exists; -} - -static error_t -rootdir_gc_slabinfo (void *hook, char **contents, ssize_t *contents_len) -{ - error_t err; - FILE *m; - const char header[] = - "cache obj slab bufs objs bufs" - " total reclaimable\n" - "name flags size size /slab usage count" - " memory memory\n"; - cache_info_array_t cache_info; - size_t mem_usage, mem_reclaimable, mem_total, mem_total_reclaimable; - mach_msg_type_number_t cache_info_count; - int i; - - cache_info = NULL; - cache_info_count = 0; - - err = host_slab_info (mach_host_self(), &cache_info, &cache_info_count); - if (err) - return err; - - m = open_memstream (contents, contents_len); - if (m == NULL) - { - err = ENOMEM; - goto out; - } - - fprintf (m, "%s", header); - - mem_total = 0; - mem_total_reclaimable = 0; - - for (i = 0; i < cache_info_count; i++) - { - mem_usage = (cache_info[i].nr_slabs * cache_info[i].slab_size) - >> 10; - mem_total += mem_usage; - mem_reclaimable = (cache_info[i].flags & CACHE_FLAGS_NO_RECLAIM) - ? 0 : (cache_info[i].nr_free_slabs - * cache_info[i].slab_size) >> 10; - mem_total_reclaimable += mem_reclaimable; - fprintf (m, - "%-21s %04x %7zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", - cache_info[i].name, cache_info[i].flags, - cache_info[i].obj_size, cache_info[i].slab_size >> 10, - cache_info[i].bufs_per_slab, cache_info[i].nr_objs, - cache_info[i].nr_bufs, mem_usage, mem_reclaimable); - } - - fprintf (m, "total: %zuk, reclaimable: %zuk\n", - mem_total, mem_total_reclaimable); - - fclose (m); - - out: - vm_deallocate (mach_task_self (), - cache_info, cache_info_count * sizeof *cache_info); - return err; -} - -/* 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, - }, - }, - { - .name = "mounts", - .hook = & (struct procfs_node_ops) { - .get_translator = rootdir_mounts_get_translator, - }, - .ops = { - .make_node = rootdir_mounts_make_node, - .exists = rootdir_mounts_exists, - } - }, - { - .name = "slabinfo", - .hook = & (struct procfs_node_ops) { - .get_contents = rootdir_gc_slabinfo, - .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