717bb04cb7
Refer to issue #8867 for details and rational. Convert sigset_t to an array type so that more than 32 signals can be supported. Why not use a uin64_t? - Using a uin32_t is more flexible if we decide to increase the number of signals beyound 64. - 64-bit accesses are not atomic, at least not on 32-bit ARMv7-M and similar - Keeping the base type as uint32_t does not introduce additional overhead due to padding to achieve 64-bit alignment of uin64_t - Some architectures still supported by NuttX do not support uin64_t types, Increased the number of signals to 64. This matches Linux. This will support all xsignals defined by Linux and also 32 real time signals (also like Linux). This is is a work in progress; a draft PR that you are encouraged to comment on.
2075 lines
58 KiB
C
2075 lines
58 KiB
C
/****************************************************************************
|
|
* fs/procfs/fs_procfsproc.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
#include <malloc.h>
|
|
|
|
#ifdef CONFIG_SCHED_CRITMONITOR
|
|
# include <time.h>
|
|
#endif
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/tls.h>
|
|
#include <nuttx/sched.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/environ.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/fs/procfs.h>
|
|
#include <nuttx/fs/ioctl.h>
|
|
#include <nuttx/mm/mm.h>
|
|
|
|
#if defined(CONFIG_SCHED_CPULOAD) || defined(CONFIG_SCHED_CRITMONITOR)
|
|
# include <nuttx/clock.h>
|
|
#endif
|
|
|
|
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS)
|
|
#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Determines the size of an intermediate buffer that must be large enough
|
|
* to handle the longest line generated by this logic.
|
|
*/
|
|
|
|
#define STATUS_LINELEN PATH_MAX
|
|
|
|
/****************************************************************************
|
|
* Private Type Definitions
|
|
****************************************************************************/
|
|
|
|
/* This enumeration identifies all of the task/thread nodes that can be
|
|
* accessed via the procfs file system.
|
|
*/
|
|
|
|
enum proc_node_e
|
|
{
|
|
PROC_LEVEL0 = 0, /* The top-level directory */
|
|
PROC_STATUS, /* Task/thread status */
|
|
PROC_CMDLINE, /* Task command line */
|
|
#ifdef CONFIG_SCHED_CPULOAD
|
|
PROC_LOADAVG, /* Average CPU utilization */
|
|
#endif
|
|
#ifdef CONFIG_SCHED_CRITMONITOR
|
|
PROC_CRITMON, /* Critical section monitor */
|
|
#endif
|
|
#if CONFIG_MM_BACKTRACE >= 0
|
|
PROC_HEAP, /* Task heap info */
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_MM
|
|
PROC_HEAP_CHECK, /* Task heap check flag */
|
|
#endif
|
|
PROC_STACK, /* Task stack info */
|
|
PROC_GROUP, /* Group directory */
|
|
PROC_GROUP_STATUS, /* Task group status */
|
|
PROC_GROUP_FD /* Group file descriptors */
|
|
#if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ENVIRON)
|
|
, PROC_GROUP_ENV /* Group environment variables */
|
|
#endif
|
|
};
|
|
|
|
/* This structure associates a relative path name with an node in the task
|
|
* procfs
|
|
*/
|
|
|
|
struct proc_node_s
|
|
{
|
|
FAR const char *relpath; /* Relative path to the node */
|
|
FAR const char *name; /* Terminal node segment name */
|
|
uint8_t node; /* Type of node (see enum proc_node_e) */
|
|
uint8_t dtype; /* dirent type (see include/dirent.h) */
|
|
};
|
|
|
|
/* This structure describes one open "file" */
|
|
|
|
struct proc_file_s
|
|
{
|
|
struct procfs_file_s base; /* Base open file structure */
|
|
FAR const struct proc_node_s *node; /* Describes the file node */
|
|
pid_t pid; /* Task/thread ID */
|
|
char line[STATUS_LINELEN]; /* Pre-allocated buffer for formatted lines */
|
|
};
|
|
|
|
/* This structure describes one open "directory" */
|
|
|
|
struct proc_dir_s
|
|
{
|
|
struct procfs_dir_priv_s base; /* Base directory private data */
|
|
FAR const struct proc_node_s *node; /* Directory node description */
|
|
pid_t pid; /* ID of task/thread for attributes */
|
|
};
|
|
|
|
/* This structure used with the env_foreach() callback */
|
|
|
|
struct proc_envinfo_s
|
|
{
|
|
FAR struct proc_file_s *procfile;
|
|
FAR char *buffer;
|
|
FAR off_t offset;
|
|
size_t buflen;
|
|
size_t remaining;
|
|
size_t totalsize;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static FAR const char *g_policy[4] =
|
|
{
|
|
"SCHED_FIFO", "SCHED_RR", "SCHED_SPORADIC"
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Helpers */
|
|
|
|
static FAR const struct proc_node_s *
|
|
proc_findnode(FAR const char *relpath);
|
|
static ssize_t proc_status(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset);
|
|
static ssize_t proc_cmdline(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset);
|
|
#ifdef CONFIG_SCHED_CPULOAD
|
|
static ssize_t proc_loadavg(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset);
|
|
#endif
|
|
#ifdef CONFIG_SCHED_CRITMONITOR
|
|
static ssize_t proc_critmon(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset);
|
|
#endif
|
|
#if CONFIG_MM_BACKTRACE >= 0
|
|
static ssize_t proc_heap(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset);
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_MM
|
|
static ssize_t proc_heapcheck(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset);
|
|
static ssize_t proc_heapcheck_write(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR const char *buffer,
|
|
size_t buflen, off_t offset);
|
|
#endif
|
|
static ssize_t proc_stack(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset);
|
|
static ssize_t proc_groupstatus(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset);
|
|
static ssize_t proc_groupfd(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset);
|
|
#if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ENVIRON)
|
|
static int proc_groupenv_callback(FAR void *arg, FAR const char *pair);
|
|
static ssize_t proc_groupenv(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset);
|
|
#endif
|
|
|
|
/* File system methods */
|
|
|
|
static int proc_open(FAR struct file *filep, FAR const char *relpath,
|
|
int oflags, mode_t mode);
|
|
static int proc_close(FAR struct file *filep);
|
|
static ssize_t proc_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t proc_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen);
|
|
static int proc_dup(FAR const struct file *oldp,
|
|
FAR struct file *newp);
|
|
|
|
static int proc_opendir(const char *relpath,
|
|
FAR struct fs_dirent_s **dir);
|
|
static int proc_closedir(FAR struct fs_dirent_s *dir);
|
|
static int proc_readdir(FAR struct fs_dirent_s *dir,
|
|
FAR struct dirent *entry);
|
|
static int proc_rewinddir(FAR struct fs_dirent_s *dir);
|
|
|
|
static int proc_stat(FAR const char *relpath, FAR struct stat *buf);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/* 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 proc_operations =
|
|
{
|
|
proc_open, /* open */
|
|
proc_close, /* close */
|
|
proc_read, /* read */
|
|
proc_write, /* write */
|
|
|
|
proc_dup, /* dup */
|
|
|
|
proc_opendir, /* opendir */
|
|
proc_closedir, /* closedir */
|
|
proc_readdir, /* readdir */
|
|
proc_rewinddir, /* rewinddir */
|
|
|
|
proc_stat /* stat */
|
|
};
|
|
|
|
/* These structures provide information about every node */
|
|
|
|
static const struct proc_node_s g_level0node =
|
|
{
|
|
"", "", (uint8_t)PROC_LEVEL0, DTYPE_DIRECTORY /* Top-level directory */
|
|
};
|
|
|
|
static const struct proc_node_s g_status =
|
|
{
|
|
"status", "status", (uint8_t)PROC_STATUS, DTYPE_FILE /* Task/thread status */
|
|
};
|
|
|
|
static const struct proc_node_s g_cmdline =
|
|
{
|
|
"cmdline", "cmdline", (uint8_t)PROC_CMDLINE, DTYPE_FILE /* Task command line */
|
|
};
|
|
|
|
#ifdef CONFIG_SCHED_CPULOAD
|
|
static const struct proc_node_s g_loadavg =
|
|
{
|
|
"loadavg", "loadavg", (uint8_t)PROC_LOADAVG, DTYPE_FILE /* Average CPU utilization */
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_SCHED_CRITMONITOR
|
|
static const struct proc_node_s g_critmon =
|
|
{
|
|
"critmon", "critmon", (uint8_t)PROC_CRITMON, DTYPE_FILE /* Critical Section Monitor */
|
|
};
|
|
#endif
|
|
|
|
#if CONFIG_MM_BACKTRACE >= 0
|
|
static const struct proc_node_s g_heap =
|
|
{
|
|
"heap", "heap", (uint8_t)PROC_HEAP, DTYPE_FILE /* Task heap info */
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_MM
|
|
static const struct proc_node_s g_heapcheck =
|
|
{
|
|
"heapcheck", "heapcheck", (uint8_t)PROC_HEAP_CHECK, DTYPE_FILE /* Task heap info */
|
|
};
|
|
#endif
|
|
|
|
static const struct proc_node_s g_stack =
|
|
{
|
|
"stack", "stack", (uint8_t)PROC_STACK, DTYPE_FILE /* Task stack info */
|
|
};
|
|
|
|
static const struct proc_node_s g_group =
|
|
{
|
|
"group", "group", (uint8_t)PROC_GROUP, DTYPE_DIRECTORY /* Group directory */
|
|
};
|
|
|
|
static const struct proc_node_s g_groupstatus =
|
|
{
|
|
"group/status", "status", (uint8_t)PROC_GROUP_STATUS, DTYPE_FILE /* Task group status */
|
|
};
|
|
|
|
static const struct proc_node_s g_groupfd =
|
|
{
|
|
"group/fd", "fd", (uint8_t)PROC_GROUP_FD, DTYPE_FILE /* Group file descriptors */
|
|
};
|
|
|
|
#if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ENVIRON)
|
|
static const struct proc_node_s g_groupenv =
|
|
{
|
|
"group/env", "env", (uint8_t)PROC_GROUP_ENV, DTYPE_FILE /* Group environment variables */
|
|
};
|
|
|
|
#endif
|
|
|
|
/* This is the list of all nodes */
|
|
|
|
static FAR const struct proc_node_s * const g_nodeinfo[] =
|
|
{
|
|
&g_status, /* Task/thread status */
|
|
&g_cmdline, /* Task command line */
|
|
#ifdef CONFIG_SCHED_CPULOAD
|
|
&g_loadavg, /* Average CPU utilization */
|
|
#endif
|
|
#ifdef CONFIG_SCHED_CRITMONITOR
|
|
&g_critmon, /* Critical section Monitor */
|
|
#endif
|
|
#if CONFIG_MM_BACKTRACE >= 0
|
|
&g_heap, /* Task heap info */
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_MM
|
|
&g_heapcheck, /* Task heap check flag */
|
|
#endif
|
|
&g_stack, /* Task stack info */
|
|
&g_group, /* Group directory */
|
|
&g_groupstatus, /* Task group status */
|
|
&g_groupfd /* Group file descriptors */
|
|
#if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ENVIRON)
|
|
, &g_groupenv /* Group environment variables */
|
|
#endif
|
|
};
|
|
|
|
#define PROC_NNODES (sizeof(g_nodeinfo)/sizeof(FAR const struct proc_node_s * const))
|
|
|
|
/* This is the list of all level0 nodes */
|
|
|
|
static const struct proc_node_s * const g_level0info[] =
|
|
{
|
|
&g_status, /* Task/thread status */
|
|
&g_cmdline, /* Task command line */
|
|
#ifdef CONFIG_SCHED_CPULOAD
|
|
&g_loadavg, /* Average CPU utilization */
|
|
#endif
|
|
#ifdef CONFIG_SCHED_CRITMONITOR
|
|
&g_critmon, /* Critical section monitor */
|
|
#endif
|
|
#if CONFIG_MM_BACKTRACE >= 0
|
|
&g_heap, /* Task heap info */
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_MM
|
|
&g_heapcheck, /* Task heap check flag */
|
|
#endif
|
|
&g_stack, /* Task stack info */
|
|
&g_group, /* Group directory */
|
|
};
|
|
#define PROC_NLEVEL0NODES (sizeof(g_level0info)/sizeof(FAR const struct proc_node_s * const))
|
|
|
|
/* This is the list of all group sub-directory nodes */
|
|
|
|
static FAR const struct proc_node_s * const g_groupinfo[] =
|
|
{
|
|
&g_groupstatus, /* Task group status */
|
|
&g_groupfd /* Group file descriptors */
|
|
#if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ENVIRON)
|
|
, &g_groupenv /* Group environment variables */
|
|
#endif
|
|
};
|
|
#define PROC_NGROUPNODES (sizeof(g_groupinfo)/sizeof(FAR const struct proc_node_s * const))
|
|
|
|
static FAR const char * const g_ttypenames[4] =
|
|
{
|
|
"Task",
|
|
"pthread",
|
|
"Kthread",
|
|
"Invalid"
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: proc_findnode
|
|
****************************************************************************/
|
|
|
|
static FAR const struct proc_node_s *proc_findnode(FAR const char *relpath)
|
|
{
|
|
int i;
|
|
|
|
/* Search every string in g_nodeinfo or until a match is found */
|
|
|
|
for (i = 0; i < PROC_NNODES; i++)
|
|
{
|
|
size_t len = strlen(g_nodeinfo[i]->relpath);
|
|
|
|
if (strncmp(g_nodeinfo[i]->relpath, relpath, len) == 0 &&
|
|
(relpath[len] == '\0' || (relpath[len] == '/' &&
|
|
relpath[len + 1] == '\0' &&
|
|
g_nodeinfo[i]->dtype == DTYPE_DIRECTORY)))
|
|
{
|
|
return g_nodeinfo[i];
|
|
}
|
|
}
|
|
|
|
/* Not found */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_status
|
|
*
|
|
* Description:
|
|
* Format:
|
|
*
|
|
* 111111111122222222223
|
|
* 123456789012345678901234567890
|
|
* Name: xxxx... Task/thread name (See
|
|
* CONFIG_TASK_NAME_SIZE)
|
|
* Type: xxxxxxx {Task, pthread, Kthread, Invalid}
|
|
* PPID: xxxxx Parent thread ID
|
|
* Group: xxxxx Group ID
|
|
* CPU: xxx CPU (CONFIG_SMP only)
|
|
* State: xxxxxxxx,xxxxxxxxx {Invalid, Waiting, Ready, Running,
|
|
* Inactive},
|
|
* {Unlock, Semaphore, Signal, MQ empty,
|
|
* MQ full}
|
|
* Flags: xxx N,P,X
|
|
* Priority: nnn Decimal, 0-255
|
|
* Scheduler: xxxxxxxxxxxxxx {SCHED_FIFO, SCHED_RR, SCHED_SPORADIC}
|
|
* Sigmask: nnnnnnnn Hexadecimal, 32-bit
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t proc_status(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
FAR const char *policy;
|
|
FAR const char *name;
|
|
char state[32];
|
|
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 = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s%.18s\n", "Name:", name);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the thread type */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%s\n",
|
|
"Type:",
|
|
g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK)
|
|
>> TCB_FLAG_TTYPE_SHIFT]);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s%d\n", "Group:",
|
|
tcb->group ? tcb->group->tg_pid : -1);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
if (tcb->task_state >= FIRST_ASSIGNED_STATE &&
|
|
tcb->task_state <= LAST_ASSIGNED_STATE)
|
|
{
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s%d\n", "CPU:", tcb->cpu);
|
|
}
|
|
else
|
|
{
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s---\n", "CPU:");
|
|
}
|
|
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
#endif
|
|
|
|
/* Show the thread state */
|
|
|
|
nxsched_get_stateinfo(tcb, state, sizeof(state));
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s%s\n", "State:", state);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show task flags */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s%c%c%c\n", "Flags:",
|
|
tcb->flags & TCB_FLAG_NONCANCELABLE ? 'N' : '-',
|
|
tcb->flags & TCB_FLAG_CANCEL_PENDING ? 'P' : '-',
|
|
tcb->flags & TCB_FLAG_EXIT_PROCESSING ? 'P' : '-');
|
|
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the thread priority */
|
|
|
|
#ifdef CONFIG_PRIORITY_INHERITANCE
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s%d (%d)\n", "Priority:",
|
|
tcb->sched_priority, tcb->base_priority);
|
|
#else
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%d\n",
|
|
"Priority:", tcb->sched_priority);
|
|
#endif
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the scheduler policy */
|
|
|
|
policy = g_policy[(tcb->flags & TCB_FLAG_POLICY_MASK) >>
|
|
TCB_FLAG_POLICY_SHIFT];
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%s\n",
|
|
"Scheduler:", policy);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the signal mask. Note: sigset_t is uint32_t on NuttX. */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s" SIGSET_FMT "\n",
|
|
"SigMask:", SIGSET_ELEM(&tcb->sigprocmask));
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
return totalsize;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_cmdline
|
|
****************************************************************************/
|
|
|
|
static ssize_t proc_cmdline(FAR struct proc_file_s *procfile,
|
|
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 = strlen(name);
|
|
memcpy(procfile->line, name, linesize);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the task / thread argument list (skipping over the name) */
|
|
|
|
linesize = group_argvstr(tcb, procfile->line, remaining);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "\n");
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
return totalsize;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_loadavg
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_SCHED_CPULOAD
|
|
static ssize_t proc_loadavg(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
struct cpuload_s cpuload;
|
|
uint32_t intpart;
|
|
uint32_t fracpart;
|
|
size_t linesize;
|
|
size_t copysize;
|
|
|
|
/* Sample the counts for the thread. clock_cpuload should only fail if
|
|
* the PID is not valid. This could happen if the thread exited sometime
|
|
* after the procfs entry was opened.
|
|
*/
|
|
|
|
clock_cpuload(procfile->pid, &cpuload);
|
|
|
|
/* On the simulator, you may hit cpuload.total == 0, but probably never on
|
|
* real hardware.
|
|
*/
|
|
|
|
if (cpuload.total > 0)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
tmp = (1000 * cpuload.active) / cpuload.total;
|
|
intpart = tmp / 10;
|
|
fracpart = tmp - 10 * intpart;
|
|
}
|
|
else
|
|
{
|
|
intpart = 0;
|
|
fracpart = 0;
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%3" PRId32 ".%01" PRId32 "%%\n",
|
|
intpart, fracpart);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, buflen,
|
|
&offset);
|
|
|
|
return copysize;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: proc_critmon
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_SCHED_CRITMONITOR
|
|
static ssize_t proc_critmon(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
struct timespec maxtime;
|
|
size_t remaining;
|
|
size_t linesize;
|
|
size_t copysize;
|
|
size_t totalsize;
|
|
|
|
remaining = buflen;
|
|
totalsize = 0;
|
|
|
|
/* Convert the for maximum time pre-emption disabled */
|
|
|
|
if (tcb->premp_max > 0)
|
|
{
|
|
up_perf_convert(tcb->premp_max, &maxtime);
|
|
}
|
|
else
|
|
{
|
|
maxtime.tv_sec = 0;
|
|
maxtime.tv_nsec = 0;
|
|
}
|
|
|
|
/* Reset the maximum */
|
|
|
|
tcb->premp_max = 0;
|
|
|
|
/* Generate output for maximum time pre-emption disabled */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%lu.%09lu,",
|
|
(unsigned long)maxtime.tv_sec,
|
|
(unsigned long)maxtime.tv_nsec);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Convert and generate output for maximum time in a critical section */
|
|
|
|
if (tcb->crit_max > 0)
|
|
{
|
|
up_perf_convert(tcb->crit_max, &maxtime);
|
|
}
|
|
else
|
|
{
|
|
maxtime.tv_sec = 0;
|
|
maxtime.tv_nsec = 0;
|
|
}
|
|
|
|
/* Reset the maximum */
|
|
|
|
tcb->crit_max = 0;
|
|
|
|
/* Generate output for maximum time in a critical section */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%lu.%09lu,",
|
|
(unsigned long)maxtime.tv_sec,
|
|
(unsigned long)maxtime.tv_nsec);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Convert and generate output for maximum time thread running */
|
|
|
|
if (tcb->run_max > 0)
|
|
{
|
|
up_perf_convert(tcb->run_max, &maxtime);
|
|
}
|
|
else
|
|
{
|
|
maxtime.tv_sec = 0;
|
|
maxtime.tv_nsec = 0;
|
|
}
|
|
|
|
/* Reset the maximum */
|
|
|
|
tcb->run_max = 0;
|
|
|
|
/* Generate output for maximum time thread running */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%lu.%09lu\n",
|
|
(unsigned long)maxtime.tv_sec,
|
|
(unsigned long)maxtime.tv_nsec);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
return totalsize;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: proc_heap
|
|
****************************************************************************/
|
|
|
|
#if CONFIG_MM_BACKTRACE >= 0
|
|
static ssize_t proc_heap(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
size_t remaining = buflen;
|
|
size_t linesize;
|
|
size_t copysize;
|
|
size_t totalsize = 0;
|
|
struct mallinfo_task info;
|
|
|
|
#ifdef CONFIG_MM_KERNEL_HEAP
|
|
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
|
|
{
|
|
info = kmm_mallinfo_task(tcb->pid);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
info = mallinfo_task(tcb->pid);
|
|
}
|
|
|
|
/* Show the heap alloc size */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s%d\n", "AllocSize:", info.uordblks);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the heap alloc block */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s%d\n", "AllocBlks:", info.aordblks);
|
|
totalsize += procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
return totalsize;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_MM
|
|
static ssize_t proc_heapcheck(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
size_t remaining = buflen;
|
|
size_t linesize;
|
|
size_t copysize;
|
|
size_t totalsize = 0;
|
|
size_t heapcheck = 0;
|
|
|
|
if (tcb->flags & TCB_FLAG_HEAP_CHECK)
|
|
{
|
|
heapcheck = 1;
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%zu\n",
|
|
"HeapCheck:", heapcheck);
|
|
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
totalsize += copysize;
|
|
return totalsize;
|
|
}
|
|
|
|
static ssize_t proc_heapcheck_write(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb,
|
|
FAR const char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
switch (atoi(buffer))
|
|
{
|
|
case 0:
|
|
tcb->flags &= ~TCB_FLAG_HEAP_CHECK;
|
|
break;
|
|
case 1:
|
|
tcb->flags |= TCB_FLAG_HEAP_CHECK;
|
|
break;
|
|
default:
|
|
ferr("ERROR: invalid argument\n");
|
|
return -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return buflen;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: proc_stack
|
|
****************************************************************************/
|
|
|
|
static ssize_t proc_stack(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
size_t remaining;
|
|
size_t linesize;
|
|
size_t copysize;
|
|
size_t totalsize;
|
|
|
|
remaining = buflen;
|
|
totalsize = 0;
|
|
|
|
/* Show the stack alloc address */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%p\n",
|
|
"StackAlloc:", tcb->stack_alloc_ptr);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the stack base address */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%p\n",
|
|
"StackBase:", tcb->stack_base_ptr);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the stack size */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%ld\n",
|
|
"StackSize:", (long)tcb->adj_stack_size);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
#ifdef CONFIG_STACK_COLORATION
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Show the stack size */
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%ld\n",
|
|
"StackUsed:", (long)up_check_tcbstack(tcb));
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
#endif
|
|
|
|
return totalsize;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_groupstatus
|
|
****************************************************************************/
|
|
|
|
static ssize_t proc_groupstatus(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
FAR struct task_group_s *group = tcb->group;
|
|
size_t remaining;
|
|
size_t linesize;
|
|
size_t copysize;
|
|
size_t totalsize;
|
|
#ifdef HAVE_GROUP_MEMBERS
|
|
int i;
|
|
#endif
|
|
|
|
DEBUGASSERT(group != NULL);
|
|
|
|
remaining = buflen;
|
|
totalsize = 0;
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%d\n",
|
|
"Main task:", group->tg_pid);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%d\n",
|
|
"Parent:", group->tg_ppid);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%-12s0x%02x\n",
|
|
"Flags:", group->tg_flags);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%d\n",
|
|
"Members:", group->tg_nmembers);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
#ifdef HAVE_GROUP_MEMBERS
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"Member IDs:");
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
for (i = 0; i < group->tg_nmembers; i++)
|
|
{
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, " %d",
|
|
group->tg_members[i]);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "\n");
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
#endif
|
|
|
|
return totalsize;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_groupfd
|
|
****************************************************************************/
|
|
|
|
static ssize_t proc_groupfd(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer,
|
|
size_t buflen, off_t offset)
|
|
{
|
|
FAR struct task_group_s *group = tcb->group;
|
|
FAR struct file *file;
|
|
char path[PATH_MAX];
|
|
size_t remaining;
|
|
size_t linesize;
|
|
size_t copysize;
|
|
size_t totalsize;
|
|
int i;
|
|
int j;
|
|
|
|
DEBUGASSERT(group != NULL);
|
|
|
|
remaining = buflen;
|
|
totalsize = 0;
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"\n%-3s %-8s %-8s %s\n",
|
|
"FD", "POS", "OFLAGS", "PATH");
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Examine each open file descriptor */
|
|
|
|
for (i = 0; i < group->tg_filelist.fl_rows; i++)
|
|
{
|
|
for (j = 0, file = group->tg_filelist.fl_files[i];
|
|
j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
|
|
j++, file++)
|
|
{
|
|
/* Is there an inode associated with the file descriptor? */
|
|
|
|
if (file->f_inode && !INODE_IS_SOCKET(file->f_inode))
|
|
{
|
|
if (file_ioctl(file, FIOC_FILEPATH, path) < 0)
|
|
{
|
|
path[0] = '\0';
|
|
}
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%3d %8ld %8x",
|
|
i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK +
|
|
j, (long)file->f_pos,
|
|
file->f_oflags);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
" %s\n", path);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_NET
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"\n%-3s %-2s %-3s %s\n",
|
|
"SD", "RF", "TYP", "FLAGS");
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
|
|
&offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
|
|
/* Examine each open socket descriptor */
|
|
|
|
for (i = 0; i < group->tg_filelist.fl_rows; i++)
|
|
{
|
|
for (j = 0, file = group->tg_filelist.fl_files[i];
|
|
j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
|
|
j++, file++)
|
|
{
|
|
/* Is there an connection associated with the socket descriptor? */
|
|
|
|
if (file->f_inode && INODE_IS_SOCKET(file->f_inode))
|
|
{
|
|
FAR struct socket *socket = file->f_priv;
|
|
FAR struct socket_conn_s *conn = socket->s_conn;
|
|
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
|
|
"%3d %3d %02x",
|
|
i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK +
|
|
j, socket->s_type, conn->s_flags);
|
|
copysize = procfs_memcpy(procfile->line, linesize, buffer,
|
|
remaining, &offset);
|
|
|
|
totalsize += copysize;
|
|
buffer += copysize;
|
|
remaining -= copysize;
|
|
|
|
if (totalsize >= buflen)
|
|
{
|
|
return totalsize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return totalsize;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_groupenv_callback
|
|
****************************************************************************/
|
|
|
|
#if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ENVIRON)
|
|
static int proc_groupenv_callback(FAR void *arg, FAR const char *pair)
|
|
{
|
|
FAR struct proc_envinfo_s *info = (FAR struct proc_envinfo_s *)arg;
|
|
FAR const char *src;
|
|
FAR const char *value;
|
|
FAR char *dest;
|
|
char name[16 + 1];
|
|
size_t linesize;
|
|
size_t copysize;
|
|
int namelen;
|
|
|
|
DEBUGASSERT(arg != NULL && pair != NULL);
|
|
|
|
/* Parse the name from the name/value pair */
|
|
|
|
value = NULL;
|
|
namelen = 0;
|
|
|
|
for (src = pair, dest = name; *src != '=' && *src != '\0'; src++)
|
|
{
|
|
if (namelen < 16)
|
|
{
|
|
*dest++ = *src;
|
|
namelen++;
|
|
}
|
|
}
|
|
|
|
/* NUL terminate the name string */
|
|
|
|
*dest = '\0';
|
|
|
|
/* Skip over the '=' to get the value */
|
|
|
|
if (*src == '=')
|
|
{
|
|
value = src + 1;
|
|
}
|
|
else
|
|
{
|
|
value = "";
|
|
}
|
|
|
|
/* Output the header */
|
|
|
|
linesize = procfs_snprintf(info->procfile->line,
|
|
STATUS_LINELEN, "%s=%s\n",
|
|
name, value);
|
|
copysize = procfs_memcpy(info->procfile->line, linesize,
|
|
info->buffer, info->remaining,
|
|
&info->offset);
|
|
|
|
info->totalsize += copysize;
|
|
info->buffer += copysize;
|
|
info->remaining -= copysize;
|
|
|
|
if (info->totalsize >= info->buflen)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: proc_groupenv
|
|
****************************************************************************/
|
|
|
|
#if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ENVIRON)
|
|
static ssize_t proc_groupenv(FAR struct proc_file_s *procfile,
|
|
FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
|
|
off_t offset)
|
|
{
|
|
FAR struct task_group_s *group = tcb->group;
|
|
struct proc_envinfo_s info;
|
|
|
|
DEBUGASSERT(group != NULL);
|
|
|
|
/* Initialize the info structure */
|
|
|
|
info.procfile = procfile;
|
|
info.buffer = buffer;
|
|
info.offset = offset;
|
|
info.buflen = buflen;
|
|
info.remaining = buflen;
|
|
info.totalsize = 0;
|
|
|
|
/* Generate output for each environment variable */
|
|
|
|
env_foreach(group, proc_groupenv_callback, &info);
|
|
return info.totalsize;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: proc_open
|
|
****************************************************************************/
|
|
|
|
static int proc_open(FAR struct file *filep, FAR const char *relpath,
|
|
int oflags, mode_t mode)
|
|
{
|
|
FAR struct proc_file_s *procfile;
|
|
FAR const struct proc_node_s *node;
|
|
FAR struct tcb_s *tcb;
|
|
FAR char *ptr;
|
|
unsigned long tmp;
|
|
pid_t pid;
|
|
|
|
finfo("Open '%s'\n", relpath);
|
|
|
|
/* The first segment of the relative path should be a task/thread ID or
|
|
* the string "self".
|
|
*/
|
|
|
|
ptr = NULL;
|
|
|
|
if (strncmp(relpath, "self", 4) == 0)
|
|
{
|
|
tmp = nxsched_gettid(); /* Get the TID of the calling task */
|
|
ptr = (FAR char *)relpath + 4; /* Discard const */
|
|
}
|
|
else
|
|
{
|
|
tmp = strtoul(relpath, &ptr, 10); /* Extract the PID from path */
|
|
}
|
|
|
|
if (ptr == NULL || *ptr != '/')
|
|
{
|
|
ferr("ERROR: Invalid path \"%s\"\n", relpath);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Skip over the slash */
|
|
|
|
ptr++;
|
|
|
|
/* A valid PID would be in the range of 0-INT_MAX (0 is reserved for the
|
|
* IDLE thread).
|
|
*/
|
|
|
|
if (tmp > INT_MAX)
|
|
{
|
|
ferr("ERROR: Invalid PID %ld\n", tmp);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Now verify that a task with this task/thread ID exists */
|
|
|
|
pid = (pid_t)tmp;
|
|
|
|
tcb = nxsched_get_tcb(pid);
|
|
if (tcb == NULL)
|
|
{
|
|
ferr("ERROR: PID %d is no longer valid\n", (int)pid);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* The remaining segments of the relpath should be a well known node in
|
|
* the task/thread tree.
|
|
*/
|
|
|
|
node = proc_findnode(ptr);
|
|
if (node == NULL)
|
|
{
|
|
ferr("ERROR: Invalid path \"%s\"\n", relpath);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* The node must be a file, not a directory */
|
|
|
|
if (!DIRENT_ISFILE(node->dtype))
|
|
{
|
|
ferr("ERROR: Path \"%s\" is not a regular file\n", relpath);
|
|
return -EISDIR;
|
|
}
|
|
|
|
/* Allocate a container to hold the task and node selection */
|
|
|
|
procfile = (FAR struct proc_file_s *)
|
|
kmm_zalloc(sizeof(struct proc_file_s));
|
|
if (procfile == NULL)
|
|
{
|
|
ferr("ERROR: Failed to allocate file container\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize the file container */
|
|
|
|
procfile->pid = pid;
|
|
procfile->node = node;
|
|
|
|
/* Save the index as the open-specific state in filep->f_priv */
|
|
|
|
filep->f_priv = (FAR void *)procfile;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_close
|
|
****************************************************************************/
|
|
|
|
static int proc_close(FAR struct file *filep)
|
|
{
|
|
FAR struct proc_file_s *procfile;
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
procfile = (FAR struct proc_file_s *)filep->f_priv;
|
|
DEBUGASSERT(procfile != NULL);
|
|
|
|
/* Release the file container structure */
|
|
|
|
kmm_free(procfile);
|
|
filep->f_priv = NULL;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_read
|
|
****************************************************************************/
|
|
|
|
static ssize_t proc_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct proc_file_s *procfile;
|
|
FAR struct tcb_s *tcb;
|
|
ssize_t ret;
|
|
|
|
finfo("buffer=%p buflen=%d\n", buffer, (int)buflen);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
procfile = (FAR struct proc_file_s *)filep->f_priv;
|
|
DEBUGASSERT(procfile != NULL);
|
|
|
|
/* Verify that the thread is still valid */
|
|
|
|
tcb = nxsched_get_tcb(procfile->pid);
|
|
if (tcb == NULL)
|
|
{
|
|
ferr("ERROR: PID %d is not valid\n", (int)procfile->pid);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Provide the requested data */
|
|
|
|
switch (procfile->node->node)
|
|
{
|
|
case PROC_STATUS: /* Task/thread status */
|
|
ret = proc_status(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
|
|
case PROC_CMDLINE: /* Task command line */
|
|
ret = proc_cmdline(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
|
|
#ifdef CONFIG_SCHED_CPULOAD
|
|
case PROC_LOADAVG: /* Average CPU utilization */
|
|
ret = proc_loadavg(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_SCHED_CRITMONITOR
|
|
case PROC_CRITMON: /* Critical section monitor */
|
|
ret = proc_critmon(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
#endif
|
|
#if CONFIG_MM_BACKTRACE >= 0
|
|
case PROC_HEAP: /* Task heap info */
|
|
ret = proc_heap(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_MM
|
|
case PROC_HEAP_CHECK: /* Task heap check flag */
|
|
ret = proc_heapcheck(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
#endif
|
|
case PROC_STACK: /* Task stack info */
|
|
ret = proc_stack(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
|
|
case PROC_GROUP_STATUS: /* Task group status */
|
|
ret = proc_groupstatus(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
|
|
case PROC_GROUP_FD: /* Group file descriptors */
|
|
ret = proc_groupfd(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
|
|
#if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ENVIRON)
|
|
case PROC_GROUP_ENV: /* Group environment variables */
|
|
ret = proc_groupenv(procfile, tcb, buffer, buflen, filep->f_pos);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* Update the file offset */
|
|
|
|
if (ret > 0)
|
|
{
|
|
filep->f_pos += ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_write
|
|
****************************************************************************/
|
|
|
|
static ssize_t proc_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct proc_file_s *procfile;
|
|
FAR struct tcb_s *tcb;
|
|
ssize_t ret;
|
|
|
|
DEBUGASSERT(filep != NULL && buffer != NULL && buflen > 0);
|
|
|
|
procfile = (FAR struct proc_file_s *)filep->f_priv;
|
|
DEBUGASSERT(procfile != NULL);
|
|
|
|
/* Verify that the thread is still valid */
|
|
|
|
tcb = nxsched_get_tcb(procfile->pid);
|
|
if (tcb == NULL)
|
|
{
|
|
ferr("ERROR: PID %d is not valid\n", (int)procfile->pid);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Provide the requested data */
|
|
|
|
switch (procfile->node->node)
|
|
{
|
|
#ifdef CONFIG_DEBUG_MM
|
|
case PROC_HEAP_CHECK:
|
|
ret = proc_heapcheck_write(procfile, tcb, buffer, buflen,
|
|
filep->f_pos);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_dup
|
|
*
|
|
* Description:
|
|
* Duplicate open file data in the new file structure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int proc_dup(FAR const struct file *oldp, FAR struct file *newp)
|
|
{
|
|
FAR struct proc_file_s *oldfile;
|
|
FAR struct proc_file_s *newfile;
|
|
|
|
finfo("Dup %p->%p\n", oldp, newp);
|
|
|
|
/* Recover our private data from the old struct file instance */
|
|
|
|
oldfile = (FAR struct proc_file_s *)oldp->f_priv;
|
|
DEBUGASSERT(oldfile != NULL);
|
|
|
|
/* Allocate a new container to hold the task and node selection */
|
|
|
|
newfile = (FAR struct proc_file_s *)kmm_malloc(sizeof(struct proc_file_s));
|
|
if (newfile == NULL)
|
|
{
|
|
ferr("ERROR: Failed to allocate file container\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* The copy the file information from the old container to the new */
|
|
|
|
memcpy(newfile, oldfile, sizeof(struct proc_file_s));
|
|
|
|
/* Save the new container in the new file structure */
|
|
|
|
newp->f_priv = (FAR void *)newfile;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_opendir
|
|
*
|
|
* Description:
|
|
* Open a directory for read access
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int proc_opendir(FAR const char *relpath,
|
|
FAR struct fs_dirent_s **dir)
|
|
{
|
|
FAR struct proc_dir_s *procdir;
|
|
FAR const struct proc_node_s *node;
|
|
FAR struct tcb_s *tcb;
|
|
unsigned long tmp;
|
|
FAR char *ptr;
|
|
pid_t pid;
|
|
|
|
finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL");
|
|
DEBUGASSERT(relpath != NULL);
|
|
|
|
/* The relative must be either:
|
|
*
|
|
* (1) "<pid>" - The sub-directory of task/thread attributes,
|
|
* (2) "self" - Which refers to the PID of the calling task, or
|
|
* (3) The name of a directory node under either of those
|
|
*/
|
|
|
|
/* Otherwise, the relative path should be a valid task/thread ID */
|
|
|
|
ptr = NULL;
|
|
|
|
if (strncmp(relpath, "self", 4) == 0)
|
|
{
|
|
tmp = nxsched_gettid(); /* Get the TID of the calling task */
|
|
ptr = (FAR char *)relpath + 4; /* Discard const */
|
|
}
|
|
else
|
|
{
|
|
tmp = strtoul(relpath, &ptr, 10); /* Extract the PID from path */
|
|
}
|
|
|
|
if (ptr == NULL || (*ptr != '\0' && *ptr != '/'))
|
|
{
|
|
/* strtoul failed or there is something in the path after the pid */
|
|
|
|
ferr("ERROR: Invalid path \"%s\"\n", relpath);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* A valid PID would be in the range of 0-INT_MAX (0 is reserved for the
|
|
* IDLE thread).
|
|
*/
|
|
|
|
if (tmp > INT_MAX)
|
|
{
|
|
ferr("ERROR: Invalid PID %ld\n", tmp);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Now verify that a task with this task/thread ID exists */
|
|
|
|
pid = (pid_t)tmp;
|
|
|
|
tcb = nxsched_get_tcb(pid);
|
|
if (tcb == NULL)
|
|
{
|
|
ferr("ERROR: PID %d is not valid\n", (int)pid);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Allocate the directory structure. Note that the index and procentry
|
|
* pointer are implicitly nullified by kmm_zalloc(). Only the remaining,
|
|
* non-zero entries will need be initialized.
|
|
*/
|
|
|
|
procdir = (FAR struct proc_dir_s *)kmm_zalloc(sizeof(struct proc_dir_s));
|
|
if (procdir == NULL)
|
|
{
|
|
ferr("ERROR: Failed to allocate the directory structure\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Was the <pid> the final element of the path? */
|
|
|
|
if (*ptr != '\0' && strcmp(ptr, "/") != 0)
|
|
{
|
|
/* There is something in the path after the pid. Skip over the path
|
|
* segment delimiter and see if we can identify the node of interest.
|
|
*/
|
|
|
|
ptr++;
|
|
node = proc_findnode(ptr);
|
|
if (node == NULL)
|
|
{
|
|
ferr("ERROR: Invalid path \"%s\"\n", relpath);
|
|
kmm_free(procdir);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* The node must be a directory, not a file */
|
|
|
|
if (!DIRENT_ISDIRECTORY(node->dtype))
|
|
{
|
|
ferr("ERROR: Path \"%s\" is not a directory\n", relpath);
|
|
kmm_free(procdir);
|
|
return -ENOTDIR;
|
|
}
|
|
|
|
/* This is a second level directory */
|
|
|
|
procdir->base.level = 2;
|
|
procdir->base.nentries = PROC_NGROUPNODES;
|
|
procdir->node = node;
|
|
}
|
|
else
|
|
{
|
|
/* Use the special level0 node */
|
|
|
|
procdir->base.level = 1;
|
|
procdir->base.nentries = PROC_NLEVEL0NODES;
|
|
procdir->node = &g_level0node;
|
|
}
|
|
|
|
procdir->pid = pid;
|
|
*dir = (FAR struct fs_dirent_s *)procdir;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_closedir
|
|
*
|
|
* Description: Close the directory listing
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int proc_closedir(FAR struct fs_dirent_s *dir)
|
|
{
|
|
DEBUGASSERT(dir != NULL);
|
|
kmm_free(dir);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_readdir
|
|
*
|
|
* Description: Read the next directory entry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int proc_readdir(FAR struct fs_dirent_s *dir,
|
|
FAR struct dirent *entry)
|
|
{
|
|
FAR struct proc_dir_s *procdir;
|
|
FAR const struct proc_node_s *node = NULL;
|
|
FAR struct tcb_s *tcb;
|
|
unsigned int index;
|
|
pid_t pid;
|
|
int ret;
|
|
|
|
DEBUGASSERT(dir != NULL);
|
|
procdir = (FAR struct proc_dir_s *)dir;
|
|
|
|
/* Have we reached the end of the directory */
|
|
|
|
index = procdir->base.index;
|
|
if (index >= procdir->base.nentries)
|
|
{
|
|
/* We signal the end of the directory by returning the special
|
|
* error -ENOENT
|
|
*/
|
|
|
|
finfo("Entry %d: End of directory\n", index);
|
|
ret = -ENOENT;
|
|
}
|
|
|
|
/* No, we are not at the end of the directory */
|
|
|
|
else
|
|
{
|
|
/* Verify that the pid still refers to an active task/thread */
|
|
|
|
pid = procdir->pid;
|
|
|
|
tcb = nxsched_get_tcb(pid);
|
|
if (tcb == NULL)
|
|
{
|
|
ferr("ERROR: PID %d is no longer valid\n", (int)pid);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* The TCB is still valid (or at least was when we entered this
|
|
* function)
|
|
*
|
|
* Handle the directory listing by the node type.
|
|
*/
|
|
|
|
switch (procdir->node->node)
|
|
{
|
|
case PROC_LEVEL0: /* Top level directory */
|
|
DEBUGASSERT(procdir->base.level == 1);
|
|
node = g_level0info[index];
|
|
break;
|
|
|
|
case PROC_GROUP: /* Group sub-directory */
|
|
DEBUGASSERT(procdir->base.level == 2);
|
|
node = g_groupinfo[index];
|
|
break;
|
|
|
|
default:
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Save the filename and file type */
|
|
|
|
entry->d_type = node->dtype;
|
|
strlcpy(entry->d_name, node->name, sizeof(entry->d_name));
|
|
|
|
/* Set up the next directory entry offset. NOTE that we could use the
|
|
* standard f_pos instead of our own private index.
|
|
*/
|
|
|
|
procdir->base.index = index + 1;
|
|
ret = OK;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_rewindir
|
|
*
|
|
* Description: Reset directory read to the first entry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int proc_rewinddir(struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct proc_dir_s *priv;
|
|
|
|
DEBUGASSERT(dir != NULL);
|
|
priv = (FAR struct proc_dir_s *)dir;
|
|
|
|
priv->base.index = 0;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_stat
|
|
*
|
|
* Description: Return information about a file or directory
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int proc_stat(const char *relpath, struct stat *buf)
|
|
{
|
|
FAR const struct proc_node_s *node;
|
|
FAR struct tcb_s *tcb;
|
|
unsigned long tmp;
|
|
FAR char *ptr;
|
|
pid_t pid;
|
|
|
|
/* Two path forms are accepted:
|
|
*
|
|
* "<pid>" - If <pid> refers to a currently active task/thread, then it
|
|
* is a directory
|
|
* "<pid>/<node>" - If <node> is a recognized node then, then it
|
|
* is a file or directory.
|
|
*/
|
|
|
|
ptr = NULL;
|
|
|
|
if (strncmp(relpath, "self", 4) == 0)
|
|
{
|
|
tmp = nxsched_gettid(); /* Get the TID of the calling task */
|
|
ptr = (FAR char *)relpath + 4; /* Discard const */
|
|
}
|
|
else
|
|
{
|
|
tmp = strtoul(relpath, &ptr, 10); /* Extract the PID from path */
|
|
}
|
|
|
|
if (ptr == NULL)
|
|
{
|
|
ferr("ERROR: Invalid path \"%s\"\n", relpath);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* A valid PID would be in the range of 0-INT_MAX (0 is reserved for the
|
|
* IDLE thread).
|
|
*/
|
|
|
|
if (tmp > INT_MAX)
|
|
{
|
|
ferr("ERROR: Invalid PID %ld\n", tmp);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Now verify that a task with this task/thread ID exists */
|
|
|
|
pid = (pid_t)tmp;
|
|
|
|
tcb = nxsched_get_tcb(pid);
|
|
if (tcb == NULL)
|
|
{
|
|
ferr("ERROR: PID %d is no longer valid\n", (int)pid);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Was the <pid> the final element of the path? */
|
|
|
|
memset(buf, 0, sizeof(struct stat));
|
|
if (*ptr == '\0' || strcmp(ptr, "/") == 0)
|
|
{
|
|
/* Yes ... It's a read-only directory */
|
|
|
|
buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
|
|
}
|
|
|
|
/* Verify that the process ID is followed by valid path segment delimiter */
|
|
|
|
else if (*ptr != '/')
|
|
{
|
|
/* We are required to return -ENOENT all all invalid paths */
|
|
|
|
ferr("ERROR: Bad delimiter '%c' in relpath '%s'\n", *ptr, relpath);
|
|
return -ENOENT;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, the second segment of the relpath should be a well
|
|
* known node of the task/thread directory structure.
|
|
*/
|
|
|
|
/* Skip over the path segment delimiter */
|
|
|
|
ptr++;
|
|
|
|
/* Lookup the well-known node associated with the relative path. */
|
|
|
|
node = proc_findnode(ptr);
|
|
if (node == NULL)
|
|
{
|
|
ferr("ERROR: Invalid path \"%s\"\n", relpath);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* If the node exists, it is the name for a read-only file or
|
|
* directory.
|
|
*/
|
|
|
|
if (node->dtype == DTYPE_FILE)
|
|
{
|
|
buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
|
|
}
|
|
else
|
|
{
|
|
buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
#endif /* CONFIG_FS_PROCFS_EXCLUDE_PROCESS */
|
|
#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */
|