diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-12-12 09:21:55 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-12-12 09:21:55 -0600 |
commit | 147415a647658c5d472338d745c5b68942eb0b2c (patch) | |
tree | 9a94ea4d00f35742480ecac85db9ef399bf3be0c /nuttx/fs/procfs/fs_procfsproc.c | |
parent | 2977ad09c854e056b26f4ae1d86e751a7acead60 (diff) | |
download | px4-nuttx-147415a647658c5d472338d745c5b68942eb0b2c.tar.gz px4-nuttx-147415a647658c5d472338d745c5b68942eb0b2c.tar.bz2 px4-nuttx-147415a647658c5d472338d745c5b68942eb0b2c.zip |
procfs/: Extenstive architectural changes and enhancements by Ken Pettit
Diffstat (limited to 'nuttx/fs/procfs/fs_procfsproc.c')
-rw-r--r-- | nuttx/fs/procfs/fs_procfsproc.c | 1003 |
1 files changed, 1003 insertions, 0 deletions
diff --git a/nuttx/fs/procfs/fs_procfsproc.c b/nuttx/fs/procfs/fs_procfsproc.c new file mode 100644 index 000000000..d646c6758 --- /dev/null +++ b/nuttx/fs/procfs/fs_procfsproc.c @@ -0,0 +1,1003 @@ +/**************************************************************************** + * fs/procfs/fs_procfsproc.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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 <nuttx/config.h> + +#include <sys/types.h> +#include <sys/statfs.h> +#include <sys/stat.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/sched.h> +#include <nuttx/kmalloc.h> +#include <nuttx/fs/fs.h> +#include <nuttx/fs/procfs.h> +#include <nuttx/fs/dirent.h> + +#include <arch/irq.h> + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) +#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS + +/**************************************************************************** + * 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 process_attr_e +{ + PROCFS_STATUS = 0, /* Task/thread status */ + PROCFS_CMDLINE, /* Command line */ +}; +#define PROCFS_NATTRS 2 + +/* This structure describes one open "file" */ + +struct process_file_s +{ + struct procfs_file_s base; /* Base open file structure */ + uint8_t type; /* See enum process_type_e */ + pid_t pid; /* Task/thread ID */ + uint8_t attr; /* See enum process_attr_e */ + char line[STATUS_LINELEN]; /* Pre-allocated buffer for formatted lines */ +}; + +/* Level 0 is the directory of active tasks */ + +struct process_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 process_level1_s +{ + struct procfs_dir_priv_s base; /* Base directory private data */ + + /* Our specific data for context control */ + + pid_t pid; /* ID of task for attributes */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* Helpers */ + +static int process_findattr(FAR const char *attr); +static size_t process_addline(FAR struct process_file_s *attr, + FAR char *buffer, size_t buflen, size_t linesize, + off_t *offset); +static ssize_t process_status(FAR struct process_file_s *attr, + FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen, + off_t offset); +static ssize_t process_cmdline(FAR struct process_file_s *attr, + FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen, + off_t offset); + +/* File system methods */ + +static int process_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); +static int process_close(FAR struct file *filep); +static ssize_t process_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); + +static int process_dup(FAR const struct file *oldp, + FAR struct file *newp); + +static int process_opendir(const char *relpath, FAR struct fs_dirent_s *dir); +static int process_closedir(FAR struct fs_dirent_s *dir); +static int process_readdir(FAR struct fs_dirent_s *dir); +static int process_rewinddir(FAR struct fs_dirent_s *dir); + +static int process_stat(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 procfs_operations process_operations = +{ + process_open, /* open */ + process_close, /* close */ + process_read, /* read */ + NULL, /* write */ + + process_dup, /* dup */ + + process_opendir, /* opendir */ + process_closedir, /* closedir */ + process_readdir, /* readdir */ + process_rewinddir, /* rewinddir */ + + process_stat /* stat */ +}; + +/* This is the list of all attribute strings. Indexing is with the same + * values as enum process_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: process_findattr + ****************************************************************************/ + +static int process_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: process_addline + ****************************************************************************/ + +static size_t process_addline(FAR struct process_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: process_status + ****************************************************************************/ + +static ssize_t process_status(FAR struct process_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 = "<noname>"; +#endif + linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", + "Name:", name); + copysize = process_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 = process_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 = process_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 = process_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 = process_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 = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; +#endif + + return totalsize; +} + +/**************************************************************************** + * Name: process_cmdline + ****************************************************************************/ + +static ssize_t process_cmdline(FAR struct process_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 = "<noname>"; +#endif + linesize = strlen(name); + memcpy(attr->line, name, linesize); + copysize = process_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 = process_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 = process_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 = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + return totalsize; +} + +/**************************************************************************** + * Name: process_open + ****************************************************************************/ + +static int process_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct process_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 = process_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 process_file_s *)kzalloc(sizeof(struct process_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: process_close + ****************************************************************************/ + +static int process_close(FAR struct file *filep) +{ + FAR struct process_file_s *attr; + + /* Recover our private data from the struct file instance */ + + attr = (FAR struct process_file_s *)filep->f_priv; + DEBUGASSERT(attr); + + /* Release the file attributes structure */ + + kfree(attr); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: process_read + ****************************************************************************/ + +static ssize_t process_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct process_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 process_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 = process_status(attr, tcb, buffer, buflen, filep->f_pos); + break; + + case PROCFS_CMDLINE: /* Command line */ + ret = process_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: process_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int process_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct process_file_s *oldattr; + FAR struct process_file_s *newattr; + + fvdbg("Dup %p->%p\n", oldp, newp); + + /* Recover our private data from the old struct file instance */ + + oldattr = (FAR struct process_file_s *)oldp->f_priv; + DEBUGASSERT(oldattr); + + /* Allocate a new container to hold the task and attribute selection */ + + newattr = (FAR struct process_file_s *)kzalloc(sizeof(struct process_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 process_file_s)); + + /* Save the new attributes in the new file structure */ + + newp->f_priv = (FAR void *)newattr; + return OK; +} + +/**************************************************************************** + * Name: process_opendir + * + * Description: + * Open a directory for read access + * + ****************************************************************************/ + +static int process_opendir(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(relpath && dir && !dir->u.procfs); + + /* The relative must be: + * + * "<pid>" - The sub-directory of task/thread attributes + */ + + FAR struct process_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 <pid> 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 process_level1_s *) + kzalloc(sizeof(struct process_level1_s)); + + if (!level1) + { + fdbg("ERROR: Failed to allocate the level1 directory structure\n"); + return -ENOMEM; + } + + level1->base.level = 1; + level1->base.nentries = PROCFS_NATTRS; + level1->base.index = 0; + level1->pid = pid; + + priv = (FAR void *)level1; + + dir->u.procfs = priv; + return OK; +} + +/**************************************************************************** + * Name: process_closedir + * + * Description: Close the directory listing + * + ****************************************************************************/ + +static int process_closedir(FAR struct fs_dirent_s *dir) +{ + FAR struct process_level0_s *priv; + + DEBUGASSERT(dir && dir->u.procfs); + priv = dir->u.procfs; + + if (priv) + { + kfree(priv); + } + + dir->u.procfs = NULL; + return OK; +} + +/**************************************************************************** + * Name: process_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int process_readdir(struct fs_dirent_s *dir) +{ + FAR struct process_level1_s *level1; + FAR struct tcb_s *tcb; + unsigned int index; + irqstate_t flags; + pid_t pid; + int ret; + + DEBUGASSERT(dir && dir->u.procfs); + level1 = dir->u.procfs; + + /* Have we reached the end of the directory */ + + index = level1->base.index; + if (index >= level1->base.nentries) + { + /* We signal the end of the directory by returning the special + * error -ENOENT + */ + + fvdbg("Entry %d: End of directory\n", index); + ret = -ENOENT; + } + + /* We are tranversing a subdirectory of task attributes */ + + else + { + DEBUGASSERT(level1->base.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->base.index = index + 1; + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: process_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int process_rewinddir(struct fs_dirent_s *dir) +{ + FAR struct process_level0_s *priv; + + DEBUGASSERT(dir && dir->u.procfs); + priv = dir->u.procfs; + + priv->index = 0; + return OK; +} + +/**************************************************************************** + * Name: process_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int process_stat(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; + + /* Two path forms are accepted: + * + * "<pid>" - If <pid> refers to a currently active task/thread, then it + * is a directory + * "<pid>/<attr>" - If <attr> is a recognized attribute then, then it + * is a file. + */ + + 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 <pid> 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 = process_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_FS_PROCFS_EXCLUDE_PROCESS */ +#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */ |