From 7e7dd916ce190c85996b7790481c336949fa78bb Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 28 Nov 2015 15:03:21 -0600 Subject: [PATCH] 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. --- ChangeLog.txt | 4 + nshlib/Kconfig | 4 +- nshlib/nsh_proccmds.c | 335 +++++++++++++++++++++++++++++------------- 3 files changed, 241 insertions(+), 102 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 8c6adb3ec..4b6414f28 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1481,3 +1481,7 @@ 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 session-specific data defined in nsh_console.h (2015-11-28). + * apps/nshlib: The 'ps' command now uses /proc// 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). diff --git a/nshlib/Kconfig b/nshlib/Kconfig index 59cb27dc6..872c6c031 100644 --- a/nshlib/Kconfig +++ b/nshlib/Kconfig @@ -341,8 +341,8 @@ config NSH_DISABLE_POWEROFF config NSH_DISABLE_PS bool "Disable ps" - default n - depends on !BUILD_PROTECTED && !BUILD_KERNEL + default y if !FS_PROCFS || FS_PROCFS_EXCLUDE_PROCESS + default n if FS_PROCFS && !FS_PROCFS_EXCLUDE_PROCESS config NSH_DISABLE_PING bool "Disable ping" diff --git a/nshlib/nsh_proccmds.c b/nshlib/nsh_proccmds.c index 79cebe34c..1bb658055 100644 --- a/nshlib/nsh_proccmds.c +++ b/nshlib/nsh_proccmds.c @@ -42,8 +42,10 @@ #include #include #include +#include +#include #include -#include +#include #include #include "nsh.h" @@ -67,43 +69,33 @@ typedef int (*exec_t)(void); -/**************************************************************************** - * Private Data - ****************************************************************************/ +/* This structure represents the parsed task characteristics */ -#ifndef CONFIG_NSH_DISABLE_PS -static const char *g_statenames[] = +struct nsh_taskstatus_s { - "INVALID ", - "PENDING ", - "READY ", - "RUNNING ", - "INACTIVE", - "WAITSEM ", + FAR const char *td_type; /* Thread type */ + FAR const char *td_state; /* Thread state */ + FAR const char *td_event; /* Thread wait event */ + FAR const char *td_flags; /* Thread flags */ + FAR const char *td_priority; /* Thread priority */ + FAR const char *td_policy; /* Thread scheduler */ #ifndef CONFIG_DISABLE_SIGNALS - "WAITSIG ", -#endif -#ifndef CONFIG_DISABLE_MQUEUE - "MQNEMPTY", - "MQNFULL " + FAR const char *td_sigmask; /* Signal mask */ #endif }; -static const char *g_ttypenames[4] = -{ - "TASK ", - "PTHREAD", - "KTHREAD", - "--?-- " -}; +/* Status strings */ -static FAR const char *g_policynames[4] = -{ - "FIFO", - "RR ", - "SPOR", - "OTHR" -}; +#if 0 /* Not used */ +static const char g_name[] = "Name:"; +#endif +static const char g_type[] = "Type:"; +static const char g_state[] = "State:"; +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 /**************************************************************************** @@ -111,23 +103,84 @@ static FAR const char *g_policynames[4] = ****************************************************************************/ /**************************************************************************** - * Name: loadavg + * Name: ps_callback ****************************************************************************/ -#ifdef NSH_HAVE_CPULOAD -static int loadavg(FAR struct nsh_vtbl_s *vtbl, , FAR const char *cmd, - pid_t pid, FAR char *buffer, size_t buflen) +#ifndef CONFIG_NSH_DISABLE_PS +static void nsh_parse_statusline(FAR char *line, + 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", - (int)pid); + if (strncmp(line, g_name, strlen(g_name)) == 0) + { + /* 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 @@ -136,77 +189,154 @@ static int loadavg(FAR struct nsh_vtbl_s *vtbl, , FAR const char *cmd, ****************************************************************************/ #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; - FAR const char *policy; -#ifdef NSH_HAVE_CPULOAD - char buffer[8]; + struct nsh_taskstatus_s status; + FAR char *filepath; + FAR char *line; + FAR char *nextline; int ret; -#endif 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]; - nsh_output(vtbl, "%5d %3d %4s %7s%c%c %8s ", - tcb->pid, tcb->sched_priority, policy, - g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK) >> TCB_FLAG_TTYPE_SHIFT], - tcb->flags & TCB_FLAG_NONCANCELABLE ? 'N' : ' ', - tcb->flags & TCB_FLAG_CANCEL_PENDING ? 'P' : ' ', - g_statenames[tcb->task_state]); + if (!DIRENT_ISDIRECTORY(entryp->d_type)) + { + /* Not a directory... skip this entry */ + + return OK; + } + + /* 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 /* Get the CPU load */ - ret = loadavg(vtbl, "ps", tcb->pid, buffer, sizeof(buffer)); - if (ret < 0) + filepath = NULL; + ret = asprintf(&filepath, "%s/%s/loadavg", dirpath, entryp->d_name); + if (ret < 0 || filepath == NULL) { - /* Make the buffer into an empty string */ - - 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, "("); -#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); + nsh_output(vtbl, g_fmtcmdfailed, "ps", "asprintf", NSH_ERRNO); + vtbl->iobuffer[0] = '\0'; } 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 (ttcb->argv[1]) + if (ret < 0) { - nsh_output(vtbl, "%p", ttcb->argv[1]); - - /* Then any additional arguments */ - - for (i = 2; ttcb->argv[i]; i++) - { - nsh_output(vtbl, ", %p", ttcb->argv[i]); - } + vtbl->iobuffer[0] = '\0'; } } - 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 @@ -243,13 +373,18 @@ int cmd_exec(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) #ifndef CONFIG_NSH_DISABLE_PS int cmd_ps(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) { -#ifdef NSH_HAVE_CPULOAD - nsh_output(vtbl, "PID PRI SCHD TYPE NP STATE CPU NAME\n"); -#else - nsh_output(vtbl, "PID PRI SCHD TYPE NP STATE NAME\n"); + nsh_output(vtbl, "%5s %3s %-8s %-7s %3s %-8s %-9s ", + "PID", "PRI", "POLICY", "TYPE", "NPX", "STATE", "EVENT"); +#ifndef CONFIG_DISABLE_SIGNALS + nsh_output(vtbl, "%-8s ", "SIGMASK"); #endif - sched_foreach(ps_callback, vtbl); - return OK; +#ifdef NSH_HAVE_CPULOAD + 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