************************* Customizing the NuttShell ************************* **Overview.** The NuttShell (NSH) is a simple shell application that may be used with NuttX. It supports a variety of commands and is (very) loosely based on the Bash shell and the common utilities used with Bash shell programming. The paragraphs in this appendix will focus on customizing NSH: Adding new commands, changing the initialization sequence, etc. The NSH Library and NSH Initialization ************************************** **Overview.** NSH is implemented as a library that can be found at ``apps/nshlib``. As a library, it can be custom built into any application that follows the NSH initialization sequence described below. As an example, the code at ``apps/examples/nsh/nsh_main.c`` illustrates how to start NSH and the logic there was intended to be incorporated into your own custom code. Although code was generated simply as an example, in the end most people just use this example code as their application ``main()`` function. That initialization performed by that example is discussed in the following paragraphs. NSH Initialization sequence ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The NSH start-up sequence is very simple. As an example, the code at ``apps/system/nsh/nsh_main.c`` illustrates how to start NSH. It simple does the following: #. This function calls ``nsh_initialize()`` which initializes the NSH library. ``nsh_initialize()`` is described in more detail below. #. If the Telnetconsole is enabled, it calls ``nsh_telnetstart()`` which resides in the NSH library. ``nsh_telnetstart()`` will start the Telnet daemon that will listen for Telnet connections and start remote NSH sessions. #. If a local console is enabled (probably on a serial port), then ``nsh_consolemain()`` is called. ``nsh_consolemain()`` also resides in the NSH library. ``nsh_consolemain()`` does not return so that finished the entire NSH initialization sequence. ``nsh_initialize()`` ~~~~~~~~~~~~~~~~~~~~ The NSH initialization function, ``nsh_initialize()``, be found in ``apps/nshlib/nsh_init.c``. It does only four things: #. ``nsh_romfsetc()``: If so configured, it executes NSH system init and start-up script that can be found at ``/etc/init.d/rc.sysinit`` and ``/etc/init.d/rcS`` in the target file system. The ``nsh_romfsetc()`` function can be found in ``apps/nshlib/nsh_romfsetc.c``. This function will (1) register a ROMFS file system, then (2) mount the ROMFS file system. ``/etc`` is the default location where a read-only, ROMFS file system is mounted by ``nsh_romfsetc()``. The ROMFS image is, itself, just built into the firmware. By default, this ``rc.sysinit`` system init script contains the following logic:: # Create a RAMDISK and mount it at XXXRDMOUNTPOINTXXX mkrd -m XXXMKRDMINORXXX -s XXMKRDSECTORSIZEXXX XXMKRDBLOCKSXXX mkfatfs /dev/ramXXXMKRDMINORXXX mount -t vfat /dev/ramXXXMKRDMINORXXX XXXRDMOUNTPOINTXXX Where the ``XXXX*XXXX`` strings get replaced in the template when the ROMFS image is created: - ``XXXMKRDMINORXXX`` will become the RAM device minor number. Default: 0 - ``XXMKRDSECTORSIZEXXX`` will become the RAM device sector size - ``XXMKRDBLOCKSXXX`` will become the number of sectors in the device. - ``XXXRDMOUNTPOINTXXX`` will become the configured mount point. Default: ``/etc`` By default, the substituted values would yield an ``rc.sysinit`` file like:: # Create a RAMDISK and mount it at /tmp mkrd -m 1 -s 512 1024 mkfatfs /dev/ram1 mount -t vfat /dev/ram1 /tmp This script will, then: - Create a RAMDISK of size 512*1024 bytes at ``/dev/ram1``, - Format a FAT file system on the RAM disk at ``/dev/ram1``, and then - Mount the FAT file system at a configured mountpoint, ``/tmp``. This ``rc.sysinit.template`` template file can be found at ``apps/nshlib/rc.sysinit.template``. The resulting ROMFS file system can be found in ``apps/nshlib/nsh_romfsimg.h``. #. ``board_app_initialize()``: Next any architecture-specific NSH initialization will be performed (if any). For the STM3240G-EVAL, this architecture specific initialization can be found at ``boards/arm/stm32/stm3240g-eval/src/stm32_appinit.c``. This it does things like: (1) Initialize SPI devices, (2) Initialize SDIO, and (3) mount any SD cards that may be inserted. #. ``nsh_netinit()``: The ``nsh_netinit()`` function can be found in ``apps/nshlib/nsh_netinit.c``. #. The start-up script ``rcS`` is executed after the system-init script to startup some application and other system service. This ``rcS`` template file can be found at ``apps/nshlib/rcS.template``. The resulting ROMFS file system can be found in ``apps/nshlib/nsh_romfsimg.h``. NSH Commands ************ **Overview.** NSH supports a variety of commands as part of the NSH program. All of the NSH commands are listed in the NSH documentation `above <#cmdoverview>`__. Not all of these commands may be available at any time, however. Many commands depend upon certain NuttX configuration options. You can enter the help command at the NSH prompt to see the commands actual available: For example, if network support is disabled, then all network-related commands will be missing from the list of commands presented by '``nsh> help``'. You can see the specific command dependencies in the table `above <#cmddependencies>`__. Adding New NSH Commands ~~~~~~~~~~~~~~~~~~~~~~~ New commands can be added to the NSH very easily. You simply need to add two things: #. The implementation of your command, and #. A new entry in the NSH command table **Implementation of Your Command.** For example, if you want to add a new a new command called ``mycmd`` to NSH, you would first implement the ``mycmd`` code in a function with this prototype: .. code-block:: c int cmd_mycmd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); The ``argc`` and ``argv`` are used to pass command line arguments to the NSH command. Command line parameters are passed in a very standard way: ``argv[0]`` will be the name of the command, and ``argv[1]`` through ``argv[argc-1]`` are the additional arguments provided on the NSH command line. The first parameter, ``vtbl``, is special. This is a pointer to session-specific state information. You don't need to know the contents of the state information, but you do need to pass this ``vtbl`` argument when you interact with the NSH logic. The only use you will need to make of the ``vtbl`` argument will be for outputting data to the console. You don't use ``printf()`` within NSH commands. Instead you would use: .. code-block:: c void nsh_output(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); So if you only wanted to output "Hello, World!" on the console, then your whole command implementation might be: .. code-block:: c int cmd_mycmd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) { nsh_output(vtbl, "Hello, World!"); return 0; } The prototype for the new command should be placed in ``apps/examples/nshlib/nsh.h``. **Adding You Command to the NSH Command Table**. All of the commands support by NSH appear in a single table called: .. code-block:: c const struct cmdmap_s g_cmdmap[] That table can be found in the file ``apps/examples/nshlib/nsh_parse.c``. The structure ``cmdmap_s`` is also defined in ``apps/nshlib/nsh_parse.c``: .. code-block:: c struct cmdmap_s { const char *cmd; /* Name of the command */ cmd_t handler; /* Function that handles the command */ uint8_t minargs; /* Minimum number of arguments (including command) */ uint8_t maxargs; /* Maximum number of arguments (including command) */ const char *usage; /* Usage instructions for 'help' command */ }; This structure provides everything that you need to describe your command: Its name (``cmd``), the function that handles the command (``cmd_mycmd()``), the minimum and maximum number of arguments needed by the command, and a string describing the command line arguments. That last string is what is printed when enter "``nsh> help``". So, for you sample command, you would add the following the to the ``g_cmdmap[]`` table: .. code-block:: c { "mycmd", cmd_mycmd, 1, 1, NULL }, This entry is particularly simply because ``mycmd`` is so simple. Look at the other commands in ``g_cmdmap[]`` for more complex examples.