apps/nshlib: The 'ps' command now uses /proc/(pid)/ to obtain task status information. A consequence of this is that you cannot use the 'ps' command if the procfs is not enabled and mounted at /proc.
This commit is contained in:
parent
082b452016
commit
7e7dd916ce
@ -1481,3 +1481,7 @@
|
|||||||
buffer. That will not work in an environment where there are multiple
|
buffer. That will not work in an environment where there are multiple
|
||||||
NSH sessions. The I/O buffer must, instead, be a part part of the
|
NSH sessions. The I/O buffer must, instead, be a part part of the
|
||||||
session-specific data defined in nsh_console.h (2015-11-28).
|
session-specific data defined in nsh_console.h (2015-11-28).
|
||||||
|
* apps/nshlib: The 'ps' command now uses /proc/<pid>/ to obtain
|
||||||
|
task status information. A consequence of this is that you cannot use
|
||||||
|
the 'ps' command if the procfs is not enabled and mounted at
|
||||||
|
/proc (2015-11-28).
|
||||||
|
@ -341,8 +341,8 @@ config NSH_DISABLE_POWEROFF
|
|||||||
|
|
||||||
config NSH_DISABLE_PS
|
config NSH_DISABLE_PS
|
||||||
bool "Disable ps"
|
bool "Disable ps"
|
||||||
default n
|
default y if !FS_PROCFS || FS_PROCFS_EXCLUDE_PROCESS
|
||||||
depends on !BUILD_PROTECTED && !BUILD_KERNEL
|
default n if FS_PROCFS && !FS_PROCFS_EXCLUDE_PROCESS
|
||||||
|
|
||||||
config NSH_DISABLE_PING
|
config NSH_DISABLE_PING
|
||||||
bool "Disable ping"
|
bool "Disable ping"
|
||||||
|
@ -42,8 +42,10 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sched.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "nsh.h"
|
#include "nsh.h"
|
||||||
@ -67,43 +69,33 @@
|
|||||||
|
|
||||||
typedef int (*exec_t)(void);
|
typedef int (*exec_t)(void);
|
||||||
|
|
||||||
/****************************************************************************
|
/* This structure represents the parsed task characteristics */
|
||||||
* Private Data
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef CONFIG_NSH_DISABLE_PS
|
struct nsh_taskstatus_s
|
||||||
static const char *g_statenames[] =
|
|
||||||
{
|
{
|
||||||
"INVALID ",
|
FAR const char *td_type; /* Thread type */
|
||||||
"PENDING ",
|
FAR const char *td_state; /* Thread state */
|
||||||
"READY ",
|
FAR const char *td_event; /* Thread wait event */
|
||||||
"RUNNING ",
|
FAR const char *td_flags; /* Thread flags */
|
||||||
"INACTIVE",
|
FAR const char *td_priority; /* Thread priority */
|
||||||
"WAITSEM ",
|
FAR const char *td_policy; /* Thread scheduler */
|
||||||
#ifndef CONFIG_DISABLE_SIGNALS
|
#ifndef CONFIG_DISABLE_SIGNALS
|
||||||
"WAITSIG ",
|
FAR const char *td_sigmask; /* Signal mask */
|
||||||
#endif
|
|
||||||
#ifndef CONFIG_DISABLE_MQUEUE
|
|
||||||
"MQNEMPTY",
|
|
||||||
"MQNFULL "
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *g_ttypenames[4] =
|
/* Status strings */
|
||||||
{
|
|
||||||
"TASK ",
|
|
||||||
"PTHREAD",
|
|
||||||
"KTHREAD",
|
|
||||||
"--?-- "
|
|
||||||
};
|
|
||||||
|
|
||||||
static FAR const char *g_policynames[4] =
|
#if 0 /* Not used */
|
||||||
{
|
static const char g_name[] = "Name:";
|
||||||
"FIFO",
|
#endif
|
||||||
"RR ",
|
static const char g_type[] = "Type:";
|
||||||
"SPOR",
|
static const char g_state[] = "State:";
|
||||||
"OTHR"
|
static const char g_flags[] = "Flags:";
|
||||||
};
|
static const char g_priority[] = "Priority:";
|
||||||
|
static const char g_scheduler[] = "Scheduler:";
|
||||||
|
#ifndef CONFIG_DISABLE_SIGNALS
|
||||||
|
static const char g_sigmask[] = "SigMask:";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -111,23 +103,84 @@ static FAR const char *g_policynames[4] =
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: loadavg
|
* Name: ps_callback
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#ifdef NSH_HAVE_CPULOAD
|
#ifndef CONFIG_NSH_DISABLE_PS
|
||||||
static int loadavg(FAR struct nsh_vtbl_s *vtbl, , FAR const char *cmd,
|
static void nsh_parse_statusline(FAR char *line,
|
||||||
pid_t pid, FAR char *buffer, size_t buflen)
|
FAR struct nsh_taskstatus_s *status)
|
||||||
{
|
{
|
||||||
char path[24];
|
/* Parse the task status.
|
||||||
|
*
|
||||||
|
* Format:
|
||||||
|
*
|
||||||
|
* 111111111122222222223
|
||||||
|
* 123456789012345678901234567890
|
||||||
|
* Name: xxxx... Task/thread name (See CONFIG_TASK_NAME_SIZE)
|
||||||
|
* Type: xxxxxxx {Task, pthread, Kthread, Invalid}
|
||||||
|
* 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, SCHED_OTHER}
|
||||||
|
* Sigmask: nnnnnnnn Hexadecimal, 32-bit
|
||||||
|
*/
|
||||||
|
|
||||||
/* Form the full path to the 'loadavg' pseudo-file */
|
#if 0 /* Not used */
|
||||||
|
/* Task name */
|
||||||
|
|
||||||
snprintf(path, sizeof(path), CONFIG_NSH_PROC_MOUNTPOINT "/%d/loadavg",
|
if (strncmp(line, g_name, strlen(g_name)) == 0)
|
||||||
(int)pid);
|
{
|
||||||
|
/* Not used */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
/* Task/thread type */
|
||||||
|
|
||||||
/* Read the 'loadavg' pseudo-file into the user buffer */
|
if (strncmp(line, g_type, strlen(g_type)) == 0)
|
||||||
|
{
|
||||||
|
/* Save the thread type */
|
||||||
|
|
||||||
return nsh_readfile(vtbl, cmd, path, buffer, buflen);
|
status->td_type = nsh_trimspaces(&line[12]);
|
||||||
|
}
|
||||||
|
else if (strncmp(line, g_state, strlen(g_state)) == 0)
|
||||||
|
{
|
||||||
|
FAR char *ptr;
|
||||||
|
|
||||||
|
/* Save the thread state */
|
||||||
|
|
||||||
|
status->td_state = nsh_trimspaces(&line[12]);
|
||||||
|
|
||||||
|
/* Check if an event follows the state */
|
||||||
|
|
||||||
|
ptr = strchr(status->td_state, ',');
|
||||||
|
if (ptr != NULL)
|
||||||
|
{
|
||||||
|
*ptr++ = '\0';
|
||||||
|
status->td_event = nsh_trimspaces(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strncmp(line, g_flags, strlen(g_flags)) == 0)
|
||||||
|
{
|
||||||
|
status->td_flags = nsh_trimspaces(&line[12]);
|
||||||
|
}
|
||||||
|
else if (strncmp(line, g_priority, strlen(g_priority)) == 0)
|
||||||
|
{
|
||||||
|
status->td_priority = nsh_trimspaces(&line[12]);
|
||||||
|
}
|
||||||
|
else if (strncmp(line, g_scheduler, strlen(g_scheduler)) == 0)
|
||||||
|
{
|
||||||
|
/* Skip over the SCHED_ part of the policy. Resultis max 8 bytes */
|
||||||
|
|
||||||
|
status->td_policy = nsh_trimspaces(&line[12+6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_DISABLE_SIGNALS
|
||||||
|
else if (strncmp(line, g_sigmask, strlen(g_sigmask)) == 0)
|
||||||
|
{
|
||||||
|
status->td_sigmask = nsh_trimspaces(&line[12]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -136,77 +189,154 @@ static int loadavg(FAR struct nsh_vtbl_s *vtbl, , FAR const char *cmd,
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#ifndef CONFIG_NSH_DISABLE_PS
|
#ifndef CONFIG_NSH_DISABLE_PS
|
||||||
static void ps_callback(FAR struct tcb_s *tcb, FAR void *arg)
|
static int ps_callback(FAR struct nsh_vtbl_s *vtbl, FAR const char *dirpath,
|
||||||
|
FAR struct dirent *entryp, FAR void *pvarg)
|
||||||
|
|
||||||
{
|
{
|
||||||
FAR struct nsh_vtbl_s *vtbl = (FAR struct nsh_vtbl_s*)arg;
|
struct nsh_taskstatus_s status;
|
||||||
FAR const char *policy;
|
FAR char *filepath;
|
||||||
#ifdef NSH_HAVE_CPULOAD
|
FAR char *line;
|
||||||
char buffer[8];
|
FAR char *nextline;
|
||||||
int ret;
|
int ret;
|
||||||
#endif
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Show task status */
|
/* Task/thread entries in the /proc directory will all be (1) directories with
|
||||||
|
* (2) all numeric names.
|
||||||
|
*/
|
||||||
|
|
||||||
policy = g_policynames[(tcb->flags & TCB_FLAG_POLICY_MASK) >> TCB_FLAG_POLICY_SHIFT];
|
if (!DIRENT_ISDIRECTORY(entryp->d_type))
|
||||||
nsh_output(vtbl, "%5d %3d %4s %7s%c%c %8s ",
|
{
|
||||||
tcb->pid, tcb->sched_priority, policy,
|
/* Not a directory... skip this entry */
|
||||||
g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK) >> TCB_FLAG_TTYPE_SHIFT],
|
|
||||||
tcb->flags & TCB_FLAG_NONCANCELABLE ? 'N' : ' ',
|
return OK;
|
||||||
tcb->flags & TCB_FLAG_CANCEL_PENDING ? 'P' : ' ',
|
}
|
||||||
g_statenames[tcb->task_state]);
|
|
||||||
|
/* Check each character in the name */
|
||||||
|
|
||||||
|
for (i = 0; i < NAME_MAX && entryp->d_name[i] != '\0'; i++)
|
||||||
|
{
|
||||||
|
if (!isdigit(entryp->d_name[i]))
|
||||||
|
{
|
||||||
|
/* Name contains something other than a numeric character */
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set all pointers to the empty string. */
|
||||||
|
|
||||||
|
status.td_type = "";
|
||||||
|
status.td_state = "";
|
||||||
|
status.td_event = "";
|
||||||
|
status.td_flags = "";
|
||||||
|
status.td_priority = "";
|
||||||
|
status.td_policy = "";
|
||||||
|
#ifndef CONFIG_DISABLE_SIGNALS
|
||||||
|
status.td_sigmask = "";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Read the task status */
|
||||||
|
|
||||||
|
filepath = NULL;
|
||||||
|
ret = asprintf(&filepath, "%s/%s/status", dirpath, entryp->d_name);
|
||||||
|
if (ret < 0 || filepath == NULL)
|
||||||
|
{
|
||||||
|
nsh_output(vtbl, g_fmtcmdfailed, "ps", "asprintf", NSH_ERRNO);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = nsh_readfile(vtbl, "ps", filepath, vtbl->iobuffer, IOBUFFERSIZE);
|
||||||
|
free(filepath);
|
||||||
|
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
/* Parse the task status. */
|
||||||
|
|
||||||
|
nextline = vtbl->iobuffer;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* Find the beginning of the next line and NUL-terminat the
|
||||||
|
* current line.
|
||||||
|
*/
|
||||||
|
|
||||||
|
line = nextline;
|
||||||
|
for (nextline++;
|
||||||
|
*nextline != '\n' && *nextline != '\0';
|
||||||
|
nextline++);
|
||||||
|
|
||||||
|
if (*nextline == '\n')
|
||||||
|
{
|
||||||
|
*nextline++ = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextline = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the current line */
|
||||||
|
|
||||||
|
nsh_parse_statusline(line, &status);
|
||||||
|
}
|
||||||
|
while (nextline != NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, print the status information */
|
||||||
|
|
||||||
|
nsh_output(vtbl, "%5s %3s %-8s %-7s %3s %-8s %-9s ",
|
||||||
|
entryp->d_name, status.td_priority, status.td_policy,
|
||||||
|
status.td_type, status.td_flags, status.td_state,
|
||||||
|
status.td_event);
|
||||||
|
|
||||||
|
#ifndef CONFIG_DISABLE_SIGNALS
|
||||||
|
nsh_output(vtbl, "%-8s ", status.td_sigmask);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NSH_HAVE_CPULOAD
|
#ifdef NSH_HAVE_CPULOAD
|
||||||
/* Get the CPU load */
|
/* Get the CPU load */
|
||||||
|
|
||||||
ret = loadavg(vtbl, "ps", tcb->pid, buffer, sizeof(buffer));
|
filepath = NULL;
|
||||||
if (ret < 0)
|
ret = asprintf(&filepath, "%s/%s/loadavg", dirpath, entryp->d_name);
|
||||||
|
if (ret < 0 || filepath == NULL)
|
||||||
{
|
{
|
||||||
/* Make the buffer into an empty string */
|
nsh_output(vtbl, g_fmtcmdfailed, "ps", "asprintf", NSH_ERRNO);
|
||||||
|
vtbl->iobuffer[0] = '\0';
|
||||||
buffer[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
nsh_output(vtbl, "%-6s ", buffer);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Show task name and arguments */
|
|
||||||
|
|
||||||
#if CONFIG_TASK_NAME_SIZE > 0
|
|
||||||
nsh_output(vtbl, "%s(", tcb->name);
|
|
||||||
#else
|
|
||||||
nsh_output(vtbl, "<noname>(");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Is this a task or a pthread? */
|
|
||||||
|
|
||||||
#ifndef CONFIG_DISABLE_PTHREAD
|
|
||||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
|
|
||||||
{
|
|
||||||
FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)tcb;
|
|
||||||
nsh_output(vtbl, "%p", ptcb->arg);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
FAR struct task_tcb_s *ttcb = (FAR struct task_tcb_s *)tcb;
|
ret = nsh_readfile(vtbl, "ps", filepath, vtbl->iobuffer, IOBUFFERSIZE);
|
||||||
|
free(filepath);
|
||||||
|
|
||||||
/* Special case 1st argument (no comma) */
|
if (ret < 0)
|
||||||
|
|
||||||
if (ttcb->argv[1])
|
|
||||||
{
|
{
|
||||||
nsh_output(vtbl, "%p", ttcb->argv[1]);
|
vtbl->iobuffer[0] = '\0';
|
||||||
|
|
||||||
/* Then any additional arguments */
|
|
||||||
|
|
||||||
for (i = 2; ttcb->argv[i]; i++)
|
|
||||||
{
|
|
||||||
nsh_output(vtbl, ", %p", ttcb->argv[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsh_output(vtbl, ")\n");
|
nsh_output(vtbl, "%6s ", nsh_trimspaces(vtbl->iobuffer));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Read the task/tread command line */
|
||||||
|
|
||||||
|
filepath = NULL;
|
||||||
|
ret = asprintf(&filepath, "%s/%s/cmdline", dirpath, entryp->d_name);
|
||||||
|
|
||||||
|
if (ret < 0 || filepath == NULL)
|
||||||
|
{
|
||||||
|
nsh_output(vtbl, g_fmtcmdfailed, "ps", "asprintf", NSH_ERRNO);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nsh_readfile(vtbl, "ps", filepath, vtbl->iobuffer, IOBUFFERSIZE);
|
||||||
|
free(filepath);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsh_output(vtbl, "%s\n", nsh_trimspaces(vtbl->iobuffer));
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -243,13 +373,18 @@ int cmd_exec(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
|
|||||||
#ifndef CONFIG_NSH_DISABLE_PS
|
#ifndef CONFIG_NSH_DISABLE_PS
|
||||||
int cmd_ps(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
|
int cmd_ps(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
|
||||||
{
|
{
|
||||||
#ifdef NSH_HAVE_CPULOAD
|
nsh_output(vtbl, "%5s %3s %-8s %-7s %3s %-8s %-9s ",
|
||||||
nsh_output(vtbl, "PID PRI SCHD TYPE NP STATE CPU NAME\n");
|
"PID", "PRI", "POLICY", "TYPE", "NPX", "STATE", "EVENT");
|
||||||
#else
|
#ifndef CONFIG_DISABLE_SIGNALS
|
||||||
nsh_output(vtbl, "PID PRI SCHD TYPE NP STATE NAME\n");
|
nsh_output(vtbl, "%-8s ", "SIGMASK");
|
||||||
#endif
|
#endif
|
||||||
sched_foreach(ps_callback, vtbl);
|
#ifdef NSH_HAVE_CPULOAD
|
||||||
return OK;
|
nsh_output(vtbl, "%6s ", "CPU");
|
||||||
|
#endif
|
||||||
|
nsh_output(vtbl, "%s\n", "COMMAND");
|
||||||
|
|
||||||
|
return nsh_foreach_direntry(vtbl, "ps", CONFIG_NSH_PROC_MOUNTPOINT,
|
||||||
|
ps_callback, NULL);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user