diff --git a/ChangeLog b/ChangeLog
index 694de79c1b..59dd5cad7c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6040,3 +6040,5 @@
apps/examples/i2schar test (2011-11-11).
* arch/arm/src/sama5/sam_ssc.c: I2S loopback test finally works
(2013-11-11).
+ * fs/procfs: Add a little, primitive procfs file system. (2013-11-13).
+
diff --git a/Documentation/README.html b/Documentation/README.html
index 1cb767d6cd..e2623d272e 100644
--- a/Documentation/README.html
+++ b/Documentation/README.html
@@ -233,8 +233,10 @@
| |- fs/
| | |- mmap/
| | | `- README.txt
- | | `- nxffs/
- | | `- README.txt
+ | | |- nxffs/
+ | | | `- README.txt
+ | | `- procfs/
+ | | `- README.txt
| |- graphics/
| | `- README.txt
| |- lib/
diff --git a/README.txt b/README.txt
index bde7d7e5f8..d9df9a3a3f 100644
--- a/README.txt
+++ b/README.txt
@@ -1161,7 +1161,9 @@ nuttx
|- fs/
| |- mmap/
| | `- README.txt
- | `- nxffs/
+ | |- nxffs/
+ | | `- README.txt
+ | `- procfs/
| `- README.txt
|- graphics/
| `- README.txt
diff --git a/fs/Kconfig b/fs/Kconfig
index 798a610f45..5570b26b42 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -16,6 +16,7 @@ source fs/nxffs/Kconfig
source fs/romfs/Kconfig
source fs/smartfs/Kconfig
source fs/binfs/Kconfig
+source fs/procfs/Kconfig
comment "System Logging"
diff --git a/fs/Makefile b/fs/Makefile
index deb98bd449..7e90c4f7e9 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -111,6 +111,7 @@ include nxffs/Make.defs
include nfs/Make.defs
include smartfs/Make.defs
include binfs/Make.defs
+include procfs/Make.defs
endif
endif
@@ -122,7 +123,7 @@ OBJS = $(AOBJS) $(COBJS)
BIN = libfs$(LIBEXT)
-SUBDIRS = mmap fat romfs nxffs nfs binfs
+SUBDIRS = mmap fat romfs nxffs nfs binfs procfs
all: $(BIN)
diff --git a/fs/fs_mount.c b/fs/fs_mount.c
index 2feaec3152..6fff3e6896 100644
--- a/fs/fs_mount.c
+++ b/fs/fs_mount.c
@@ -63,7 +63,7 @@
/* Configuration ************************************************************/
/* In the canonical case, a file system is bound to a block driver. However,
* some less typical cases a block driver is not required. Examples are
- * pseudo file systems (like BINFS) and MTD file systems (like NXFFS).
+ * pseudo file systems (like BINFS or PROCFS) and MTD file systems (like NXFFS).
*
* These file systems all require block drivers:
*/
@@ -74,7 +74,8 @@
/* These file systems do not require block drivers */
-#if defined(CONFIG_FS_NXFFS) || defined(CONFIG_FS_BINFS) || defined(CONFIG_NFS)
+#if defined(CONFIG_FS_NXFFS) || defined(CONFIG_FS_BINFS) || \
+ defined(CONFIG_FS_PROCFS) || defined(CONFIG_NFS)
# define NONBDFS_SUPPORT
#endif
@@ -128,6 +129,9 @@ extern const struct mountpt_operations nfs_operations;
#ifdef CONFIG_FS_BINFS
extern const struct mountpt_operations binfs_operations;
#endif
+#ifdef CONFIG_FS_PROCFS
+extern const struct mountpt_operations procfs_operations;
+#endif
static const struct fsmap_t g_nonbdfsmap[] =
{
@@ -140,7 +144,10 @@ static const struct fsmap_t g_nonbdfsmap[] =
#ifdef CONFIG_FS_BINFS
{ "binfs", &binfs_operations },
#endif
- { NULL, NULL },
+#ifdef CONFIG_FS_PROCFS
+ { "procfs", &procfs_operations },
+#endif
+ { NULL, NULL },
};
#endif /* NONBDFS_SUPPORT */
diff --git a/fs/procfs/Kconfig b/fs/procfs/Kconfig
new file mode 100644
index 0000000000..15e4e46da8
--- /dev/null
+++ b/fs/procfs/Kconfig
@@ -0,0 +1,13 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config FS_PROCFS
+ bool "PROCFS File System"
+ default n
+ ---help---
+ The PROCFS file system is provides access to task status through the
+ NuttX file system. The PROCFS may, for example, be mount at /proc.
+ Then information about all of the currently active tasks and threads
+ will be available in proc/.
\ No newline at end of file
diff --git a/fs/procfs/Make.defs b/fs/procfs/Make.defs
new file mode 100644
index 0000000000..8eae9027c4
--- /dev/null
+++ b/fs/procfs/Make.defs
@@ -0,0 +1,48 @@
+############################################################################
+# fs/procfs/Make.defs
+#
+# Copyright (C) 2013 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name Nuttx nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+ifeq ($(CONFIG_FS_PROCFS),y)
+# Files required for procfs file system support
+
+ASRCS +=
+CSRCS += fs_procfs.c
+
+# Include procfs build support
+
+DEPPATH += --dep-path procfs
+VPATH += :procfs
+CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)fs$(DELIM)procfs}
+
+endif
diff --git a/fs/procfs/README.txt b/fs/procfs/README.txt
new file mode 100755
index 0000000000..d4fa55211b
--- /dev/null
+++ b/fs/procfs/README.txt
@@ -0,0 +1,12 @@
+fs/procfs README
+================
+
+ This is a tiny procfs file system that allows read-only access to a few
+ attributes of a task or thread. This tiny procfs fs file system can be
+ built into the system by enabling:
+
+ CONFIG_FS_PROCFS=y
+
+ It can then be mounted from the NSH command like like:
+
+ nsh> mount -t procfs /proc
diff --git a/fs/procfs/fs_procfs.c b/fs/procfs/fs_procfs.c
new file mode 100644
index 0000000000..599052603f
--- /dev/null
+++ b/fs/procfs/fs_procfs.c
@@ -0,0 +1,1203 @@
+/****************************************************************************
+ * fs/procfs/fs_procfs.c
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define STATUS_LINELEN 32
+
+#ifndef MIN
+# define MIN(a,b) ((a < b) ? a : b)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* This enumeration identifies all of the thread attributes that can be
+ * accessed via the procfs file system.
+ */
+
+enum procfs_attr_e
+{
+ PROCFS_STATUS = 0, /* Task/thread status */
+ PROCFS_CMDLINE, /* Command line */
+};
+#define PROCFS_NATTRS 2
+
+/* This structure describes one open "file" */
+
+struct procfs_file_s
+{
+ pid_t pid; /* Task/thread ID */
+ uint8_t attr; /* See enum procfs_attr_e */
+ char line[STATUS_LINELEN]; /* Pre-allocated buffer for formatted lines */
+};
+
+/* The generic proc/ pseudo directory structure */
+
+struct procfs_level_s
+{
+ uint8_t level; /* Directory level. Currently 0 or 1 */
+ uint16_t index; /* Index to the next directory entry */
+ uint16_t nentries; /* Number of directory entries */
+};
+
+/* Level 0 is the directory of active tasks */
+
+struct procfs_level0_s
+{
+ uint8_t level; /* Directory level. Currently 0 or 1 */
+ uint16_t index; /* Index to the next directory entry */
+ uint16_t nentries; /* Number of directory entries */
+
+ pid_t pid[CONFIG_MAX_TASKS]; /* Snapshot of all active task IDs */
+};
+
+/* Level 1 is the directory of task attributes */
+
+struct procfs_level1_s
+{
+ uint8_t level; /* Directory level. Currently 0 or 1 */
+ uint16_t index; /* Index to the next directory entry */
+ uint16_t nentries; /* Number of directory entries */
+
+ pid_t pid; /* ID of task for attributes */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Helpers */
+
+static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg);
+static int procfs_findattr(FAR const char *attr);
+static size_t procfs_addline(FAR struct procfs_file_s *attr,
+ FAR char *buffer, size_t buflen, size_t linesize,
+ off_t *offset);
+static ssize_t procfs_status(FAR struct procfs_file_s *attr,
+ FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
+ off_t offset);
+static ssize_t procfs_cmdline(FAR struct procfs_file_s *attr,
+ FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
+ off_t offset);
+
+/* File system methods */
+
+static int procfs_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode);
+static int procfs_close(FAR struct file *filep);
+static ssize_t procfs_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+static int procfs_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg);
+
+static int procfs_dup(FAR const struct file *oldp,
+ FAR struct file *newp);
+
+static int procfs_opendir(FAR struct inode *mountpt, const char *relpath,
+ FAR struct fs_dirent_s *dir);
+static int procfs_closedir(FAR struct inode *mountpt,
+ FAR struct fs_dirent_s *dir);
+static int procfs_readdir(FAR struct inode *mountpt,
+ FAR struct fs_dirent_s *dir);
+static int procfs_rewinddir(FAR struct inode *mountpt,
+ FAR struct fs_dirent_s *dir);
+
+static int procfs_bind(FAR struct inode *blkdriver,
+ FAR const void *data, FAR void **handle);
+static int procfs_unbind(FAR void *handle, FAR struct inode **blkdriver);
+static int procfs_statfs(FAR struct inode *mountpt,
+ FAR struct statfs *buf);
+
+static int procfs_stat(FAR struct inode *mountpt,
+ FAR const char *relpath, FAR struct stat *buf);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* See fs_mount.c -- this structure is explicitly externed there.
+ * We use the old-fashioned kind of initializers so that this will compile
+ * with any compiler.
+ */
+
+const struct mountpt_operations procfs_operations =
+{
+ procfs_open, /* open */
+ procfs_close, /* close */
+ procfs_read, /* read */
+ NULL, /* write */
+ NULL, /* seek */
+ procfs_ioctl, /* ioctl */
+
+ NULL, /* sync */
+ procfs_dup, /* dup */
+
+ procfs_opendir, /* opendir */
+ procfs_closedir, /* closedir */
+ procfs_readdir, /* readdir */
+ procfs_rewinddir, /* rewinddir */
+
+ procfs_bind, /* bind */
+ procfs_unbind, /* unbind */
+ procfs_statfs, /* statfs */
+
+ NULL, /* unlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* rename */
+ procfs_stat /* stat */
+};
+
+/* This is the list of all attribute strings. Indexing is with the same
+ * values as enum procfs_attr_e.
+ */
+
+static const char *g_attrstrings[PROCFS_NATTRS] =
+{
+ "status",
+ "cmdline"
+};
+
+static const char *g_statenames[] =
+{
+ "Invalid",
+ "Pending unlock",
+ "Ready",
+ "Running",
+ "Inactive",
+ "Semaphore wait",
+#ifndef CONFIG_DISABLE_MQUEUE
+ "Signal wait",
+#endif
+#ifndef CONFIG_DISABLE_MQUEUE
+ "MQ not empty wait",
+ "MQ no full wait"
+#endif
+};
+
+static const char *g_ttypenames[4] =
+{
+ "Task",
+ "pthread",
+ "Kernel thread",
+ "--?--"
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: procfs_enum
+ ****************************************************************************/
+
+static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg)
+{
+ FAR struct procfs_level0_s *dir = (FAR struct procfs_level0_s *)arg;
+ int index;
+
+ DEBUGASSERT(dir);
+
+ /* Add the PID to the list */
+
+ index = dir->nentries;
+ DEBUGASSERT(index < CONFIG_MAX_TASKS);
+
+ dir->pid[index] = tcb->pid;
+ dir->nentries = index + 1;
+}
+
+/****************************************************************************
+ * Name: procfs_findattr
+ ****************************************************************************/
+
+static int procfs_findattr(FAR const char *attr)
+{
+ int i;
+
+ /* Search every string in g_attrstrings or until a match is found */
+
+ for (i = 0; i < PROCFS_NATTRS; i++)
+ {
+ if (strcmp(g_attrstrings[i], attr) == 0)
+ {
+ return i;
+ }
+ }
+
+ /* Not found */
+
+ return -ENOENT;
+}
+
+/****************************************************************************
+ * Name: procfs_addline
+ ****************************************************************************/
+
+static size_t procfs_addline(FAR struct procfs_file_s *attr,
+ FAR char *buffer, size_t buflen,
+ size_t linesize, off_t *offset)
+{
+ size_t copysize;
+ size_t lnoffset;
+
+ /* Will this line take us past the offset? */
+
+ lnoffset = *offset;
+ if (linesize < lnoffset)
+ {
+ /* No... decrement the offset and return without doing anything */
+
+ *offset -= linesize;
+ return 0;
+ }
+
+ /* Handle the remaining offset */
+
+ linesize -= lnoffset;
+ buffer += lnoffset;
+ *offset = 0;
+
+ /* Copy the line into the user buffer */
+
+ copysize = MIN(linesize, buflen);
+ memcpy(buffer, &attr->line[lnoffset], copysize);
+ return copysize;
+}
+
+/****************************************************************************
+ * Name: procfs_status
+ ****************************************************************************/
+
+static ssize_t procfs_status(FAR struct procfs_file_s *attr,
+ FAR struct tcb_s *tcb, FAR char *buffer,
+ size_t buflen, off_t offset)
+{
+ FAR const char *name;
+ size_t remaining;
+ size_t linesize;
+ size_t copysize;
+ size_t totalsize;
+
+ remaining = buflen;
+ totalsize = 0;
+
+ /* Show the task name */
+
+#if CONFIG_TASK_NAME_SIZE > 0
+ name = tcb->name;
+#else
+ name = "";
+#endif
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n",
+ "Name:", name);
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the thread type */
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Type:",
+ g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK) >>
+ TCB_FLAG_TTYPE_SHIFT]);
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the thread state */
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "State:",
+ g_statenames[tcb->task_state]);
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the thread priority */
+
+#ifdef CONFIG_PRIORITY_INHERITANCE
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d (%d)\n", "Priority:",
+ tcb->sched_priority, tcb->base_priority);
+#else
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d\n", "Priority:",
+ tcb->sched_priority);
+#endif
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the scheduler */
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Scheduler:",
+ tcb->flags & TCB_FLAG_ROUND_ROBIN ? "SCHED_RR" : "SCHED_FIFO");
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the signal mast */
+
+#ifndef CONFIG_DISABLE_SIGNALS
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%08x\n", "SigMask:",
+ tcb->sigprocmask);
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+#endif
+
+ return totalsize;
+}
+
+/****************************************************************************
+ * Name: procfs_cmdline
+ ****************************************************************************/
+
+static ssize_t procfs_cmdline(FAR struct procfs_file_s *attr,
+ FAR struct tcb_s *tcb, FAR char *buffer,
+ size_t buflen, off_t offset)
+{
+ FAR struct task_tcb_s *ttcb;
+ FAR const char *name;
+ FAR char **argv;
+ size_t remaining;
+ size_t linesize;
+ size_t copysize;
+ size_t totalsize;
+
+ remaining = buflen;
+ totalsize = 0;
+
+ /* Show the task name */
+
+#if CONFIG_TASK_NAME_SIZE > 0
+ name = tcb->name;
+#else
+ name = "";
+#endif
+ linesize = strlen(name);
+ memcpy(attr->line, name, linesize);
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+#ifndef CONFIG_DISABLE_PTHREAD
+ /* Show the pthread argument */
+
+ if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
+ {
+ FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)tcb;
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, " 0x%p\n", ptcb->arg);
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ return totalsize;
+ }
+#endif
+
+ /* Show the task argument list (skipping over the name) */
+
+ ttcb = (FAR struct task_tcb_s *)tcb;
+
+ for (argv = ttcb->argv + 1; *argv; argv++)
+ {
+ linesize = snprintf(attr->line, STATUS_LINELEN, " %s", *argv);
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+ }
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, "\n");
+ copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ return totalsize;
+}
+
+/****************************************************************************
+ * Name: procfs_open
+ ****************************************************************************/
+
+static int procfs_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode)
+{
+ FAR struct procfs_file_s *attr;
+ FAR struct tcb_s *tcb;
+ FAR char *ptr;
+ irqstate_t flags;
+ unsigned long tmp;
+ pid_t pid;
+ int attrndx;
+
+ fvdbg("Open '%s'\n", relpath);
+
+ /* PROCFS is read-only. Any attempt to open with any kind of write
+ * access is not permitted.
+ *
+ * REVISIT: Write-able proc files could be quite useful.
+ */
+
+ if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0)
+ {
+ fdbg("ERROR: Only O_RDONLY supported\n");
+ return -EACCES;
+ }
+
+ /* The first segment of the relative path should be a task/thread ID */
+
+ ptr = NULL;
+ tmp = strtoul(relpath, &ptr, 10);
+
+ if (!ptr || *ptr != '/')
+ {
+ fdbg("ERROR: Invalid path \"%s\"\n", relpath);
+ return -ENOENT;
+ }
+
+ /* Skip over the slash */
+
+ ptr++;
+
+ /* A valid PID would be in the range of 0-32767 (0 is reserved for the
+ * IDLE thread).
+ */
+
+ if (tmp >= 32768)
+ {
+ fdbg("ERROR: Invalid PID %ld\n", tmp);
+ return -ENOENT;
+ }
+
+ /* Now verify that a task with this task/thread ID exists */
+
+ pid = (pid_t)tmp;
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
+ return -ENOENT;
+ }
+
+ /* The second segment of the relpath should be a well known attribute of
+ * the task/thread.
+ */
+
+ attrndx = procfs_findattr(ptr);
+ if (attrndx < 0)
+ {
+ fdbg("ERROR: Invalid attribute %s\n", ptr);
+ return -ENOENT;
+ }
+
+ /* Allocate a container to hold the task and attribute selection */
+
+ attr = (FAR struct procfs_file_s *)kzalloc(sizeof(struct procfs_file_s));
+ if (!attr)
+ {
+ fdbg("ERROR: Failed to allocate file attributes\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the file attributes */
+
+ attr->pid = pid;
+ attr->attr = attrndx;
+
+ /* Save the index as the open-specific state in filep->f_priv */
+
+ filep->f_priv = (FAR void *)attr;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_close
+ ****************************************************************************/
+
+static int procfs_close(FAR struct file *filep)
+{
+ FAR struct procfs_file_s *attr;
+
+ /* Recover our private data from the struct file instance */
+
+ attr = (FAR struct procfs_file_s *)filep->f_priv;
+ DEBUGASSERT(attr);
+
+ /* Release the file attributes structure */
+
+ kfree(attr);
+ filep->f_priv = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_read
+ ****************************************************************************/
+
+static ssize_t procfs_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
+{
+ FAR struct procfs_file_s *attr;
+ FAR struct tcb_s *tcb;
+ irqstate_t flags;
+ ssize_t ret;
+
+ fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);
+
+ /* Recover our private data from the struct file instance */
+
+ attr = (FAR struct procfs_file_s *)filep->f_priv;
+ DEBUGASSERT(attr);
+
+ /* Verify that the thread is still valid */
+
+ flags = irqsave();
+ tcb = sched_gettcb(attr->pid);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is not valid\n", (int)attr->pid);
+ irqrestore(flags);
+ return -ENODEV;
+ }
+
+ /* Provide the requested data */
+
+ switch (attr->attr)
+ {
+ default:
+ case PROCFS_STATUS: /* Task/thread status */
+ ret = procfs_status(attr, tcb, buffer, buflen, filep->f_pos);
+ break;
+
+ case PROCFS_CMDLINE: /* Command line */
+ ret = procfs_cmdline(attr, tcb, buffer, buflen, filep->f_pos);
+ break;
+ }
+
+ irqrestore(flags);
+
+ /* Update the file offset */
+
+ if (ret > 0)
+ {
+ filep->f_pos += ret;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: procfs_ioctl
+ ****************************************************************************/
+
+static int procfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ fvdbg("cmd: %d arg: %08lx\n", cmd, arg);
+
+ /* No IOCTL commands supported */
+
+ return -ENOTTY;
+}
+
+/****************************************************************************
+ * Name: procfs_dup
+ *
+ * Description:
+ * Duplicate open file data in the new file structure.
+ *
+ ****************************************************************************/
+
+static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp)
+{
+ FAR struct procfs_file_s *oldattr;
+ FAR struct procfs_file_s *newattr;
+
+ fvdbg("Dup %p->%p\n", oldp, newp);
+
+ /* Recover our private data from the old struct file instance */
+
+ oldattr = (FAR struct procfs_file_s *)oldp->f_priv;
+ DEBUGASSERT(oldattr);
+
+ /* Allocate a new container to hold the task and attribute selection */
+
+ newattr = (FAR struct procfs_file_s *)kzalloc(sizeof(struct procfs_file_s));
+ if (!newattr)
+ {
+ fdbg("ERROR: Failed to allocate file attributes\n");
+ return -ENOMEM;
+ }
+
+ /* The copy the file attribtes from the old attributes to the new */
+
+ memcpy(newattr, oldattr, sizeof(struct procfs_file_s));
+
+ /* Save the new attributes in the new file structure */
+
+ newp->f_priv = (FAR void *)newattr;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_opendir
+ *
+ * Description:
+ * Open a directory for read access
+ *
+ ****************************************************************************/
+
+static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
+ FAR struct fs_dirent_s *dir)
+{
+ FAR struct tcb_s *tcb;
+ FAR void *priv = NULL;
+ irqstate_t flags;
+
+ fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
+ DEBUGASSERT(mountpt && relpath && dir && !dir->u.procfs);
+
+ /* The relative must be either:
+ *
+ * "" - The top level directory of task/thread IDs
+ * "" - The sub-directory of task/thread attributes
+ */
+
+ if (!relpath || relpath[0] == '\0')
+ {
+ FAR struct procfs_level0_s *level0;
+
+ /* The path refers to the top level directory. Allocate the level0
+ * dirent structure.
+ */
+
+ level0 = (FAR struct procfs_level0_s *)
+ kzalloc(sizeof(struct procfs_level0_s));
+
+ if (!level0)
+ {
+ fdbg("ERROR: Failed to allocate the level0 directory structure\n");
+ return -ENOMEM;
+ }
+
+ /* Take a snapshot of all currently active tasks. Any new tasks
+ * added between the opendir() and closedir() call will not be
+ * visible.
+ *
+ * NOTE that interrupts must be disabled throughout the traversal.
+ */
+
+ flags = irqsave();
+ sched_foreach(procfs_enum, level0);
+ irqrestore(flags);
+
+ priv = (FAR void *)level0;
+ }
+ else
+ {
+ FAR struct procfs_level1_s *level1;
+ unsigned long tmp;
+ FAR char *ptr;
+ pid_t pid;
+
+ /* Otherwise, the relative path should be a valid task/thread ID */
+
+ ptr = NULL;
+ tmp = strtoul(relpath, &ptr, 10);
+
+ if (!ptr || (*ptr != '\0' && strcmp(ptr, "/") != 0))
+ {
+ /* strtoul failed or there is something in the path after the pid */
+
+ fdbg("ERROR: Invalid path \"%s\"\n", relpath);
+ return -ENOENT;
+ }
+
+ /* A valid PID would be in the range of 0-32767 (0 is reserved for the
+ * IDLE thread).
+ */
+
+ if (tmp >= 32768)
+ {
+ fdbg("ERROR: Invalid PID %ld\n", tmp);
+ return -ENOENT;
+ }
+
+ /* Now verify that a task with this task/thread ID exists */
+
+ pid = (pid_t)tmp;
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is not valid\n", (int)pid);
+ return -ENOENT;
+ }
+
+ /* Was the the final element of the path? */
+
+ if (*ptr != '\0' && strcmp(ptr, "/") != 0)
+ {
+ /* There is something in the path after the pid */
+
+ fdbg("ERROR: Invalid path \"%s\"\n", relpath);
+ return -ENOENT;
+ }
+
+ /* The path refers to the 1st level sbdirectory. Allocate the level1
+ * dirent structure.
+ */
+
+ level1 = (FAR struct procfs_level1_s *)
+ kzalloc(sizeof(struct procfs_level1_s));
+
+ if (!level1)
+ {
+ fdbg("ERROR: Failed to allocate the level1 directory structure\n");
+ return -ENOMEM;
+ }
+
+ level1->level = 1;
+ level1->nentries = PROCFS_NATTRS;
+ level1->pid = pid;
+
+ priv = (FAR void *)level1;
+ }
+
+ dir->u.procfs = priv;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_closedir
+ *
+ * Description: Close the directory listing
+ *
+ ****************************************************************************/
+
+static int procfs_closedir(FAR struct inode *mountpt,
+ FAR struct fs_dirent_s *dir)
+{
+ FAR struct procfs_level_s *priv;
+
+ DEBUGASSERT(mountpt && dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ if (priv)
+ {
+ kfree(priv);
+ }
+
+ dir->u.procfs = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_readdir
+ *
+ * Description: Read the next directory entry
+ *
+ ****************************************************************************/
+
+static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ FAR struct procfs_level_s *priv;
+ FAR struct tcb_s *tcb;
+ unsigned int index;
+ irqstate_t flags;
+ pid_t pid;
+ int ret;
+
+ DEBUGASSERT(mountpt && dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ /* Have we reached the end of the directory */
+
+ index = priv->index;
+ if (index >= priv->nentries)
+ {
+ /* We signal the end of the directory by returning the special
+ * error -ENOENT
+ */
+
+ fvdbg("Entry %d: End of directory\n", index);
+ ret = -ENOENT;
+ }
+
+ /* Are tranversing a first level directory of task IDs */
+
+ else if (priv->level == 0)
+ {
+ FAR struct procfs_level0_s *level0 = (FAR struct procfs_level0_s *)priv;
+
+ /* Verify that the pid still refers to an active task/thread */
+
+ pid = level0->pid[index];
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
+ return -ENOENT;
+ }
+
+ /* Save the filename=pid and file type=directory */
+
+ dir->fd_dir.d_type = DTYPE_DIRECTORY;
+ snprintf(dir->fd_dir.d_name, NAME_MAX+1, "%d", (int)pid);
+
+ /* Set up the next directory entry offset. NOTE that we could use the
+ * standard f_pos instead of our own private index.
+ */
+
+ level0->index = index + 1;
+ ret = OK;
+ }
+
+ /* No.. We must be tranversing a subdirectory of task attributes */
+
+ else
+ {
+ FAR struct procfs_level1_s *level1 = (FAR struct procfs_level1_s *)priv;
+
+ DEBUGASSERT(priv->level == 1);
+
+ /* Verify that the pid still refers to an active task/thread */
+
+ pid = level1->pid;
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
+ return -ENOENT;
+ }
+
+ /* Save the filename=pid and file type=directory */
+
+ dir->fd_dir.d_type = DTYPE_FILE;
+ strncpy(dir->fd_dir.d_name, g_attrstrings[index], NAME_MAX+1);
+
+ /* Set up the next directory entry offset. NOTE that we could use the
+ * standard f_pos instead of our own private index.
+ */
+
+ level1->index = index + 1;
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: procfs_rewindir
+ *
+ * Description: Reset directory read to the first entry
+ *
+ ****************************************************************************/
+
+static int procfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ FAR struct procfs_level_s *priv;
+
+ DEBUGASSERT(mountpt && dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ priv->index = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_bind
+ *
+ * Description: This implements a portion of the mount operation. This
+ * function allocates and initializes the mountpoint private data and
+ * binds the blockdriver inode to the filesystem private data. The final
+ * binding of the private data (containing the blockdriver) to the
+ * mountpoint is performed by mount().
+ *
+ ****************************************************************************/
+
+static int procfs_bind(FAR struct inode *blkdriver, const void *data,
+ void **handle)
+{
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_unbind
+ *
+ * Description: This implements the filesystem portion of the umount
+ * operation.
+ *
+ ****************************************************************************/
+
+static int procfs_unbind(void *handle, FAR struct inode **blkdriver)
+{
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_statfs
+ *
+ * Description: Return filesystem statistics
+ *
+ ****************************************************************************/
+
+static int procfs_statfs(struct inode *mountpt, struct statfs *buf)
+{
+ /* Fill in the statfs info */
+
+ memset(buf, 0, sizeof(struct statfs));
+ buf->f_type = PROCFS_MAGIC;
+ buf->f_bsize = 0;
+ buf->f_blocks = 0;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_namelen = NAME_MAX;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: procfs_stat
+ *
+ * Description: Return information about a file or directory
+ *
+ ****************************************************************************/
+
+static int procfs_stat(struct inode *mountpt, const char *relpath,
+ struct stat *buf)
+{
+ FAR struct tcb_s *tcb;
+ unsigned long tmp;
+ FAR char *ptr;
+ irqstate_t flags;
+ pid_t pid;
+ int ret;
+
+ /* Three path forms are accepted:
+ *
+ * "" - The relative path refers to the top level directory
+ * "" - If refers to a currently active task/thread, then it
+ * is a directory
+ * "/" - If is a recognized attribute then, then it
+ * is a file.
+ */
+
+ if (!relpath || relpath[0] == '\0')
+ {
+ /* The path refers to the top level directory */
+ /* It's a read-only directory */
+
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+
+ }
+ else
+ {
+ /* Otherwise, the first segment of the relative path should be a valid
+ * task/thread ID
+ */
+
+ ptr = NULL;
+ tmp = strtoul(relpath, &ptr, 10);
+
+ if (!ptr)
+ {
+ fdbg("ERROR: Invalid path \"%s\"\n", relpath);
+ return -ENOENT;
+ }
+
+ /* A valid PID would be in the range of 0-32767 (0 is reserved for the
+ * IDLE thread).
+ */
+
+ if (tmp >= 32768)
+ {
+ fdbg("ERROR: Invalid PID %ld\n", tmp);
+ return -ENOENT;
+ }
+
+ /* Now verify that a task with this task/thread ID exists */
+
+ pid = (pid_t)tmp;
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
+ return -ENOENT;
+ }
+
+ /* Was the the final element of the path? */
+
+ if (*ptr == '\0' || strcmp(ptr, "/") == 0)
+ {
+ /* Yes ... It's a read-only directory */
+
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+ }
+ else
+ {
+ /* Otherwise, the second segment of the relpath should be a well
+ * known attribute of the task/thread.
+ */
+
+ ret = procfs_findattr(ptr);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Invalid attribute %s\n", ptr);
+ return -ENOENT;
+ }
+
+ /* It's a read-only file name */
+
+ buf->st_mode = S_IFREG|S_IROTH|S_IRGRP|S_IRUSR;
+ }
+ }
+
+ /* File/directory size, access block size */
+
+ buf->st_size = 0;
+ buf->st_blksize = 0;
+ buf->st_blocks = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */
diff --git a/include/nuttx/fs/dirent.h b/include/nuttx/fs/dirent.h
index 1235790ccb..8c21fbe715 100644
--- a/include/nuttx/fs/dirent.h
+++ b/include/nuttx/fs/dirent.h
@@ -196,6 +196,9 @@ struct fs_dirent_s
#ifdef CONFIG_FS_BINFS
struct fs_binfsdir_s binfs;
#endif
+#ifdef CONFIG_FS_PROCFS
+ FAR void *procfs;
+#endif
#ifdef CONFIG_FS_NXFFS
struct fs_nxffsdir_s nxffs;
#endif
diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index e14ff7c582..ff19e30ef6 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -624,6 +624,10 @@ FAR struct tcb_s *sched_self(void);
void sched_foreach(sched_foreach_t handler, FAR void *arg);
+/* Give a task ID, look up the corresponding TCB */
+
+FAR struct tcb_s *sched_gettcb(pid_t pid);
+
/* File system helpers **********************************************************/
/* These functions all extract lists from the group structure assocated with the
* currently executing task.
diff --git a/include/sys/statfs.h b/include/sys/statfs.h
index 9b0b52fa84..b56756a720 100644
--- a/include/sys/statfs.h
+++ b/include/sys/statfs.h
@@ -99,6 +99,7 @@
/* NuttX specific file-systems */
#define BINFS_MAGIC 0x4242
+#define PROCFS_MAGIC 0x434f5250
#define NXFFS_MAGIC 0x4747
#define SMARTFS_MAGIC 0x54524D53
diff --git a/sched/os_internal.h b/sched/os_internal.h
index 06cb3cf010..ed5014cdcc 100644
--- a/sched/os_internal.h
+++ b/sched/os_internal.h
@@ -258,7 +258,6 @@ int sched_reprioritize(FAR struct tcb_s *tcb, int sched_priority);
#else
# define sched_reprioritize(tcb,sched_priority) sched_setpriority(tcb,sched_priority)
#endif
-FAR struct tcb_s *sched_gettcb(pid_t pid);
bool sched_verifytcb(FAR struct tcb_s *tcb);
int sched_releasetcb(FAR struct tcb_s *tcb, uint8_t ttype);