diff --git a/Documentation/applications/system/toybox/index.rst b/Documentation/applications/system/toybox/index.rst new file mode 100644 index 0000000000000..9e345f3ae6559 --- /dev/null +++ b/Documentation/applications/system/toybox/index.rst @@ -0,0 +1,54 @@ +=================================== +``toybox`` Toybox Command Suite +=================================== + +Overview +======== + +Toybox (https://landley.net/toybox/) is a 0BSD-licensed multi-call binary +providing a POSIX/LSB command suite and a small interactive shell. The +``toybox`` application downloads a pinned upstream release during the +build, patches it for NuttX, and builds it as a single NuttX task +(``PROGNAME=toybox``). + +Run with no arguments it acts as an interactive ``toybox>`` command +prompt, suitable for use as the system's ``CONFIG_INIT_ENTRYPOINT``. Run +with arguments (``toybox ls``, ``toybox cat file``) it runs one command +and exits, which is how it is invoked as an NSH builtin. + +Toybox does not depend on NSH in either direction. When both are enabled, +each can invoke the other at runtime: NSH via its own +``CONFIG_NSH_BUILTIN_APPS``, Toybox via ``CONFIG_SYSTEM_TOYBOX_BUILTIN_BRIDGE``. + +Configuration +============= + +Enable the application with ``CONFIG_SYSTEM_TOYBOX``, under +:menuselection:`System Libraries and NSH Add-Ons` in ``menuconfig``. + +- ``CONFIG_SYSTEM_TOYBOX_VERSION`` -- upstream release tag to download +- ``CONFIG_SYSTEM_TOYBOX_PRIORITY``, ``CONFIG_SYSTEM_TOYBOX_STACKSIZE`` +- ``CONFIG_SYSTEM_TOYBOX_BUILTIN_BRIDGE`` -- run other NuttX builtin + applications (``nsh``, ``hello``, ...) from the Toybox prompt + +Which commands are built in is selected individually under the "Toybox +commands" submenu (one ``CONFIG_SYSTEM_TOYBOX_CMD_`` option per +applet). + +Usage +===== + +.. code-block:: console + + nsh> toybox ls / + nsh> toybox + +The second form starts the interactive prompt; ``exit`` or ``quit`` +leaves it. + +Known limitations +================= + +- ``ps`` builds but lists no processes: it parses Linux's + ``/proc//stat``, which has no equivalent in NuttX's procfs. +- ``grep -r`` is unreliable against procfs. diff --git a/boards/arm/stm32f4/stm32f4discovery/configs/toybox/defconfig b/boards/arm/stm32f4/stm32f4discovery/configs/toybox/defconfig new file mode 100644 index 0000000000000..d8fad21601d21 --- /dev/null +++ b/boards/arm/stm32f4/stm32f4discovery/configs/toybox/defconfig @@ -0,0 +1,53 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_FPU is not set +CONFIG_ALLOW_MIT_COMPONENTS=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="stm32f4discovery" +CONFIG_ARCH_BOARD_STM32F4_DISCOVERY=y +CONFIG_ARCH_BUTTONS=y +CONFIG_ARCH_CHIP="stm32f4" +CONFIG_ARCH_CHIP_STM32=y +CONFIG_ARCH_CHIP_STM32F407VG=y +CONFIG_ARCH_CHIP_STM32F4=y +CONFIG_ARCH_SETJMP_H=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_EXAMPLES_HELLO=y +CONFIG_FS_NOTIFY=y +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_HOST_WINDOWS=y +CONFIG_INIT_ENTRYPOINT="toybox_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_LIBC_EXECFUNCS=y +CONFIG_LIBC_LOCALE=y +CONFIG_LIBC_LOCALTIME=y +CONFIG_LINE_MAX=64 +CONFIG_MM_REGIONS=2 +CONFIG_PIPES=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PSEUDOFS_SOFTLINKS=y +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HAVE_PARENT=y +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_STM32_JTAG_SW_ENABLE=y +CONFIG_STM32_PWR=y +CONFIG_STM32_SPI1=y +CONFIG_STM32_USART2=y +CONFIG_SYSTEM_TOYBOX=y +CONFIG_USART2_RXBUFSIZE=128 +CONFIG_USART2_SERIAL_CONSOLE=y +CONFIG_USART2_TXBUFSIZE=128 diff --git a/boards/sim/sim/sim/configs/toybox/defconfig b/boards/sim/sim/sim/configs/toybox/defconfig new file mode 100644 index 0000000000000..b8a6c76c23267 --- /dev/null +++ b/boards/sim/sim/sim/configs/toybox/defconfig @@ -0,0 +1,79 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +CONFIG_ARCH="sim" +CONFIG_ARCH_BOARD="sim" +CONFIG_ARCH_BOARD_SIM=y +CONFIG_ARCH_CHIP="sim" +CONFIG_ARCH_SIM=y +CONFIG_BOARDCTL=y +CONFIG_BOARDCTL_APP_SYMTAB=y +CONFIG_BOARDCTL_MKRD=y +CONFIG_BOARDCTL_POWEROFF=y +CONFIG_BOARD_LOOPSPERMSEC=0 +CONFIG_BOOT_RUNFROMEXTSRAM=y +CONFIG_COVERAGE_ALL=y +CONFIG_COVERAGE_TOOLCHAIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_ASSERTIONS_EXPRESSION=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEV_GPIO=y +CONFIG_DEV_LOOP=y +CONFIG_ETC_FATDEVNO=2 +CONFIG_ETC_ROMFS=y +CONFIG_ETC_ROMFSDEVNO=1 +CONFIG_EXAMPLES_GPIO=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FS_BINFS=y +CONFIG_FS_FAT=y +CONFIG_FS_HOSTFS=y +CONFIG_FS_PROCFS=y +CONFIG_FS_RAMMAP=y +CONFIG_FS_ROMFS=y +CONFIG_GPIO_LOWER_HALF=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=4096 +CONFIG_INIT_ENTRYPOINT="toybox_main" +CONFIG_IOEXPANDER=y +CONFIG_IOEXPANDER_DUMMY=y +CONFIG_LIBC_ENVPATH=y +CONFIG_LIBC_EXECFUNCS=y +CONFIG_LIBC_LOCALE=y +CONFIG_LIBC_LOCALE_CATALOG=y +CONFIG_LIBC_LOCALE_GETTEXT=y +CONFIG_LIBC_LOCALTIME=y +CONFIG_LIBC_MAX_EXITFUNS=1 +CONFIG_LIBC_NUMBERED_ARGS=y +CONFIG_NDEBUG=y +CONFIG_PATH_INITIAL="/bin" +CONFIG_PIPES=y +CONFIG_PSEUDOFS_ATTRIBUTES=y +CONFIG_PSEUDOFS_FILE=y +CONFIG_PSEUDOFS_SOFTLINKS=y +CONFIG_READLINE_CMD_HISTORY=y +CONFIG_READLINE_EDIT_EMACS=y +CONFIG_READLINE_TABCOMPLETION=y +CONFIG_RTC=y +CONFIG_RTC_ARCH=y +CONFIG_RTC_DRIVER=y +CONFIG_SCHED_BACKTRACE=y +CONFIG_SCHED_EVENTS=y +CONFIG_SCHED_HAVE_PARENT=y +CONFIG_SCHED_WAITPID=y +CONFIG_SIM_HOSTFS=y +CONFIG_SIM_WALLTIME_SIGNAL=y +CONFIG_START_MONTH=6 +CONFIG_START_YEAR=2008 +CONFIG_SYSTEM_CLE=y +CONFIG_SYSTEM_DUMPSTACK=y +CONFIG_SYSTEM_GCOV=y +CONFIG_SYSTEM_READLINE=y +CONFIG_SYSTEM_TOYBOX=y +CONFIG_TESTING_OSTEST=y diff --git a/fs/inode/fs_inodesearch.c b/fs/inode/fs_inodesearch.c index 1aecb529ad01e..67a14cf474eef 100644 --- a/fs/inode/fs_inodesearch.c +++ b/fs/inode/fs_inodesearch.c @@ -558,15 +558,32 @@ FAR const char *inode_nextname(FAR const char *name) name++; } - /* Skip single '.' path segment, but not '..' */ + /* Skip single '.' path segment, but not '..'. This includes a lone + * trailing '.' as the final path component (e.g. "/foo/."), which + * refers to "foo" itself the same way "/foo/./" would -- without this, + * a trailing '.' is instead treated as a literal child name to look up + * under "foo" and fails to resolve, since no real node is ever named + * ".", rather than resolving to the node the search already reached. + */ - if (*name == '.' && *(name + 1) == '/') + if (*name == '.' && (*(name + 1) == '/' || *(name + 1) == '\0')) { - /* If there is a '/' after '.', - * continue searching from the next character - */ + if (*(name + 1) == '/') + { + /* If there is a '/' after '.', + * continue searching from the next character + */ - name = inode_nextname(name); + name = inode_nextname(name); + } + else + { + /* Lone trailing '.': point past it, at the terminating NUL, + * the same as if the path had ended one character earlier. + */ + + name++; + } } return name; diff --git a/include/sys/wait.h b/include/sys/wait.h index 4f653a8fbe426..f693436cf4227 100644 --- a/include/sys/wait.h +++ b/include/sys/wait.h @@ -96,6 +96,15 @@ pid_t wait(FAR int *stat_loc); int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options); pid_t waitpid(pid_t pid, FAR int *stat_loc, int options); +/* wait4() is waitpid() plus the reaped child's struct rusage in one call. + * struct rusage is forward-declared here (not #include ) + * since it is only ever used as a pointer type in this prototype. + */ + +struct rusage; +pid_t wait4(pid_t pid, FAR int *stat_loc, int options, + FAR struct rusage *rusage); + #undef EXTERN #if defined(__cplusplus) } diff --git a/libs/libc/grp/lib_getgrbufr.c b/libs/libc/grp/lib_getgrbufr.c index 80bbd3b25dea3..0dd3e2594d86b 100644 --- a/libs/libc/grp/lib_getgrbufr.c +++ b/libs/libc/grp/lib_getgrbufr.c @@ -77,7 +77,15 @@ int getgrbuf_r(gid_t gid, FAR const char *name, FAR const char *passwd, namesize = strlen(name) + 1; passwdsize = strlen(passwd) + 1; - padlen = sizeof(FAR void *) - ((uintptr_t)buf % sizeof(FAR char *)); + + /* Bytes needed to round 'buf' up to the next pointer-aligned address. + * The two's-complement modulo trick below yields 0 when 'buf' is + * already aligned; "sizeof(void *) - (addr % sizeof(void *))" (the + * previous formula) does not, always returning a full alignment unit + * in that case, which made the buflen check below always fail. + */ + + padlen = (-(uintptr_t)buf) % sizeof(FAR void *); reqdlen = sizeof(FAR void *) + namesize + passwdsize; if (buflen < padlen + reqdlen) diff --git a/libs/libc/unistd/CMakeLists.txt b/libs/libc/unistd/CMakeLists.txt index eb4ca8e5e937d..6950a69d4efed 100644 --- a/libs/libc/unistd/CMakeLists.txt +++ b/libs/libc/unistd/CMakeLists.txt @@ -76,6 +76,10 @@ set(SRCS lib_confstr.c lib_ulimit.c) +if(CONFIG_SCHED_WAITPID) + list(APPEND SRCS lib_wait4.c) +endif() + if(NOT CONFIG_SCHED_USER_IDENTITY) list( APPEND diff --git a/libs/libc/unistd/Make.defs b/libs/libc/unistd/Make.defs index e3e5ed3a2148d..c78e7640c9e09 100644 --- a/libs/libc/unistd/Make.defs +++ b/libs/libc/unistd/Make.defs @@ -36,6 +36,10 @@ CSRCS += lib_getsid.c lib_getgroups.c lib_setpgid.c lib_setsid.c CSRCS += lib_lockf.c lib_flock.c lib_getpass.c CSRCS += lib_chdir.c lib_fchdir.c lib_confstr.c lib_ulimit.c +ifeq ($(CONFIG_SCHED_WAITPID),y) +CSRCS += lib_wait4.c +endif + ifneq ($(CONFIG_SCHED_USER_IDENTITY),y) CSRCS += lib_setuid.c lib_setgid.c lib_getuid.c lib_getgid.c CSRCS += lib_seteuid.c lib_setegid.c lib_geteuid.c lib_getegid.c diff --git a/libs/libc/unistd/lib_wait4.c b/libs/libc/unistd/lib_wait4.c new file mode 100644 index 0000000000000..747eeb3d3f3d6 --- /dev/null +++ b/libs/libc/unistd/lib_wait4.c @@ -0,0 +1,64 @@ +/**************************************************************************** + * libs/libc/unistd/lib_wait4.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 + +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: wait4 + * + * Description: + * wait4() is waitpid() plus the reaped child's resource usage in one + * call. Built entirely on top of the existing waitpid()/getrusage() + * primitives (each already correctly proxied across build separation -- + * waitpid() is a real syscall, getrusage() is plain libc), so this needs + * no syscall plumbing of its own and works unmodified in flat, + * protected, and kernel builds alike. + * + * NuttX's getrusage() only supports RUSAGE_SELF/RUSAGE_CHILDREN (not a + * specific pid), so the returned usage is exact only when the caller has + * a single outstanding child at a time. + * + ****************************************************************************/ + +pid_t wait4(pid_t pid, FAR int *stat_loc, int options, + FAR struct rusage *rusage) +{ + pid_t ret = waitpid(pid, stat_loc, options); + + if (ret > 0 && rusage != NULL) + { + getrusage(RUSAGE_CHILDREN, rusage); + } + + return ret; +}