This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit 740fda96302cc6f379907ce03f62d6754c657a1c Author: cuiziwei <[email protected]> AuthorDate: Tue Apr 28 17:32:22 2026 +0800 system/popen: support no-shell mode via posix_spawnp When NSH_LIBRARY is not available, dpopen()/popen() can still execute commands by splitting the command string by whitespace and calling posix_spawnp() directly. Shell syntax (pipes, redirects, globbing) is not supported in this mode. Add CONFIG_SYSTEM_POPEN_MAXARGUMENTS (default 7) to control the argv array size for the no-shell path. Remove the hard dependency on NSH_LIBRARY from SYSTEM_POPEN so the feature can be used in minimal configurations without a shell. Signed-off-by: cuiziwei <[email protected]> --- system/popen/Kconfig | 27 ++++++++++++++++++----- system/popen/dpopen.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/system/popen/Kconfig b/system/popen/Kconfig index 606efa032..6256af7c4 100644 --- a/system/popen/Kconfig +++ b/system/popen/Kconfig @@ -7,7 +7,7 @@ config SYSTEM_POPEN bool "popen()/pclose()/dpopen()/dpclose() Functions" default n select SCHED_WAITPID - depends on NSH_LIBRARY && PIPES + depends on PIPES ---help--- Enable support for the popen(), pclose(), dpopen(), and dpclose() interfaces. @@ -20,16 +20,20 @@ config SYSTEM_POPEN popen()/pclose() are thin wrappers around dpopen()/dpclose() that additionally wrap the fd in a FILE stream. - Commands are executed through the NSH shell (sh -c command), - supporting full shell syntax including pipes, redirects, - and globbing. + When NSH is available (NSH_LIBRARY), commands are executed + through the shell (sh -c command), supporting full shell + syntax including pipes, redirects, and globbing. + + When NSH is not available, commands are executed directly + via posix_spawnp(). The command string must be a simple + "program arg1 arg2" form without shell syntax. if SYSTEM_POPEN config SYSTEM_POPEN_SHPATH string "Path to shell command" default "/bin/sh" - depends on SYSTEM_NSH=m || BUILD_KERNEL + depends on (SYSTEM_NSH=m || BUILD_KERNEL) && NSH_LIBRARY ---help--- This is the full path to the program in a mounted file system that implements the system() command. That is, a program that starts the @@ -53,4 +57,17 @@ config SYSTEM_POPEN_PRIORITY ---help--- The priority of the shell. +config SYSTEM_POPEN_MAXARGUMENTS + int "Maximum number of command arguments (no-shell mode)" + default 7 + depends on !NSH_LIBRARY + ---help--- + When NSH is not available, dpopen()/popen() splits the command + string by whitespace into an argv array. This sets the maximum + number of arguments (excluding the program name and the + terminating NULL). + + This is analogous to CONFIG_NSH_MAXARGUMENTS but applies + only to the no-shell path. + endif diff --git a/system/popen/dpopen.c b/system/popen/dpopen.c index dd8bc9b7d..785fcc202 100644 --- a/system/popen/dpopen.c +++ b/system/popen/dpopen.c @@ -27,6 +27,7 @@ #include <sys/wait.h> #include <sys/ioctl.h> #include <unistd.h> +#include <string.h> #include <sched.h> #include <spawn.h> #include <debug.h> @@ -37,7 +38,17 @@ # include <sys/socket.h> #endif -#include "nshlib/nshlib.h" +#ifdef CONFIG_NSH_LIBRARY +# include "nshlib/nshlib.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_NSH_LIBRARY +# define DPOPEN_MAX_ARGV (CONFIG_SYSTEM_POPEN_MAXARGUMENTS + 1) +#endif /**************************************************************************** * Public Functions @@ -58,6 +69,10 @@ * When NSH is available, commands are executed through the shell * (sh -c command), supporting full shell syntax. * + * When NSH is not available, the command is split by whitespace and + * executed directly via posix_spawnp(). Shell syntax (pipes, redirects, + * globbing) is not supported in this mode. + * * Input Parameters: * command - The command string to execute * oflag - O_RDONLY to read child stdout, O_WRONLY to write child stdin, @@ -74,7 +89,14 @@ int dpopen(FAR const char *command, int oflag, FAR pid_t *pid) struct sched_param param; posix_spawnattr_t attr; posix_spawn_file_actions_t file_actions; +#ifdef CONFIG_NSH_LIBRARY FAR char *argv[4]; +#else + char cmdbuf[PATH_MAX]; + FAR char *argv[DPOPEN_MAX_ARGV]; + FAR char *saveptr; + int argc = 0; +#endif int fd[2]; int childfd; int parentfd; @@ -224,21 +246,52 @@ int dpopen(FAR const char *command, int oflag, FAR pid_t *pid) * appropriately. */ +#ifdef CONFIG_NSH_LIBRARY + /* Shell mode: execute command through sh -c */ + argv[1] = "-c"; argv[2] = (FAR char *)command; argv[3] = NULL; -#ifdef CONFIG_SYSTEM_POPEN_SHPATH +# ifdef CONFIG_SYSTEM_POPEN_SHPATH argv[0] = CONFIG_SYSTEM_POPEN_SHPATH; errcode = posix_spawn(pid, argv[0], &file_actions, &attr, argv, NULL); -#else +# else *pid = task_spawn("dpopen", nsh_system, &file_actions, &attr, argv + 1, NULL); if (*pid < 0) { errcode = -*pid; } +# endif +#else + /* No-shell mode: split command and execute directly via posix_spawnp. + * The command must be in "program arg1 arg2" form -- no shell syntax. + */ + + if (strlcpy(cmdbuf, command, sizeof(cmdbuf)) >= sizeof(cmdbuf)) + { + errcode = ENAMETOOLONG; + goto errout_with_actions; + } + + do + { + argv[argc] = strtok_r(argc ? NULL : cmdbuf, " \t", &saveptr); + } + while (argv[argc] != NULL && ++argc < DPOPEN_MAX_ARGV - 1); + + argv[argc] = NULL; + + if (argc == 0) + { + errcode = EINVAL; + goto errout_with_actions; + } + + errcode = posix_spawnp(pid, argv[0], &file_actions, + &attr, argv, NULL); #endif if (errcode != 0)
