diff options
author | patacongo <patacongo@7fd9a85b-ad96-42d3-883c-3090e2eb8679> | 2011-12-19 19:24:09 +0000 |
---|---|---|
committer | patacongo <patacongo@7fd9a85b-ad96-42d3-883c-3090e2eb8679> | 2011-12-19 19:24:09 +0000 |
commit | add995c32e86f9de8fa8fc05172435332c25a895 (patch) | |
tree | 0191fde92a5c4dcd55a24b2aa760fa4c88713242 /apps/nshlib | |
download | px4-firmware-add995c32e86f9de8fa8fc05172435332c25a895.tar.gz px4-firmware-add995c32e86f9de8fa8fc05172435332c25a895.tar.bz2 px4-firmware-add995c32e86f9de8fa8fc05172435332c25a895.zip |
Completes coding of the PWM module
git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@4200 7fd9a85b-ad96-42d3-883c-3090e2eb8679
Diffstat (limited to 'apps/nshlib')
-rw-r--r-- | apps/nshlib/Makefile | 124 | ||||
-rw-r--r-- | apps/nshlib/README.txt | 1049 | ||||
-rw-r--r-- | apps/nshlib/nsh.h | 497 | ||||
-rw-r--r-- | apps/nshlib/nsh_apps.c | 120 | ||||
-rw-r--r-- | apps/nshlib/nsh_dbgcmds.c | 355 | ||||
-rw-r--r-- | apps/nshlib/nsh_ddcmd.c | 640 | ||||
-rw-r--r-- | apps/nshlib/nsh_envcmds.c | 336 | ||||
-rw-r--r-- | apps/nshlib/nsh_fscmds.c | 1341 | ||||
-rw-r--r-- | apps/nshlib/nsh_mmcmds.c | 95 | ||||
-rw-r--r-- | apps/nshlib/nsh_netcmds.c | 863 | ||||
-rw-r--r-- | apps/nshlib/nsh_netinit.c | 170 | ||||
-rw-r--r-- | apps/nshlib/nsh_parse.c | 1336 | ||||
-rw-r--r-- | apps/nshlib/nsh_proccmds.c | 309 | ||||
-rw-r--r-- | apps/nshlib/nsh_romfsetc.c | 124 | ||||
-rw-r--r-- | apps/nshlib/nsh_romfsimg.h | 89 | ||||
-rw-r--r-- | apps/nshlib/nsh_serial.c | 508 | ||||
-rw-r--r-- | apps/nshlib/nsh_telnetd.c | 853 | ||||
-rw-r--r-- | apps/nshlib/nsh_test.c | 437 | ||||
-rw-r--r-- | apps/nshlib/nsh_timcmds.c | 330 | ||||
-rw-r--r-- | apps/nshlib/rcS.template | 5 |
20 files changed, 9581 insertions, 0 deletions
diff --git a/apps/nshlib/Makefile b/apps/nshlib/Makefile new file mode 100644 index 000000000..91ffc2c5e --- /dev/null +++ b/apps/nshlib/Makefile @@ -0,0 +1,124 @@ +############################################################################ +# apps/nshlib/Makefile +# +# Copyright (C) 2011 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt <spudmonkey@racsa.co.cr> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# NSH Library + +ASRCS = +CSRCS = nsh_parse.c nsh_fscmds.c nsh_ddcmd.c nsh_proccmds.c nsh_mmcmds.c \ + nsh_envcmds.c nsh_dbgcmds.c + +ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) +CSRCS += nsh_apps.c +endif + +ifeq ($(CONFIG_NSH_ROMFSETC),y) +CSRCS += nsh_romfsetc.c +endif + +ifeq ($(CONFIG_NET),y) +CSRCS += nsh_netinit.c nsh_netcmds.c +endif + +ifeq ($(CONFIG_RTC),y) +CSRCS += nsh_timcmds.c +endif + +ifeq ($(CONFIG_NSH_CONSOLE),y) +CSRCS += nsh_serial.c +endif + +ifeq ($(CONFIG_NSH_TELNET),y) +CSRCS += nsh_telnetd.c +endif + +ifneq ($(CONFIG_NSH_DISABLESCRIPT),y) +CSRCS += nsh_test.c +endif + + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(WINTOOL),y) + BIN = "${shell cygpath -w $(APPDIR)/libapps$(LIBEXT)}" +else + BIN = "$(APPDIR)/libapps$(LIBEXT)" +endif + +ROOTDEPPATH = --dep-path . +VPATH = + +# Build targets + +all: .built +.PHONY: context .depend depend clean distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + @( for obj in $(OBJS) ; do \ + $(call ARCHIVE, $(BIN), $${obj}); \ + done ; ) + @touch .built + +context: + +.depend: Makefile $(SRCS) + @$(MKDEP) $(ROOTDEPPATH) \ + $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + @rm -f *.o *~ .*.swp .built + $(call CLEAN) + +distclean: clean + @rm -f Make.dep .depend + +-include Make.dep + diff --git a/apps/nshlib/README.txt b/apps/nshlib/README.txt new file mode 100644 index 000000000..fad1ac104 --- /dev/null +++ b/apps/nshlib/README.txt @@ -0,0 +1,1049 @@ +apps/nshlib +^^^^^^^^^^^ + + This directory contains the NuttShell (NSH) library. This library can be + linked with other logic to provide a simple shell application for NuttX. + + - Console/NSH Front End + - Command Overview + - Conditional Command Execution + - Built-In Variables + - Current Working Directory + Environment Variables + - NSH Start-Up Script + - Simple Commands + - NSH Configuration Settings + Command Dependencies on Configuration Settings + NSH-Specific Configuration Settings + - Common Problems + +Console/NSH Front End +^^^^^^^^^^^^^^^^^^^^^ + + Using settings in the configuration file, NSH may be configured to + use either the serial stdin/out or a telnet connection as the console + or BOTH. When NSH is started, you will see the following welcome on + either console: + + NuttShell (NSH) + nsh> + + 'nsh>' is the NSH prompt and indicates that you may enter a command + from the console. + +Command Overview +^^^^^^^^^^^^^^^^ + + This directory contains the NuttShell (NSH). This is a simple + shell-like application. At present, NSH supports the following commands + forms: + + Simple command: <cmd> + Command with re-directed output: <cmd> > <file> + <cmd> >> <file> + Background command: <cmd> & + Re-directed background command: <cmd> > <file> & + <cmd> >> <file> & + + Where: + + <cmd> is any one of the simple commands listed later. + <file> is the full or relative path to any writable object + in the filesystem name space (file or character driver). + Such objects will be referred to simply as files throughout + this README. + + NSH executes at the mid-priority (128). Backgrounded commands can + be made to execute at higher or lower priorities using nice: + + [nice [-d <niceness>>]] <cmd> [> <file>|>> <file>] [&] + + Where <niceness> is any value between -20 and 19 where lower + (more negative values) correspond to higher priorities. The + default niceness is 10. + +Conditional Command Execution +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + An if-then[-else]-fi construct is also supported in order to + support conditional execution of commands. This works from the + command line but is primarily intended for use within NSH scripts + (see the sh commnd). The syntax is as follows: + + if <cmd> + then + [sequence of <cmd>] + else + [sequence of <cmd>] + fi + +Built-In Variables +^^^^^^^^^^^^^^^^^^ + + $? - The result of the last simple command execution + +Current Working Directory +^^^^^^^^^^^^^^^^^^^^^^^^^ + + All path arguments to commands may be either an absolute path or a + path relative to the current working directory. The current working + directory is set using the 'cd' command and can be queried either + by using the 'pwd' command or by using the 'echo $PWD' command. + + Environment Variables: + ---------------------- + + PWD - The current working directory + OLDPWD - The previous working directory + +NSH Start-Up Script +^^^^^^^^^^^^^^^^^^^ + +NSH supports options to provide a start up script for NSH. In general +this capability is enabled with CONFIG_NSH_ROMFSETC, but has +several other related configuration options as described in the final +section of this README. This capability also depends on: + + - CONFIG_DISABLE_MOUNTPOINT not set + - CONFIG_NFILE_DESCRIPTORS < 4 + - CONFIG_FS_ROMFS + +Default Start-Up Behavior +------------------------- + +The implementation that is provided is intended to provide great flexibility +for the use of Start-Up files. This paragraph will discuss the general +behavior when all of the configuration options are set to the default +values. + +In this default case, enabling CONFIG_NSH_ROMFSETC will cause +NSH to behave as follows at NSH startup time: + +- NSH will create a read-only RAM disk (a ROM disk), containing a tiny + ROMFS filesystem containing the following: + + |--init.d/ + `-- rcS + + Where rcS is the NSH start-up script + +- NSH will then mount the ROMFS filesystem at /etc, resulting in: + + |--dev/ + | `-- ram0 + `--etc/ + `--init.d/ + `-- rcS + +- By default, the contents of rcS script are: + + # Create a RAMDISK and mount it at XXXRDMOUNTPOUNTXXX + + mkrd -m 1 -s 512 1024 + mkfatfs /dev/ram1 + mount -t vfat /dev/ram1 /tmp + +- NSH will execute the script at /etc/init.d/rcS at start-up (before the + first NSH prompt. After execution of the script, the root FS will look + like: + + |--dev/ + | |-- ram0 + | `-- ram1 + |--etc/ + | `--init.d/ + | `-- rcS + `--tmp/ + +Modifying the ROMFS Image +------------------------- + +The contents of the /etc directory are retained in the file +apps/nshlib/nsh_romfsimg.h (OR, if CONFIG_NSH_ARCHROMFS +is defined, include/arch/board/rcs.template). In order to modify +the start-up behavior, there are three things to study: + +1. Configuration Options. + The additional CONFIG_NSH_ROMFSETC configuration options + discussed in the final section of this README. + +2. tools/mkromfsimg.sh Script. + The script tools/mkromfsimg.sh creates nsh_romfsimg.h. + It is not automatically executed. If you want to change the + configuration settings associated with creating and mounting + the /tmp directory, then it will be necessary to re-generate + this header file using the mkromfsimg.sh script. + + The behavior of this script depends upon three things: + + - The configuration settings of the installed NuttX configuration. + - The genromfs tool (available from http://romfs.sourceforge.net). + - The file apps/nshlib/rcS.template (OR, if + CONFIG_NSH_ARCHROMFS is defined, include/arch/board/rcs.template) + +3. rcS.template. + The file apps/nshlib/rcS.template contains the general form + of the rcS file; configured values are plugged into this + template file to produce the final rcS file. + +NOTE: + + apps/nshlib/rcS.template generates the standard, default + nsh_romfsimg.h file. If CONFIG_NSH_ARCHROMFS is defined + in the NuttX configuration file, then a custom, board-specific + nsh_romfsimg.h file residing in configs/<board>/include will be + used. NOTE when the OS is configured, include/arch/board will + be linked to configs/<board>/include. + +All of the startup-behavior is contained in rcS.template. The +role of mkromfsimg.sh is to (1) apply the specific configuration +settings to rcS.template to create the final rcS, and (2) to +generate the header file nsh_romfsimg.h containg the ROMFS +file system image. + +Simple Commands +^^^^^^^^^^^^^^^ + +o [ <expression> ] +o test <expression> + + These are two alternative forms of the same command. They support + evaluation of a boolean expression which sets $?. This command + is used most frequently as the conditional command following the + 'if' in the if-then[-else]-fi construct. + + Expression Syntax: + ------------------ + + expression = simple-expression | !expression | + expression -o expression | expression -a expression + + simple-expression = unary-expression | binary-expression + + unary-expression = string-unary | file-unary + + string-unary = -n string | -z string + + file-unary = -b file | -c file | -d file | -e file | -f file | + -r file | -s file | -w file + + binary-expression = string-binary | numeric-binary + + string-binary = string = string | string == string | string != string + + numeric-binary = integer -eq integer | integer -ge integer | + integer -gt integer | integer -le integer | + integer -lt integer | integer -ne integer + +o cat <path> [<path> [<path> ...]] + + This command copies and concatentates all of the files at <path> + to the console (or to another file if the output is redirected). + +o cd [<dir-path>|-|~|..] + + Changes the current working directory (PWD). Also sets the + previous working directory environment variable (OLDPWD). + + FORMS: + ------ + + 'cd <dir-path>' sets the current working directory to <dir-path>. + 'cd -' sets the current working directory to the previous + working directory ($OLDPWD). Equivalent to 'cd $OLDPWD'. + 'cd' or 'cd ~' set the current working directory to the 'home' + directory. The 'home' directory can be configured by setting + CONFIG_LIB_HOMEDIR in the configuration file. The default + 'home' directory is '/'. + 'cd ..' sets the current working directory to the parent directory. + +o cp <source-path> <dest-path> + + Copy of the contents of the file at <source-path> to the location + in the filesystem indicated by <path-path> + +o date [-s "MMM DD HH:MM:SS YYYY"] + + Show or set the current date and time. This command is only supported + if the platform supported RTC hardware (CONFIG_RTC=y). + + Only one format is used both on display and when setting the date/time: + MMM DD HH:MM:SS YYYY. For example, + + data -s "Sep 1 11:30:00 2011" + + 24-hour time format is assumed. + +o dd if=<infile> of=<outfile> [bs=<sectsize>] [count=<sectors>] [skip=<sectors>] + + Copy blocks from <infile> to <outfile>. <nfile> or <outfile> may + be the path to a standard file, a character device, or a block device. + + Examples: + + 1. Read from character device, write to regular file. This will + create a new file of the specified size filled with zero. + + nsh> dd if=/dev/zero of=/tmp/zeros bs=64 count=16 + nsh> ls -l /tmp + /tmp: + -rw-rw-rw- 1024 ZEROS + + 2. Read from character device, write to block device. This will + fill the entire block device with zeros. + + nsh> ls -l /dev + /dev: + brw-rw-rw- 0 ram0 + crw-rw-rw- 0 zero + nsh> dd if=/dev/zero of=/dev/ram0 + + 3. Read from a block devic, write to a character device. This + will read the entire block device and dump the contents in + the bit bucket. + + nsh> ls -l /dev + /dev: + crw-rw-rw- 0 null + brw-rw-rw- 0 ram0 + nsh> dd if=/dev/ram0 of=/dev/null + +o echo [<string|$name> [<string|$name>...]] + + Copy the sequence of strings and expanded environment variables to + console out (or to a file if the output is re-directed). + +o exec <hex-address> + + Execute the user logic at address <hex-address>. NSH will pause + until the execution unless the user logic is executed in background + via 'exec <hex-address> &' + +o exit + + Exit NSH. Only useful if you have started some other tasks (perhaps + using the 'exec' command') and you would like to have NSH out of the + way. + +o free + + Show the current state of the memory allocator. For example, + + nsh> free + free + total used free largest + Mem: 4194288 1591552 2602736 2601584 + + Where: + total - This is the total size of memory allocated for use + by malloc in bytes. + used - This is the total size of memory occupied by + chunks handed out by malloc. + free - This is the total size of memory occupied by + free (not in use) chunks. + largest - Size of the largest free (not in use) chunk + +o get [-b|-n] [-f <local-path>] -h <ip-address> <remote-path> + + Use TFTP to copy the file at <remote-address> from the host whose IP + address is identified by <ip-address>. Other options: + + -f <local-path> + The file will be saved relative to the current working directory + unless <local-path> is provided. + -b|-n + Selects either binary ("octect") or test ("netascii") transfer + mode. Default: text. + +o help + + Presents summary information about each command to console. + +o ifconfig + + Show the current configuration of the network, for example: + + nsh> ifconfig + eth0 HWaddr 00:18:11:80:10:06 + IPaddr:10.0.0.2 DRaddr:10.0.0.1 Mask:255.255.255.0 + + if uIP statistics are enabled (CONFIG_NET_STATISTICS), then + this command will also show the detailed state of uIP. + +o kill -<signal> <pid> + + Send the <signal> to the task identified by <pid>. + +o losetup [-d <dev-path>] | [[-o <offset>] [-r] <ldev-path> <file-path>] + + Setup or teardown the loop device: + + 1. Teardown the setup for the loop device at <dev-path>: + + losetup d <dev-path> + + 2. Setup the loop device at <dev-path> to access the file at <file-path> + as a block device: + + losetup [-o <offset>] [-r] <dev-path> <file-path> + + Example: + + nsh> dd if=/dev/zero of=/tmp/image bs=512 count=512 + nsh> ls -l /tmp + /tmp: + -rw-rw-rw- 262144 IMAGE + nsh> losetup /dev/loop0 /tmp/image + nsh> ls -l /dev + /dev: + brw-rw-rw- 0 loop0 + nsh> mkfatfs /dev/loop0 + nsh> mount -t vfat /dev/loop0 /mnt/example + nsh> ls -l /mnt + ls -l /mnt + /mnt: + drw-rw-rw- 0 example/ + nsh> echo "This is a test" >/mnt/example/atest.txt + nsh> ls -l /mnt/example + /mnt/example: + -rw-rw-rw- 16 ATEST.TXT + nsh> cat /mnt/example/atest.txt + This is a test + nsh> + +o ls [-lRs] <dir-path> + + Show the contents of the directory at <dir-path>. NOTE: + <dir-path> must refer to a directory and no other filesystem + object. + + Options: + -------- + + -R Show the constents of specified directory and all of its + sub-directories. + -s Show the size of the files along with the filenames in the + listing + -l Show size and mode information along with the filenames + in the listing. + +o mb <hex-address>[=<hex-value>][ <hex-byte-count>] +o mh <hex-address>[=<hex-value>][ <hex-byte-count>] +o mw <hex-address>[=<hex-value>][ <hex-byte-count>] + + Access memory using byte size access (mb), 16-bit accesses (mh), + or 32-bit access (mw). In each case, + + <hex-address>. Specifies the address to be accessed. The current + value at that address will always be read and displayed. + <hex-address>=<hex-value>. Read the value, then write <hex-value> + to the location. + <hex-byte-count>. Perform the mb, mh, or mw operation on a total + of <hex-byte-count> bytes, increment the <hex-address> appropriately + after each access + + Example + + nsh> mh 0 16 + 0 = 0x0c1e + 2 = 0x0100 + 4 = 0x0c1e + 6 = 0x0110 + 8 = 0x0c1e + a = 0x0120 + c = 0x0c1e + e = 0x0130 + 10 = 0x0c1e + 12 = 0x0140 + 14 = 0x0c1e + nsh> + +o mkdir <path> + + Create the directory at <path>. All components of of <path> + except the final directory name must exist on a mounted file + system; the final directory must not. + + Recall that NuttX uses a pseudo filesystem for its root file system. + The mkdir command can only be used to create directories in volumes + set up with the mount command; it cannot be used to create directories + in the pseudo filesystem. + + Example: + ^^^^^^^^ + + nsh> mkdir /mnt/fs/tmp + nsh> ls -l /mnt/fs + /mnt/fs: + drw-rw-rw- 0 TESTDIR/ + drw-rw-rw- 0 TMP/ + nsh> + +o mkfatfs <path> + + Format a fat file system on the block device specified by path. + NSH provides this command to access the mkfatfs() NuttX API. + This block device must reside in the NuttX psuedo filesystem and + must have been created by some call to register_blockdriver() (see + include/nuttx/fs.h). + +o mkfifo <path> + + Creates a FIFO character device anywhere in the pseudo file system, + creating whatever psuedo directories that may be needed to complete + the full path. By convention, however, device drivers are place in + the standard /dev directory. After it is created, the FIFO device + may be used as any other device driver. NSH provides this command + to access the mkfifo() NuttX API. + + Example: + ^^^^^^^^ + + nsh> ls -l /dev + /dev: + crw-rw-rw- 0 console + crw-rw-rw- 0 null + brw-rw-rw- 0 ram0 + nsh> mkfifo /dev/fifo + nsh> ls -l /dev + ls -l /dev + /dev: + crw-rw-rw- 0 console + crw-rw-rw- 0 fifo + crw-rw-rw- 0 null + brw-rw-rw- 0 ram0 + nsh> + +o mkrd [-m <minor>] [-s <sector-size>] <nsectors> + + Create a ramdisk consisting of <nsectors>, each of size + <sector-size> (or 512 bytes if <sector-size> is not specified. + The ramdisk will be registered as /dev/ram<n> (if <n> is not + specified, mkrd will attempt to register the ramdisk as + /dev/ram0. + + Example: + ^^^^^^^^ + + nsh> ls /dev + /dev: + console + null + ttyS0 + ttyS1 + nsh> mkrd 1024 + nsh> ls /dev + /dev: + console + null + ram0 + ttyS0 + ttyS1 + nsh> + + Once the ramdisk has been created, it may be formatted using + the mkfatfs command and mounted using the mount command. + + Example: + ^^^^^^^^ + nsh> mkrd 1024 + nsh> mkfatfs /dev/ram0 + nsh> mount -t vfat /dev/ram0 /tmp + nsh> ls /tmp + /tmp: + nsh> + +o mount -t <fstype> <block-device> <dir-path> + + The 'mount' command mounts a file system in the NuttX psuedo + filesystem. 'mount' performs a three way associating, binding + + File system. The '-t <fstype>' option identifies the type of + file system that has been formatted on the <block-device>. As + of this writing, vfat is the only supported value for <fstype> + + Block Device. The <block-device> argument is the full or relative + path to a block driver inode in the psuedo filesystem. By convention, + this is a name under the /dev sub-directory. This <block-device> + must have been previously formatted with the same file system + type as specified by <fstype> + + Mount Point. The mount point is the location in the psuedo file + system where the mounted volume will appear. This mount point + can only reside in the NuttX psuedo filesystem. By convention, this + mount point is a subdirectory under /mnt. The mount command will + create whatever psuedo directories that may be needed to complete + the full path but the full path must not already exist. + + After the volume has been mounted in the NuttX psuedo file + system, it may be access in the same way as other objects in the + file system. + + Example: + ^^^^^^^^ + + nsh> ls -l /dev + /dev: + crw-rw-rw- 0 console + crw-rw-rw- 0 null + brw-rw-rw- 0 ram0 + nsh> ls /mnt + nsh: ls: no such directory: /mnt + nsh> mount -t vfat /dev/ram0 /mnt/fs + nsh> ls -l /mnt/fs/testdir + /mnt/fs/testdir: + -rw-rw-rw- 15 TESTFILE.TXT + nsh> echo "This is a test" >/mnt/fs/testdir/example.txt + nsh> ls -l /mnt/fs/testdir + /mnt/fs/testdir: + -rw-rw-rw- 15 TESTFILE.TXT + -rw-rw-rw- 16 EXAMPLE.TXT + nsh> cat /mnt/fs/testdir/example.txt + This is a test + nsh> + +o ps + + Show the currently active threads and tasks. For example, + + nsh> ps + PID PRI SCHD TYPE NP STATE NAME + 0 0 FIFO TASK READY Idle Task() + 1 128 RR TASK RUNNING init() + 2 128 FIFO TASK WAITSEM nsh_telnetmain() + 3 100 RR PTHREAD WAITSEM <pthread>(21) + nsh> + +o ping [-c <count>] [-i <interval>] <ip-address> + + Test the network communication with a remote peer. Example, + + nsh> 10.0.0.1 + PING 10.0.0.1 56 bytes of data + 56 bytes from 10.0.0.1: icmp_seq=1 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=2 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=3 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=4 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=5 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=6 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=7 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=8 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=9 time=0 ms + 56 bytes from 10.0.0.1: icmp_seq=10 time=0 ms + 10 packets transmitted, 10 received, 0% packet loss, time 10190 ms + nsh> + +o put [-b|-n] [-f <remote-path>] -h <ip-address> <local-path> + + Copy the file at <local-address> to the host whose IP address is + identified by <ip-address>. Other options: + + -f <remote-path> + The file will be saved with the same name on the host unless + unless <local-path> is provided. + -b|-n + Selects either binary ("octect") or test ("netascii") transfer + mode. Default: text. + +o pwd + + Show the current working directory. + + nsh> cd /dev + nsh> pwd + /dev + nsh> + + Same as 'echo $PWD' + + nsh> echo $PWD + /dev + nsh> + +o rm <file-path> + + Remove the specified <file-path> name from the mounted file system. + Recall that NuttX uses a pseudo filesystem for its root file system. + The rm command can only be used to remove (unlink) files in volumes + set up with the mount command; it cannot be used to remove names from + the pseudo filesystem. + + Example: + ^^^^^^^^ + + nsh> ls /mnt/fs/testdir + /mnt/fs/testdir: + TESTFILE.TXT + EXAMPLE.TXT + nsh> rm /mnt/fs/testdir/example.txt + nsh> ls /mnt/fs/testdir + /mnt/fs/testdir: + TESTFILE.TXT + nsh> + +o rmdir <dir-path> + + Remove the specified <dir-path> directory from the mounted file system. + Recall that NuttX uses a pseudo filesystem for its root file system. The + rmdir command can only be used to remove directories from volumes set up + with the mount command; it cannot be used to remove directories from the + pseudo filesystem. + + Example: + ^^^^^^^^ + + nsh> mkdir /mnt/fs/tmp + nsh> ls -l /mnt/fs + /mnt/fs: + drw-rw-rw- 0 TESTDIR/ + drw-rw-rw- 0 TMP/ + nsh> rmdir /mnt/fs/tmp + nsh> ls -l /mnt/fs + ls -l /mnt/fs + /mnt/fs: + drw-rw-rw- 0 TESTDIR/ + nsh> + +o set <name> <value> + + Set the environment variable <name> to the sting <value>. + For example, + + nsh> echo $foobar + + nsh> set foobar foovalue + nsh> echo $foobar + foovalue + nsh> + +o sh <script-path> + + Execute the sequence of NSH commands in the file referred + to by <script-path>. + +o sleep <sec> + + Pause execution (sleep) of <sec> seconds. + +o unset <name> + + Remove the value associated with the environment variable + <name>. Example: + + nsh> echo $foobar + foovalue + nsh> unset foobar + nsh> echo $foobar + + nsh> + +o usleep <usec> + + Pause execution (sleep) of <usec> microseconds. + +o wget [-o <local-path>] <url> + + Use HTTP to copy the file at <url> to the current directory. + Options: + + -o <local-path> + The file will be saved relative to the current working directory + and with the same name as on the HTTP server unless <local-path> + is provided. + +o xd <hex-address> <byte-count> + + Dump <byte-count> bytes of data from address <hex-address> + + Example: + ^^^^^^^^ + + nsh> xd 410e0 512 + Hex dump: + 0000: 00 00 00 00 9c 9d 03 00 00 00 00 01 11 01 10 06 ................ + 0010: 12 01 11 01 25 08 13 0b 03 08 1b 08 00 00 02 24 ....%..........$ + ... + 01f0: 08 3a 0b 3b 0b 49 13 00 00 04 13 01 01 13 03 08 .:.;.I.......... + nsh> + +NSH Configuration Settings +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The availability of the above commands depends upon features that +may or may not be enabled in the NuttX configuration file. The +following table indicates the dependency of each command on NuttX +configuration settings. General configuration settings are discussed +in the NuttX Porting Guide. Configuration settings specific to NSH +as discussed at the bottom of this README file. + +Command Dependencies on Configuration Settings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Command Depends on Configuration + ---------- -------------------------- + [ !CONFIG_NSH_DISABLESCRIPT + cat CONFIG_NFILE_DESCRIPTORS > 0 + cd !CONFIG_DISABLE_ENVIRON && CONFIG_NFILE_DESCRIPTORS > 0 + cp CONFIG_NFILE_DESCRIPTORS > 0 + dd CONFIG_NFILE_DESCRIPTORS > 0 + echo -- + exec -- + exit -- + free -- + get CONFIG_NET && CONFIG_NET_UDP && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NET_BUFSIZE >= 558 (see note 1) + help -- + ifconfig CONFIG_NET + kill !CONFIG_DISABLE_SIGNALS + losetup !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 + ls CONFIG_NFILE_DESCRIPTORS > 0 + mb,mh,mw --- + mkdir !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_WRITABLE (see note 4) + mkfatfs !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_FAT + mkfifo CONFIG_NFILE_DESCRIPTORS > 0 + mkrd !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_WRITABLE (see note 4) + mount !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_READABLE (see note 3) + ping CONFIG_NET && CONFIG_NET_ICMP && CONFIG_NET_ICMP_PING && !CONFIG_DISABLE_CLOCK && !CONFIG_DISABLE_SIGNALS + ps -- + put CONFIG_NET && CONFIG_NET_UDP && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NET_BUFSIZE >= 558 (see note 1,2) + pwd !CONFIG_DISABLE_ENVIRON && CONFIG_NFILE_DESCRIPTORS > 0 + rm !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_WRITABLE (see note 4) + rmdir !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_WRITABLE (see note 4) + set !CONFIG_DISABLE_ENVIRON + sh CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !CONFIG_NSH_DISABLESCRIPT + sleep !CONFIG_DISABLE_SIGNALS + test !CONFIG_NSH_DISABLESCRIPT + umount !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_READABLE + unset !CONFIG_DISABLE_ENVIRON + usleep !CONFIG_DISABLE_SIGNALS + get CONFIG_NET && CONFIG_NET_TCP && CONFIG_NFILE_DESCRIPTORS > 0 + xd --- + +* NOTES: + 1. Because of hardware padding, the actual buffersize required for put and get + operations size may be larger. + 2. Special TFTP server start-up optionss will probably be required to permit + creation of file for the correct operation of the put command. + 3. CONFIG_FS_READABLE is not a user configuration but is set automatically + if any readable filesystem is selected. At present, this is either CONFIG_FS_FAT + and CONFIG_FS_ROMFS. + 4. CONFIG_FS_WRITABLE is not a user configuration but is set automatically + if any writable filesystem is selected. At present, this is only CONFIG_FS_FAT. + +In addition, each NSH command can be individually disabled via one of the following +settings. All of these settings make the configuration of NSH potentially complex but +also allow it to squeeze into very small memory footprints. + + CONFIG_NSH_DISABLE_CAT, CONFIG_NSH_DISABLE_CD, CONFIG_NSH_DISABLE_CP, + CONFIG_NSH_DISABLE_DD, CONFIG_NSH_DISABLE_ECHO, CONFIG_NSH_DISABLE_EXEC, + CONFIG_NSH_DISABLE_EXIT, CONFIG_NSH_DISABLE_FREE, CONFIG_NSH_DISABLE_GET, + CONFIG_NSH_DISABLE_HELP, CONFIG_NSH_DISABLE_IFCONFIG, CONFIG_NSH_DISABLE_KILL, + CONFIG_NSH_DISABLE_LOSETUP, CONFIG_NSH_DISABLE_LS, CONFIG_NSH_DISABLE_MB, + CONFIG_NSH_DISABLE_MKDIR, CONFIG_NSH_DISABLE_MKFATFS, CONFIG_NSH_DISABLE_MKFIFO, + CONFIG_NSH_DISABLE_MKRD, CONFIG_NSH_DISABLE_MH, CONFIG_NSH_DISABLE_MOUNT, + CONFIG_NSH_DISABLE_MW, CONFIG_NSH_DISABLE_PS, CONFIG_NSH_DISABLE_PING, + CONFIG_NSH_DISABLE_PUT, CONFIG_NSH_DISABLE_PWD, CONFIG_NSH_DISABLE_RM, + CONFIG_NSH_DISABLE_RMDIR, CONFIG_NSH_DISABLE_SET, CONFIG_NSH_DISABLE_SH, + CONFIG_NSH_DISABLE_SLEEP, CONFIG_NSH_DISABLE_TEST, CONFIG_NSH_DISABLE_UMOUNT, + CONFIG_NSH_DISABLE_UNSET, CONFIG_NSH_DISABLE_USLEEP, CONFIG_NSH_DISABLE_WGET, + CONFIG_NSH_DISABLE_XD + +NSH-Specific Configuration Settings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The behavior of NSH can be modified with the following settings in + the configs/<board-name>/defconfig file: + + * CONFIG_NSH_BUILTIN_APPS + Support external registered, "named" applications that can be + executed from the NSH command line (see apps/README.txt for + more information). + + * CONFIG_NSH_FILEIOSIZE + Size of a static I/O buffer used for file access (ignored if + there is no filesystem). Default is 1024. + + * CONFIG_NSH_STRERROR + strerror(errno) makes more readable output but strerror() is + very large and will not be used unless this setting is 'y' + + * CONFIG_NSH_LINELEN + The maximum length of one command line and of one output line. + Default: 80 + + * CONFIG_NSH_STACKSIZE + The stack size to use when spawning new threads or tasks. Such + new threads are generated when a command is executed in background + or as new TELNET connections are established. + + * CONFIG_NSH_NESTDEPTH + The maximum number of nested if-then[-else]-fi sequences that + are permissable. Default: 3 + + * CONFIG_NSH_DISABLESCRIPT + This can be set to 'y' to suppress support for scripting. This + setting disables the 'sh', 'test', and '[' commands and the + if-then[-else]-fi construct. This would only be set on systems + where a minimal footprint is a necessity and scripting is not. + + * CONFIG_NSH_DISABLEBG + This can be set to 'y' to suppress support for background + commands. This setting disables the 'nice' command prefix and + the '&' command suffix. This would only be set on systems + where a minimal footprint is a necessity and background command + execution is not. + + * CONFIG_NSH_MMCSDMINOR + If the architecture supports an MMC/SD slot and if the NSH + architecture specific logic is present, this option will provide + the MMC/SD minor number, i.e., the MMC/SD block driver will + be registered as /dev/mmcsdN where N is the minor number. + Default is zero. + + * CONFIG_NSH_ROMFSETC + Mount a ROMFS filesystem at /etc and provide a startup script + at /etc/init.d/rcS. The default startup script will mount + a FAT FS RAMDISK at /tmp but the logic is easily extensible. + + * CONFIG_NSH_CONSOLE + If CONFIG_NSH_CONSOLE is set to 'y', then a serial + console front-end is selected. + + * CONFIG_NSH_CONDEV + If CONFIG_NSH_CONSOLE is set to 'y', then CONFIG_NSH_CONDEV + may also be set to select the serial device used to support + the NSH console. This should be set to the quoted name of a + readable/write-able character driver such as: + CONFIG_NSH_CONDEV="/dev/ttyS1". This is useful, for example, + to separate the NSH command line from the system console when + the system console is used to provide debug output. Default: + stdin and stdout (probably "/dev/console") + + NOTE: When any other device other than /dev/console is used + for a user interface, (1) linefeeds (\n) will not be expanded to + carriage return / linefeeds (\r\n). You will need to set + your terminal program to account for this. And (2) input is + not automatically echoed so you will have to turn local echo on. + + * CONFIG_NSH_TELNET + If CONFIG_NSH_TELNET is set to 'y', then a TELENET + server front-end is selected. When this option is provided, + you may log into NuttX remotely using telnet in order to + access NSH. + + * CONFIG_NSH_ARCHINIT + Set if your board provides architecture specific initialization + via the board-specific function nsh_archinitialize(). This + function will be called early in NSH initialization to allow + board logic to do such things as configure MMC/SD slots. + + One or both of CONFIG_NSH_CONSOLE and CONFIG_NSH_TELNET + must be defined. If CONFIG_NSH_TELNET is selected, then there some + other configuration settings that apply: + + * CONFIG_NET=y + Of course, networking must be enabled + + * CONFIG_NSOCKET_DESCRIPTORS + And, of course, you must allocate some socket descriptors. + + * CONFIG_NET_TCP=y + TCP/IP support is required for telnet (as well as various other TCP-related + configuration settings). + + * CONFIG_NSH_IOBUFFER_SIZE + Determines the size of the I/O buffer to use for sending/ + receiving TELNET commands/reponses + + * CONFIG_NSH_DHCPC + Obtain the IP address via DHCP. + + * CONFIG_NSH_IPADDR + If CONFIG_NSH_DHCPC is NOT set, then the static IP + address must be provided. + + * CONFIG_NSH_DRIPADDR + Default router IP address + + * CONFIG_NSH_NETMASK + Network mask + + * CONFIG_NSH_NOMAC + Set if your ethernet hardware has no built-in MAC address. + If set, a bogus MAC will be assigned. + + If you use DHCPC, then some special configuration network options are + required. These include: + + * CONFIG_NET=y + Of course, networking must be enabled + + * CONFIG_NSOCKET_DESCRIPTORS + And, of course, you must allocate some socket descriptors. + + * CONFIG_NET_UDP=y + UDP support is required for DHCP (as well as various other UDP-related + configuration settings) + + * CONFIG_NET_BROADCAST=y + UDP broadcast support is needed. + + * CONFIG_NET_BUFSIZE=650 (or larger) + Per RFC2131 (p. 9), the DHCP client must be prepared to receive DHCP + messages of up to 576 bytes (excluding Ethernet, IP, or UDP headers and FCS). + + If CONFIG_NSH_ROMFSETC is selected, then the following additional + configuration setting apply: + + * CONFIG_NSH_ROMFSMOUNTPT + The default mountpoint for the ROMFS volume is /etc, but that + can be changed with this setting. This must be a absolute path + beginning with '/'. + + * CONFIG_NSH_INITSCRIPT + This is the relative path to the startup script within the mountpoint. + The default is init.d/rcS. This is a relative path and must not + start with '/'. + + * CONFIG_NSH_ROMFSDEVNO + This is the minor number of the ROMFS block device. The default is + '0' corresponding to /dev/ram0. + + * CONFIG_NSH_ROMFSSECTSIZE + This is the sector size to use with the ROMFS volume. Since the + default volume is very small, this defaults to 64 but should be + increased if the ROMFS volume were to be become large. Any value + selected must be a power of 2. + + When the default rcS file used when CONFIG_NSH_ROMFSETC is + selected, it will mount a FAT FS under /tmp. The following selections + describe that FAT FS. + + * CONFIG_NSH_FATDEVNO + This is the minor number of the FAT FS block device. The default is + '1' corresponding to /dev/ram1. + + * CONFIG_NSH_FATSECTSIZE + This is the sector size use with the FAT FS. Default is 512. + + * CONFIG_NSH_FATNSECTORS + This is the number of sectors to use with the FAT FS. Defalt is + 1024. The amount of memory used by the FAT FS will be + CONFIG_NSH_FATSECTSIZE * CONFIG_NSH_FATNSECTORS + bytes. + + * CONFIG_NSH_FATMOUNTPT + This is the location where the FAT FS will be mounted. Default + is /tmp. + +Common Problems +^^^^^^^^^^^^^^^ + + Problem: + Using NSH over serial, the "nsh>" prompt repeats over and over again + with no serial input. + Usual Cause: + NSH over serial needs to use the interrupt driven serial driver + (drivers/serial/serial.c) not the polled serial driver (drivers/serial/lowconsole.c). + Make sure that the polled console is disabled in the OS configuration + file, .config. That file should have CONFIG_DEV_LOWCONSOLE=n for + NSH over serial. diff --git a/apps/nshlib/nsh.h b/apps/nshlib/nsh.h new file mode 100644 index 000000000..fd265982f --- /dev/null +++ b/apps/nshlib/nsh.h @@ -0,0 +1,497 @@ +/**************************************************************************** + * apps/nshlib/nsh.h + * + * Copyright (C) 2007-2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __APPS_NSHLIB_NSH_H +#define __APPS_NSHLIB_NSH_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <errno.h> + +#ifdef CONFIG_NSH_CONSOLE +# include <stdio.h> +#endif + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/* The telnetd interface and background commands require pthread support */ + +#ifdef CONFIG_DISABLE_PTHREAD +# undef CONFIG_NSH_TELNET +# ifndef CONFIG_NSH_DISABLEBG +# define CONFIG_NSH_DISABLEBG 1 +# endif +#endif + +/* Telnetd requires networking support */ + +#ifndef CONFIG_NET +# undef CONFIG_NSH_TELNET +#endif + +/* One front end must be defined */ + +#if !defined(CONFIG_NSH_CONSOLE) && !defined(CONFIG_NSH_TELNET) +# error "No NSH front end defined" +#endif + +/* Verify support for ROMFS /etc directory support options */ + +#ifdef CONFIG_NSH_ROMFSETC +# ifdef CONFIG_DISABLE_MOUNTPOINT +# error "Mountpoint support is disabled" +# undef CONFIG_NSH_ROMFSETC +# endif +# if CONFIG_NFILE_DESCRIPTORS < 4 +# error "Not enough file descriptors" +# undef CONFIG_NSH_ROMFSETC +# endif +# ifndef CONFIG_FS_ROMFS +# error "ROMFS support not enabled" +# undef CONFIG_NSH_ROMFSETC +# endif +# ifndef CONFIG_NSH_ROMFSMOUNTPT +# define CONFIG_NSH_ROMFSMOUNTPT "/etc" +# endif +# ifdef CONFIG_NSH_INIT +# ifndef CONFIG_NSH_INITSCRIPT +# define CONFIG_NSH_INITSCRIPT "init.d/rcS" +# endif +# endif +# undef NSH_INITPATH +# define NSH_INITPATH CONFIG_NSH_ROMFSMOUNTPT "/" CONFIG_NSH_INITSCRIPT +# ifndef CONFIG_NSH_ROMFSDEVNO +# define CONFIG_NSH_ROMFSDEVNO 0 +# endif +# ifndef CONFIG_NSH_ROMFSSECTSIZE +# define CONFIG_NSH_ROMFSSECTSIZE 64 +# endif +# define NSECTORS(b) (((b)+CONFIG_NSH_ROMFSSECTSIZE-1)/CONFIG_NSH_ROMFSSECTSIZE) +# define STR_RAMDEVNO(m) #m +# define MKMOUNT_DEVNAME(m) "/dev/ram" STR_RAMDEVNO(m) +# define MOUNT_DEVNAME MKMOUNT_DEVNAME(CONFIG_NSH_ROMFSDEVNO) +#else +# undef CONFIG_NSH_ROMFSMOUNTPT +# undef CONFIG_NSH_INIT +# undef CONFIG_NSH_INITSCRIPT +# undef CONFIG_NSH_ROMFSDEVNO +# undef CONFIG_NSH_ROMFSSECTSIZE +#endif + +/* This is the maximum number of arguments that will be accepted for a command */ + +#define NSH_MAX_ARGUMENTS 6 + +/* strerror() produces much nicer output but is, however, quite large and + * will only be used if CONFIG_NSH_STRERROR is defined. + */ + +#ifdef CONFIG_NSH_STRERROR +# define NSH_ERRNO strerror(errno) +# define NSH_ERRNO_OF(err) strerror(err) +#else +# define NSH_ERRNO (errno) +# define NSH_ERRNO_OF(err) (err) +#endif + +/* Maximum size of one command line (telnet or serial) */ + +#ifndef CONFIG_NSH_LINELEN +# define CONFIG_NSH_LINELEN 80 +#endif + +/* The following two settings are used only in the telnetd interface */ + +#ifndef CONFIG_NSH_IOBUFFER_SIZE +# define CONFIG_NSH_IOBUFFER_SIZE 512 +#endif + +/* As threads are created to handle each request, a stack must be allocated + * for the thread. Use a default if the user provided no stacksize. + */ + +#ifndef CONFIG_NSH_STACKSIZE +# define CONFIG_NSH_STACKSIZE 4096 +#endif + +/* The maximum number of nested if-then[-else]-fi sequences that + * are permissable. + */ + +#ifndef CONFIG_NSH_NESTDEPTH +# define CONFIG_NSH_NESTDEPTH 3 +#endif + +/* Define to enable dumping of all input/output buffers */ + +#undef CONFIG_NSH_TELNETD_DUMPBUFFER +#undef CONFIG_NSH_FULLPATH + +/* Make sure that the home directory is defined */ + +#ifndef CONFIG_LIB_HOMEDIR +# define CONFIG_LIB_HOMEDIR "/" +#endif + +/* Method access macros */ + +#define nsh_clone(v) (v)->clone(v) +#define nsh_release(v) (v)->release(v) +#define nsh_write(v,b,n) (v)->write(v,b,n) +#define nsh_linebuffer(v) (v)->linebuffer(v) +#define nsh_redirect(v,f,s) (v)->redirect(v,f,s) +#define nsh_undirect(v,s) (v)->undirect(v,s) +#define nsh_exit(v) (v)->exit(v) + +#ifdef CONFIG_CPP_HAVE_VARARGS +# define nsh_output(v, fmt...) (v)->output(v, ##fmt) +#else +# define nsh_output vtbl->output +#endif + +/* Size of info to be saved in call to nsh_redirect */ + +#define SAVE_SIZE (sizeof(int) + sizeof(FILE*) + sizeof(bool)) + +/* Stubs used when working directory is not supported */ + +#if CONFIG_NFILE_DESCRIPTORS <= 0 || defined(CONFIG_DISABLE_ENVIRON) +# define nsh_getfullpath(v,p) ((char*)(p)) +# define nsh_freefullpath(p) +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum nsh_parser_e +{ + NSH_PARSER_NORMAL = 0, + NSH_PARSER_IF, + NSH_PARSER_THEN, + NSH_PARSER_ELSE +}; + +struct nsh_state_s +{ + uint8_t ns_ifcond : 1; /* Value of command in 'if' statement */ + uint8_t ns_disabled : 1; /* TRUE: Unconditionally disabled */ + uint8_t ns_unused : 4; + uint8_t ns_state : 2; /* Parser state (see enum nsh_parser_e) */ +}; + +struct nsh_parser_s +{ +#ifndef CONFIG_NSH_DISABLEBG + bool np_bg; /* true: The last command executed in background */ +#endif + bool np_redirect; /* true: Output from the last command was re-directed */ + bool np_fail; /* true: The last command failed */ +#ifndef CONFIG_NSH_DISABLESCRIPT + uint8_t np_ndx; /* Current index into np_st[] */ +#endif +#ifndef CONFIG_NSH_DISABLEBG + int np_nice; /* "nice" value applied to last background cmd */ +#endif + + /* This is a stack of parser state information. It supports nested + * execution of commands that span multiple lines (like if-then-else-fi) + */ + +#ifndef CONFIG_NSH_DISABLESCRIPT + struct nsh_state_s np_st[CONFIG_NSH_NESTDEPTH]; +#endif +}; + +struct nsh_vtbl_s +{ + /* This function pointers are "hooks" into the front end logic to + * handle things like output of command results, redirection, etc. + * -- all of which must be done in a way that is unique to the nature + * of the front end. + */ + +#ifndef CONFIG_NSH_DISABLEBG + FAR struct nsh_vtbl_s *(*clone)(FAR struct nsh_vtbl_s *vtbl); + void (*addref)(FAR struct nsh_vtbl_s *vtbl); + void (*release)(FAR struct nsh_vtbl_s *vtbl); +#endif + ssize_t (*write)(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes); + int (*output)(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); + FAR char *(*linebuffer)(FAR struct nsh_vtbl_s *vtbl); + void (*redirect)(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); + void (*undirect)(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); + void (*exit)(FAR struct nsh_vtbl_s *vtbl); + + /* Parser state data */ + + struct nsh_parser_s np; +}; + +typedef int (*cmd_t)(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern const char g_nshgreeting[]; +extern const char g_nshprompt[]; +extern const char g_nshsyntax[]; +extern const char g_fmtargrequired[]; +extern const char g_fmtarginvalid[]; +extern const char g_fmtargrange[]; +extern const char g_fmtcmdnotfound[]; +extern const char g_fmtnosuch[]; +extern const char g_fmttoomanyargs[]; +extern const char g_fmtdeepnesting[]; +extern const char g_fmtcontext[]; +extern const char g_fmtcmdfailed[]; +extern const char g_fmtcmdoutofmemory[]; +extern const char g_fmtinternalerror[]; +#ifndef CONFIG_DISABLE_SIGNALS +extern const char g_fmtsignalrecvd[]; +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initialization */ + +#ifdef CONFIG_NSH_ROMFSETC +extern int nsh_romfsetc(void); +#else +# define nsh_romfsetc() (-ENOSYS) +#endif + +#ifdef CONFIG_NET +extern int nsh_netinit(void); +#else +# define nsh_netinit() (-ENOSYS) +#endif + +#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_NSH_DISABLESCRIPT) +extern int nsh_script(FAR struct nsh_vtbl_s *vtbl, const char *cmd, const char *path); +#endif + +/* Architecture-specific initialization */ + +#ifdef CONFIG_NSH_ARCHINIT +extern int nsh_archinitialize(void); +#else +# define nsh_archinitialize() (-ENOSYS) +#endif + +/* Message handler */ + +extern int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline); + +/* Application interface */ + +#ifdef CONFIG_NSH_BUILTIN_APPS +extern int nsh_execapp(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, + FAR char **argv); +#endif + +/* Working directory support */ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +extern FAR const char *nsh_getcwd(void); +extern char *nsh_getfullpath(FAR struct nsh_vtbl_s *vtbl, const char *relpath); +extern void nsh_freefullpath(char *relpath); +#endif + +/* Debug */ + +extern void nsh_dumpbuffer(FAR struct nsh_vtbl_s *vtbl, const char *msg, + const uint8_t *buffer, ssize_t nbytes); + +/* Shell command handlers */ + +#ifndef CONFIG_NSH_DISABLE_ECHO + extern int cmd_echo(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif +#ifndef CONFIG_NSH_DISABLE_EXEC + extern int cmd_exec(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif +#ifndef CONFIG_NSH_DISABLE_MB + extern int cmd_mb(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif +#ifndef CONFIG_NSH_DISABLE_MH + extern int cmd_mh(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif +#ifndef CONFIG_NSH_DISABLE_MW + extern int cmd_mw(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif +#ifndef CONFIG_NSH_DISABLE_FREE + extern int cmd_free(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif +#ifndef CONFIG_NSH_DISABLE_PS + extern int cmd_ps(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif +#ifndef CONFIG_NSH_DISABLE_XD + extern int cmd_xd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif + +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_TEST) +extern int cmd_test(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +extern int cmd_lbracket(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif + +#ifndef CONFIG_DISABLE_CLOCK +# if defined (CONFIG_RTC) && !defined(CONFIG_NSH_DISABLE_DATE) +extern int cmd_date(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +#endif + +#if CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_CAT + extern int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_CP + extern int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_DD + extern int cmd_dd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_LS + extern int cmd_ls(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# if CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_NSH_DISABLESCRIPT) +# ifndef CONFIG_NSH_DISABLE_SH + extern int cmd_sh(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# endif /* CONFIG_NFILE_STREAMS && !CONFIG_NSH_DISABLESCRIPT */ +# ifndef CONFIG_DISABLE_MOUNTPOINT +# ifndef CONFIG_NSH_DISABLE_LOSETUP + extern int cmd_losetup(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_MKFIFO + extern int cmd_mkfifo(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifdef CONFIG_FS_READABLE +# ifndef CONFIG_NSH_DISABLE_MOUNT + extern int cmd_mount(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_UMOUNT + extern int cmd_umount(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifdef CONFIG_FS_WRITABLE +# ifndef CONFIG_NSH_DISABLE_MKDIR + extern int cmd_mkdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_MKRD + extern int cmd_mkrd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_RM + extern int cmd_rm(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_RMDIR + extern int cmd_rmdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# endif /* CONFIG_FS_WRITABLE */ +# endif /* CONFIG_FS_READABLE */ +# ifdef CONFIG_FS_FAT +# ifndef CONFIG_NSH_DISABLE_MKFATFS + extern int cmd_mkfatfs(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# endif /* CONFIG_FS_FAT */ +# endif /* !CONFIG_DISABLE_MOUNTPOINT */ +# if !defined(CONFIG_DISABLE_ENVIRON) +# ifndef CONFIG_NSH_DISABLE_CD + extern int cmd_cd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_PWD + extern int cmd_pwd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# endif /* !CONFIG_DISABLE_MOUNTPOINT */ +#endif /* CONFIG_NFILE_DESCRIPTORS */ + +#if defined(CONFIG_NET) +# ifndef CONFIG_NSH_DISABLE_IFCONFIG + extern int cmd_ifconfig(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +#if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_GET + extern int cmd_get(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_PUT + extern int cmd_put(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +#endif +#if defined(CONFIG_NET_TCP) && CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_WGET + extern int cmd_wget(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +#endif +#if defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) && \ + !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_DISABLE_SIGNALS) +# ifndef CONFIG_NSH_DISABLE_PING + extern int cmd_ping(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +#endif +#endif + +#ifndef CONFIG_DISABLE_ENVIRON +# ifndef CONFIG_NSH_DISABLE_SET + extern int cmd_set(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_UNSET + extern int cmd_unset(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +#endif /* CONFIG_DISABLE_ENVIRON */ + +#ifndef CONFIG_DISABLE_SIGNALS +# ifndef CONFIG_NSH_DISABLE_KILL + extern int cmd_kill(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_SLEEP + extern int cmd_sleep(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_USLEEP + extern int cmd_usleep(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +# endif +#endif /* CONFIG_DISABLE_SIGNALS */ + +#endif /* __APPS_NSHLIB_NSH_H */ diff --git a/apps/nshlib/nsh_apps.c b/apps/nshlib/nsh_apps.c new file mode 100644 index 000000000..d70c0d10f --- /dev/null +++ b/apps/nshlib/nsh_apps.c @@ -0,0 +1,120 @@ +/**************************************************************************** + * apps/nshlib/nsh_apps.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Uros Platise <uros.platise@isotel.eu> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#ifdef CONFIG_SCHED_WAITPID +# include <sys/wait.h> +#endif + +#include <stdbool.h> +#include <errno.h> +#include <string.h> + +#include <apps/apps.h> + +#include "nsh.h" + +#ifdef CONFIG_NSH_BUILTIN_APPS + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_execute + ****************************************************************************/ + +int nsh_execapp(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, + FAR char **argv) +{ + int ret = OK; + + /* Try to find command within pre-built application list. */ + + ret = exec_namedapp(cmd, (FAR const char **)argv); + if (ret < 0) + { + return -errno; + } + +#ifdef CONFIG_SCHED_WAITPID + if (vtbl->np.np_bg == false) + { + waitpid(ret, NULL, 0); + } + else +#endif + { + struct sched_param param; + sched_getparam(0, ¶m); + nsh_output(vtbl, "%s [%d:%d]\n", cmd, ret, param.sched_priority); + } + + return OK; +} + +#endif /* CONFIG_NSH_BUILTIN_APPS */ + + diff --git a/apps/nshlib/nsh_dbgcmds.c b/apps/nshlib/nsh_dbgcmds.c new file mode 100644 index 000000000..e9d6d9dab --- /dev/null +++ b/apps/nshlib/nsh_dbgcmds.c @@ -0,0 +1,355 @@ +/**************************************************************************** + * apps/nshlib/dbg_dbgcmds.c + * + * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "nsh.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct dbgmem_s +{ + bool dm_write; /* true: perfrom write operation */ + void *dm_addr; /* Address to access */ + uint32_t dm_value; /* Value to write */ + unsigned int dm_count; /* The number of bytes to access */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mem_parse + ****************************************************************************/ + +int mem_parse(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv, + struct dbgmem_s *mem) +{ + char *pcvalue = strchr(argv[1], '='); + unsigned long lvalue = 0; + + /* Check if we are writing a value */ + + if (pcvalue) + { + *pcvalue = '\0'; + pcvalue++; + + lvalue = (unsigned long)strtol(pcvalue, NULL, 16); + if (lvalue > 0xffffffff) + { + return -EINVAL; + } + + mem->dm_write = true; + mem->dm_value = (uint32_t)lvalue; + } + else + { + mem->dm_write = false; + mem->dm_value = 0; + } + + /* Get the address to be accessed */ + + mem->dm_addr = (void*)((uintptr_t)strtol(argv[1], NULL, 16)); + + /* Get the number of bytes to access */ + + if (argc > 2) + { + mem->dm_count = (unsigned int)strtol(argv[2], NULL, 16); + } + else + { + mem->dm_count = 1; + } + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_mb + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_MB +int cmd_mb(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct dbgmem_s mem; + volatile uint8_t *ptr; + int ret; + int i; + + ret = mem_parse(vtbl, argc, argv, &mem); + if (ret == 0) + { + /* Loop for the number of requested bytes */ + + for (i = 0, ptr = (volatile uint8_t*)mem.dm_addr; i < mem.dm_count; i++, ptr++) + { + /* Print the value at the address */ + + nsh_output(vtbl, " %p = 0x%02x", ptr, *ptr); + + /* Are we supposed to write a value to this address? */ + + if (mem.dm_write) + { + /* Yes, was the supplied value within range? */ + + if (mem.dm_value > 0x000000ff) + { + nsh_output(vtbl, g_fmtargrange, argv[0]); + return ERROR; + } + + /* Write the value and re-read the address so that we print its + * current value (if the address is a process address, then the + * value read might not necessarily be the value written). + */ + + *ptr = (uint8_t)mem.dm_value; + nsh_output(vtbl, " -> 0x%02x", *ptr); + } + + /* Make sure we end it with a newline */ + + nsh_output(vtbl, "\n", *ptr); + } + } + return ret; +} +#endif + +/**************************************************************************** + * Name: cmd_mh + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_MH +int cmd_mh(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct dbgmem_s mem; + volatile uint16_t *ptr; + int ret; + int i; + + ret = mem_parse(vtbl, argc, argv, &mem); + if (ret == 0) + { + /* Loop for the number of requested bytes */ + + for (i = 0, ptr = (volatile uint16_t*)mem.dm_addr; i < mem.dm_count; i += 2, ptr++) + { + /* Print the value at the address */ + + nsh_output(vtbl, " %p = 0x%04x", ptr, *ptr); + + /* Are we supposed to write a value to this address? */ + + if (mem.dm_write) + { + /* Yes, was the supplied value within range? */ + + if (mem.dm_value > 0x0000ffff) + { + nsh_output(vtbl, g_fmtargrange, argv[0]); + return ERROR; + } + + /* Write the value and re-read the address so that we print its + * current value (if the address is a process address, then the + * value read might not necessarily be the value written). + */ + + *ptr = (uint16_t)mem.dm_value; + nsh_output(vtbl, " -> 0x%04x", *ptr); + } + + /* Make sure we end it with a newline */ + + nsh_output(vtbl, "\n", *ptr); + } + } + return ret; +} +#endif + +/**************************************************************************** + * Name: cmd_mw + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_MW +int cmd_mw(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct dbgmem_s mem; + volatile uint32_t *ptr; + int ret; + int i; + + ret = mem_parse(vtbl, argc, argv, &mem); + if (ret == 0) + { + /* Loop for the number of requested bytes */ + + for (i = 0, ptr = (volatile uint32_t*)mem.dm_addr; i < mem.dm_count; i += 4, ptr++) + { + /* Print the value at the address */ + + nsh_output(vtbl, " %p = 0x%08x", ptr, *ptr); + + /* Are we supposed to write a value to this address? */ + + if (mem.dm_write) + { + /* Write the value and re-read the address so that we print its + * current value (if the address is a process address, then the + * value read might not necessarily be the value written). + */ + + *ptr = mem.dm_value; + nsh_output(vtbl, " -> 0x%08x", *ptr); + } + + /* Make sure we end it with a newline */ + + nsh_output(vtbl, "\n", *ptr); + } + } + return ret; +} +#endif + +/**************************************************************************** + * Name: nsh_dumpbuffer + ****************************************************************************/ + +void nsh_dumpbuffer(FAR struct nsh_vtbl_s *vtbl, const char *msg, + const uint8_t *buffer, ssize_t nbytes) +{ + char line[128]; + int ch; + int i; + int j; + + nsh_output(vtbl, "%s:\n", msg); + for (i = 0; i < nbytes; i += 16) + { + sprintf(line, "%04x: ", i); + + for ( j = 0; j < 16; j++) + { + if (i + j < nbytes) + { + sprintf(&line[strlen(line)], "%02x ", buffer[i+j] ); + } + else + { + strcpy(&line[strlen(line)], " "); + } + } + + for ( j = 0; j < 16; j++) + { + if (i + j < nbytes) + { + ch = buffer[i+j]; + sprintf(&line[strlen(line)], "%c", ch >= 0x20 && ch <= 0x7e ? ch : '.'); + } + } + nsh_output(vtbl, "%s\n", line); + } +} + +/**************************************************************************** + * Name: cmd_xd + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_XD +int cmd_xd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + FAR char *addr; + FAR char *endptr; + int nbytes; + + addr = (char*)((uintptr_t)strtol(argv[1], &endptr, 16)); + if (argv[0][0] == '\0' || *endptr != '\0') + { + return ERROR; + } + + nbytes = (int)strtol(argv[2], &endptr, 0); + if (argv[0][0] == '\0' || *endptr != '\0' || nbytes < 0) + { + return ERROR; + } + + nsh_dumpbuffer(vtbl, "Hex dump", (uint8_t*)addr, nbytes); + return OK; +} +#endif + diff --git a/apps/nshlib/nsh_ddcmd.c b/apps/nshlib/nsh_ddcmd.c new file mode 100644 index 000000000..11652fe7e --- /dev/null +++ b/apps/nshlib/nsh_ddcmd.c @@ -0,0 +1,640 @@ +/**************************************************************************** + * apps/nshlib/nsh_ddcmd.c + * + * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <debug.h> +#include <errno.h> + +#include <nuttx/fs.h> +#include "nsh.h" + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_NSH_DISABLE_DD) + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/* If no sector size is specified wity BS=, then the following default value + * is used. + */ + +#define DEFAULT_SECTSIZE 512 + +/* At present, piping of input and output are not support, i.e., both of= + * and if= arguments are required. + */ + +#undef CAN_PIPE_FROM_STD + +/* Function pointer calls are only need if block drivers are supported + * (or, rather, if mount points are supported in the file system) + */ + +#ifndef CONFIG_DISABLE_MOUNTPOINT +# define DD_INFD ((dd)->inf.fd) +# define DD_INHANDLE ((dd)->inf.handle) +# define DD_OUTFD ((dd)->outf.fd) +# define DD_OUTHANDLE ((dd)->outf.handle) +# define DD_READ(dd) ((dd)->infread(dd)) +# define DD_WRITE(dd) ((dd)->outfwrite(dd)) +# define DD_INCLOSE(dd) ((dd)->infclose(dd)) +# define DD_OUTCLOSE(dd) ((dd)->outfclose(dd)) +#else +# define DD_INFD ((dd)->infd) +# undef DD_INHANDLE +# define DD_OUTFD ((dd)->outfd) +# undef DD_OUTHANDLE +# define DD_READ(dd) dd_readch(dd) +# define DD_WRITE(dd) dd_writech(dd) +# define DD_INCLOSE(dd) dd_infclosech(dd) +# define DD_OUTCLOSE(dd) dd_outfclosech(dd) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct dd_s +{ + FAR struct nsh_vtbl_s *vtbl; + +#ifndef CONFIG_DISABLE_MOUNTPOINT + union + { + FAR void *handle; /* BCH lib handle for block device*/ + int fd; /* File descriptor of the character device */ + } inf; +#else + int infd; /* File descriptor of the input device */ +#endif + +#ifndef CONFIG_DISABLE_MOUNTPOINT + union + { + FAR void *handle; /* BCH lib handle for block device*/ + int fd; /* File descriptor of the character device */ + } outf; +#else + int outfd; /* File descriptor of the output device */ +#endif + + uint32_t nsectors; /* Number of sectors to transfer */ + uint32_t sector; /* The current sector number */ + uint32_t skip; /* The number of sectors skipped on input */ + bool eof; /* true: The of the input or output file has been hit */ + uint16_t sectsize; /* Size of one sector */ + uint16_t nbytes; /* Number of valid bytes in the buffer */ + uint8_t *buffer; /* Buffer of data to write to the output file */ + + /* Function pointers to handle differences between block and character devices */ + +#ifndef CONFIG_DISABLE_MOUNTPOINT + int (*infread)(struct dd_s *dd); + void (*infclose)(struct dd_s *dd); + int (*outfwrite)(struct dd_s *dd); + void (*outfclose)(struct dd_s *dd); +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char g_dd[] = "dd"; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: dd_outfcloseblk + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_MOUNTPOINT +static void dd_outfcloseblk(struct dd_s *dd) +{ + (void)bchlib_teardown(DD_OUTHANDLE); +} +#endif + + +/**************************************************************************** + * Name: dd_outfclosech + ****************************************************************************/ + +static void dd_outfclosech(struct dd_s *dd) +{ + (void)close(DD_OUTFD); +} + +/**************************************************************************** + * Name: dd_infcloseblk + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_MOUNTPOINT +static void dd_infcloseblk(struct dd_s *dd) +{ + (void)bchlib_teardown(DD_INHANDLE); +} +#endif + +/**************************************************************************** + * Name: dd_infclosech + ****************************************************************************/ + +static void dd_infclosech(struct dd_s *dd) +{ + (void)close(DD_INFD); +} + +/**************************************************************************** + * Name: dd_writeblk + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_MOUNTPOINT +static int dd_writeblk(struct dd_s *dd) +{ + ssize_t nbytes; + off_t offset = (dd->sector - dd->skip) * dd->sectsize; + + /* Write the sector at the specified offset */ + + nbytes = bchlib_write(DD_OUTHANDLE, (char*)dd->buffer, offset, dd->sectsize); + if (nbytes < 0) + { + /* bchlib_write return -EFBIG on attempts to write past the end of + * the device. + */ + + if (nbytes == -EFBIG) + { + dd->eof = true; /* Set end-of-file */ + } + else + { + FAR struct nsh_vtbl_s *vtbl = dd->vtbl; + nsh_output(vtbl, g_fmtcmdfailed, g_dd, "bshlib_write", NSH_ERRNO_OF(-nbytes)); + return ERROR; + } + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: dd_writech + ****************************************************************************/ + +static int dd_writech(struct dd_s *dd) +{ + uint8_t *buffer = dd->buffer; + uint16_t written ; + ssize_t nbytes; + + /* Is the out buffer full (or is this the last one)? */ + + written = 0; + do + { + nbytes = write(DD_OUTFD, buffer, dd->sectsize - written); + if (nbytes < 0) + { + FAR struct nsh_vtbl_s *vtbl = dd->vtbl; + nsh_output(vtbl, g_fmtcmdfailed, g_dd, "write", NSH_ERRNO_OF(-nbytes)); + return ERROR; + } + + written += nbytes; + buffer += nbytes; + } + while (written < dd->sectsize); + + return OK; +} + +/**************************************************************************** + * Name: dd_readblk + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_MOUNTPOINT +static int dd_readblk(struct dd_s *dd) +{ + ssize_t nbytes; + off_t offset = dd->sector * dd->sectsize; + + nbytes = bchlib_read(DD_INHANDLE, (char*)dd->buffer, offset, dd->sectsize); + if (nbytes < 0) + { + FAR struct nsh_vtbl_s *vtbl = dd->vtbl; + nsh_output(vtbl, g_fmtcmdfailed, g_dd, "bshlib_read", NSH_ERRNO_OF(-nbytes)); + return ERROR; + } + + /* bchlib_read return 0 on attempts to write past the end of the device. */ + + dd->nbytes = nbytes; + dd->eof = (nbytes == 0); + return OK; +} +#endif + +/**************************************************************************** + * Name: dd_readch + ****************************************************************************/ + +static int dd_readch(struct dd_s *dd) +{ + uint8_t *buffer = dd->buffer; + ssize_t nbytes; + + dd->nbytes = 0; + do + { + nbytes = read(DD_INFD, buffer, dd->sectsize - dd->nbytes); + if (nbytes < 0) + { + FAR struct nsh_vtbl_s *vtbl = dd->vtbl; + nsh_output(vtbl, g_fmtcmdfailed, g_dd, "read", NSH_ERRNO_OF(-nbytes)); + return ERROR; + } + + dd->nbytes += nbytes; + buffer += nbytes; + } + while (dd->nbytes < dd->sectsize && nbytes > 0); + + dd->eof |= (dd->nbytes == 0); + return OK; +} + +/**************************************************************************** + * Name: dd_infopen + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_MOUNTPOINT +static int dd_filetype(const char *filename) +{ + struct stat sb; + int ret; + + /* Get the type of the file */ + + ret = stat(filename, &sb); + if (ret < 0) + { + return ERROR; /* Return -1 on failure */ + } + + return S_ISBLK(sb.st_mode); /* Return true(1) if block, false(0) if char */ +} +#endif + +/**************************************************************************** + * Name: dd_infopen + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_MOUNTPOINT +static inline int dd_infopen(const char *name, struct dd_s *dd) +{ + FAR struct nsh_vtbl_s *vtbl = dd->vtbl; + int ret; + int type; + + /* Get the type of the input file */ + + type = dd_filetype(name); + if (type < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, g_dd, "stat", NSH_ERRNO_OF(-type)); + return type; + } + + /* Open the input file */ + + if (!type) + { + DD_INFD = open(name, O_RDONLY); + if (DD_INFD < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, g_dd, "open", NSH_ERRNO); + return ERROR; + } + + dd->infread = dd_readch; /* Character oriented read */ + dd->infclose = dd_infclosech; + } + else + { + ret = bchlib_setup(name, true, &DD_INHANDLE); + if (ret < 0) + { + return ERROR; + } + + dd->infread = dd_readblk; + dd->infclose = dd_infcloseblk; + } + return OK; +} +#else +static inline int dd_infopen(const char *name, struct dd_s *dd) +{ + DD_INFD = open(name, O_RDONLY); + if (DD_INFD < 0) + { + FAR struct nsh_vtbl_s *vtbl = dd->vtbl; + nsh_output(vtbl, g_fmtcmdfailed, g_dd, "open", NSH_ERRNO); + return ERROR; + } + return OK; +} +#endif + +/**************************************************************************** + * Name: dd_outfopen + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_MOUNTPOINT +static inline int dd_outfopen(const char *name, struct dd_s *dd) +{ + int type; + int ret = OK; + + /* Get the type of the output file */ + + type = dd_filetype(name); + + /* Open the block driver for input */ + + if (type == true) + { + ret = bchlib_setup(name, true, &DD_OUTHANDLE); + if (ret < 0) + { + return ERROR; + } + + dd->outfwrite = dd_writeblk; /* Block oriented write */ + dd->outfclose = dd_outfcloseblk; + } + + /* Otherwise, the file is character oriented or does not exist */ + + else + { + DD_OUTFD = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (DD_OUTFD < 0) + { + FAR struct nsh_vtbl_s *vtbl = dd->vtbl; + nsh_output(vtbl, g_fmtcmdfailed, g_dd, "open", NSH_ERRNO); + return ERROR; + } + + dd->outfwrite = dd_writech; /* Character oriented write */ + dd->outfclose = dd_outfclosech; + } + return OK; +} +#else +static inline int dd_outfopen(const char *name, struct dd_s *dd) +{ + DD_OUTFD = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (DD_OUTFD < 0) + { + nsh_output(dd->vtbl, g_fmtcmdfailed, g_dd, "open", NSH_ERRNO); + return ERROR; + } + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_dd + ****************************************************************************/ + +int cmd_dd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct dd_s dd; + char *infile = NULL; + char *outfile = NULL; + int ret = ERROR; + int i; + + /* Initialize the dd structure */ + + memset(&dd, 0, sizeof(struct dd_s)); + dd.vtbl = vtbl; /* For nsh_output */ + dd.sectsize = DEFAULT_SECTSIZE; /* Sector size if 'bs=' not provided */ + dd.nsectors = 0xffffffff; /* MAX_UINT32 */ + + /* If no IF= option is provided on the command line, then read + * from stdin. + */ + +#ifdef CAN_PIPE_FROM_STD + DD_INFD = 0; /* stdin */ +#ifndef CONFIG_NSH_DISABLE_DD + dd.infread = readch; /* Character oriented read */ + dd.infclose = noclose; /* Don't close stdin */ +#endif +#endif + /* If no OF= option is provided on the command line, then write + * to stdout. + */ + +#ifdef CAN_PIPE_FROM_STD + DD_OUTDF = 1; /* stdout */ +#ifndef CONFIG_NSH_DISABLE_DD + dd.outfwrite = writech; /* Character oriented write */ + dd.outfclose = noclose; /* Don't close stdout */ +#endif +#endif + + /* Parse command line parameters */ + + for (i = 1; i < argc; i++) + { + if (strncmp(argv[i], "if=", 3) == 0) + { + infile = nsh_getfullpath(vtbl, &argv[i][3]); + } + else if (strncmp(argv[i], "of=", 3) == 0) + { + outfile = nsh_getfullpath(vtbl, &argv[i][3]); + } + else if (strncmp(argv[i], "bs=", 3) == 0) + { + dd.sectsize = atoi(&argv[i][3]); + } + else if (strncmp(argv[i], "count=", 6) == 0) + { + dd.nsectors = atoi(&argv[i][6]); + } + else if (strncmp(argv[i], "skip=", 5) == 0) + { + dd.skip = atoi(&argv[i][5]); + } + } + +#ifndef CAN_PIPE_FROM_STD + if (!infile || !outfile) + { + nsh_output(vtbl, g_fmtargrequired, g_dd); + goto errout_with_paths; + } +#endif + + if (dd.skip < 0 || dd.skip > dd.nsectors) + { + nsh_output(vtbl, g_fmtarginvalid, g_dd); + goto errout_with_paths; + } + + /* Allocate the I/O buffer */ + + dd.buffer = malloc(dd.sectsize); + if (!dd.buffer) + { + nsh_output(vtbl, g_fmtcmdoutofmemory, g_dd); + goto errout_with_paths; + } + + /* Open the input file */ + + ret = dd_infopen(infile, &dd); + if (ret < 0) + { + goto errout_with_paths; + } + + /* Open the output file */ + + ret = dd_outfopen(outfile, &dd); + if (ret < 0) + { + goto errout_with_inf; + } + + /* Then perform the data transfer */ + + dd.sector = 0; + while (!dd.eof && dd.nsectors > 0) + { + /* Read one sector from from the input */ + + ret = DD_READ(&dd); + if (ret < 0) + { + goto errout_with_outf; + } + + /* Has the incoming data stream ended? */ + + if (!dd.eof) + { + /* Pad with zero if necessary (at the end of file only) */ + + for (i = dd.nbytes; i < dd.sectsize; i++) + { + dd.buffer[i] = 0; + } + + /* Write one sector to the output file */ + + if (dd.sector >= dd.skip) + { + ret = DD_WRITE(&dd); + if (ret < 0) + { + goto errout_with_outf; + } + + /* Decrement to show that a sector was written */ + + dd.nsectors--; + } + + /* Increment the sector number */ + + dd.sector++; + } + } + ret = OK; + +errout_with_outf: + DD_INCLOSE(&dd); +errout_with_inf: + DD_OUTCLOSE(&dd); + free(dd.buffer); +errout_with_paths: + if (infile) + { + free(infile); + } + if (outfile) + { + free(outfile); + } + return ret; +} + +#endif /* CONFIG_NFILE_DESCRIPTORS && !CONFIG_NSH_DISABLE_DD */ + diff --git a/apps/nshlib/nsh_envcmds.c b/apps/nshlib/nsh_envcmds.c new file mode 100644 index 000000000..520dd76e7 --- /dev/null +++ b/apps/nshlib/nsh_envcmds.c @@ -0,0 +1,336 @@ +/**************************************************************************** + * apps/nshlib/nsh_envcmds.c + * + * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <libgen.h> +#include <errno.h> + +#include "nsh.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +static const char g_pwd[] = "PWD"; +static const char g_oldpwd[] = "OLDPWD"; +static const char g_home[] = CONFIG_LIB_HOMEDIR; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_getwd + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +static inline FAR const char *nsh_getwd(const char *wd) +{ + const char *val; + + /* If no working directory is defined, then default to the home directory */ + + val = getenv(wd); + if (!val) + { + val = g_home; + } + return val; +} +#endif + +/**************************************************************************** + * Name: nsh_getdirpath + ****************************************************************************/ + +static inline char *nsh_getdirpath(FAR struct nsh_vtbl_s *vtbl, + const char *dirpath, const char *relpath) +{ + char *alloc; + int len; + + /* Handle the special case where the dirpath is simply */ + + if (strcmp(dirpath, "/") == 0) + { + len = strlen(relpath) + 2; + alloc = (char*)malloc(len); + if (alloc) + { + sprintf(alloc, "/%s", relpath); + } + } + else + { + len = strlen(dirpath) + strlen(relpath) + 2; + alloc = (char*)malloc(len); + if (alloc) + { + sprintf(alloc, "%s/%s", dirpath, relpath); + } + } + + if (!alloc) + { + nsh_output(vtbl, g_fmtcmdoutofmemory, "nsh_getdirpath"); + } + return alloc; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_getwd + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +FAR const char *nsh_getcwd(void) +{ + return nsh_getwd(g_pwd); +} +#endif +/**************************************************************************** + * Name: nsh_getfullpath + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +char *nsh_getfullpath(FAR struct nsh_vtbl_s *vtbl, const char *relpath) +{ + const char *wd; + + /* Handle some special cases */ + + if (!relpath || relpath[0] == '\0') + { + /* No relative path provided */ + + return strdup(g_home); + } + else if (relpath[0] == '/') + { + return strdup(relpath); + } + + /* Get the path to the current working directory */ + + wd = nsh_getcwd(); + + /* Fake the '.' directory */ + + if (strcmp(relpath, ".") == 0) + { + return strdup(wd); + } + + /* Return the full path */ + + return nsh_getdirpath(vtbl, wd, relpath); +} +#endif + +/**************************************************************************** + * Name: nsh_freefullpath + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +void nsh_freefullpath(char *relpath) +{ + if (relpath) + { + free(relpath); + } +} +#endif + +/**************************************************************************** + * Name: cmd_cd + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +#ifndef CONFIG_NSH_DISABLE_CD +int cmd_cd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + const char *path = argv[1]; + char *alloc = NULL; + char *fullpath = NULL; + int ret = OK; + + /* Check for special arguments */ + + if (argc < 2 || strcmp(path, "~") == 0) + { + path = g_home; + } + else if (strcmp(path, "-") == 0) + { + alloc = strdup(nsh_getwd(g_oldpwd)); + path = alloc; + } + else if (strcmp(path, "..") == 0) + { + alloc = strdup(nsh_getcwd()); + path = dirname(alloc); + } + else + { + fullpath = nsh_getfullpath(vtbl, path); + path = fullpath; + } + + /* Set the new workding directory */ + + ret = chdir(path); + if (ret != 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "chdir", NSH_ERRNO); + ret = ERROR; + } + + /* Free any memory that was allocated */ + + if (alloc) + { + free(alloc); + } + + if (fullpath) + { + nsh_freefullpath(fullpath); + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_echo + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_ECHO +int cmd_echo(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + int i; + + /* echo each argument, separated by a space as it must have been on the + * command line + */ + + for (i = 1; i < argc; i++) + { + nsh_output(vtbl, "%s ", argv[i]); + } + nsh_output(vtbl, "\n"); + return OK; +} +#endif + +/**************************************************************************** + * Name: cmd_pwd + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +#ifndef CONFIG_NSH_DISABLE_PWD +int cmd_pwd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + nsh_output(vtbl, "%s\n", nsh_getcwd()); + return OK; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_set + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_ENVIRON +#ifndef CONFIG_NSH_DISABLE_SET +int cmd_set(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + int ret = setenv(argv[1], argv[2], TRUE); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "setenv", NSH_ERRNO); + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_unset + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_ENVIRON +#ifndef CONFIG_NSH_DISABLE_UNSET +int cmd_unset(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + int ret = unsetenv(argv[1]); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "unsetenv", NSH_ERRNO); + } + return ret; +} +#endif +#endif diff --git a/apps/nshlib/nsh_fscmds.c b/apps/nshlib/nsh_fscmds.c new file mode 100644 index 000000000..f0451ce02 --- /dev/null +++ b/apps/nshlib/nsh_fscmds.c @@ -0,0 +1,1341 @@ +/**************************************************************************** + * apps/nshlib/nsh_fscmds.c + * + * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> + +#if CONFIG_NFILE_DESCRIPTORS > 0 +# include <sys/stat.h> +# include <fcntl.h> +# if !defined(CONFIG_DISABLE_MOUNTPOINT) +# ifdef CONFIG_FS_READABLE /* Need at least one filesytem in configuration */ +# include <sys/mount.h> +# include <nuttx/ramdisk.h> +# endif +# ifdef CONFIG_FS_FAT +# include <nuttx/mkfatfs.h> +# endif +#endif +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <limits.h> +#include <libgen.h> +#include <errno.h> +#include <debug.h> + +#include "nsh.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#define LSFLAGS_SIZE 1 +#define LSFLAGS_LONG 2 +#define LSFLAGS_RECURSIVE 4 + +/* The size of the I/O buffer may be specified in the + * configs/<board-name>defconfig file -- provided that it is at least as + * large as PATH_MAX. + */ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +# ifdef CONFIG_NSH_FILEIOSIZE +# if CONFIG_NSH_FILEIOSIZE > (PATH_MAX + 1) +# define IOBUFFERSIZE CONFIG_NSH_FILEIOSIZE +# else +# define IOBUFFERSIZE (PATH_MAX + 1) +# endif +# else +# define IOBUFFERSIZE 1024 +# endif +# else +# define IOBUFFERSIZE (PATH_MAX + 1) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef int (*direntry_handler_t)(FAR struct nsh_vtbl_s *, const char *, struct dirent *, void *); + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Common buffer for file I/O. Note the use of this common buffer precludes + * multiple copies of NSH running concurrently. It should be allocated per + * NSH instance and retained in the "vtbl" as is done for the telnet + * connection. + */ + +static char g_iobuffer[IOBUFFERSIZE]; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: trim_dir + ****************************************************************************/ + +static void trim_dir(char *arg) +{ + /* Skip any trailing '/' characters (unless it is also the leading '/') */ + + int len = strlen(arg) - 1; + while (len > 0 && arg[len] == '/') + { + arg[len] = '\0'; + len--; + } +} + +/**************************************************************************** + * Name: nsh_getdirpath + ****************************************************************************/ + +static char *nsh_getdirpath(const char *path, const char *file) +{ + /* Handle the case where all that is left is '/' */ + + if (strcmp(path, "/") == 0) + { + sprintf(g_iobuffer, "/%s", file); + } + else + { + sprintf(g_iobuffer, "%s/%s", path, file); + } + + g_iobuffer[PATH_MAX] = '\0'; + return strdup(g_iobuffer); +} + +/**************************************************************************** + * Name: foreach_direntry + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +static int foreach_direntry(FAR struct nsh_vtbl_s *vtbl, const char *cmd, const char *dirpath, + direntry_handler_t handler, void *pvarg) +{ + DIR *dirp; + int ret = OK; + + /* Trim trailing '/' from directory names */ + +#ifdef CONFIG_NSH_FULLPATH + trim_dir(arg); +#endif + + /* Open the directory */ + + dirp = opendir(dirpath); + + if (!dirp) + { + /* Failed to open the directory */ + + nsh_output(vtbl, g_fmtnosuch, cmd, "directory", dirpath); + return ERROR; + } + + /* Read each directory entry */ + + for (;;) + { + struct dirent *entryp = readdir(dirp); + if (!entryp) + { + /* Finished with this directory */ + + break; + } + + /* Call the handler with this directory entry */ + + if (handler(vtbl, dirpath, entryp, pvarg) < 0) + { + /* The handler reported a problem */ + + ret = ERROR; + break; + } + } + + closedir(dirp); + return ret; +} +#endif + +/**************************************************************************** + * Name: ls_specialdir + ****************************************************************************/ + +static inline int ls_specialdir(const char *dir) +{ + /* '.' and '..' directories are not listed like normal directories */ + + return (strcmp(dir, ".") == 0 || strcmp(dir, "..") == 0); +} + +/**************************************************************************** + * Name: ls_handler + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +static int ls_handler(FAR struct nsh_vtbl_s *vtbl, const char *dirpath, struct dirent *entryp, void *pvarg) +{ + unsigned int lsflags = (unsigned int)pvarg; + int ret; + + /* Check if any options will require that we stat the file */ + + if ((lsflags & (LSFLAGS_SIZE|LSFLAGS_LONG)) != 0) + { + struct stat buf; + char *fullpath = nsh_getdirpath(dirpath, entryp->d_name); + + /* Yes, stat the file */ + + ret = stat(fullpath, &buf); + free(fullpath); + if (ret != 0) + { + nsh_output(vtbl, g_fmtcmdfailed, "ls", "stat", NSH_ERRNO); + return ERROR; + } + + if ((lsflags & LSFLAGS_LONG) != 0) + { + char details[] = "----------"; + if (S_ISDIR(buf.st_mode)) + { + details[0]='d'; + } + else if (S_ISCHR(buf.st_mode)) + { + details[0]='c'; + } + else if (S_ISBLK(buf.st_mode)) + { + details[0]='b'; + } + + if ((buf.st_mode & S_IRUSR) != 0) + { + details[1]='r'; + } + + if ((buf.st_mode & S_IWUSR) != 0) + { + details[2]='w'; + } + + if ((buf.st_mode & S_IXUSR) != 0) + { + details[3]='x'; + } + + if ((buf.st_mode & S_IRGRP) != 0) + { + details[4]='r'; + } + + if ((buf.st_mode & S_IWGRP) != 0) + { + details[5]='w'; + } + + if ((buf.st_mode & S_IXGRP) != 0) + { + details[6]='x'; + } + + if ((buf.st_mode & S_IROTH) != 0) + { + details[7]='r'; + } + + if ((buf.st_mode & S_IWOTH) != 0) + { + details[8]='w'; + } + + if ((buf.st_mode & S_IXOTH) != 0) + { + details[9]='x'; + } + + nsh_output(vtbl, " %s", details); + } + + if ((lsflags & LSFLAGS_SIZE) != 0) + { + nsh_output(vtbl, "%8d", buf.st_size); + } + } + + /* then provide the filename that is common to normal and verbose output */ + +#ifdef CONFIG_NSH_FULLPATH + nsh_output(vtbl, " %s/%s", arg, entryp->d_name); +#else + nsh_output(vtbl, " %s", entryp->d_name); +#endif + + if (DIRENT_ISDIRECTORY(entryp->d_type) && !ls_specialdir(entryp->d_name)) + { + nsh_output(vtbl, "/\n"); + } + else + { + nsh_output(vtbl, "\n"); + } + return OK; +} +#endif + +/**************************************************************************** + * Name: ls_recursive + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +static int ls_recursive(FAR struct nsh_vtbl_s *vtbl, const char *dirpath, + struct dirent *entryp, void *pvarg) +{ + int ret = OK; + + /* Is this entry a directory (and not one of the special directories, . and ..)? */ + + if (DIRENT_ISDIRECTORY(entryp->d_type) && !ls_specialdir(entryp->d_name)) + { + /* Yes.. */ + + char *newpath; + newpath = nsh_getdirpath(dirpath, entryp->d_name); + + /* List the directory contents */ + + nsh_output(vtbl, "%s:\n", newpath); + + /* Traverse the directory */ + + ret = foreach_direntry(vtbl, "ls", newpath, ls_handler, pvarg); + if (ret == 0) + { + /* Then recurse to list each directory within the directory */ + + ret = foreach_direntry(vtbl, "ls", newpath, ls_recursive, pvarg); + free(newpath); + } + } + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_cat + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +#ifndef CONFIG_NSH_DISABLE_CAT +int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char buffer[IOBUFFERSIZE]; + char *fullpath; + int fd; + int i; + int ret = OK; + + /* Loop for each file name on the command line */ + + for (i = 1; i < argc && ret == OK; i++) + { + /* Get the fullpath to the file */ + + fullpath = nsh_getfullpath(vtbl, argv[i]); + if (fullpath) + { + /* Open the file for reading */ + + fd = open(fullpath, O_RDONLY); + if (fd < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO); + } + else + { + /* And just dump it byte for byte into stdout */ + + for (;;) + { + int nbytesread = read(fd, buffer, IOBUFFERSIZE); + + /* Check for read errors */ + + if (nbytesread < 0) + { + /* EINTR is not an error (but will stop stop the cat) */ + +#ifndef CONFIG_DISABLE_SIGNALS + if (errno == EINTR) + { + nsh_output(vtbl, g_fmtsignalrecvd, argv[0]); + } + else +#endif + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "read", NSH_ERRNO); + } + + ret = ERROR; + break; + } + + /* Check for data successfully read */ + + else if (nbytesread > 0) + { + int nbyteswritten = 0; + + while (nbyteswritten < nbytesread) + { + ssize_t n = nsh_write(vtbl, buffer, nbytesread); + if (n < 0) + { + /* EINTR is not an error (but will stop stop the cat) */ + + #ifndef CONFIG_DISABLE_SIGNALS + if (errno == EINTR) + { + nsh_output(vtbl, g_fmtsignalrecvd, argv[0]); + } + else +#endif + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "write", NSH_ERRNO); + } + ret = ERROR; + break; + } + else + { + nbyteswritten += n; + } + } + } + + /* Otherwise, it is the end of file */ + + else + { + break; + } + } + + (void)close(fd); + } + + /* Free the allocated full path */ + + nsh_freefullpath(fullpath); + } + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_cp + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +#ifndef CONFIG_NSH_DISABLE_CP +int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct stat buf; + char *srcpath = NULL; + char *destpath = NULL; + char *allocpath = NULL; + int oflags = O_WRONLY|O_CREAT|O_TRUNC; + int rdfd; + int wrfd; + int ret = ERROR; + + /* Get the full path to the source file */ + + srcpath = nsh_getfullpath(vtbl, argv[1]); + if (!srcpath) + { + goto errout; + } + + /* Open the source file for reading */ + + rdfd = open(srcpath, O_RDONLY); + if (rdfd < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO); + goto errout_with_srcpath; + } + + /* Get the full path to the destination file or directory */ + + destpath = nsh_getfullpath(vtbl, argv[2]); + if (!destpath) + { + goto errout_with_rdfd; + } + + /* Check if the destination is a directory */ + + ret = stat(destpath, &buf); + if (ret == 0) + { + /* Something exists here... is it a directory? */ + + if (S_ISDIR(buf.st_mode)) + { + /* Yes, it is a directory. Remove any trailing '/' characters from the path */ + + trim_dir(argv[2]); + + /* Construct the full path to the new file */ + + allocpath = nsh_getdirpath(argv[2], basename(argv[1]) ); + if (!allocpath) + { + nsh_output(vtbl, g_fmtcmdoutofmemory, argv[0]); + goto errout_with_destpath; + } + + /* Open then dest for writing */ + + nsh_freefullpath(destpath); + destpath = allocpath; + } + else if (!S_ISREG(buf.st_mode)) + { + /* Maybe it is a driver? */ + + oflags = O_WRONLY; + } + } + + /* Now open the destination */ + + wrfd = open(destpath, oflags, 0666); + if (wrfd < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO); + goto errout_with_allocpath; + } + + /* Now copy the file */ + + for (;;) + { + int nbytesread; + int nbyteswritten; + + do + { + nbytesread = read(rdfd, g_iobuffer, IOBUFFERSIZE); + if (nbytesread == 0) + { + /* End of file */ + + ret = OK; + goto errout_with_wrfd; + } + else if (nbytesread < 0) + { + /* EINTR is not an error (but will still stop the copy) */ + +#ifndef CONFIG_DISABLE_SIGNALS + if (errno == EINTR) + { + nsh_output(vtbl, g_fmtsignalrecvd, argv[0]); + } + else +#endif + { + /* Read error */ + + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "read", NSH_ERRNO); + } + goto errout_with_wrfd; + } + } + while (nbytesread <= 0); + + do + { + nbyteswritten = write(wrfd, g_iobuffer, nbytesread); + if (nbyteswritten >= 0) + { + nbytesread -= nbyteswritten; + } + else + { + /* EINTR is not an error (but will still stop the copy) */ + +#ifndef CONFIG_DISABLE_SIGNALS + if (errno == EINTR) + { + nsh_output(vtbl, g_fmtsignalrecvd, argv[0]); + } + else +#endif + { + /* Read error */ + + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "write", NSH_ERRNO); + } + goto errout_with_wrfd; + } + } + while (nbytesread > 0); + } + +errout_with_wrfd: + close(wrfd); + +errout_with_allocpath: + if (allocpath) + { + free(allocpath); + } + +errout_with_destpath: + if (destpath && !allocpath) + { + nsh_freefullpath(destpath); + } + +errout_with_rdfd: + close(rdfd); + +errout_with_srcpath: + if (srcpath) + { + nsh_freefullpath(srcpath); + } +errout: + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_losetup + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_MOUNTPOINT) +#ifndef CONFIG_NSH_DISABLE_LOSETUP +int cmd_losetup(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *loopdev = NULL; + char *filepath = NULL; + bool teardown = false; + bool readonly = false; + off_t offset = 0; + bool badarg = false; + int ret = ERROR; + int option; + + /* Get the losetup options: Two forms are supported: + * + * losetup -d <loop-device> + * losetup [-o <offset>] [-r] <loop-device> <filename> + * + * NOTE that the -o and -r options are accepted with the -d option, but + * will be ignored. + */ + + while ((option = getopt(argc, argv, "d:o:r")) != ERROR) + { + switch (option) + { + case 'd': + loopdev = nsh_getfullpath(vtbl, optarg); + teardown = true; + break; + + case 'o': + offset = atoi(optarg); + break; + + case 'r': + readonly = true; + break; + + case '?': + default: + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + break; + } + } + + /* If a bad argument was encountered, then return without processing the command */ + + if (badarg) + { + goto errout_with_paths; + } + + /* If this is not a tear down operation, then additional command line + * parameters are required. + */ + + if (!teardown) + { + /* There must be two arguments on the command line after the options */ + + if (optind + 1 < argc) + { + loopdev = nsh_getfullpath(vtbl, argv[optind]); + optind++; + + filepath = nsh_getfullpath(vtbl, argv[optind]); + optind++; + } + else + { + nsh_output(vtbl, g_fmtargrequired, argv[0]); + goto errout_with_paths; + } + } + + /* There should be nothing else on the command line */ + + if (optind < argc) + { + nsh_output(vtbl, g_fmttoomanyargs, argv[0]); + goto errout_with_paths; + } + + /* Perform the teardown operation */ + + if (teardown) + { + /* Tear down the loop device. */ + + ret = loteardown(loopdev); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "loteardown", NSH_ERRNO_OF(-ret)); + goto errout_with_paths; + } + } + else + { + /* Set up the loop device */ + + ret = losetup(loopdev, filepath, 512, offset, readonly); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "losetup", NSH_ERRNO_OF(-ret)); + goto errout_with_paths; + } + } + + /* Free memory */ + +errout_with_paths: + if (loopdev) + { + free(loopdev); + } + + if (filepath) + { + free(filepath); + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_ls + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +#ifndef CONFIG_NSH_DISABLE_LS +int cmd_ls(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + const char *relpath; + unsigned int lsflags = 0; + char *fullpath; + bool badarg = false; + int ret; + + /* Get the ls options */ + + int option; + while ((option = getopt(argc, argv, "lRs")) != ERROR) + { + switch (option) + { + case 'l': + lsflags |= (LSFLAGS_SIZE|LSFLAGS_LONG); + break; + + case 'R': + lsflags |= LSFLAGS_RECURSIVE; + break; + + case 's': + lsflags |= LSFLAGS_SIZE; + break; + + case '?': + default: + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + break; + } + } + + /* If a bad argument was encountered, then return without processing the command */ + + if (badarg) + { + return ERROR; + } + + /* There may be one argument after the options */ + + if (optind + 1 < argc) + { + nsh_output(vtbl, g_fmttoomanyargs, argv[0]); + return ERROR; + } + else if (optind >= argc) + { +#ifndef CONFIG_DISABLE_ENVIRON + relpath = nsh_getcwd(); +#else + nsh_output(vtbl, g_fmtargrequired, argv[0]); + return ERROR; +#endif + } + else + { + relpath = argv[optind]; + } + + /* Get the fullpath to the directory */ + + fullpath = nsh_getfullpath(vtbl, relpath); + if (!fullpath) + { + return ERROR; + } + + /* List the directory contents */ + + nsh_output(vtbl, "%s:\n", fullpath); + ret = foreach_direntry(vtbl, "ls", fullpath, ls_handler, (void*)lsflags); + if (ret == OK && (lsflags & LSFLAGS_RECURSIVE) != 0) + { + /* Then recurse to list each directory within the directory */ + + ret = foreach_direntry(vtbl, "ls", fullpath, ls_recursive, (void*)lsflags); + } + nsh_freefullpath(fullpath); + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_mkdir + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE) +#ifndef CONFIG_NSH_DISABLE_MKDIR +int cmd_mkdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *fullpath = nsh_getfullpath(vtbl, argv[1]); + int ret = ERROR; + + if (fullpath) + { + ret = mkdir(fullpath, 0777); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkdir", NSH_ERRNO); + } + nsh_freefullpath(fullpath); + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_mkfatfs + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_FAT) +#ifndef CONFIG_NSH_DISABLE_MKFATFS +int cmd_mkfatfs(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct fat_format_s fmt = FAT_FORMAT_INITIALIZER; + char *fullpath = nsh_getfullpath(vtbl, argv[1]); + int ret = ERROR; + + if (fullpath) + { + ret = mkfatfs(fullpath, &fmt); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkfatfs", NSH_ERRNO); + } + nsh_freefullpath(fullpath); + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_mkfifo + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +#ifndef CONFIG_NSH_DISABLE_MKFIFO +int cmd_mkfifo(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *fullpath = nsh_getfullpath(vtbl, argv[1]); + int ret = ERROR; + + if (fullpath) + { + ret = mkfifo(fullpath, 0777); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkfifo", NSH_ERRNO); + } + nsh_freefullpath(fullpath); + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_mkrd + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE) +#ifndef CONFIG_NSH_DISABLE_MKRD +int cmd_mkrd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + const char *fmt; + uint8_t *buffer; + uint32_t nsectors; + bool badarg = false; + int sectsize = 512; + int minor = 0; + int ret; + + /* Get the mount options */ + + int option; + while ((option = getopt(argc, argv, ":m:s:")) != ERROR) + { + switch (option) + { + case 'm': + minor = atoi(optarg); + if (minor < 0 || minor > 255) + { + nsh_output(vtbl, g_fmtargrange, argv[0]); + badarg = true; + } + break; + + case 's': + sectsize = atoi(optarg); + if (minor < 0 || minor > 16384) + { + nsh_output(vtbl, g_fmtargrange, argv[0]); + badarg = true; + } + break; + + case ':': + nsh_output(vtbl, g_fmtargrequired, argv[0]); + badarg = true; + break; + + case '?': + default: + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + break; + } + } + + /* If a bad argument was encountered, then return without processing the command */ + + if (badarg) + { + return ERROR; + } + + /* There should be exactly on parameter left on the command-line */ + + if (optind == argc-1) + { + nsectors = (uint32_t)atoi(argv[optind]); + } + else if (optind >= argc) + { + fmt = g_fmttoomanyargs; + goto errout_with_fmt; + } + else + { + fmt = g_fmtargrequired; + goto errout_with_fmt; + } + + /* Allocate the memory backing up the ramdisk */ + + buffer = (uint8_t*)malloc(sectsize * nsectors); + if (!buffer) + { + fmt = g_fmtcmdoutofmemory; + goto errout_with_fmt; + } + +#ifdef CONFIG_DEBUG_VERBOSE + memset(buffer, 0, sectsize * nsectors); +#endif + dbg("RAMDISK at %p\n", buffer); + + /* Then register the ramdisk */ + + ret = ramdisk_register(minor, buffer, nsectors, sectsize, true); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "ramdisk_register", NSH_ERRNO_OF(-ret)); + free(buffer); + return ERROR; + } + return ret; + +errout_with_fmt: + nsh_output(vtbl, fmt, argv[0]); + return ERROR; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_mount + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_READABLE) +#ifndef CONFIG_NSH_DISABLE_MOUNT +int cmd_mount(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *source; + char *target; + char *filesystem = 0; + bool badarg = false; + int ret; + + /* Get the mount options */ + + int option; + while ((option = getopt(argc, argv, ":t:")) != ERROR) + { + switch (option) + { + case 't': + filesystem = optarg; + break; + + case ':': + nsh_output(vtbl, g_fmtargrequired, argv[0]); + badarg = true; + break; + + case '?': + default: + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + break; + } + } + + /* If a bad argument was encountered, then return without processing the command */ + + if (badarg) + { + return ERROR; + } + + /* There are two required arguments after the options */ + + if (optind + 2 < argc) + { + nsh_output(vtbl, g_fmttoomanyargs, argv[0]); + return ERROR; + } + else if (optind + 2 > argc) + { + nsh_output(vtbl, g_fmtargrequired, argv[0]); + return ERROR; + } + + /* The source and target pathes might be relative to the current + * working directory. + */ + + source = nsh_getfullpath(vtbl, argv[optind]); + if (!source) + { + return ERROR; + } + + target = nsh_getfullpath(vtbl, argv[optind+1]); + if (!source) + { + nsh_freefullpath(source); + return ERROR; + } + + /* Perform the mount */ + + ret = mount(source, target, filesystem, 0, NULL); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mount", NSH_ERRNO); + } + + nsh_freefullpath(source); + nsh_freefullpath(target); + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_rm + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE) +#ifndef CONFIG_NSH_DISABLE_RM +int cmd_rm(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *fullpath = nsh_getfullpath(vtbl, argv[1]); + int ret = ERROR; + + if (fullpath) + { + ret = unlink(fullpath); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "unlink", NSH_ERRNO); + } + nsh_freefullpath(fullpath); + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_rmdir + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE) +#ifndef CONFIG_NSH_DISABLE_RMDIR +int cmd_rmdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *fullpath = nsh_getfullpath(vtbl, argv[1]); + int ret = ERROR; + + if (fullpath) + { + ret = rmdir(fullpath); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "rmdir", NSH_ERRNO); + } + nsh_freefullpath(fullpath); + } + return ret; +} +#endif +#endif + +/**************************************************************************** + * Name: nsh_script + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_NSH_DISABLESCRIPT) +int nsh_script(FAR struct nsh_vtbl_s *vtbl, const char *cmd, const char *path) +{ + char *fullpath; + FILE *stream; + char *buffer; + char *pret; + int ret = ERROR; + + /* The path to the script may be relative to the current working directory */ + + fullpath = nsh_getfullpath(vtbl, path); + if (!fullpath) + { + return ERROR; + } + + /* Get a reference to the common input buffer */ + + buffer = nsh_linebuffer(vtbl); + if (buffer) + { + /* Open the file containing the script */ + + stream = fopen(fullpath, "r"); + if (!stream) + { + nsh_output(vtbl, g_fmtcmdfailed, cmd, "fopen", NSH_ERRNO); + nsh_freefullpath(fullpath); + return ERROR; + } + + /* Loop, processing each command line in the script file (or + * until an error occurs) + */ + + do + { + /* Get the next line of input from the file*/ + + fflush(stdout); + pret = fgets(buffer, CONFIG_NSH_LINELEN, stream); + if (pret) + { + /* Parse process the command. NOTE: this is recursive... + * we got to cmd_sh via a call to nsh_parse. So some + * considerable amount of stack may be used. + */ + + ret = nsh_parse(vtbl, buffer); + } + } + while (pret && ret == OK); + fclose(stream); + } + + nsh_freefullpath(fullpath); + return ret; +} +#endif + +/**************************************************************************** + * Name: cmd_sh + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_NSH_DISABLESCRIPT) +#ifndef CONFIG_NSH_DISABLE_SH +int cmd_sh(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + return nsh_script(vtbl, argv[0], argv[1]); +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_umount + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_READABLE) +#ifndef CONFIG_NSH_DISABLE_UMOUNT +int cmd_umount(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *fullpath = nsh_getfullpath(vtbl, argv[1]); + int ret = ERROR; + + if (fullpath) + { + /* Perform the umount */ + + ret = umount(fullpath); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "umount", NSH_ERRNO); + } + nsh_freefullpath(fullpath); + } + return ret; +} +#endif +#endif diff --git a/apps/nshlib/nsh_mmcmds.c b/apps/nshlib/nsh_mmcmds.c new file mode 100644 index 000000000..4b6e0bf41 --- /dev/null +++ b/apps/nshlib/nsh_mmcmds.c @@ -0,0 +1,95 @@ +/**************************************************************************** + * apps/nshlib/dbg_mmcmds.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdlib.h> + +#include "nsh.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_free + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_FREE +int cmd_free(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct mallinfo mem; + +#ifdef CONFIG_CAN_PASS_STRUCTS + mem = mallinfo(); +#else + (void)mallinfo(&mem); +#endif + + nsh_output(vtbl, " total used free largest\n"); + nsh_output(vtbl, "Mem: %11d%11d%11d%11d\n", + mem.arena, mem.uordblks, mem.fordblks, mem.mxordblk); + + return OK; +} +#endif diff --git a/apps/nshlib/nsh_netcmds.c b/apps/nshlib/nsh_netcmds.c new file mode 100644 index 000000000..8285da295 --- /dev/null +++ b/apps/nshlib/nsh_netcmds.c @@ -0,0 +1,863 @@ +/**************************************************************************** + * apps/nshlib/nsh_netcmds.c + * + * Copyright (C) 2007-2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#ifdef CONFIG_NET + +#include <sys/stat.h> /* Needed for open */ +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sched.h> +#include <fcntl.h> /* Needed for open */ +#include <libgen.h> /* Needed for basename */ +#include <errno.h> + +#include <nuttx/net.h> +#include <nuttx/clock.h> +#include <net/ethernet.h> +#include <net/uip/uip.h> +#include <net/uip/uip-arch.h> +#include <netinet/ether.h> + +#ifdef CONFIG_NET_STATISTICS +# include <net/uip/uip.h> +#endif + +#if defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) && \ + !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_DISABLE_SIGNALS) +# include <apps/netutils/uiplib.h> +#endif + +#if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +# include <apps/netutils/uiplib.h> +# include <apps/netutils/tftp.h> +#endif + +#if defined(CONFIG_NET_TCP) && CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_WGET +# include <apps/netutils/uiplib.h> +# include <apps/netutils/webclient.h> +# endif +#endif + +#include "nsh.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#define DEFAULT_PING_DATALEN 56 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +struct tftpc_args_s +{ + bool binary; /* true:binary ("octect") false:text ("netascii") */ + bool allocated; /* true: destpath is allocated */ + char *destpath; /* Path at destination */ + const char *srcpath; /* Path at src */ + in_addr_t ipaddr; /* Host IP address */ +}; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#if defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) && \ + !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_DISABLE_SIGNALS) +static uint16_t g_pingid = 0; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ping_newid + ****************************************************************************/ + +#if defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) && \ + !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_DISABLE_SIGNALS) +static inline uint16_t ping_newid(void) +{ + irqstate_t save = irqsave(); + uint16_t ret = ++g_pingid; + irqrestore(save); + return ret; +} +#endif + +/**************************************************************************** + * Name: uip_statistics + ****************************************************************************/ + +#if defined(CONFIG_NET_STATISTICS) && !defined(CONFIG_NSH_DISABLE_IFCONFIG) +static inline void uip_statistics(FAR struct nsh_vtbl_s *vtbl) +{ + nsh_output(vtbl, "uIP IP "); +#ifdef CONFIG_NET_TCP + nsh_output(vtbl, " TCP"); +#endif +#ifdef CONFIG_NET_UDP + nsh_output(vtbl, " UDP"); +#endif +#ifdef CONFIG_NET_ICMP + nsh_output(vtbl, " ICMP"); +#endif + nsh_output(vtbl, "\n"); + + /* Received packets */ + + nsh_output(vtbl, "Received %04x",uip_stat.ip.recv); +#ifdef CONFIG_NET_TCP + nsh_output(vtbl, " %04x",uip_stat.tcp.recv); +#endif +#ifdef CONFIG_NET_UDP + nsh_output(vtbl, " %04x",uip_stat.udp.recv); +#endif +#ifdef CONFIG_NET_ICMP + nsh_output(vtbl, " %04x",uip_stat.icmp.recv); +#endif + nsh_output(vtbl, "\n"); + + /* Dropped packets */ + + nsh_output(vtbl, "Dropped %04x",uip_stat.ip.drop); +#ifdef CONFIG_NET_TCP + nsh_output(vtbl, " %04x",uip_stat.tcp.drop); +#endif +#ifdef CONFIG_NET_UDP + nsh_output(vtbl, " %04x",uip_stat.udp.drop); +#endif +#ifdef CONFIG_NET_ICMP + nsh_output(vtbl, " %04x",uip_stat.icmp.drop); +#endif + nsh_output(vtbl, "\n"); + + nsh_output(vtbl, " IP VHL: %04x HBL: %04x\n", + uip_stat.ip.vhlerr, uip_stat.ip.hblenerr); + nsh_output(vtbl, " LBL: %04x Frg: %04x\n", + uip_stat.ip.lblenerr, uip_stat.ip.fragerr); + + nsh_output(vtbl, " Checksum %04x",uip_stat.ip.chkerr); +#ifdef CONFIG_NET_TCP + nsh_output(vtbl, " %04x",uip_stat.tcp.chkerr); +#endif +#ifdef CONFIG_NET_UDP + nsh_output(vtbl, " %04x",uip_stat.udp.chkerr); +#endif +#ifdef CONFIG_NET_ICMP + nsh_output(vtbl, " ----"); +#endif + nsh_output(vtbl, "\n"); + +#ifdef CONFIG_NET_TCP + nsh_output(vtbl, " TCP ACK: %04x SYN: %04x\n", + uip_stat.tcp.ackerr, uip_stat.tcp.syndrop); + nsh_output(vtbl, " RST: %04x %04x\n", + uip_stat.tcp.rst, uip_stat.tcp.synrst); +#endif + + nsh_output(vtbl, " Type %04x",uip_stat.ip.protoerr); +#ifdef CONFIG_NET_TCP + nsh_output(vtbl, " ----"); +#endif +#ifdef CONFIG_NET_UDP + nsh_output(vtbl, " ----"); +#endif +#ifdef CONFIG_NET_ICMP + nsh_output(vtbl, " %04x",uip_stat.icmp.typeerr); +#endif + nsh_output(vtbl, "\n"); + + /* Sent packets */ + + nsh_output(vtbl, "Sent ----",uip_stat.ip.sent); +#ifdef CONFIG_NET_TCP + nsh_output(vtbl, " %04x",uip_stat.tcp.sent); +#endif +#ifdef CONFIG_NET_UDP + nsh_output(vtbl, " %04x",uip_stat.udp.sent); +#endif +#ifdef CONFIG_NET_ICMP + nsh_output(vtbl, " %04x",uip_stat.icmp.sent); +#endif + nsh_output(vtbl, "\n"); + +#ifdef CONFIG_NET_TCP + nsh_output(vtbl, " Rexmit ---- %04x",uip_stat.tcp.rexmit); +#ifdef CONFIG_NET_UDP + nsh_output(vtbl, " ----"); +#endif +#ifdef CONFIG_NET_ICMP + nsh_output(vtbl, " ----"); +#endif + nsh_output(vtbl, "\n"); +#endif + nsh_output(vtbl, "\n"); +} +#else +# define uip_statistics(vtbl) +#endif + + +/**************************************************************************** + * Name: ifconfig_callback + ****************************************************************************/ + +int ifconfig_callback(FAR struct uip_driver_s *dev, void *arg) +{ + struct nsh_vtbl_s *vtbl = (struct nsh_vtbl_s*)arg; + struct in_addr addr; + + nsh_output(vtbl, "%s\tHWaddr %s\n", dev->d_ifname, ether_ntoa(&dev->d_mac)); + addr.s_addr = dev->d_ipaddr; + nsh_output(vtbl, "\tIPaddr:%s ", inet_ntoa(addr)); + addr.s_addr = dev->d_draddr; + nsh_output(vtbl, "DRaddr:%s ", inet_ntoa(addr)); + addr.s_addr = dev->d_netmask; + nsh_output(vtbl, "Mask:%s\n\n", inet_ntoa(addr)); + return OK; +} + +/**************************************************************************** + * Name: tftpc_parseargs + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +int tftpc_parseargs(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv, + struct tftpc_args_s *args) +{ + FAR const char *fmt = g_fmtarginvalid; + bool badarg = false; + int option; + + /* Get the ping options */ + + memset(args, 0, sizeof(struct tftpc_args_s)); + while ((option = getopt(argc, argv, ":bnf:h:")) != ERROR) + { + switch (option) + { + case 'b': + args->binary = true; + break; + + case 'n': + args->binary = false; + break; + + case 'f': + args->destpath = optarg; + break; + + case 'h': + if (!uiplib_ipaddrconv(optarg, (FAR unsigned char*)&args->ipaddr)) + { + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + } + break; + + case ':': + nsh_output(vtbl, g_fmtargrequired, argv[0]); + badarg = true; + break; + + case '?': + default: + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + break; + } + } + + /* If a bad argument was encountered, then return without processing the command */ + + if (badarg) + { + return ERROR; + } + + /* There should be exactly one parameter left on the command-line */ + + if (optind == argc-1) + { + args->srcpath = argv[optind]; + } + + /* optind == argc means that there is nothing left on the command-line */ + + else if (optind >= argc) + { + fmt = g_fmtargrequired; + goto errout; + } + + /* optind < argc-1 means that there are too many arguments on the + * command-line + */ + + else + { + fmt = g_fmttoomanyargs; + goto errout; + } + + /* The HOST IP address is also required */ + + if (!args->ipaddr) + { + fmt = g_fmtargrequired; + goto errout; + } + + /* If the destpath was not provided, then we have do a little work. */ + + if (!args->destpath) + { + char *tmp1; + char *tmp2; + + /* Copy the srcpath... baseanme might modify it */ + + fmt = g_fmtcmdoutofmemory; + tmp1 = strdup(args->srcpath); + if (!tmp1) + { + goto errout; + } + + /* Get the basename of the srcpath */ + + tmp2 = basename(tmp1); + if (!tmp2) + { + free(tmp1); + goto errout; + } + + /* Use that basename as the destpath */ + + args->destpath = strdup(tmp2); + free(tmp1); + if (!args->destpath) + { + goto errout; + } + args->allocated = true; + } + + return OK; + +errout: + nsh_output(vtbl, fmt, argv[0]); + return ERROR; +} +#endif + +/**************************************************************************** + * Name: wget_callback + ****************************************************************************/ + +#if defined(CONFIG_NET_TCP) && CONFIG_NFILE_DESCRIPTORS > 0 +#ifndef CONFIG_NSH_DISABLE_WGET +static void wget_callback(FAR char **buffer, int offset, int datend, + FAR int *buflen, FAR void *arg) +{ + (void)write((int)arg, &((*buffer)[offset]), datend - offset); +} +#endif +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_get + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +#ifndef CONFIG_NSH_DISABLE_GET +int cmd_get(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct tftpc_args_s args; + char *fullpath; + + /* Parse the input parameter list */ + + if (tftpc_parseargs(vtbl, argc, argv, &args) != OK) + { + return ERROR; + } + + /* Get the full path to the local file */ + + fullpath = nsh_getfullpath(vtbl, args.srcpath); + + /* Then perform the TFTP get operation */ + + if (tftpget(args.srcpath, fullpath, args.ipaddr, args.binary) != OK) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "tftpget", NSH_ERRNO); + } + + /* Release any allocated memory */ + + if (args.allocated) + { + free(args.destpath); + } + free(fullpath); + return OK; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_ifconfig + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_IFCONFIG +int cmd_ifconfig(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct in_addr addr; + in_addr_t ip; + + /* With one or no arguments, ifconfig simply shows the status of ethernet + * device: + * + * ifconfig + * ifconfig [nic_name] + */ + + if (argc <= 2) + { + netdev_foreach(ifconfig_callback, vtbl); + uip_statistics(vtbl); + return OK; + } + + /* If both the network interface name and an IP address are supplied as + * arguments, then ifconfig will set the address of the ethernet device: + * + * ifconfig nic_name ip_address + */ + + /* Set host ip address */ + + ip = addr.s_addr = inet_addr(argv[2]); + uip_sethostaddr(argv[1], &addr); + + /* Set gateway */ + + ip = NTOHL(ip); + ip &= ~0x000000ff; + ip |= 0x00000001; + + addr.s_addr = HTONL(ip); + uip_setdraddr(argv[1], &addr); + + /* Set netmask */ + + addr.s_addr = inet_addr("255.255.255.0"); + uip_setnetmask(argv[1], &addr); + + return OK; +} +#endif + +/**************************************************************************** + * Name: cmd_ping + ****************************************************************************/ + +#if defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) && \ + !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_DISABLE_SIGNALS) +#ifndef CONFIG_NSH_DISABLE_PING +int cmd_ping(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + FAR const char *fmt = g_fmtarginvalid; + const char *staddr; + uip_ipaddr_t ipaddr; + uint32_t start; + uint32_t next; + uint32_t dsec = 10; + uint16_t id; + bool badarg = false; + int count = 10; + int option; + int seqno; + int replies = 0; + int elapsed; + int tmp; + int i; + + /* Get the ping options */ + + while ((option = getopt(argc, argv, ":c:i:")) != ERROR) + { + switch (option) + { + case 'c': + count = atoi(optarg); + if (count < 1 || count > 10000) + { + nsh_output(vtbl, g_fmtargrange, argv[0]); + badarg = true; + } + break; + + case 'i': + tmp = atoi(optarg); + if (tmp < 1 || tmp >= 4294) + { + nsh_output(vtbl, g_fmtargrange, argv[0]); + badarg = true; + } + else + { + dsec = 10 * tmp; + } + break; + + case ':': + nsh_output(vtbl, g_fmtargrequired, argv[0]); + badarg = true; + break; + + case '?': + default: + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + break; + } + } + + /* If a bad argument was encountered, then return without processing the command */ + + if (badarg) + { + return ERROR; + } + + /* There should be exactly on parameter left on the command-line */ + + if (optind == argc-1) + { + staddr = argv[optind]; + if (!uiplib_ipaddrconv(staddr, (FAR unsigned char*)&ipaddr)) + { + goto errout; + } + } + else if (optind >= argc) + { + fmt = g_fmttoomanyargs; + goto errout; + } + else + { + fmt = g_fmtargrequired; + goto errout; + } + + /* Get the ID to use */ + + id = ping_newid(); + + /* Loop for the specified count */ + + nsh_output(vtbl, "PING %s %d bytes of data\n", staddr, DEFAULT_PING_DATALEN); + start = g_system_timer; + for (i = 1; i <= count; i++) + { + /* Send the ECHO request and wait for the response */ + + next = g_system_timer; + seqno = uip_ping(ipaddr, id, i, DEFAULT_PING_DATALEN, dsec); + + /* Was any response returned? We can tell if a non-negative sequence + * number was returned. + */ + + if (seqno >= 0 && seqno <= i) + { + /* Get the elpased time from the time that the request was + * sent until the response was received. If we got a response + * to an earlier request, then fudge the elpased time. + */ + + elapsed = TICK2MSEC(g_system_timer - next); + if (seqno < i) + { + elapsed += 100*dsec*(i - seqno); + } + + /* Report the receipt of the reply */ + + nsh_output(vtbl, "%d bytes from %s: icmp_seq=%d time=%d ms\n", + DEFAULT_PING_DATALEN, staddr, seqno, elapsed); + replies++; + } + + /* Wait for the remainder of the interval. If the last seqno<i, + * then this is a bad idea... we will probably lose the response + * to the current request! + */ + + elapsed = TICK2DSEC(g_system_timer - next); + if (elapsed < dsec) + { + usleep(100000*dsec); + } + } + + /* Get the total elapsed time */ + + elapsed = TICK2MSEC(g_system_timer - start); + + /* Calculate the percentage of lost packets */ + + tmp = (100*(count - replies) + (count >> 1)) / count; + + nsh_output(vtbl, "%d packets transmitted, %d received, %d%% packet loss, time %d ms\n", + count, replies, tmp, elapsed); + return OK; + +errout: + nsh_output(vtbl, fmt, argv[0]); + return ERROR; +} +#endif +#endif /* CONFIG_NET_ICMP && CONFIG_NET_ICMP_PING */ + +/**************************************************************************** + * Name: cmd_put + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +#ifndef CONFIG_NSH_DISABLE_PUT +int cmd_put(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + struct tftpc_args_s args; + char *fullpath; + + /* Parse the input parameter list */ + + if (tftpc_parseargs(vtbl, argc, argv, &args) != OK) + { + return ERROR; + } + + /* Get the full path to the local file */ + + fullpath = nsh_getfullpath(vtbl, args.srcpath); + + /* Then perform the TFTP put operation */ + + if (tftpput(fullpath, args.destpath, args.ipaddr, args.binary) != OK) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "tftpput", NSH_ERRNO); + } + + /* Release any allocated memory */ + + if (args.allocated) + { + free(args.destpath); + } + free(fullpath); + return OK; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_wget + ****************************************************************************/ + +#if defined(CONFIG_NET_TCP) && CONFIG_NFILE_DESCRIPTORS > 0 +#ifndef CONFIG_NSH_DISABLE_WGET +int cmd_wget(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *localfile = NULL; + char *allocfile = NULL; + char *buffer = NULL; + char *fullpath = NULL; + char *url; + const char *fmt; + bool badarg = false; + int option; + int fd = -1; + int ret; + + /* Get the wget options */ + + while ((option = getopt(argc, argv, ":o:")) != ERROR) + { + switch (option) + { + case 'o': + localfile = optarg; + break; + + case ':': + nsh_output(vtbl, g_fmtargrequired, argv[0]); + badarg = true; + break; + + case '?': + default: + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + break; + } + } + + /* If a bad argument was encountered, then return without processing the command */ + + if (badarg) + { + return ERROR; + } + + /* There should be exactly on parameter left on the command-line */ + + if (optind == argc-1) + { + url = argv[optind]; + } + else if (optind >= argc) + { + fmt = g_fmttoomanyargs; + goto errout; + } + else + { + fmt = g_fmtargrequired; + goto errout; + } + + /* Get the local file name */ + + if (!localfile) + { + allocfile = strdup(url); + localfile = basename(allocfile); + } + + /* Get the full path to the local file */ + + fullpath = nsh_getfullpath(vtbl, localfile); + + /* Open the local file for writing */ + + fd = open(fullpath, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO); + ret = ERROR; + goto exit; + } + + /* Allocate an I/O buffer */ + + buffer = malloc(512); + if (!buffer) + { + fmt = g_fmtcmdoutofmemory; + goto errout; + } + + /* And perform the wget */ + + ret = wget(url, buffer, 512, wget_callback, (FAR void *)fd); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "wget", NSH_ERRNO); + goto exit; + } + + /* Free allocated resources */ + +exit: + if (fd >= 0) + { + close(fd); + } + if (allocfile) + { + free(allocfile); + } + if (fullpath) + { + free(fullpath); + } + if (buffer) + { + free(buffer); + } + return ret; + +errout: + nsh_output(vtbl, fmt, argv[0]); + ret = ERROR; + goto exit; +} +#endif +#endif + +#endif /* CONFIG_NET */ diff --git a/apps/nshlib/nsh_netinit.c b/apps/nshlib/nsh_netinit.c new file mode 100644 index 000000000..245d50380 --- /dev/null +++ b/apps/nshlib/nsh_netinit.c @@ -0,0 +1,170 @@ +/**************************************************************************** + * apps/nshlib/nsh_netinit.c + * + * Copyright (C) 2010-2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * This is influenced by similar logic from uIP: + * + * Author: Adam Dunkels <adam@sics.se> + * Copyright (c) 2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <debug.h> + +#include <net/if.h> + +#include <apps/netutils/uiplib.h> +#if defined(CONFIG_NSH_DHCPC) +# include <apps/netutils/resolv.h> +# include <apps/netutils/dhcpc.h> +#endif + +#include "nsh.h" + +#ifdef CONFIG_NET + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_netinit + * + * Description: + * Initialize the network per the selected NuttX configuration + * + ****************************************************************************/ + +int nsh_netinit(void) +{ + struct in_addr addr; +#if defined(CONFIG_NSH_DHCPC) + FAR void *handle; +#endif +#if defined(CONFIG_NSH_DHCPC) || defined(CONFIG_NSH_NOMAC) + uint8_t mac[IFHWADDRLEN]; +#endif + +/* Many embedded network interfaces must have a software assigned MAC */ + +#ifdef CONFIG_NSH_NOMAC + mac[0] = 0x00; + mac[1] = 0xe0; + mac[2] = 0xb0; + mac[3] = 0x0b; + mac[4] = 0xba; + mac[5] = 0xbe; + uip_setmacaddr("eth0", mac); +#endif + + /* Set up our host address */ + +#if !defined(CONFIG_NSH_DHCPC) + addr.s_addr = HTONL(CONFIG_NSH_IPADDR); +#else + addr.s_addr = 0; +#endif + uip_sethostaddr("eth0", &addr); + + /* Set up the default router address */ + + addr.s_addr = HTONL(CONFIG_NSH_DRIPADDR); + uip_setdraddr("eth0", &addr); + + /* Setup the subnet mask */ + + addr.s_addr = HTONL(CONFIG_NSH_NETMASK); + uip_setnetmask("eth0", &addr); + +#if defined(CONFIG_NSH_DHCPC) + /* Set up the resolver */ + + resolv_init(); +#endif + +#if defined(CONFIG_NSH_DHCPC) + /* Get the MAC address of the NIC */ + + uip_getmacaddr("eth0", mac); + + /* Set up the DHCPC modules */ + + handle = dhcpc_open(&mac, IFHWADDRLEN); + + /* Get an IP address. Note that there is no logic for renewing the IP address in this + * example. The address should be renewed in ds.lease_time/2 seconds. + */ + + if (handle) + { + struct dhcpc_state ds; + (void)dhcpc_request(handle, &ds); + uip_sethostaddr("eth1", &ds.ipaddr); + if (ds.netmask.s_addr != 0) + { + uip_setnetmask("eth0", &ds.netmask); + } + if (ds.default_router.s_addr != 0) + { + uip_setdraddr("eth0", &ds.default_router); + } + if (ds.dnsaddr.s_addr != 0) + { + resolv_conf(&ds.dnsaddr); + } + dhcpc_close(handle); + } +#endif + return OK; +} + +#endif /* CONFIG_NET */ diff --git a/apps/nshlib/nsh_parse.c b/apps/nshlib/nsh_parse.c new file mode 100644 index 000000000..a193ebc9e --- /dev/null +++ b/apps/nshlib/nsh_parse.c @@ -0,0 +1,1336 @@ +/**************************************************************************** + * apps/nshlib/nsh_parse.c + * + * Copyright (C) 2007-2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/stat.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sched.h> +#include <fcntl.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/version.h> + +#ifndef CONFIG_NSH_DISABLEBG +# include <pthread.h> +#endif + +#ifdef CONFIG_NSH_BUILTIN_APPS +# include <apps/apps.h> +#endif +#include <apps/nsh.h> + +#include "nsh.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/* Argument list size + * + * argv[0]: The command name. + * argv[1]: The beginning of argument (up to NSH_MAX_ARGUMENTS) + * argv[argc-3]: Possibly '>' or '>>' + * argv[argc-2]: Possibly <file> + * argv[argc-1]: Possibly '&' (if pthreads are enabled) + * argv[argc]: NULL terminating pointer + * + * Maximum size is NSH_MAX_ARGUMENTS+5 + */ + +#ifndef CONFIG_NSH_DISABLEBG +# define MAX_ARGV_ENTRIES (NSH_MAX_ARGUMENTS+5) +#else +# define MAX_ARGV_ENTRIES (NSH_MAX_ARGUMENTS+4) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +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 */ +}; + +#ifndef CONFIG_NSH_DISABLEBG +struct cmdarg_s +{ + FAR struct nsh_vtbl_s *vtbl; /* For front-end interaction */ + int fd; /* FD for output redirection */ + int argc; /* Number of arguments in argv */ + FAR char *argv[MAX_ARGV_ENTRIES]; /* Argument list */ +}; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_HELP + static int cmd_help(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif + +#ifndef CONFIG_NSH_DISABLE_EXIT + static int cmd_exit(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif +static int cmd_unrecognized(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char g_delim[] = " \t\n"; +static const char g_redirect1[] = ">"; +static const char g_redirect2[] = ">>"; +static const char g_exitstatus[] = "$?"; +static const char g_success[] = "0"; +static const char g_failure[] = "1"; + +static const struct cmdmap_s g_cmdmap[] = +{ +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_TEST) + { "[", cmd_lbracket, 4, NSH_MAX_ARGUMENTS, "<expression> ]" }, +#endif + +#ifndef CONFIG_NSH_DISABLE_HELP + { "?", cmd_help, 1, 1, NULL }, +#endif + +#if CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_CAT + { "cat", cmd_cat, 2, NSH_MAX_ARGUMENTS, "<path> [<path> [<path> ...]]" }, +# endif +#ifndef CONFIG_DISABLE_ENVIRON +# ifndef CONFIG_NSH_DISABLE_CD + { "cd", cmd_cd, 1, 2, "[<dir-path>|-|~|..]" }, +# endif +#endif +# ifndef CONFIG_NSH_DISABLE_CP + { "cp", cmd_cp, 3, 3, "<source-path> <dest-path>" }, +# endif +#endif + +#if defined (CONFIG_RTC) && !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_NSH_DISABLE_DATE) + { "date", cmd_date, 1, 3, "[-s \"MMM DD HH:MM:SS YYYY\"]" }, +#endif + +#if CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_DD + { "dd", cmd_dd, 3, 6, "if=<infile> of=<outfile> [bs=<sectsize>] [count=<sectors>] [skip=<sectors>]" }, +# endif +#endif + +#ifndef CONFIG_NSH_DISABLE_ECHO +# ifndef CONFIG_DISABLE_ENVIRON + { "echo", cmd_echo, 0, NSH_MAX_ARGUMENTS, "[<string|$name> [<string|$name>...]]" }, +# else + { "echo", cmd_echo, 0, NSH_MAX_ARGUMENTS, "[<string> [<string>...]]" }, +# endif +#endif + +#ifndef CONFIG_NSH_DISABLE_EXEC + { "exec", cmd_exec, 2, 3, "<hex-address>" }, +#endif +#ifndef CONFIG_NSH_DISABLE_EXIT + { "exit", cmd_exit, 1, 1, NULL }, +#endif + +#ifndef CONFIG_NSH_DISABLE_FREE + { "free", cmd_free, 1, 1, NULL }, +#endif + +#if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_GET + { "get", cmd_get, 4, 7, "[-b|-n] [-f <local-path>] -h <ip-address> <remote-path>" }, +# endif +#endif + +#ifndef CONFIG_NSH_DISABLE_HELP + { "help", cmd_help, 1, 1, NULL }, +#endif + +#ifdef CONFIG_NET +# ifndef CONFIG_NSH_DISABLE_IFCONFIG + { "ifconfig", cmd_ifconfig, 1, 3, "[nic_name [ip]]" }, +# endif +#endif + +#ifndef CONFIG_DISABLE_SIGNALS +# ifndef CONFIG_NSH_DISABLE_KILL + { "kill", cmd_kill, 3, 3, "-<signal> <pid>" }, +# endif +#endif + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_MOUNTPOINT) +# ifndef CONFIG_NSH_DISABLE_LOSETUP + { "losetup", cmd_losetup, 3, 6, "[-d <dev-path>] | [[-o <offset>] [-r] <dev-path> <file-path>]" }, +# endif +#endif + +#if CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_LS + { "ls", cmd_ls, 1, 5, "[-lRs] <dir-path>" }, +# endif +#endif + +#ifndef CONFIG_NSH_DISABLE_MB + { "mb", cmd_mb, 2, 3, "<hex-address>[=<hex-value>][ <hex-byte-count>]" }, +#endif + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE) +# ifndef CONFIG_NSH_DISABLE_MKDIR + { "mkdir", cmd_mkdir, 2, 2, "<path>" }, +# endif +#endif + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_FAT) +# ifndef CONFIG_NSH_DISABLE_MKFATFS + { "mkfatfs", cmd_mkfatfs, 2, 2, "<path>" }, +# endif +#endif + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_MKFIFO + { "mkfifo", cmd_mkfifo, 2, 2, "<path>" }, +# endif +#endif + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE) +# ifndef CONFIG_NSH_DISABLE_MKRD + { "mkrd", cmd_mkrd, 2, 6, "[-m <minor>] [-s <sector-size>] <nsectors>" }, +# endif +#endif + +#ifndef CONFIG_NSH_DISABLE_MH + { "mh", cmd_mh, 2, 3, "<hex-address>[=<hex-value>][ <hex-byte-count>]" }, +#endif + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_READABLE) +# ifndef CONFIG_NSH_DISABLE_MOUNT + { "mount", cmd_mount, 4, 5, "-t <fstype> <block-device> <dir-path>" }, +# endif +#endif + +#ifndef CONFIG_NSH_DISABLE_MW + { "mw", cmd_mw, 2, 3, "<hex-address>[=<hex-value>][ <hex-byte-count>]" }, +#endif + +#if defined(CONFIG_NET) && defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) && \ + !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_DISABLE_SIGNALS) +# ifndef CONFIG_NSH_DISABLE_PING + { "ping", cmd_ping, 2, 6, "[-c <count>] [-i <interval>] <ip-address>" }, +# endif +#endif + +#ifndef CONFIG_NSH_DISABLE_PS + { "ps", cmd_ps, 1, 1, NULL }, +#endif + +#if defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_PUT + { "put", cmd_put, 4, 7, "[-b|-n] [-f <remote-path>] -h <ip-address> <local-path>" }, +# endif +#endif + +#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON) +# ifndef CONFIG_NSH_DISABLE_PWD + { "pwd", cmd_pwd, 1, 1, NULL }, +# endif +#endif + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE) +# ifndef CONFIG_NSH_DISABLE_RM + { "rm", cmd_rm, 2, 2, "<file-path>" }, +# endif +# ifndef CONFIG_NSH_DISABLE_RMDIR + { "rmdir", cmd_rmdir, 2, 2, "<dir-path>" }, +# endif +#endif + +#ifndef CONFIG_DISABLE_ENVIRON +# ifndef CONFIG_NSH_DISABLE_SET + { "set", cmd_set, 3, 3, "<name> <value>" }, +# endif +#endif + +#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_NSH_DISABLESCRIPT) +# ifndef CONFIG_NSH_DISABLE_SH + { "sh", cmd_sh, 2, 2, "<script-path>" }, +# endif +#endif + +#ifndef CONFIG_DISABLE_SIGNALS +# ifndef CONFIG_NSH_DISABLE_SLEEP + { "sleep", cmd_sleep, 2, 2, "<sec>" }, +# endif +#endif + +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_TEST) + { "test", cmd_test, 3, NSH_MAX_ARGUMENTS, "<expression>" }, +#endif + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_READABLE) +# ifndef CONFIG_NSH_DISABLE_UMOUNT + { "umount", cmd_umount, 2, 2, "<dir-path>" }, +# endif +#endif + +#ifndef CONFIG_DISABLE_ENVIRON +# ifndef CONFIG_NSH_DISABLE_UNSET + { "unset", cmd_unset, 2, 2, "<name>" }, +# endif +#endif + +#ifndef CONFIG_DISABLE_SIGNALS +# ifndef CONFIG_NSH_DISABLE_USLEEP + { "usleep", cmd_usleep, 2, 2, "<usec>" }, +# endif +#endif + +#if defined(CONFIG_NET_TCP) && CONFIG_NFILE_DESCRIPTORS > 0 +# ifndef CONFIG_NSH_DISABLE_GET + { "wget", cmd_wget, 2, 4, "[-o <local-path>] <url>" }, +# endif +#endif + +#ifndef CONFIG_NSH_DISABLE_XD + { "xd", cmd_xd, 3, 3, "<hex-address> <byte-count>" }, +#endif + { NULL, NULL, 1, 1, NULL } +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* If NuttX versioning information is available, Include that information + * in the NSH greeting. + */ + +#if CONFIG_VERSION_MAJOR != 0 || CONFIG_VERSION_MINOR != 0 +const char g_nshgreeting[] = "\nNuttShell (NSH) NuttX-" CONFIG_VERSION_STRING "\n"; +#else +const char g_nshgreeting[] = "\nNuttShell (NSH)\n"; +#endif + +/* The NSH prompt */ + +const char g_nshprompt[] = "nsh> "; + +/* Common, message formats */ + +const char g_nshsyntax[] = "nsh: %s: syntax error\n"; +const char g_fmtargrequired[] = "nsh: %s: missing required argument(s)\n"; +const char g_fmtarginvalid[] = "nsh: %s: argument invalid\n"; +const char g_fmtargrange[] = "nsh: %s: value out of range\n"; +const char g_fmtcmdnotfound[] = "nsh: %s: command not found\n"; +const char g_fmtnosuch[] = "nsh: %s: no such %s: %s\n"; +const char g_fmttoomanyargs[] = "nsh: %s: too many arguments\n"; +const char g_fmtdeepnesting[] = "nsh: %s: nesting too deep\n"; +const char g_fmtcontext[] = "nsh: %s: not valid in this context\n"; +#ifdef CONFIG_NSH_STRERROR +const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %s\n"; +#else +const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %d\n"; +#endif +const char g_fmtcmdoutofmemory[] = "nsh: %s: out of memory\n"; +const char g_fmtinternalerror[] = "nsh: %s: Internal error\n"; +#ifndef CONFIG_DISABLE_SIGNALS +const char g_fmtsignalrecvd[] = "nsh: %s: Interrupted by signal\n"; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_help + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_HELP +static int cmd_help(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + const struct cmdmap_s *ptr; +#ifdef CONFIG_NSH_BUILTIN_APPS + FAR const char * name; + int i; +#endif + + nsh_output(vtbl, "NSH command forms:\n"); +#ifndef CONFIG_NSH_DISABLEBG + nsh_output(vtbl, " [nice [-d <niceness>>]] <cmd> [> <file>|>> <file>] [&]\n"); +#else + nsh_output(vtbl, " <cmd> [> <file>|>> <file>]\n"); +#endif +#ifndef CONFIG_NSH_DISABLESCRIPT + nsh_output(vtbl, "OR\n"); + nsh_output(vtbl, " if <cmd>\n"); + nsh_output(vtbl, " then\n"); + nsh_output(vtbl, " [sequence of <cmd>]\n"); + nsh_output(vtbl, " else\n"); + nsh_output(vtbl, " [sequence of <cmd>]\n"); + nsh_output(vtbl, " fi\n"); +#endif + nsh_output(vtbl, "Where <cmd> is one of:\n"); + for (ptr = g_cmdmap; ptr->cmd; ptr++) + { + if (ptr->usage) + { + nsh_output(vtbl, " %s %s\n", ptr->cmd, ptr->usage); + } + else + { + nsh_output(vtbl, " %s\n", ptr->cmd); + } + } + + /* List the set of available built-in commands */ + +#ifdef CONFIG_NSH_BUILTIN_APPS + nsh_output(vtbl, "\nBuiltin Apps:\n"); + for (i = 0; (name = namedapp_getname(i)) != NULL; i++) + { + nsh_output(vtbl, " %s\n", name); + } +#endif + + return OK; +} +#endif + +/**************************************************************************** + * Name: cmd_unrecognized + ****************************************************************************/ + +static int cmd_unrecognized(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + nsh_output(vtbl, g_fmtcmdnotfound, argv[0]); + return ERROR; +} + +/**************************************************************************** + * Name: cmd_exit + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_EXIT +static int cmd_exit(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + nsh_exit(vtbl); + return OK; +} +#endif + +/**************************************************************************** + * Name: nsh_execute + ****************************************************************************/ + +static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, int argc, char *argv[]) +{ + const struct cmdmap_s *cmdmap; + const char *cmd; + cmd_t handler = cmd_unrecognized; + int ret; + + /* The form of argv is: + * + * argv[0]: The command name. This is argv[0] when the arguments + * are, finally, received by the command vtblr + * argv[1]: The beginning of argument (up to NSH_MAX_ARGUMENTS) + * argv[argc]: NULL terminating pointer + */ + + cmd = argv[0]; + + /* Try to find a command in the application library. + */ + +#ifdef CONFIG_NSH_BUILTIN_APPS + if (nsh_execapp(vtbl, cmd, argv) == OK) + { + /* The pre-built application was successfully started -- return OK. */ + + return OK; + } +#endif + + + /* See if the command is one that we understand */ + + for (cmdmap = g_cmdmap; cmdmap->cmd; cmdmap++) + { + if (strcmp(cmdmap->cmd, cmd) == 0) + { + /* Check if a valid number of arguments was provided. We + * do this simple, imperfect checking here so that it does + * not have to be performed in each command. + */ + + if (argc < cmdmap->minargs) + { + /* Fewer than the minimum number were provided */ + + nsh_output(vtbl, g_fmtargrequired, cmd); + return ERROR; + } + else if (argc > cmdmap->maxargs) + { + /* More than the maximum number were provided */ + + nsh_output(vtbl, g_fmttoomanyargs, cmd); + return ERROR; + } + else + { + /* A valid number of arguments were provided (this does + * not mean they are right). + */ + + handler = cmdmap->handler; + break; + } + } + } + + ret = handler(vtbl, argc, argv); + return ret; +} + +/**************************************************************************** + * Name: nsh_releaseargs + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static void nsh_releaseargs(struct cmdarg_s *arg) +{ + FAR struct nsh_vtbl_s *vtbl = arg->vtbl; + int i; + + /* If the output was redirected, then file descriptor should + * be closed. The created task has its one, independent copy of + * the file descriptor + */ + + if (vtbl->np.np_redirect) + { + (void)close(arg->fd); + } + + /* Released the cloned vtbl instance */ + + nsh_release(vtbl); + + /* Release the cloned args */ + + for (i = 0; i < arg->argc; i++) + { + free(arg->argv[i]); + } + free(arg); +} +#endif + +/**************************************************************************** + * Name: nsh_child + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static pthread_addr_t nsh_child(pthread_addr_t arg) +{ + struct cmdarg_s *carg = (struct cmdarg_s *)arg; + int ret; + + dbg("BG %s\n", carg->argv[0]); + + /* Execute the specified command on the child thread */ + + ret = nsh_execute(carg->vtbl, carg->argc, carg->argv); + + /* Released the cloned arguments */ + + dbg("BG %s complete\n", carg->argv[0]); + nsh_releaseargs(carg); + return (void*)ret; +} +#endif + +/**************************************************************************** + * Name: nsh_cloneargs + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static inline struct cmdarg_s *nsh_cloneargs(FAR struct nsh_vtbl_s *vtbl, + int fd, int argc, char *argv[]) +{ + struct cmdarg_s *ret = (struct cmdarg_s *)zalloc(sizeof(struct cmdarg_s)); + int i; + + if (ret) + { + ret->vtbl = vtbl; + ret->fd = fd; + ret->argc = argc; + + for (i = 0; i < argc; i++) + { + ret->argv[i] = strdup(argv[i]); + } + } + return ret; +} +#endif + +/**************************************************************************** + * Name: nsh_argument + ****************************************************************************/ + +char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr) +{ + char *pbegin = *saveptr; + char *pend = NULL; + const char *term; +#ifndef CONFIG_DISABLE_ENVIRON + bool quoted = false; +#endif + + /* Find the beginning of the next token */ + + for (; + *pbegin && strchr(g_delim, *pbegin) != NULL; + pbegin++); + + /* If we are at the end of the string with nothing + * but delimiters found, then return NULL. + */ + + if (!*pbegin) + { + return NULL; + } + + /* Does the token begin with '>' -- redirection of output? */ + + if (*pbegin == '>') + { + /* Yes.. does it begin with ">>"? */ + + if (*(pbegin + 1) == '>') + { + *saveptr = pbegin + 2; + pbegin = (char*)g_redirect2; + } + else + { + *saveptr = pbegin + 1; + pbegin = (char*)g_redirect1; + } + } + + /* Does the token begin with '#' -- comment */ + + else if (*pbegin == '#') + { + /* Return NULL meaning that we are at the end of the line */ + + *saveptr = pbegin; + pbegin = NULL; + } + else + { + /* Otherwise, we are going to have to parse to find the end of + * the token. Does the token begin with '"'? + */ + + if (*pbegin == '"') + { + /* Yes.. then only another '"' can terminate the string */ + + pbegin++; + term = "\""; +#ifndef CONFIG_DISABLE_ENVIRON + quoted = true; +#endif + } + else + { + /* No, then any of the usual terminators will terminate the argument */ + + term = g_delim; + } + + /* Find the end of the string */ + + for (pend = pbegin + 1; + *pend && strchr(term, *pend) == NULL; + pend++); + + /* pend either points to the end of the string or to + * the first delimiter after the string. + */ + + if (*pend) + { + /* Turn the delimiter into a null terminator */ + + *pend++ = '\0'; + } + + /* Save the pointer where we left off */ + + *saveptr = pend; + +#ifndef CONFIG_DISABLE_ENVIRON + /* Check for references to environment variables */ + + if (pbegin[0] == '$' && !quoted) + { + /* Check for built-in variables */ + + if (strcmp(pbegin, g_exitstatus) == 0) + { + if (vtbl->np.np_fail) + { + return (char*)g_failure; + } + else + { + return (char*)g_success; + } + } + + /* Not a built-in? Return the value of the environment variable with this name */ + + else + { + char *value = getenv(pbegin+1); + if (value) + { + return value; + } + else + { + return (char*)""; + } + } + } +#endif + } + + /* Return the beginning of the token. */ + + return pbegin; +} + +/**************************************************************************** + * Name: nsh_cmdenabled + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLESCRIPT +static inline bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl) +{ + struct nsh_parser_s *np = &vtbl->np; + bool ret = !np->np_st[np->np_ndx].ns_disabled; + if (ret) + { + switch (np->np_st[np->np_ndx].ns_state) + { + case NSH_PARSER_NORMAL : + case NSH_PARSER_IF: + default: + break; + + case NSH_PARSER_THEN: + ret = !np->np_st[np->np_ndx].ns_ifcond; + break; + + case NSH_PARSER_ELSE: + ret = np->np_st[np->np_ndx].ns_ifcond; + break; + } + } + return ret; +} +#endif + +/**************************************************************************** + * Name: nsh_ifthenelse + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLESCRIPT +static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr) +{ + struct nsh_parser_s *np = &vtbl->np; + FAR char *cmd = *ppcmd; + bool disabled; + + if (cmd) + { + /* Check if the command is preceeded by "if" */ + + if (strcmp(cmd, "if") == 0) + { + /* Get the cmd following the if */ + + *ppcmd = nsh_argument(vtbl, saveptr); + if (!*ppcmd) + { + nsh_output(vtbl, g_fmtarginvalid, "if"); + goto errout; + } + + /* Verify that "if" is valid in this context */ + + if (np->np_st[np->np_ndx].ns_state != NSH_PARSER_NORMAL && + np->np_st[np->np_ndx].ns_state != NSH_PARSER_THEN && + np->np_st[np->np_ndx].ns_state != NSH_PARSER_ELSE) + { + nsh_output(vtbl, g_fmtcontext, "if"); + goto errout; + } + + /* Check if we have exceeded the maximum depth of nesting */ + + if (np->np_ndx >= CONFIG_NSH_NESTDEPTH-1) + { + nsh_output(vtbl, g_fmtdeepnesting, "if"); + goto errout; + } + + /* "Push" the old state and set the new state */ + + disabled = !nsh_cmdenabled(vtbl); + np->np_ndx++; + np->np_st[np->np_ndx].ns_state = NSH_PARSER_IF; + np->np_st[np->np_ndx].ns_disabled = disabled; + np->np_st[np->np_ndx].ns_ifcond = false; + } + else if (strcmp(cmd, "then") == 0) + { + /* Get the cmd following the then -- there shouldn't be one */ + + *ppcmd = nsh_argument(vtbl, saveptr); + if (*ppcmd) + { + nsh_output(vtbl, g_fmtarginvalid, "then"); + goto errout; + } + + /* Verify that "then" is valid in this context */ + + if (np->np_st[np->np_ndx].ns_state != NSH_PARSER_IF) + { + nsh_output(vtbl, g_fmtcontext, "then"); + goto errout; + } + np->np_st[np->np_ndx].ns_state = NSH_PARSER_THEN; + } + else if (strcmp(cmd, "else") == 0) + { + /* Get the cmd following the else -- there shouldn't be one */ + + *ppcmd = nsh_argument(vtbl, saveptr); + if (*ppcmd) + { + nsh_output(vtbl, g_fmtarginvalid, "else"); + goto errout; + } + + /* Verify that "then" is valid in this context */ + + if (np->np_st[np->np_ndx].ns_state != NSH_PARSER_THEN) + { + nsh_output(vtbl, g_fmtcontext, "else"); + goto errout; + } + np->np_st[np->np_ndx].ns_state = NSH_PARSER_ELSE; + } + else if (strcmp(cmd, "fi") == 0) + { + /* Get the cmd following the fi -- there should be one */ + + *ppcmd = nsh_argument(vtbl, saveptr); + if (*ppcmd) + { + nsh_output(vtbl, g_fmtarginvalid, "fi"); + goto errout; + } + + /* Verify that "fi" is valid in this context */ + + if (np->np_st[np->np_ndx].ns_state != NSH_PARSER_THEN && + np->np_st[np->np_ndx].ns_state != NSH_PARSER_ELSE) + { + nsh_output(vtbl, g_fmtcontext, "fi"); + goto errout; + } + + if (np->np_ndx < 1) /* Shouldn't happen */ + { + nsh_output(vtbl, g_fmtinternalerror, "if"); + goto errout; + } + + /* "Pop" the previous state */ + + np->np_ndx--; + } + else if (np->np_st[np->np_ndx].ns_state == NSH_PARSER_IF) + { + nsh_output(vtbl, g_fmtcontext, cmd); + goto errout; + } + } + return OK; + +errout: + np->np_ndx = 0; + np->np_st[0].ns_state = NSH_PARSER_NORMAL; + np->np_st[0].ns_disabled = false; + np->np_st[0].ns_ifcond = false; + return ERROR; +} +#endif + +/**************************************************************************** + * Name: nsh_saveresult + ****************************************************************************/ + +static inline int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, bool result) +{ + struct nsh_parser_s *np = &vtbl->np; + +#ifndef CONFIG_NSH_DISABLESCRIPT + if (np->np_st[np->np_ndx].ns_state == NSH_PARSER_IF) + { + np->np_fail = false; + np->np_st[np->np_ndx].ns_ifcond = result; + return OK; + } + else +#endif + { + np->np_fail = result; + return result ? ERROR : OK; + } +} + +/**************************************************************************** + * Name: nsh_nice + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static inline int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr) +{ + FAR char *cmd = *ppcmd; + + vtbl->np.np_nice = 0; + if (cmd) + { + /* Check if the command is preceded by "nice" */ + + if (strcmp(cmd, "nice") == 0) + { + /* Nicenesses range from -20 (most favorable scheduling) to 19 + * (least favorable). Default is 10. + */ + + vtbl->np.np_nice = 10; + + /* Get the cmd (or -d option of nice command) */ + + cmd = nsh_argument(vtbl, saveptr); + if (cmd && strcmp(cmd, "-d") == 0) + { + FAR char *val = nsh_argument(vtbl, saveptr); + if (val) + { + char *endptr; + vtbl->np.np_nice = (int)strtol(val, &endptr, 0); + if (vtbl->np.np_nice > 19 || vtbl->np.np_nice < -20 || + endptr == val || *endptr != '\0') + { + nsh_output(vtbl, g_fmtarginvalid, "nice"); + return ERROR; + } + cmd = nsh_argument(vtbl, saveptr); + } + } + + /* Return the real command name */ + + *ppcmd = cmd; + } + } + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_initialize + ****************************************************************************/ + +void nsh_initialize(void) +{ + /* Mount the /etc filesystem */ + + (void)nsh_romfsetc(); + + /* Perform architecture-specific initialization (if available) */ + + (void)nsh_archinitialize(); + + /* Bring up the network */ + + (void)nsh_netinit(); +} + +/**************************************************************************** + * Name: nsh_parse + ****************************************************************************/ + +int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) +{ + FAR char *argv[MAX_ARGV_ENTRIES]; + FAR char *saveptr; + FAR char *cmd; + FAR char *redirfile = NULL; + int fd = -1; + int oflags = 0; + int argc; + int ret; + + /* Initialize parser state */ + + memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *)); +#ifndef CONFIG_NSH_DISABLEBG + vtbl->np.np_bg = false; +#endif + vtbl->np.np_redirect = false; + + /* Parse out the command at the beginning of the line */ + + saveptr = cmdline; + cmd = nsh_argument(vtbl, &saveptr); + + /* Handler if-then-else-fi */ + +#ifndef CONFIG_NSH_DISABLESCRIPT + if (nsh_ifthenelse(vtbl, &cmd, &saveptr) != 0) + { + goto errout; + } +#endif + + /* Handle nice */ + +#ifndef CONFIG_NSH_DISABLEBG + if (nsh_nice(vtbl, &cmd, &saveptr) != 0) + { + goto errout; + } +#endif + + /* Check if any command was provided -OR- if command processing is + * currently disabled. + */ + +#ifndef CONFIG_NSH_DISABLESCRIPT + if (!cmd || !nsh_cmdenabled(vtbl)) +#else + if (!cmd) +#endif + { + /* An empty line is not an error and an unprocessed command cannot + * generate an error, but neither should they change the last + * command status. + */ + + return OK; + } + + /* Parse all of the arguments following the command name. The form + * of argv is: + * + * argv[0]: The command name. + * argv[1]: The beginning of argument (up to NSH_MAX_ARGUMENTS) + * argv[argc-3]: Possibly '>' or '>>' + * argv[argc-2]: Possibly <file> + * argv[argc-1]: Possibly '&' + * argv[argc]: NULL terminating pointer + * + * Maximum size is NSH_MAX_ARGUMENTS+5 + */ + + argv[0] = cmd; + for (argc = 1; argc < MAX_ARGV_ENTRIES-1; argc++) + { + argv[argc] = nsh_argument(vtbl, &saveptr); + if (!argv[argc]) + { + break; + } + } + argv[argc] = NULL; + + /* Check if the command should run in background */ + +#ifndef CONFIG_NSH_DISABLEBG + if (argc > 1 && strcmp(argv[argc-1], "&") == 0) + { + vtbl->np.np_bg = true; + argv[argc-1] = NULL; + argc--; + } +#endif + + + /* Check if the output was re-directed using > or >> */ + + if (argc > 2) + { + /* Check for redirection to a new file */ + + if (strcmp(argv[argc-2], g_redirect1) == 0) + { + vtbl->np.np_redirect = true; + oflags = O_WRONLY|O_CREAT|O_TRUNC; + redirfile = nsh_getfullpath(vtbl, argv[argc-1]); + argc -= 2; + } + + /* Check for redirection by appending to an existing file */ + + else if (strcmp(argv[argc-2], g_redirect2) == 0) + { + vtbl->np.np_redirect = true; + oflags = O_WRONLY|O_CREAT|O_APPEND; + redirfile = nsh_getfullpath(vtbl, argv[argc-1]); + argc -= 2; + } + } + + /* Redirected output? */ + + if (vtbl->np.np_redirect) + { + /* Open the redirection file. This file will eventually + * be closed by a call to either nsh_release (if the command + * is executed in the background) or by nsh_undirect if the + * command is executed in the foreground. + */ + + fd = open(redirfile, oflags, 0666); + nsh_freefullpath(redirfile); + redirfile = NULL; + + if (fd < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, cmd, "open", NSH_ERRNO); + goto errout; + } + } + + /* Check if the maximum number of arguments was exceeded */ + + if (argc > NSH_MAX_ARGUMENTS) + { + nsh_output(vtbl, g_fmttoomanyargs, cmd); + } + + /* Handle the case where the command is executed in background. + * However is app is to be started as namedapp new process will + * be created anyway, so skip this step. */ + +#ifndef CONFIG_NSH_DISABLEBG + if (vtbl->np.np_bg +#ifdef CONFIG_NSH_BUILTIN_APPS + && namedapp_isavail(argv[0]) < 0 +#endif + ) + { + struct sched_param param; + struct nsh_vtbl_s *bkgvtbl; + struct cmdarg_s *args; + pthread_attr_t attr; + pthread_t thread; + + /* Get a cloned copy of the vtbl with reference count=1. + * after the command has been processed, the nsh_release() call + * at the end of nsh_child() will destroy the clone. + */ + + bkgvtbl = nsh_clone(vtbl); + if (!bkgvtbl) + { + goto errout_with_redirect; + } + + /* Create a container for the command arguments */ + + args = nsh_cloneargs(bkgvtbl, fd, argc, argv); + if (!args) + { + nsh_release(bkgvtbl); + goto errout_with_redirect; + } + + /* Handle redirection of output via a file descriptor */ + + if (vtbl->np.np_redirect) + { + (void)nsh_redirect(bkgvtbl, fd, NULL); + } + + /* Get the execution priority of this task */ + + ret = sched_getparam(0, ¶m); + if (ret != 0) + { + nsh_output(vtbl, g_fmtcmdfailed, cmd, "sched_getparm", NSH_ERRNO); + nsh_releaseargs(args); + nsh_release(bkgvtbl); + goto errout; + } + + /* Determine the priority to execute the command */ + + if (vtbl->np.np_nice != 0) + { + int priority = param.sched_priority - vtbl->np.np_nice; + if (vtbl->np.np_nice < 0) + { + int max_priority = sched_get_priority_max(SCHED_NSH); + if (priority > max_priority) + { + priority = max_priority; + } + } + else + { + int min_priority = sched_get_priority_min(SCHED_NSH); + if (priority < min_priority) + { + priority = min_priority; + } + } + param.sched_priority = priority; + } + + /* Set up the thread attributes */ + + (void)pthread_attr_init(&attr); + (void)pthread_attr_setschedpolicy(&attr, SCHED_NSH); + (void)pthread_attr_setschedparam(&attr, ¶m); + + /* Execute the command as a separate thread at the appropriate priority */ + + ret = pthread_create(&thread, &attr, nsh_child, (pthread_addr_t)args); + if (ret != 0) + { + nsh_output(vtbl, g_fmtcmdfailed, cmd, "pthread_create", NSH_ERRNO_OF(ret)); + nsh_releaseargs(args); + nsh_release(bkgvtbl); + goto errout; + } + nsh_output(vtbl, "%s [%d:%d]\n", cmd, thread, param.sched_priority); + } + else +#endif + { + uint8_t save[SAVE_SIZE]; + + /* Handle redirection of output via a file descriptor */ + + if (vtbl->np.np_redirect) + { + nsh_redirect(vtbl, fd, save); + } + + /* Then execute the command in "foreground" -- i.e., while the user waits + * for the next prompt. + */ + + ret = nsh_execute(vtbl, argc, argv); + + /* Restore the original output. Undirect will close the redirection + * file descriptor. + */ + + if (vtbl->np.np_redirect) + { + nsh_undirect(vtbl, save); + } + + if (ret < 0) + { + goto errout; + } + } + + /* Return success if the command succeeded (or at least, starting of the + * command task succeeded). + */ + + return nsh_saveresult(vtbl, false); + +#ifndef CONFIG_NSH_DISABLEBG +errout_with_redirect: + if (vtbl->np.np_redirect) + { + close(fd); + } +#endif +errout: + return nsh_saveresult(vtbl, true); +} diff --git a/apps/nshlib/nsh_proccmds.c b/apps/nshlib/nsh_proccmds.c new file mode 100644 index 000000000..c0dc1713f --- /dev/null +++ b/apps/nshlib/nsh_proccmds.c @@ -0,0 +1,309 @@ +/**************************************************************************** + * apps/nshlib/nsh_proccmds.c + * + * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sched.h> +#include <errno.h> + +#include "nsh.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The returned value should be zero for sucess or TRUE or non zero for + * failure or FALSE. + */ + +typedef int (*exec_t)(void); + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_PS +static const char *g_statenames[] = +{ + "INVALID ", + "PENDING ", + "READY ", + "RUNNING ", + "INACTIVE", + "WAITSEM ", +#ifndef CONFIG_DISABLE_MQUEUE + "WAITSIG ", +#endif +#ifndef CONFIG_DISABLE_MQUEUE + "MQNEMPTY", + "MQNFULL " +#endif +}; + +static const char *g_ttypenames[4] = +{ + "TASK ", + "PTHREAD", + "KTHREAD", + "--?-- " +}; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ps_task + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_PS +static void ps_task(FAR _TCB *tcb, FAR void *arg) +{ + struct nsh_vtbl_s *vtbl = (struct nsh_vtbl_s*)arg; +#if CONFIG_MAX_TASK_ARGS > 2 + int i; +#endif + + /* Show task status */ + + nsh_output(vtbl, "%5d %3d %4s %7s%c%c %8s ", + tcb->pid, tcb->sched_priority, + tcb->flags & TCB_FLAG_ROUND_ROBIN ? "RR " : "FIFO", + 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]); + + /* Show task name and arguments */ + + nsh_output(vtbl, "%s(", tcb->argv[0]); + + /* Special case 1st argument (no comma) */ + + if (tcb->argv[1]) + { + nsh_output(vtbl, "%p", tcb->argv[1]); + } + + /* Then any additional arguments */ + +#if CONFIG_MAX_TASK_ARGS > 2 + for (i = 2; i <= CONFIG_MAX_TASK_ARGS && tcb->argv[i]; i++) + { + nsh_output(vtbl, ", %p", tcb->argv[i]); + } +#endif + nsh_output(vtbl, ")\n"); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_exec + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_EXEC +int cmd_exec(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *endptr; + uintptr_t addr; + + addr = (uintptr_t)strtol(argv[1], &endptr, 0); + if (!addr || endptr == argv[1] || *endptr != '\0') + { + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + nsh_output(vtbl, "Calling %p\n", (exec_t)addr); + return ((exec_t)addr)(); +} +#endif + +/**************************************************************************** + * Name: cmd_ps + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_PS +int cmd_ps(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + nsh_output(vtbl, "PID PRI SCHD TYPE NP STATE NAME\n"); + sched_foreach(ps_task, vtbl); + return OK; +} +#endif + +/**************************************************************************** + * Name: cmd_kill + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_SIGNALS +#ifndef CONFIG_NSH_DISABLE_KILL +int cmd_kill(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *ptr; + char *endptr; + long signal; + long pid; + + /* Check incoming parameters. The first parameter should be "-<signal>" */ + + ptr = argv[1]; + if (*ptr != '-' || ptr[1] < '0' || ptr[1] > '9') + { + goto invalid_arg; + } + + /* Extract the signal number */ + + signal = strtol(&ptr[1], &endptr, 0); + + /* The second parameter should be <pid> */ + + ptr = argv[2]; + if (*ptr < '0' || *ptr > '9') + { + goto invalid_arg; + } + + /* Extract athe pid */ + + pid = strtol(ptr, &endptr, 0); + + /* Send the signal. Kill return values: + * + * EINVAL An invalid signal was specified. + * EPERM The process does not have permission to send the signal to any + * of the target processes. + * ESRCH The pid or process group does not exist. + * ENOSYS Do not support sending signals to process groups. + */ + + if (kill((pid_t)pid, (int)signal) == 0) + { + return OK; + } + + switch (errno) + { + case EINVAL: + goto invalid_arg; + + case ESRCH: + nsh_output(vtbl, g_fmtnosuch, argv[0], "task", argv[2]); + return ERROR; + + case EPERM: + case ENOSYS: + default: + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "kill", NSH_ERRNO); + return ERROR; + } + +invalid_arg: + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_sleep + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_SIGNALS +#ifndef CONFIG_NSH_DISABLE_SLEEP +int cmd_sleep(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *endptr; + long secs; + + secs = strtol(argv[1], &endptr, 0); + if (!secs || endptr == argv[1] || *endptr != '\0') + { + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + sleep(secs); + return OK; +} +#endif +#endif + +/**************************************************************************** + * Name: cmd_usleep + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_SIGNALS +#ifndef CONFIG_NSH_DISABLE_USLEEP +int cmd_usleep(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + char *endptr; + long usecs; + + usecs = strtol(argv[1], &endptr, 0); + if (!usecs || endptr == argv[1] || *endptr != '\0') + { + nsh_output(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + usleep(usecs); + return OK; +} +#endif +#endif diff --git a/apps/nshlib/nsh_romfsetc.c b/apps/nshlib/nsh_romfsetc.c new file mode 100644 index 000000000..d97e6591a --- /dev/null +++ b/apps/nshlib/nsh_romfsetc.c @@ -0,0 +1,124 @@ +/**************************************************************************** + * apps/nshlib/nsh_romfsetc.c + * + * Copyright (C) 2008-2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/mount.h> +#include <debug.h> +#include <errno.h> + +#include <nuttx/ramdisk.h> + +#include "nsh.h" + +#ifdef CONFIG_NSH_ROMFSETC + +/* Should we use the default ROMFS image? Or a custom, board-specific + * ROMFS image? + */ + +#ifdef CONFIG_NSH_ARCHROMFS +# include <arch/board/nsh_romfsimg.h> +#else +# include "nsh_romfsimg.h" +#endif + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_romfsetc + ****************************************************************************/ + +int nsh_romfsetc(void) +{ + int ret; + + /* Create a ROM disk for the /etc filesystem */ + + ret = romdisk_register(CONFIG_NSH_ROMFSDEVNO, romfs_img, + NSECTORS(romfs_img_len), CONFIG_NSH_ROMFSSECTSIZE); + if (ret < 0) + { + dbg("nsh: romdisk_register failed: %d\n", -ret); + return ERROR; + } + + /* Mount the file system */ + + vdbg("Mounting ROMFS filesystem at target=%s with source=%s\n", + CONFIG_NSH_ROMFSMOUNTPT, MOUNT_DEVNAME); + + ret = mount(MOUNT_DEVNAME, CONFIG_NSH_ROMFSMOUNTPT, "romfs", MS_RDONLY, NULL); + if (ret < 0) + { + dbg("nsh: mount(%s,%s,romfs) failed: %d\n", + MOUNT_DEVNAME, CONFIG_NSH_ROMFSMOUNTPT, errno); + return ERROR; + } + return OK; +} + +#endif /* CONFIG_NSH_ROMFSETC */ + diff --git a/apps/nshlib/nsh_romfsimg.h b/apps/nshlib/nsh_romfsimg.h new file mode 100644 index 000000000..49b0ad166 --- /dev/null +++ b/apps/nshlib/nsh_romfsimg.h @@ -0,0 +1,89 @@ +unsigned char romfs_img[] = { + 0x2d, 0x72, 0x6f, 0x6d, 0x31, 0x66, 0x73, 0x2d, 0x00, 0x00, 0x01, 0x50, + 0x9f, 0x13, 0x82, 0x87, 0x4e, 0x53, 0x48, 0x49, 0x6e, 0x69, 0x74, 0x56, + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xff, 0xff, 0x97, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0xd1, 0xd1, 0xff, 0x80, 0x2e, 0x2e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x2d, 0x96, 0x03, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x64, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xff, 0xff, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6e, 0x8d, 0x9c, 0xab, 0x58, 0x72, 0x63, 0x53, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x52, + 0x41, 0x4d, 0x44, 0x49, 0x53, 0x4b, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x20, 0x61, 0x74, 0x20, 0x2f, + 0x74, 0x6d, 0x70, 0x0a, 0x0a, 0x6d, 0x6b, 0x72, 0x64, 0x20, 0x2d, 0x6d, + 0x20, 0x32, 0x20, 0x2d, 0x73, 0x20, 0x35, 0x31, 0x32, 0x20, 0x31, 0x30, + 0x32, 0x34, 0x0a, 0x6d, 0x6b, 0x66, 0x61, 0x74, 0x66, 0x73, 0x20, 0x2f, + 0x64, 0x65, 0x76, 0x2f, 0x72, 0x61, 0x6d, 0x32, 0x0a, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x20, 0x2d, 0x74, 0x20, 0x76, 0x66, 0x61, 0x74, 0x20, 0x2f, + 0x64, 0x65, 0x76, 0x2f, 0x72, 0x61, 0x6d, 0x32, 0x20, 0x2f, 0x74, 0x6d, + 0x70, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0xd1, 0xd1, 0xff, 0xe0, 0x2e, 0x2e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; +unsigned int romfs_img_len = 1024; diff --git a/apps/nshlib/nsh_serial.c b/apps/nshlib/nsh_serial.c new file mode 100644 index 000000000..ccb25477d --- /dev/null +++ b/apps/nshlib/nsh_serial.c @@ -0,0 +1,508 @@ +/**************************************************************************** + * apps/nshlib/nsh_serial.c + * + * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name Gregory Nutt nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdarg.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include "nsh.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Are we using the NuttX console for I/O? Or some other character device? */ + +#ifdef CONFIG_NSH_CONDEV +# define INFD(p) ((p)->ss_confd) +# define INSTREAM(p) ((p)->ss_constream) +# define OUTFD(p) ((p)->ss_confd) +# define OUTSTREAM(p) ((p)->ss_constream) +#else +# define INFD(p) 0 +# define INSTREAM(p) stdin +# define OUTFD(p) 1 +# define OUTSTREAM(p) stdout +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct serial_s +{ + /* NSH front-end call table */ + + struct nsh_vtbl_s ss_vtbl; + + /* NSH input/output streams */ + +#ifdef CONFIG_NSH_CONDEV + int ss_confd; /* Console I/O file descriptor */ +#endif + int ss_outfd; /* Output file descriptor (possibly redirected) */ +#ifdef CONFIG_NSH_CONDEV + FILE *ss_constream; /* Console I/O stream (possibly redirected) */ +#endif + FILE *ss_outstream; /* Output stream */ + + /* Line input buffer */ + + char ss_line[CONFIG_NSH_LINELEN]; +}; + +struct serialsave_s +{ + int ss_outfd; /* Re-directed output file descriptor */ + FILE *ss_outstream; /* Re-directed output stream */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static FAR struct nsh_vtbl_s *nsh_consoleclone(FAR struct nsh_vtbl_s *vtbl); +static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl); +#endif +static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes); +static int nsh_consoleoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); +static FAR char *nsh_consolelinebuffer(FAR struct nsh_vtbl_s *vtbl); +static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); +static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); +static void nsh_consoleexit(FAR struct nsh_vtbl_s *vtbl); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_allocstruct + ****************************************************************************/ + +static inline FAR struct serial_s *nsh_allocstruct(void) +{ + struct serial_s *pstate = (struct serial_s *)zalloc(sizeof(struct serial_s)); + if (pstate) + { + /* Initialize the call table */ + +#ifndef CONFIG_NSH_DISABLEBG + pstate->ss_vtbl.clone = nsh_consoleclone; + pstate->ss_vtbl.release = nsh_consolerelease; +#endif + pstate->ss_vtbl.write = nsh_consolewrite; + pstate->ss_vtbl.output = nsh_consoleoutput; + pstate->ss_vtbl.linebuffer = nsh_consolelinebuffer; + pstate->ss_vtbl.redirect = nsh_consoleredirect; + pstate->ss_vtbl.undirect = nsh_consoleundirect; + pstate->ss_vtbl.exit = nsh_consoleexit; + + /* (Re-) open the console input device */ + +#ifdef CONFIG_NSH_CONDEV + pstate->ss_confd = open(CONFIG_NSH_CONDEV, O_RDWR); + if (pstate->ss_confd < 0) + { + free(pstate); + return NULL; + } + + /* Create a standard C stream on the console device */ + + pstate->ss_constream = fdopen(pstate->ss_confd, "r+"); + if (!pstate->ss_constream) + { + close(pstate->ss_confd); + free(pstate); + return NULL; + } +#endif + + /* Initialize the output stream */ + + pstate->ss_outfd = OUTFD(pstate); + pstate->ss_outstream = OUTSTREAM(pstate); + } + return pstate; +} + +/**************************************************************************** + * Name: nsh_openifnotopen + ****************************************************************************/ + +static int nsh_openifnotopen(struct serial_s *pstate) +{ + /* The stream is open in a lazy fashion. This is done because the file + * descriptor may be opened on a different task than the stream. + */ + + if (!pstate->ss_outstream) + { + pstate->ss_outstream = fdopen(pstate->ss_outfd, "w"); + if (!pstate->ss_outstream) + { + return ERROR; + } + } + return 0; +} + +/**************************************************************************** + * Name: nsh_closeifnotclosed + * + * Description: + * Close the output stream if it is not the standard output stream. + * + ****************************************************************************/ + +static void nsh_closeifnotclosed(struct serial_s *pstate) +{ + if (pstate->ss_outstream == OUTSTREAM(pstate)) + { + fflush(OUTSTREAM(pstate)); + pstate->ss_outfd = OUTFD(pstate); + } + else + { + if (pstate->ss_outstream) + { + fflush(pstate->ss_outstream); + fclose(pstate->ss_outstream); + } + else if (pstate->ss_outfd >= 0 && pstate->ss_outfd != OUTFD(pstate)) + { + close(pstate->ss_outfd); + } + + pstate->ss_outfd = -1; + pstate->ss_outstream = NULL; + } +} + +/**************************************************************************** + * Name: nsh_consolewrite + * + * Description: + * write a buffer to the remote shell window. + * + * Currently only used by cat. + * + ****************************************************************************/ + +static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes) +{ + FAR struct serial_s *pstate = (FAR struct serial_s *)vtbl; + ssize_t ret; + + /* The stream is open in a lazy fashion. This is done because the file + * descriptor may be opened on a different task than the stream. The + * actual open will then occur with the first output from the new task. + */ + + if (nsh_openifnotopen(pstate) != 0) + { + return (ssize_t)ERROR; + } + + /* Write the data to the output stream */ + + ret = fwrite(buffer, 1, nbytes, pstate->ss_outstream); + if (ret < 0) + { + dbg("[%d] Failed to send buffer: %d\n", pstate->ss_outfd, errno); + } + return ret; +} + +/**************************************************************************** + * Name: nsh_consoleoutput + * + * Description: + * Print a string to the currently selected stream. + * + ****************************************************************************/ + +static int nsh_consoleoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...) +{ + FAR struct serial_s *pstate = (FAR struct serial_s *)vtbl; + va_list ap; + int ret; + + /* The stream is open in a lazy fashion. This is done because the file + * descriptor may be opened on a different task than the stream. The + * actual open will then occur with the first output from the new task. + */ + + if (nsh_openifnotopen(pstate) != 0) + { + return ERROR; + } + + va_start(ap, fmt); + ret = vfprintf(pstate->ss_outstream, fmt, ap); + va_end(ap); + + return ret; +} + +/**************************************************************************** + * Name: nsh_consolelinebuffer + * + * Description: + * Return a reference to the current line buffer + * + ****************************************************************************/ + +static FAR char *nsh_consolelinebuffer(FAR struct nsh_vtbl_s *vtbl) +{ + FAR struct serial_s *pstate = (FAR struct serial_s *)vtbl; + return pstate->ss_line; +} + +/**************************************************************************** + * Name: nsh_consoleclone + * + * Description: + * Make an independent copy of the vtbl + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static FAR struct nsh_vtbl_s *nsh_consoleclone(FAR struct nsh_vtbl_s *vtbl) +{ + FAR struct serial_s *pclone = nsh_allocstruct(); + return &pclone->ss_vtbl; +} +#endif + +/**************************************************************************** + * Name: nsh_consolerelease + * + * Description: + * Release the cloned instance + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl) +{ + FAR struct serial_s *pstate = (FAR struct serial_s *)vtbl; + + /* Close the output stream */ + + nsh_closeifnotclosed(pstate); + + /* Close the console stream */ + +#ifdef CONFIG_NSH_CONDEV + (void)fclose(pstate->ss_constream); +#endif + + /* Then release the vtable container */ + + free(pstate); +} +#endif + +/**************************************************************************** + * Name: nsh_consoleredirect + * + * Description: + * Set up for redirected output. This function is called from nsh_parse() + * in two different contexts: + * + * 1) Redirected background commands of the form: command > xyz.text & + * + * In this case: + * - vtbl: A newly allocated and initialized instance created by + * nsh_consoleclone, + * - fd:- The file descriptor of the redirected output + * - save: NULL + * + * nsh_consolerelease() will perform the clean-up when the clone is + * destroyed. + * + * 2) Redirected foreground commands of the form: command > xyz.txt + * + * In this case: + * - vtbl: The current state structure, + * - fd: The file descriptor of the redirected output + * - save: Where to save the re-directed registers. + * + * nsh_consoleundirect() will perform the clean-up after the redirected + * command completes. + * + ****************************************************************************/ + +static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save) +{ + FAR struct serial_s *pstate = (FAR struct serial_s *)vtbl; + FAR struct serialsave_s *ssave = (FAR struct serialsave_s *)save; + + /* Case 1: Redirected foreground commands */ + + if (ssave) + { + /* pstate->ss_outstream and ss_outfd refer refer to the + * currently opened output stream. If the output stream is open, flush + * any pending output. + */ + + if (pstate->ss_outstream) + { + fflush(pstate->ss_outstream); + } + + /* Save the current fd and stream values. These will be restored + * when nsh_consoleundirect() is called. + */ + + ssave->ss_outfd = pstate->ss_outfd; + ssave->ss_outstream = pstate->ss_outstream; + } + else + { + /* nsh_consoleclone() set pstate->ss_outfd and ss_outstream to refer + * to standard out. We just want to leave these alone and overwrite + * them with the fd for the re-directed stream. + */ + } + + /* In either case, set the fd of the new, re-directed output and nullify + * the output stream (it will be fdopen'ed if it is used). + */ + + pstate->ss_outfd = fd; + pstate->ss_outstream = NULL; +} + +/**************************************************************************** + * Name: nsh_consoleundirect + * + * Description: + * Set up for redirected output + * + ****************************************************************************/ + +static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save) +{ + FAR struct serial_s *pstate = (FAR struct serial_s *)vtbl; + FAR struct serialsave_s *ssave = (FAR struct serialsave_s *)save; + + nsh_closeifnotclosed(pstate); + pstate->ss_outfd = ssave->ss_outfd; + pstate->ss_outstream = ssave->ss_outstream; +} + +/**************************************************************************** + * Name: nsh_consoleexit + * + * Description: + * Exit the shell task + * + ****************************************************************************/ + +static void nsh_consoleexit(FAR struct nsh_vtbl_s *vtbl) +{ + exit(0); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_consolemain + ****************************************************************************/ + +int nsh_consolemain(int argc, char *argv[]) +{ + FAR struct serial_s *pstate = nsh_allocstruct(); + DEBUGASSERT(pstate); + + /* Present a greeting */ + + fputs(g_nshgreeting, pstate->ss_outstream); + fflush(pstate->ss_outstream); + + /* Execute the startup script */ + +#ifdef CONFIG_NSH_ROMFSETC + (void)nsh_script(&pstate->ss_vtbl, "init", NSH_INITPATH); +#endif + + /* Then enter the command line parsing loop */ + + for (;;) + { + /* Display the prompt string */ + + fputs(g_nshprompt, pstate->ss_outstream); + fflush(pstate->ss_outstream); + + /* Get the next line of input */ + + if (fgets(pstate->ss_line, CONFIG_NSH_LINELEN, INSTREAM(pstate))) + { + /* Parse process the command */ + + (void)nsh_parse(&pstate->ss_vtbl, pstate->ss_line); + fflush(pstate->ss_outstream); + } + } + return OK; +} diff --git a/apps/nshlib/nsh_telnetd.c b/apps/nshlib/nsh_telnetd.c new file mode 100644 index 000000000..0670c9a05 --- /dev/null +++ b/apps/nshlib/nsh_telnetd.c @@ -0,0 +1,853 @@ +/**************************************************************************** + * apps/nshlib/nsh_telnetd.c + * + * Copyright (C) 2007-2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * This is a leverage of similar logic from uIP: + * + * Author: Adam Dunkels <adam@sics.se> + * Copyright (c) 2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <pthread.h> +#include <semaphore.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <net/if.h> +#include <apps/netutils/uiplib.h> +#if defined(CONFIG_NSH_DHCPC) +# include <apps/netutils/resolv.h> +# include <apps/netutils/dhcpc.h> +#endif + +#include "nsh.h" + +#ifdef CONFIG_NSH_TELNET + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#define ISO_nl 0x0a +#define ISO_cr 0x0d + +#define STATE_NORMAL 0 +#define STATE_IAC 1 +#define STATE_WILL 2 +#define STATE_WONT 3 +#define STATE_DO 4 +#define STATE_DONT 5 +#define STATE_CLOSE 6 + +#define TELNET_IAC 255 +#define TELNET_WILL 251 +#define TELNET_WONT 252 +#define TELNET_DO 253 +#define TELNET_DONT 254 + +#ifdef CONFIG_NSH_TELNETD_DUMPBUFFER +# define nsh_telnetdump(vtbl,msg,buf,nb) nsh_dumpbuffer(vtbl,msg,buf,nb) +#else +# define nsh_telnetdump(vtbl,msg,buf,nb) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct telnetio_s +{ + sem_t tio_sem; + int tio_sockfd; + uint8_t tio_bufndx; + uint8_t tio_state; + char tio_inbuffer[CONFIG_NSH_IOBUFFER_SIZE]; +}; + +struct redirect_s +{ + int rd_fd; /* Re-direct file descriptor */ + FILE *rd_stream; /* Re-direct stream */ +}; + +struct telnetsave_s +{ + bool ts_redirected; + union + { + struct telnetio_s *tn; + struct redirect_s rd; + } u; +}; + +struct telnetd_s +{ + struct nsh_vtbl_s tn_vtbl; + uint16_t tn_sndlen; + bool tn_redirected; + union + { + struct telnetio_s *tn; + struct redirect_s rd; + } u; + char tn_outbuffer[CONFIG_NSH_IOBUFFER_SIZE]; + char tn_cmd[CONFIG_NSH_LINELEN]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static void tio_semtake(struct telnetio_s *tio); +static FAR struct nsh_vtbl_s *nsh_telnetclone(FAR struct nsh_vtbl_s *vtbl); +#endif +static void nsh_telnetrelease(FAR struct nsh_vtbl_s *vtbl); +static ssize_t nsh_telnetwrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes); +static int nsh_telnetoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); +static int nsh_redirectoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); +static FAR char *nsh_telnetlinebuffer(FAR struct nsh_vtbl_s *vtbl); +static void nsh_telnetredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); +static void nsh_telnetundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); +static void nsh_telnetexit(FAR struct nsh_vtbl_s *vtbl); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tio_semtake + ****************************************************************************/ + +static void tio_semtake(struct telnetio_s *tio) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&tio->tio_sem) != 0) + { + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: tio_semgive + ****************************************************************************/ + +#define tio_semgive(tio) ASSERT(sem_post(&tio->tio_sem) == 0) + +/**************************************************************************** + * Name: nsh_allocstruct + ****************************************************************************/ + +static FAR struct telnetd_s *nsh_allocstruct(void) +{ + struct telnetd_s *pstate = (struct telnetd_s *)zalloc(sizeof(struct telnetd_s)); + if (pstate) + { +#ifndef CONFIG_NSH_DISABLEBG + pstate->tn_vtbl.clone = nsh_telnetclone; + pstate->tn_vtbl.release = nsh_telnetrelease; +#endif + pstate->tn_vtbl.write = nsh_telnetwrite; + pstate->tn_vtbl.output = nsh_telnetoutput; + pstate->tn_vtbl.linebuffer = nsh_telnetlinebuffer; + pstate->tn_vtbl.redirect = nsh_telnetredirect; + pstate->tn_vtbl.undirect = nsh_telnetundirect; + pstate->tn_vtbl.exit = nsh_telnetexit; + } + return pstate; +} + +/**************************************************************************** + * Name: nsh_openifnotopen + ****************************************************************************/ + +static int nsh_openifnotopen(struct telnetd_s *pstate) +{ + struct redirect_s *rd = &pstate->u.rd; + + /* The stream is open in a lazy fashion. This is done because the file + * descriptor may be opened on a different task than the stream. + */ + + if (!rd->rd_stream) + { + rd->rd_stream = fdopen(rd->rd_fd, "w"); + if (!rd->rd_stream) + { + return ERROR; + } + } + return 0; +} + +/**************************************************************************** + * Name: nsh_closeifnotclosed + ****************************************************************************/ + +static void nsh_closeifnotclosed(struct telnetd_s *pstate) +{ + struct redirect_s *rd = &pstate->u.rd; + + if (rd->rd_stream == stdout) + { + fflush(stdout); + rd->rd_fd = 1; + } + else + { + if (rd->rd_stream) + { + fflush(rd->rd_stream); + fclose(rd->rd_stream); + } + else if (rd->rd_fd >= 0 && rd->rd_fd != 1) + { + close(rd->rd_fd); + } + + rd->rd_fd = -1; + rd->rd_stream = NULL; + } +} + +/**************************************************************************** + * Name: nsh_putchar + * + * Description: + * Add another parsed character to the TELNET command string + * + * Assumption: + * Caller holds TIO semaphore + * + ****************************************************************************/ + +static void nsh_putchar(struct telnetd_s *pstate, uint8_t ch) +{ + struct telnetio_s *tio = pstate->u.tn; + + /* Ignore carriage returns */ + + if (ch == ISO_cr) + { + return; + } + + /* Add all other characters to the cmd buffer */ + + pstate->tn_cmd[tio->tio_bufndx] = ch; + + /* If a newline was added or if the buffer is full, then process it now */ + + if (ch == ISO_nl || tio->tio_bufndx == (CONFIG_NSH_LINELEN - 1)) + { + pstate->tn_cmd[tio->tio_bufndx] = '\0'; + nsh_telnetdump(&pstate->tn_vtbl, "TELNET CMD", + (uint8_t*)pstate->tn_cmd, strlen(pstate->tn_cmd)); + nsh_parse(&pstate->tn_vtbl, pstate->tn_cmd); + tio->tio_bufndx = 0; + } + else + { + tio->tio_bufndx++; + vdbg("Add '%c', bufndx=%d\n", ch, tio->tio_bufndx); + } +} + +/**************************************************************************** + * Name: nsh_sendopt + * + * Description: + * + ****************************************************************************/ + +static void nsh_sendopt(struct telnetd_s *pstate, uint8_t option, uint8_t value) +{ + struct telnetio_s *tio = pstate->u.tn; + uint8_t optbuf[4]; + optbuf[0] = TELNET_IAC; + optbuf[1] = option; + optbuf[2] = value; + optbuf[3] = 0; + + nsh_telnetdump(&pstate->tn_vtbl, "Send optbuf", optbuf, 4); + tio_semtake(tio); /* Only one call to send at a time */ + if (send(tio->tio_sockfd, optbuf, 4, 0) < 0) + { + dbg("[%d] Failed to send TELNET_IAC: %d\n", tio->tio_sockfd, errno); + } + tio_semgive(tio); +} + +/**************************************************************************** + * Name: nsh_flush + * + * Description: + * Dump the buffered output info. + * + ****************************************************************************/ + +static void nsh_flush(FAR struct telnetd_s *pstate) +{ + struct telnetio_s *tio = pstate->u.tn; + + if (pstate->tn_sndlen > 0) + { + nsh_telnetdump(&pstate->tn_vtbl, "Shell output", + (uint8_t*)pstate->tn_outbuffer, pstate->tn_sndlen); + tio_semtake(tio); /* Only one call to send at a time */ + if (send(tio->tio_sockfd, pstate->tn_outbuffer, pstate->tn_sndlen, 0) < 0) + { + dbg("[%d] Failed to send response: %d\n", tio->tio_sockfd, errno); + } + tio_semgive(tio); + } + pstate->tn_sndlen = 0; +} + +/**************************************************************************** + * Name: nsh_receive + * + * Description: + * Process a received TELENET buffer + * + ****************************************************************************/ + +static int nsh_receive(struct telnetd_s *pstate, size_t len) +{ + struct telnetio_s *tio = pstate->u.tn; + char *ptr = tio->tio_inbuffer; + uint8_t ch; + + while (len > 0) + { + ch = *ptr++; + len--; + + vdbg("ch=%02x state=%d\n", ch, tio->tio_state); + switch (tio->tio_state) + { + case STATE_IAC: + if (ch == TELNET_IAC) + { + nsh_putchar(pstate, ch); + tio->tio_state = STATE_NORMAL; + } + else + { + switch (ch) + { + case TELNET_WILL: + tio->tio_state = STATE_WILL; + break; + + case TELNET_WONT: + tio->tio_state = STATE_WONT; + break; + + case TELNET_DO: + tio->tio_state = STATE_DO; + break; + + case TELNET_DONT: + tio->tio_state = STATE_DONT; + break; + + default: + tio->tio_state = STATE_NORMAL; + break; + } + } + break; + + case STATE_WILL: + /* Reply with a DONT */ + + nsh_sendopt(pstate, TELNET_DONT, ch); + tio->tio_state = STATE_NORMAL; + break; + + case STATE_WONT: + /* Reply with a DONT */ + + nsh_sendopt(pstate, TELNET_DONT, ch); + tio->tio_state = STATE_NORMAL; + break; + + case STATE_DO: + /* Reply with a WONT */ + + nsh_sendopt(pstate, TELNET_WONT, ch); + tio->tio_state = STATE_NORMAL; + break; + + case STATE_DONT: + /* Reply with a WONT */ + + nsh_sendopt(pstate, TELNET_WONT, ch); + tio->tio_state = STATE_NORMAL; + break; + + case STATE_NORMAL: + if (ch == TELNET_IAC) + { + tio->tio_state = STATE_IAC; + } + else + { + nsh_putchar(pstate, ch); + } + break; + } + } + return OK; +} + +/**************************************************************************** + * Name: nsh_connection + * + * Description: + * Each time a new connection to port 23 is made, a new thread is created + * that begins at this entry point. There should be exactly one argument + * and it should be the socket descriptor (+1). + * + ****************************************************************************/ + +static void *nsh_connection(void *arg) +{ + struct telnetd_s *pstate = nsh_allocstruct(); + struct telnetio_s *tio = (struct telnetio_s *)zalloc(sizeof(struct telnetio_s)); + struct nsh_vtbl_s *vtbl = &pstate->tn_vtbl; + int sockfd = (int)arg; + int ret = ERROR; + + dbg("[%d] Started\n", sockfd); + + /* Verify that the state structure was successfully allocated */ + + if (pstate && tio) + { + /* Initialize the thread state structure */ + + sem_init(&tio->tio_sem, 0, 1); + tio->tio_sockfd = sockfd; + tio->tio_state = STATE_NORMAL; + pstate->u.tn = tio; + + /* Output a greeting */ + + nsh_output(vtbl, g_nshgreeting); + + /* Execute the startup script */ + +#if defined(CONFIG_NSH_ROMFSETC) && !defined(CONFIG_NSH_CONSOLE) + (void)nsh_script(vtbl, "init", NSH_INITPATH); +#endif + + /* Loop processing each TELNET command */ + + do + { + /* Display the prompt string */ + + nsh_output(vtbl, g_nshprompt); + nsh_flush(pstate); + + /* Read a buffer of data from the TELNET client */ + + ret = recv(tio->tio_sockfd, tio->tio_inbuffer, + CONFIG_NSH_IOBUFFER_SIZE, 0); + if (ret > 0) + { + + /* Process the received TELNET data */ + + nsh_telnetdump(vtbl, "Received buffer", + (uint8_t*)tio->tio_inbuffer, ret); + ret = nsh_receive(pstate, ret); + } + } + while (ret >= 0 && tio->tio_state != STATE_CLOSE); + dbg("[%d] ret=%d tn.tio_state=%d\n", sockfd, ret, tio->tio_state); + + /* End of command processing -- Clean up and exit */ + } + + /* Exit the task */ + + if (pstate) + { + free(pstate); + } + + if (tio) + { + sem_destroy(&tio->tio_sem); + free(tio); + } + + dbg("[%d] Exitting\n", sockfd); + close(sockfd); + return NULL; +} + +/**************************************************************************** + * Name: nsh_telnetwrite + * + * Description: + * write a buffer to the remote shell window. + * + * Currently only used by cat. + * + ****************************************************************************/ + +static ssize_t nsh_telnetwrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes) +{ + struct telnetd_s *pstate = (struct telnetd_s *)vtbl; + struct telnetio_s *tio = pstate->u.tn; + ssize_t ret = nbytes; + + /* Flush anything already in the output buffer */ + + nsh_flush(pstate); + + /* Then write the user buffer */ + + nsh_telnetdump(&pstate->tn_vtbl, "Buffer output",(uint8_t*)buffer, nbytes); + + tio_semtake(tio); /* Only one call to send at a time */ + ret = send(tio->tio_sockfd, buffer, nbytes, 0); + if (ret < 0) + { + dbg("[%d] Failed to send buffer: %d\n", tio->tio_sockfd, errno); + } + + tio_semgive(tio); + return ret; +} + +/**************************************************************************** + * Name: nsh_telnetoutput + * + * Description: + * Print a string to the remote shell window. + * + * This function is implemented by the shell GUI / telnet server and + * can be called by the shell back-end to output a string in the + * shell window. The string is automatically appended with a linebreak. + * + ****************************************************************************/ + +static int nsh_telnetoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...) +{ + struct telnetd_s *pstate = (struct telnetd_s *)vtbl; + int nbytes = pstate->tn_sndlen; + int len; + va_list ap; + + /* Put the new info into the buffer. Here we are counting on the fact that + * no output strings will exceed CONFIG_NSH_LINELEN! + */ + + va_start(ap, fmt); + vsnprintf(&pstate->tn_outbuffer[nbytes], + (CONFIG_NSH_IOBUFFER_SIZE - 1) - nbytes, fmt, ap); + va_end(ap); + + /* Get the size of the new string just added and the total size of + * buffered data + */ + + len = strlen(&pstate->tn_outbuffer[nbytes]); + nbytes += len; + + /* Expand any terminating \n to \r\n */ + + if (nbytes < (CONFIG_NSH_IOBUFFER_SIZE - 2) && + pstate->tn_outbuffer[nbytes-1] == '\n') + { + pstate->tn_outbuffer[nbytes-1] = ISO_cr; + pstate->tn_outbuffer[nbytes] = ISO_nl; + pstate->tn_outbuffer[nbytes+1] = '\0'; + nbytes++; + } + pstate->tn_sndlen = nbytes; + + /* Flush to the network if the buffer does not have room for one more + * maximum length string. + */ + + if (nbytes > CONFIG_NSH_IOBUFFER_SIZE - CONFIG_NSH_LINELEN) + { + nsh_flush(pstate); + } + + return len; +} + +/**************************************************************************** + * Name: nsh_redirectoutput + * + * Description: + * Print a string to the currently selected stream. + * + ****************************************************************************/ + +static int nsh_redirectoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...) +{ + FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; + va_list ap; + int ret; + + /* The stream is open in a lazy fashion. This is done because the file + * descriptor may be opened on a different task than the stream. The + * actual open will then occur with the first output from the new task. + */ + + if (nsh_openifnotopen(pstate) != 0) + { + return ERROR; + } + + va_start(ap, fmt); + ret = vfprintf(pstate->u.rd.rd_stream, fmt, ap); + va_end(ap); + + return ret; +} + +/**************************************************************************** + * Name: nsh_telnetlinebuffer + * + * Description: + * Return a reference to the current line buffer + * +* ****************************************************************************/ + +static FAR char *nsh_telnetlinebuffer(FAR struct nsh_vtbl_s *vtbl) +{ + struct telnetd_s *pstate = (struct telnetd_s *)vtbl; + return pstate->tn_cmd; +} + +/**************************************************************************** + * Name: nsh_telnetclone + * + * Description: + * Make an independent copy of the vtbl + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static FAR struct nsh_vtbl_s *nsh_telnetclone(FAR struct nsh_vtbl_s *vtbl) +{ + FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; + FAR struct telnetd_s *pclone = nsh_allocstruct(); + FAR struct nsh_vtbl_s *ret = NULL; + + if (pclone) + { + if (pstate->tn_redirected) + { + pclone->tn_redirected = true; + pclone->tn_vtbl.output = nsh_redirectoutput; + pclone->u.rd.rd_fd = pstate->u.rd.rd_fd; + pclone->u.rd.rd_stream = NULL; + } + else + { + pclone->u.tn = pstate->u.tn; + } + ret = &pclone->tn_vtbl; + } + return ret; +} +#endif + +/**************************************************************************** + * Name: nsh_telnetrelease + * + * Description: + * Release the cloned instance + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLEBG +static void nsh_telnetrelease(FAR struct nsh_vtbl_s *vtbl) +{ + FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; + + if (pstate->tn_redirected) + { + nsh_closeifnotclosed(pstate); + } + else + { + nsh_flush(pstate); + } + free(pstate); +} +#endif + +/**************************************************************************** + * Name: nsh_telnetredirect + * + * Description: + * Set up for redirected output. This function is called from nsh_parse() + * in two different contexts: + * + * 1) Redirected background commands of the form: command > xyz.text & + * + * In this case: + * - vtbl: A newly allocated and initialized instance created by + * nsh_telnetclone, + * - fd:- The file descriptor of the redirected output + * - save: NULL + * + * nsh_telnetrelease() will perform the clean-up when the clone is + * destroyed. + * + * 2) Redirected foreground commands of the form: command > xyz.txt + * + * In this case: + * - vtbl: The current state structure, + * - fd: The file descriptor of the redirected output + * - save: Where to save the re-directed registers. + * + * nsh_telnetundirect() will perform the clean-up after the redirected + * command completes. + * + ****************************************************************************/ + +static void nsh_telnetredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save) +{ + FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; + FAR struct telnetsave_s *ssave = (FAR struct telnetsave_s *)save; + + if (pstate->tn_redirected) + { + (void)nsh_openifnotopen(pstate); + fflush(pstate->u.rd.rd_stream); + if (!ssave) + { + fclose(pstate->u.rd.rd_stream); + } + } + + if (ssave) + { + ssave->ts_redirected = pstate->tn_redirected; + memcpy(&ssave->u.rd, &pstate->u.rd, sizeof(struct redirect_s)); + } + + pstate->tn_redirected = true; + pstate->u.rd.rd_fd = fd; + pstate->u.rd.rd_stream = NULL; +} + +/**************************************************************************** + * Name: nsh_telnetundirect + * + * Description: + * Set up for redirected output + * + ****************************************************************************/ + +static void nsh_telnetundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save) +{ + FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; + FAR struct telnetsave_s *ssave = (FAR struct telnetsave_s *)save; + + if (pstate->tn_redirected) + { + nsh_closeifnotclosed(pstate); + } + + pstate->tn_redirected = ssave->ts_redirected; + memcpy(&pstate->u.rd, &ssave->u.rd, sizeof(struct redirect_s)); +} + +/**************************************************************************** + * Name: nsh_telnetexit + * + * Description: + * Quit the shell instance + * + ****************************************************************************/ + +static void nsh_telnetexit(FAR struct nsh_vtbl_s *vtbl) +{ + struct telnetd_s *pstate = (struct telnetd_s *)vtbl; + pstate->u.tn->tio_state = STATE_CLOSE; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_telnetmain + * + * Description: + * This is the main processing thread for telnetd. It never returns + * unless an error occurs + * + ****************************************************************************/ + +int nsh_telnetmain(int argc, char *argv[]) +{ + /* Execute nsh_connection() on each connection to port 23 */ + + uip_server(HTONS(23), nsh_connection, CONFIG_NSH_STACKSIZE); + return OK; +} + +#endif /* CONFIG_NSH_TELNET */ diff --git a/apps/nshlib/nsh_test.c b/apps/nshlib/nsh_test.c new file mode 100644 index 000000000..dd63d6796 --- /dev/null +++ b/apps/nshlib/nsh_test.c @@ -0,0 +1,437 @@ +/**************************************************************************** + * apps/nshlib/nsh_test.c + * + * Copyright (C) 2008, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/* Test syntax: + * + * expression = simple-expression | !expression | + * expression -o expression | expression -a expression + * + * simple-expression = unary-expression | binary-expression + * + * unary-expression = string-unary | file-unary + * + * string-unary = -n string | -z string + * + * file-unary = -b file | -c file | -d file | -e file | -f file | + * -r file | -s file | -w file + * + * binary-expression = string-binary | numeric-binary + * + * string-binary = string = string | string == string | string != string + * + * numeric-binary = integer -eq integer | integer -ge integer | + * integer -gt integer | integer -le integer | + * integer -lt integer | integer -ne integer + * + * Note that the smallest expression consists of two strings. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> + +#include "nsh.h" + +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_TEST) + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#define TEST_TRUE OK +#define TEST_FALSE ERROR +#define TEST_ERROR 1 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: binaryexpression + ****************************************************************************/ + +static inline int binaryexpression(FAR struct nsh_vtbl_s *vtbl, char **argv) +{ + char *endptr; + long integer1; + long integer2; + + /* STRING2 = STRING2 */ + + if (strcmp(argv[1], "=") == 0 || strcmp(argv[1], "==") == 0) + { + /* Return true if the strings are identical */ + + return strcmp(argv[0], argv[2]) == 0 ? TEST_TRUE : TEST_FALSE; + } + + /* STRING1 != STRING2 */ + + if (strcmp(argv[1], "!=") == 0) + { + /* Return true if the strings are different */ + + return strcmp(argv[0], argv[2]) != 0 ? TEST_TRUE : TEST_FALSE; + } + + /* The remaining operators assuming that the two values are integers */ + + integer1 = strtol(argv[0], &endptr, 0); + if (argv[0][0] == '\0' || *endptr != '\0') + { + return TEST_ERROR; + } + + integer2 = strtol(argv[2], &endptr, 0); + if (argv[2][0] == '\0' || *endptr != '\0') + { + return TEST_ERROR; + } + + /* INTEGER1 -eq INTEGER2 */ + + if (strcmp(argv[1], "-eq") == 0) + { + /* Return true if the strings are different */ + + return integer1 == integer2 ? TEST_TRUE : TEST_FALSE; + } + + /* INTEGER1 -ge INTEGER2 */ + + if (strcmp(argv[1], "-ge") == 0) + { + /* Return true if the strings are different */ + + return integer1 >= integer2 ? TEST_TRUE : TEST_FALSE; + } + + /* INTEGER1 -gt INTEGER2 */ + + if (strcmp(argv[1], "-gt") == 0) + { + /* Return true if the strings are different */ + + return integer1 > integer2 ? TEST_TRUE : TEST_FALSE; + } + + /* INTEGER1 -le INTEGER2 */ + + if (strcmp(argv[1], "-le") == 0) + { + /* Return true if the strings are different */ + + return integer1 <= integer2 ? TEST_TRUE : TEST_FALSE; + } + + /* INTEGER1 -lt INTEGER2 */ + + if (strcmp(argv[1], "-lt") == 0) + { + /* Return true if the strings are different */ + + return integer1 < integer2 ? TEST_TRUE : TEST_FALSE; + } + + /* INTEGER1 -ne INTEGER2 */ + + if (strcmp(argv[1], "-ne") == 0) + { + /* Return true if the strings are different */ + + return integer1 != integer2 ? TEST_TRUE : TEST_FALSE; + } + + return TEST_ERROR; +} + +/**************************************************************************** + * Name: unaryexpression + ****************************************************************************/ + +static inline int unaryexpression(FAR struct nsh_vtbl_s *vtbl, char **argv) +{ + struct stat buf; + char *fullpath; + int ret; + + /* -n STRING */ + + if (strcmp(argv[0], "-n") == 0) + { + /* Return true if the length of the string is non-zero */ + + return strlen(argv[1]) != 0 ? TEST_TRUE : TEST_FALSE; + } + + /* -z STRING */ + + if (strcmp(argv[0], "-z") == 0) + { + /* Return true if the length of the string is zero */ + + return strlen(argv[1]) == 0 ? TEST_TRUE : TEST_FALSE; + } + + /* All of the remaining assume that the following argument is the + * path to a file. + */ + + fullpath = nsh_getfullpath(vtbl, argv[1]); + if (!fullpath) + { + return TEST_FALSE; + } + + ret = stat(fullpath, &buf); + nsh_freefullpath(fullpath); + + if (ret != 0) + { + /* The file does not exist (or another error occurred) -- return FALSE */ + + return TEST_FALSE; + } + + /* -b FILE */ + + if (strcmp(argv[0], "-b") == 0) + { + /* Return true if the path is a block device */ + + return S_ISBLK(buf.st_mode) ? TEST_TRUE : TEST_FALSE; + } + + /* -c FILE */ + + if (strcmp(argv[0], "-c") == 0) + { + /* Return true if the path is a character device */ + + return S_ISCHR(buf.st_mode) ? TEST_TRUE : TEST_FALSE; + } + + /* -d FILE */ + + if (strcmp(argv[0], "-d") == 0) + { + /* Return true if the path is a directory */ + + return S_ISDIR(buf.st_mode) ? TEST_TRUE : TEST_FALSE; + } + + /* -e FILE */ + + if (strcmp(argv[0], "-e") == 0) + { + /* Return true if the file exists */ + + return TEST_TRUE; + } + + /* -f FILE */ + + if (strcmp(argv[0], "-f") == 0) + { + /* Return true if the path refers to a regular file */ + + return S_ISREG(buf.st_mode) ? TEST_TRUE : TEST_FALSE; + } + + /* -r FILE */ + + if (strcmp(argv[0], "-r") == 0) + { + /* Return true if the file is readable */ + + return (buf.st_mode & (S_IRUSR|S_IRGRP|S_IROTH)) != 0 ? TEST_TRUE : TEST_FALSE; + } + + /* -s FILE */ + + if (strcmp(argv[0], "-s") == 0) + { + /* Return true if the size of the file is greater than zero */ + + return buf.st_size > 0 ? TEST_TRUE : TEST_FALSE; + } + + /* -w FILE */ + + if (strcmp(argv[0], "-w") == 0) + { + /* Return true if the file is write-able */ + + return (buf.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0 ? TEST_TRUE : TEST_FALSE; + } + + /* Unrecognized operator */ + + return TEST_ERROR; +} + +/**************************************************************************** + * Name: expression + ****************************************************************************/ + +static int expression(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + int value; + int i = 0; + + /* Check for unary operations on expressions */ + + if (strcmp(argv[0], "!") == 0) + { + if (argc < 2) + { + goto errout_syntax; + } + return expression(vtbl, argc-1, &argv[1]) == TEST_TRUE ? TEST_FALSE : TEST_TRUE; + } + + /* Check for unary operations on simple, typed arguments */ + + else if (argv[0][0] == '-') + { + if (argc < 2) + { + goto errout_syntax; + } + i += 2; + value = unaryexpression(vtbl, argv); + } + + /* Check for binary operations on simple, typed arguments */ + + else + { + if (argc < 3) + { + goto errout_syntax; + } + i += 3; + value = binaryexpression(vtbl, argv); + } + + /* Test if there any failure */ + + if (value == TEST_ERROR) + { + goto errout_syntax; + } + + /* Is there anything after the simple expression? */ + + if (i < argc) + { + /* EXPRESSION -a EXPRESSION */ + + if (strcmp(argv[i], "-a") == 0) + { + if (value != TEST_TRUE) + { + return TEST_FALSE; + } + else + { + i++; + return expression(vtbl, argc-i, &argv[i]); + } + } + + /* EXPRESSION -o EXPRESSION */ + + else if (strcmp(argv[i], "-o") == 0) + { + if (value == TEST_TRUE) + { + return TEST_TRUE; + } + else + { + i++; + return expression(vtbl, argc-i, &argv[i]); + } + } + else + { + goto errout_syntax; + } + } + return value; + +errout_syntax: + nsh_output(vtbl, g_nshsyntax, "test"); + return TEST_FALSE; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_test + ****************************************************************************/ + +int cmd_test(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + return expression(vtbl, argc-1, &argv[1]); +} + +/**************************************************************************** + * Name: cmd_lbracket + ****************************************************************************/ + +int cmd_lbracket(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + if (strcmp(argv[argc-1], "]") != 0) + { + nsh_output(vtbl, g_nshsyntax, argv[0]); + return ERROR; + } + else + { + return expression(vtbl, argc-2, &argv[1]); + } +} + +#endif /* !CONFIG_NSH_DISABLESCRIPT && !CONFIG_NSH_DISABLE_TEST */ diff --git a/apps/nshlib/nsh_timcmds.c b/apps/nshlib/nsh_timcmds.c new file mode 100644 index 000000000..04a79e77d --- /dev/null +++ b/apps/nshlib/nsh_timcmds.c @@ -0,0 +1,330 @@ +/**************************************************************************** + * apps/nshlib/dbg_timcmds.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +#include "nsh.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#define MAX_TIME_STRING 80 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#if defined (CONFIG_RTC) && !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_NSH_DISABLE_DATE) +static FAR const char * const g_datemontab[] = +{ + "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec" +}; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: date_month + ****************************************************************************/ + +#if defined (CONFIG_RTC) && !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_NSH_DISABLE_DATE) +static inline int date_month(FAR const char *abbrev) +{ + int i; + + for (i = 0; i < 12; i++) + { + if (strncasecmp(g_datemontab[i], abbrev, 3) == 0) + { + return i; + } + } + return ERROR; +} +#endif + +/**************************************************************************** + * Name: date_gettime + ****************************************************************************/ + +#if defined (CONFIG_RTC) && !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_NSH_DISABLE_DATE) +static inline int date_showtime(FAR struct nsh_vtbl_s *vtbl, FAR const char *name) +{ + static const char format[] = "%b %d %H:%M:%S %Y"; + struct timespec ts; + struct tm tm; + char timbuf[MAX_TIME_STRING]; + int ret; + + /* Get the current time */ + + ret = clock_gettime(CLOCK_REALTIME, &ts); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, name, "clock_gettime", NSH_ERRNO); + return ERROR; + } + + /* Break the current time up into the format needed by strftime */ + + (void)gmtime_r((FAR const time_t*)&ts.tv_sec, &tm); + + /* Show the current time in the requested format */ + + (void)strftime(timbuf, MAX_TIME_STRING, format, &tm); + nsh_output(vtbl, "%s\n", timbuf); + return OK; +} +#endif + +/**************************************************************************** + * Name: date_settime + ****************************************************************************/ + +#if defined (CONFIG_RTC) && !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_NSH_DISABLE_DATE) +static inline int date_settime(FAR struct nsh_vtbl_s *vtbl, FAR const char *name, + FAR char *newtime) +{ + struct timespec ts; + struct tm tm; + FAR char *token; + FAR char *saveptr; + long result; + int ret; + + /* Only this date format is supported: MMM DD HH:MM:SS YYYY */ + /* Get the month abbreviation */ + + token = strtok_r(newtime, " \t",&saveptr); + if (token == NULL) + { + goto errout_bad_parm; + } + + tm.tm_mon = date_month(token); + if (tm.tm_mon < 0) + { + goto errout_bad_parm; + } + + /* Get the day of the month. NOTE: Accepts day-of-month up to 31 for all months */ + + token = strtok_r(NULL, " \t",&saveptr); + if (token == NULL) + { + goto errout_bad_parm; + } + + result = strtol(token, NULL, 10); + if (result < 1 || result > 31) + { + goto errout_bad_parm; + } + tm.tm_mday = (int)result; + + /* Get the hours */ + + token = strtok_r(NULL, " \t:", &saveptr); + if (token == NULL) + { + goto errout_bad_parm; + } + + result = strtol(token, NULL, 10); + if (result < 0 || result > 23) + { + goto errout_bad_parm; + } + tm.tm_hour = (int)result; + + /* Get the minutes */ + + token = strtok_r(NULL, " \t:", &saveptr); + if (token == NULL) + { + goto errout_bad_parm; + } + + result = strtol(token, NULL, 10); + if (result < 0 || result > 59) + { + goto errout_bad_parm; + } + tm.tm_min = (int)result; + + /* Get the seconds */ + + token = strtok_r(NULL, " \t:", &saveptr); + if (token == NULL) + { + goto errout_bad_parm; + } + + result = strtol(token, NULL, 10); + if (result < 0 || result > 61) + { + goto errout_bad_parm; + } + tm.tm_sec = (int)result; + + /* And finally the year */ + + token = strtok_r(NULL, " \t", &saveptr); + if (token == NULL) + { + goto errout_bad_parm; + } + + result = strtol(token, NULL, 10); + if (result < 1900 || result > 2100) + { + goto errout_bad_parm; + } + tm.tm_year = (int)result - 1900; + + /* Convert this to the right form, then set the timer */ + + ts.tv_sec = mktime(&tm); + ts.tv_nsec = 0; + + ret = clock_settime(CLOCK_REALTIME, &ts); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, name, "clock_settime", NSH_ERRNO); + return ERROR; + } + return OK; + +errout_bad_parm: + nsh_output(vtbl, g_fmtarginvalid, name); + return ERROR; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmd_date + ****************************************************************************/ + +#if defined (CONFIG_RTC) && !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_NSH_DISABLE_DATE) +int cmd_date(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + FAR char *newtime = NULL; + FAR const char *errfmt; + bool badarg = false; + int option; + int ret; + + /* Get the date options: date [-s time] [+FORMAT] */ + + while ((option = getopt(argc, argv, "s:")) != ERROR) + { + if (option == 's') + { + /* We will be setting the time */ + + newtime = optarg; + } + else /* option = '?' */ + { + /* We need to parse to the end anyway so that getopt stays healthy */ + + badarg = true; + } + } + + /* If a bad argument was encountered then exit with an error */ + + if (badarg) + { + errfmt = g_fmtarginvalid; + goto errout; + } + + /* optind < argc-1 means that there are additional, unexpected arguments on + * th command-line + */ + + if (optind < argc) + { + errfmt = g_fmttoomanyargs; + goto errout; + } + + /* Display or set the time */ + + if (newtime) + { + ret = date_settime(vtbl, argv[0], newtime); + } + else + { + ret = date_showtime(vtbl, argv[0]); + } + return ret; + +errout: + nsh_output(vtbl, errfmt, argv[0]); + return ERROR; +} +#endif diff --git a/apps/nshlib/rcS.template b/apps/nshlib/rcS.template new file mode 100644 index 000000000..996f37fb1 --- /dev/null +++ b/apps/nshlib/rcS.template @@ -0,0 +1,5 @@ +# Create a RAMDISK and mount it at XXXRDMOUNTPOUNTXXX + +mkrd -m XXXMKRDMINORXXX -s XXMKRDSECTORSIZEXXX XXMKRDBLOCKSXXX +mkfatfs /dev/ramXXXMKRDMINORXXX +mount -t vfat /dev/ramXXXMKRDMINORXXX XXXRDMOUNTPOUNTXXX |